1/*	$OpenBSD: move.c,v 1.13 2016/08/27 02:02:44 guenther 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 <sys/time.h>
34#include <ctype.h>
35#include <poll.h>
36#include <termios.h>
37#include <unistd.h>
38
39#include "robots.h"
40
41#define	ESC	'\033'
42
43/*
44 * get_move:
45 *	Get and execute a move from the player
46 */
47void
48get_move(void)
49{
50	int	c;
51	int retval;
52	struct timespec t, tn;
53#ifdef FANCY
54	int lastmove;
55#endif
56
57	if (Waiting)
58		return;
59
60#ifdef	FANCY
61	if (Pattern_roll) {
62		if (Next_move >= Move_list)
63			lastmove = *Next_move;
64		else
65			lastmove = -1;	/* flag for "first time in" */
66	}
67#endif
68	if (Real_time) {
69		t = tv;
70		clock_gettime(CLOCK_MONOTONIC, &tn);
71	}
72	for (;;) {
73		if (Teleport && must_telep())
74			goto teleport;
75		if (Running)
76			c = Run_ch;
77		else if (Count != 0)
78			c = Cnt_move;
79#ifdef	FANCY
80		else if (Num_robots > 1 && Stand_still)
81			c = '>';
82		else if (Num_robots > 1 && Pattern_roll) {
83			if (*++Next_move == '\0') {
84				if (lastmove < 0)
85					goto over;
86				Next_move = Move_list;
87			}
88			c = *Next_move;
89			mvaddch(0, 0, c);
90			if (c == lastmove)
91				goto over;
92		}
93#endif
94		else {
95over:
96			if (Real_time) {
97				struct pollfd pfd[1];
98
99				pfd[0].fd = STDIN_FILENO;
100				pfd[0].events = POLLIN;
101				retval = ppoll(pfd, 1, &t, NULL);
102				if (retval > 0)
103					c = getchar();
104				else	/* Don't move if timed out or error */
105					c = ' ';
106			} else {
107				c = getchar();
108				/* Can't use digits in real time mode, or digit/ESC
109				 * is an effective way to stop the game.
110				 */
111				if (isdigit(c)) {
112					Count = (c - '0');
113					while (isdigit(c = getchar()))
114						Count = Count * 10 + (c - '0');
115					if (c == ESC)
116						goto over;
117					Cnt_move = c;
118					if (Count)
119						leaveok(stdscr, TRUE);
120				}
121			}
122		}
123
124		switch (c) {
125		  case ' ':
126		  case '.':
127			if (do_move(0, 0))
128				goto ret;
129			break;
130		  case 'y':
131			if (do_move(-1, -1))
132				goto ret;
133			break;
134		  case 'k':
135			if (do_move(-1, 0))
136				goto ret;
137			break;
138		  case 'u':
139			if (do_move(-1, 1))
140				goto ret;
141			break;
142		  case 'h':
143			if (do_move(0, -1))
144				goto ret;
145			break;
146		  case 'l':
147			if (do_move(0, 1))
148				goto ret;
149			break;
150		  case 'b':
151			if (do_move(1, -1))
152				goto ret;
153			break;
154		  case 'j':
155			if (do_move(1, 0))
156				goto ret;
157			break;
158		  case 'n':
159			if (do_move(1, 1))
160				goto ret;
161			break;
162		  case 'Y': case 'U': case 'H': case 'J':
163		  case 'K': case 'L': case 'B': case 'N':
164		  case '>':
165			Running = TRUE;
166			if (c == '>')
167				Run_ch = ' ';
168			else
169				Run_ch = tolower(c);
170			leaveok(stdscr, TRUE);
171			break;
172		  case 'q':
173		  case 'Q':
174			if (query("Really quit?"))
175				quit(0);
176			refresh();
177			break;
178		  case 'w':
179		  case 'W':
180			Waiting = TRUE;
181			leaveok(stdscr, TRUE);
182#ifndef NCURSES_VERSION
183			flushok(stdscr, FALSE);
184#endif
185			goto ret;
186		  case 't':
187		  case 'T':
188teleport:
189			Running = FALSE;
190			mvaddch(My_pos.y, My_pos.x, ' ');
191			My_pos = *rnd_pos();
192			mvaddch(My_pos.y, My_pos.x, PLAYER);
193			leaveok(stdscr, FALSE);
194			refresh();
195			flushinp();
196			goto ret;
197		  case CTRL('L'):
198			wrefresh(curscr);
199			break;
200		  case EOF:
201			quit(0);
202			break;
203		  default:
204			beep();
205			reset_count();
206			break;
207		}
208		if (Real_time) {
209			/* Update current time. */
210			clock_gettime(CLOCK_MONOTONIC, &t);
211
212			/* Check whether tv time has passed. */
213			timespecadd(&tn, &tv, &tn);
214			if (timespeccmp(&tn, &t, <))
215				goto ret;
216
217			/* Keep the difference otherwise. */
218			timespecsub(&tn, &t, &t);
219		}
220	}
221ret:
222	if (Count > 0)
223		if (--Count == 0)
224			leaveok(stdscr, FALSE);
225}
226
227/*
228 * must_telep:
229 *	Must I teleport; i.e., is there anywhere I can move without
230 * being eaten?
231 */
232bool
233must_telep(void)
234{
235	int		x, y;
236	static COORD	newpos;
237
238#ifdef	FANCY
239	if (Stand_still && Num_robots > 1 && eaten(&My_pos))
240		return TRUE;
241#endif
242
243	for (y = -1; y <= 1; y++) {
244		newpos.y = My_pos.y + y;
245		if (newpos.y <= 0 || newpos.y >= Y_FIELDSIZE)
246			continue;
247		for (x = -1; x <= 1; x++) {
248			newpos.x = My_pos.x + x;
249			if (newpos.x <= 0 || newpos.x >= X_FIELDSIZE)
250				continue;
251			if (Field[newpos.y][newpos.x] > 0)
252				continue;
253			if (!eaten(&newpos))
254				return FALSE;
255		}
256	}
257	return TRUE;
258}
259
260/*
261 * do_move:
262 *	Execute a move
263 */
264bool
265do_move(int dy, int dx)
266{
267	static COORD	newpos;
268
269	newpos.y = My_pos.y + dy;
270	newpos.x = My_pos.x + dx;
271	if (newpos.y <= 0 || newpos.y >= Y_FIELDSIZE ||
272	    newpos.x <= 0 || newpos.x >= X_FIELDSIZE ||
273	    Field[newpos.y][newpos.x] > 0 || eaten(&newpos)) {
274		if (Running) {
275			Running = FALSE;
276			leaveok(stdscr, FALSE);
277			move(My_pos.y, My_pos.x);
278			refresh();
279		} else {
280			beep();
281			reset_count();
282		}
283		return FALSE;
284	}
285	else if (dy == 0 && dx == 0)
286		return TRUE;
287	mvaddch(My_pos.y, My_pos.x, ' ');
288	My_pos = newpos;
289	mvaddch(My_pos.y, My_pos.x, PLAYER);
290	if (!jumping())
291		refresh();
292	return TRUE;
293}
294
295/*
296 * eaten:
297 *	Player would get eaten at this place
298 */
299bool
300eaten(COORD *pos)
301{
302	int	x, y;
303
304	for (y = pos->y - 1; y <= pos->y + 1; y++) {
305		if (y <= 0 || y >= Y_FIELDSIZE)
306			continue;
307		for (x = pos->x - 1; x <= pos->x + 1; x++) {
308			if (x <= 0 || x >= X_FIELDSIZE)
309				continue;
310			if (Field[y][x] == 1)
311				return TRUE;
312		}
313	}
314	return FALSE;
315}
316
317/*
318 * reset_count:
319 *	Reset the count variables
320 */
321void
322reset_count(void)
323{
324	Count = 0;
325	Running = FALSE;
326	leaveok(stdscr, FALSE);
327	refresh();
328}
329
330/*
331 * jumping:
332 *	See if we are jumping, i.e., we should not refresh.
333 */
334bool
335jumping(void)
336{
337	return (Jump && (Count || Running || Waiting));
338}
339