xref: /openbsd/games/robots/move.c (revision cecf84d4)
1 /*	$OpenBSD: move.c,v 1.10 2014/11/03 22:14:54 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 				struct pollfd pfd[1];
94 
95 				pfd[0].fd = STDIN_FILENO;
96 				pfd[0].events = POLLIN;
97 				retval = poll(pfd, 1,
98 				    t.tv_sec * 1000 + t.tv_usec / 1000);
99 				if (retval > 0)
100 					c = getchar();
101 				else	/* Don't move if timed out or error */
102 					c = ' ';
103 			} else {
104 				c = getchar();
105 				/* Can't use digits in real time mode, or digit/ESC
106 				 * is an effective way to stop the game.
107 				 */
108 				if (isdigit(c)) {
109 					Count = (c - '0');
110 					while (isdigit(c = getchar()))
111 						Count = Count * 10 + (c - '0');
112 					if (c == ESC)
113 						goto over;
114 					Cnt_move = c;
115 					if (Count)
116 						leaveok(stdscr, TRUE);
117 				}
118 			}
119 		}
120 
121 		switch (c) {
122 		  case ' ':
123 		  case '.':
124 			if (do_move(0, 0))
125 				goto ret;
126 			break;
127 		  case 'y':
128 			if (do_move(-1, -1))
129 				goto ret;
130 			break;
131 		  case 'k':
132 			if (do_move(-1, 0))
133 				goto ret;
134 			break;
135 		  case 'u':
136 			if (do_move(-1, 1))
137 				goto ret;
138 			break;
139 		  case 'h':
140 			if (do_move(0, -1))
141 				goto ret;
142 			break;
143 		  case 'l':
144 			if (do_move(0, 1))
145 				goto ret;
146 			break;
147 		  case 'b':
148 			if (do_move(1, -1))
149 				goto ret;
150 			break;
151 		  case 'j':
152 			if (do_move(1, 0))
153 				goto ret;
154 			break;
155 		  case 'n':
156 			if (do_move(1, 1))
157 				goto ret;
158 			break;
159 		  case 'Y': case 'U': case 'H': case 'J':
160 		  case 'K': case 'L': case 'B': case 'N':
161 		  case '>':
162 			Running = TRUE;
163 			if (c == '>')
164 				Run_ch = ' ';
165 			else
166 				Run_ch = tolower(c);
167 			leaveok(stdscr, TRUE);
168 			break;
169 		  case 'q':
170 		  case 'Q':
171 			if (query("Really quit?"))
172 				quit(0);
173 			refresh();
174 			break;
175 		  case 'w':
176 		  case 'W':
177 			Waiting = TRUE;
178 			leaveok(stdscr, TRUE);
179 #ifndef NCURSES_VERSION
180 			flushok(stdscr, FALSE);
181 #endif
182 			goto ret;
183 		  case 't':
184 		  case 'T':
185 teleport:
186 			Running = FALSE;
187 			mvaddch(My_pos.y, My_pos.x, ' ');
188 			My_pos = *rnd_pos();
189 			mvaddch(My_pos.y, My_pos.x, PLAYER);
190 			leaveok(stdscr, FALSE);
191 			refresh();
192 			flushinp();
193 			goto ret;
194 		  case CTRL('L'):
195 			wrefresh(curscr);
196 			break;
197 		  case EOF:
198 			quit(0);
199 			break;
200 		  default:
201 			beep();
202 			reset_count();
203 			break;
204 		}
205 		if (Real_time) {
206 			(void)gettimeofday(&t, &tz);
207 			t.tv_sec = tod.tv_sec + tv.tv_sec - t.tv_sec;
208 			t.tv_usec = tod.tv_usec + tv.tv_usec - t.tv_usec;
209 			if (t.tv_usec < 0) {
210 				t.tv_sec--;
211 				t.tv_usec += 1000000;	/* Now it must be > 0 */
212 			}
213 			if (t.tv_sec < 0)
214 				goto ret;
215 		}
216 	}
217 ret:
218 	if (Count > 0)
219 		if (--Count == 0)
220 			leaveok(stdscr, FALSE);
221 }
222 
223 /*
224  * must_telep:
225  *	Must I teleport; i.e., is there anywhere I can move without
226  * being eaten?
227  */
228 bool
229 must_telep(void)
230 {
231 	int		x, y;
232 	static COORD	newpos;
233 
234 #ifdef	FANCY
235 	if (Stand_still && Num_robots > 1 && eaten(&My_pos))
236 		return TRUE;
237 #endif
238 
239 	for (y = -1; y <= 1; y++) {
240 		newpos.y = My_pos.y + y;
241 		if (newpos.y <= 0 || newpos.y >= Y_FIELDSIZE)
242 			continue;
243 		for (x = -1; x <= 1; x++) {
244 			newpos.x = My_pos.x + x;
245 			if (newpos.x <= 0 || newpos.x >= X_FIELDSIZE)
246 				continue;
247 			if (Field[newpos.y][newpos.x] > 0)
248 				continue;
249 			if (!eaten(&newpos))
250 				return FALSE;
251 		}
252 	}
253 	return TRUE;
254 }
255 
256 /*
257  * do_move:
258  *	Execute a move
259  */
260 bool
261 do_move(int dy, int dx)
262 {
263 	static COORD	newpos;
264 
265 	newpos.y = My_pos.y + dy;
266 	newpos.x = My_pos.x + dx;
267 	if (newpos.y <= 0 || newpos.y >= Y_FIELDSIZE ||
268 	    newpos.x <= 0 || newpos.x >= X_FIELDSIZE ||
269 	    Field[newpos.y][newpos.x] > 0 || eaten(&newpos)) {
270 		if (Running) {
271 			Running = FALSE;
272 			leaveok(stdscr, FALSE);
273 			move(My_pos.y, My_pos.x);
274 			refresh();
275 		} else {
276 			beep();
277 			reset_count();
278 		}
279 		return FALSE;
280 	}
281 	else if (dy == 0 && dx == 0)
282 		return TRUE;
283 	mvaddch(My_pos.y, My_pos.x, ' ');
284 	My_pos = newpos;
285 	mvaddch(My_pos.y, My_pos.x, PLAYER);
286 	if (!jumping())
287 		refresh();
288 	return TRUE;
289 }
290 
291 /*
292  * eaten:
293  *	Player would get eaten at this place
294  */
295 bool
296 eaten(COORD *pos)
297 {
298 	int	x, y;
299 
300 	for (y = pos->y - 1; y <= pos->y + 1; y++) {
301 		if (y <= 0 || y >= Y_FIELDSIZE)
302 			continue;
303 		for (x = pos->x - 1; x <= pos->x + 1; x++) {
304 			if (x <= 0 || x >= X_FIELDSIZE)
305 				continue;
306 			if (Field[y][x] == 1)
307 				return TRUE;
308 		}
309 	}
310 	return FALSE;
311 }
312 
313 /*
314  * reset_count:
315  *	Reset the count variables
316  */
317 void
318 reset_count(void)
319 {
320 	Count = 0;
321 	Running = FALSE;
322 	leaveok(stdscr, FALSE);
323 	refresh();
324 }
325 
326 /*
327  * jumping:
328  *	See if we are jumping, i.e., we should not refresh.
329  */
330 bool
331 jumping(void)
332 {
333 	return (Jump && (Count || Running || Waiting));
334 }
335