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