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 */ 47 void 48 get_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 { 95 over: 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': 188 teleport: 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 } 221 ret: 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 */ 232 bool 233 must_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 */ 264 bool 265 do_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 */ 299 bool 300 eaten(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 */ 321 void 322 reset_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 */ 334 bool 335 jumping(void) 336 { 337 return (Jump && (Count || Running || Waiting)); 338 } 339