Mercurial > hg > early-roguelike
comparison arogue7/chase.c @ 125:adfa37e67084
Import Advanced Rogue 7.7 from the Roguelike Restoration Project (r1490)
| author | John "Elwin" Edwards |
|---|---|
| date | Fri, 08 May 2015 15:24:40 -0400 |
| parents | |
| children | f9ef86cf22b2 |
comparison
equal
deleted
inserted
replaced
| 124:d10fc4a065ac | 125:adfa37e67084 |
|---|---|
| 1 /* | |
| 2 * chase.c - Code for one object to chase another | |
| 3 * | |
| 4 * Advanced Rogue | |
| 5 * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T | |
| 6 * All rights reserved. | |
| 7 * | |
| 8 * Based on "Rogue: Exploring the Dungeons of Doom" | |
| 9 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman | |
| 10 * All rights reserved. | |
| 11 * | |
| 12 * See the file LICENSE.TXT for full copyright and licensing information. | |
| 13 */ | |
| 14 | |
| 15 /* | |
| 16 * Code for one object to chase another | |
| 17 * | |
| 18 */ | |
| 19 | |
| 20 #include <ctype.h> | |
| 21 #include <limits.h> | |
| 22 #include "curses.h" | |
| 23 #include "rogue.h" | |
| 24 #define MAXINT INT_MAX | |
| 25 #define MININT INT_MIN | |
| 26 | |
| 27 | |
| 28 /* | |
| 29 * Canblink checks if the monster can teleport (blink). If so, it will | |
| 30 * try to blink the monster next to the player. | |
| 31 */ | |
| 32 | |
| 33 bool | |
| 34 can_blink(tp) | |
| 35 register struct thing *tp; | |
| 36 { | |
| 37 register int y, x, index=9; | |
| 38 coord tryp; /* To hold the coordinates for use in diag_ok */ | |
| 39 bool spots[9], found_one=FALSE; | |
| 40 | |
| 41 /* | |
| 42 * First, can the monster even blink? And if so, there is only a 50% | |
| 43 * chance that it will do so. And it won't blink if it is running or | |
| 44 * held. | |
| 45 */ | |
| 46 if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) || | |
| 47 on(*tp, ISFLEE) || | |
| 48 tp->t_action == A_FREEZE || | |
| 49 (rnd(12) < 6)) return(FALSE); | |
| 50 | |
| 51 | |
| 52 /* Initialize the spots as illegal */ | |
| 53 do { | |
| 54 spots[--index] = FALSE; | |
| 55 } while (index > 0); | |
| 56 | |
| 57 /* Find a suitable spot next to the player */ | |
| 58 for (y=hero.y-1; y<hero.y+2; y++) | |
| 59 for (x=hero.x-1; x<hero.x+2; x++, index++) { | |
| 60 /* Make sure x coordinate is in range and that we are | |
| 61 * not at the player's position | |
| 62 */ | |
| 63 if (x<0 || x >= cols || index == 4) continue; | |
| 64 | |
| 65 /* Is it OK to move there? */ | |
| 66 if (step_ok(y, x, NOMONST, tp) && | |
| 67 (!isatrap(mvwinch(cw, y, x)) || | |
| 68 rnd(10) >= tp->t_stats.s_intel || | |
| 69 on(*tp, ISFLY))) { | |
| 70 /* OK, we can go here. But don't go there if | |
| 71 * monster can't get at player from there | |
| 72 */ | |
| 73 tryp.y = y; | |
| 74 tryp.x = x; | |
| 75 if (diag_ok(&tryp, &hero, tp)) { | |
| 76 spots[index] = TRUE; | |
| 77 found_one = TRUE; | |
| 78 } | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 /* If we found one, go to it */ | |
| 83 if (found_one) { | |
| 84 char rch; /* What's really where the creatures moves to */ | |
| 85 | |
| 86 /* Find a legal spot */ | |
| 87 while (spots[index=rnd(9)] == FALSE) continue; | |
| 88 | |
| 89 /* Get the coordinates */ | |
| 90 y = hero.y + (index/3) - 1; | |
| 91 x = hero.x + (index % 3) - 1; | |
| 92 | |
| 93 /* Move the monster from the old space */ | |
| 94 mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch); | |
| 95 | |
| 96 /* Move it to the new space */ | |
| 97 tp->t_oldch = CCHAR( mvwinch(cw, y, x) ); | |
| 98 | |
| 99 /* Display the creature if our hero can see it */ | |
| 100 if (cansee(y, x) && | |
| 101 off(*tp, ISINWALL) && | |
| 102 !invisible(tp)) | |
| 103 mvwaddch(cw, y, x, tp->t_type); | |
| 104 | |
| 105 /* Fix the monster window */ | |
| 106 mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' '); /* Clear old position */ | |
| 107 mvwaddch(mw, y, x, tp->t_type); | |
| 108 | |
| 109 /* Record the new position */ | |
| 110 tp->t_pos.y = y; | |
| 111 tp->t_pos.x = x; | |
| 112 | |
| 113 /* If the monster is on a trap, trap it */ | |
| 114 rch = CCHAR( mvinch(y, x) ); | |
| 115 if (isatrap(rch)) { | |
| 116 if (cansee(y, x)) tp->t_oldch = rch; | |
| 117 be_trapped(tp, &(tp->t_pos)); | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 return(found_one); | |
| 122 } | |
| 123 | |
| 124 /* | |
| 125 * Can_shoot determines if the monster (er) has a direct line of shot | |
| 126 * at the prey (ee). If so, it returns the direction in which to shoot. | |
| 127 */ | |
| 128 | |
| 129 coord * | |
| 130 can_shoot(er, ee) | |
| 131 register coord *er, *ee; | |
| 132 { | |
| 133 static coord shoot_dir; | |
| 134 | |
| 135 /* | |
| 136 * They must be in the same room or very close (at door) | |
| 137 */ | |
| 138 if (roomin(er) != roomin(ee) && DISTANCE(er->y,er->x,ee->y,ee->x) > 1) | |
| 139 return(NULL); | |
| 140 | |
| 141 /* Do we have a straight shot? */ | |
| 142 if (!straight_shot(er->y, er->x, ee->y, ee->x, &shoot_dir)) return(NULL); | |
| 143 else return(&shoot_dir); | |
| 144 } | |
| 145 | |
| 146 /* | |
| 147 * chase: | |
| 148 * Find the spot for the chaser(er) to move closer to the | |
| 149 * chasee(ee). Rer is the room of the chaser, and ree is the | |
| 150 * room of the creature being chased (chasee). | |
| 151 */ | |
| 152 | |
| 153 chase(tp, ee, rer, ree, flee) | |
| 154 register struct thing *tp; | |
| 155 register coord *ee; | |
| 156 register struct room *rer, *ree; | |
| 157 bool flee; /* True if destination (ee) is player and monster is running away | |
| 158 * or the player is in a wall and the monster can't get to it | |
| 159 */ | |
| 160 { | |
| 161 int dist, thisdist, monst_dist = MAXINT; | |
| 162 register coord *er = &tp->t_pos; | |
| 163 struct thing *prey; /* What we are chasing */ | |
| 164 coord ch_ret; /* Where chasing takes you */ | |
| 165 char ch, mch; | |
| 166 bool next_player = FALSE; | |
| 167 | |
| 168 /* | |
| 169 * set the distance from the chas(er) to the chas(ee) here and then | |
| 170 * we won't have to reset it unless the chas(er) moves (instead of shoots) | |
| 171 */ | |
| 172 dist = DISTANCE(er->y, er->x, ee->y, ee->x); | |
| 173 | |
| 174 /* | |
| 175 * See if our destination is a monster or player. If so, make "prey" point | |
| 176 * to it. | |
| 177 */ | |
| 178 if (ce(hero, *ee)) prey = &player; /* Is it the player? */ | |
| 179 else if (tp->t_dest && ce(*(tp->t_dest), *ee)) { /* Is it a monster? */ | |
| 180 struct linked_list *item; | |
| 181 | |
| 182 /* What is the monster we're chasing? */ | |
| 183 item = find_mons(ee->y, ee->x); | |
| 184 if (item != NULL) prey = THINGPTR(item); | |
| 185 else prey = NULL; | |
| 186 } | |
| 187 else prey = NULL; | |
| 188 | |
| 189 /* We will use at least one movement period */ | |
| 190 tp->t_no_move = movement(tp); | |
| 191 if (on(*tp, ISFLY)) /* If the creature is flying, speed it up */ | |
| 192 tp->t_no_move /= 2; | |
| 193 | |
| 194 /* | |
| 195 * If the thing is confused or it can't see the player, | |
| 196 * let it move randomly. | |
| 197 */ | |
| 198 if ((on(*tp, ISHUH) && rnd(10) < 8) || | |
| 199 (prey && on(*prey, ISINVIS) && off(*tp, CANSEE))) { /* invisible prey */ | |
| 200 /* | |
| 201 * get a valid random move | |
| 202 */ | |
| 203 tp->t_newpos = *rndmove(tp); | |
| 204 dist = DISTANCE(tp->t_newpos.y, tp->t_newpos.x, ee->y, ee->x); | |
| 205 } | |
| 206 | |
| 207 /* | |
| 208 * Otherwise, find the empty spot next to the chaser that is | |
| 209 * closest to the chasee. | |
| 210 */ | |
| 211 else { | |
| 212 register int ey, ex, x, y; | |
| 213 int dist_to_old = MININT; /* Dist from goal to old position */ | |
| 214 | |
| 215 /* | |
| 216 * This will eventually hold where we move to get closer | |
| 217 * If we can't find an empty spot, we stay where we are. | |
| 218 */ | |
| 219 dist = flee ? 0 : MAXINT; | |
| 220 ch_ret = *er; | |
| 221 | |
| 222 /* Are we at our goal already? */ | |
| 223 if (!flee && ce(ch_ret, *ee)) { | |
| 224 turn_off(*tp, ISRUN); /* So stop running! */ | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 ey = er->y + 1; | |
| 229 ex = er->x + 1; | |
| 230 | |
| 231 /* Check all possible moves */ | |
| 232 for (x = er->x - 1; x <= ex; x++) { | |
| 233 if (x < 0 || x >= cols) /* Don't try off the board */ | |
| 234 continue; | |
| 235 for (y = er->y - 1; y <= ey; y++) { | |
| 236 coord tryp; | |
| 237 | |
| 238 if ((y < 1) || (y >= lines - 2)) /* Don't try off the board */ | |
| 239 continue; | |
| 240 | |
| 241 /* Don't try the player if not going after the player */ | |
| 242 if ((flee || !ce(hero, *ee) || on(*tp, ISFRIENDLY)) && | |
| 243 x == hero.x && y == hero.y) { | |
| 244 next_player = TRUE; | |
| 245 continue; | |
| 246 } | |
| 247 | |
| 248 tryp.x = x; | |
| 249 tryp.y = y; | |
| 250 | |
| 251 /* Is there a monster on this spot closer to our goal? | |
| 252 * Don't look in our spot or where we were. | |
| 253 */ | |
| 254 if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) && | |
| 255 isalpha(mch = CCHAR( mvwinch(mw, y, x) ) )) { | |
| 256 int test_dist; | |
| 257 | |
| 258 test_dist = DISTANCE(y, x, ee->y, ee->x); | |
| 259 if (test_dist <= 25 && /* Let's be fairly close */ | |
| 260 test_dist < monst_dist) { | |
| 261 /* Could we really move there? */ | |
| 262 mvwaddch(mw, y, x, ' '); /* Temporarily blank monst */ | |
| 263 if (diag_ok(er, &tryp, tp)) monst_dist = test_dist; | |
| 264 mvwaddch(mw, y, x, mch); /* Restore monster */ | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 /* Can we move onto the spot? */ | |
| 269 if (!diag_ok(er, &tryp, tp)) continue; | |
| 270 | |
| 271 ch = CCHAR( mvwinch(cw, y, x) ); /* Screen character */ | |
| 272 | |
| 273 /* | |
| 274 * Stepping on player is NOT okay if we are fleeing. | |
| 275 * If we are friendly to the player and there is a monster | |
| 276 * in the way that is not of our race, it is okay to move | |
| 277 * there. | |
| 278 */ | |
| 279 if (step_ok(y, x, FIGHTOK, tp) && | |
| 280 (off(*tp, ISFLEE) || ch != PLAYER)) | |
| 281 { | |
| 282 /* | |
| 283 * If it is a trap, an intelligent monster may not | |
| 284 * step on it (unless our hero is on top!) | |
| 285 */ | |
| 286 if ((isatrap(ch)) && | |
| 287 (rnd(10) < tp->t_stats.s_intel) && | |
| 288 (!on(*tp, ISFLY)) && | |
| 289 (y != hero.y || x != hero.x)) | |
| 290 continue; | |
| 291 | |
| 292 /* | |
| 293 * OK -- this place counts | |
| 294 */ | |
| 295 thisdist = DISTANCE(y, x, ee->y, ee->x); | |
| 296 | |
| 297 /* Adjust distance if we are being shot at */ | |
| 298 if (tp->t_wasshot && tp->t_stats.s_intel > 5 && | |
| 299 prey != NULL) { | |
| 300 /* Move out of line of sight */ | |
| 301 if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) { | |
| 302 if (flee) thisdist -= SHOTPENALTY; | |
| 303 else thisdist += SHOTPENALTY; | |
| 304 } | |
| 305 | |
| 306 /* But do we want to leave the room? */ | |
| 307 else if (rer && rer == ree && ch == DOOR) | |
| 308 thisdist += DOORPENALTY; | |
| 309 } | |
| 310 | |
| 311 /* Don't move to the last position if we can help it | |
| 312 * (unless out prey just moved there) | |
| 313 */ | |
| 314 if (ce(tryp, tp->t_oldpos) && (flee || !ce(tryp, hero))) | |
| 315 dist_to_old = thisdist; | |
| 316 | |
| 317 else if ((flee && (thisdist > dist)) || | |
| 318 (!flee && (thisdist < dist))) | |
| 319 { | |
| 320 ch_ret = tryp; | |
| 321 dist = thisdist; | |
| 322 } | |
| 323 } | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 /* If we aren't trying to get the player, but he is in our way, | |
| 328 * hit him (unless we have been turned or are friendly). next_player | |
| 329 * being TRUE -> we are next to the player but don't want to hit him. | |
| 330 * | |
| 331 * If we are friendly to the player, following him, and standing next | |
| 332 * to him, we will try to help him out in battle. | |
| 333 */ | |
| 334 if (next_player && off(*tp, WASTURNED)) { | |
| 335 if (off(*tp, ISFRIENDLY) && | |
| 336 ((flee && ce(ch_ret, *er)) || | |
| 337 (!flee && DISTANCE(er->y, er->x, ee->y, ee->x) < dist)) && | |
| 338 step_ok(tp->t_dest->y, tp->t_dest->x, NOMONST, tp)) { | |
| 339 /* Okay to hit player */ | |
| 340 debug("Switching to hero."); | |
| 341 tp->t_newpos = hero; | |
| 342 tp->t_action = A_MOVE; | |
| 343 return; | |
| 344 } | |
| 345 else if (on(*tp, ISFRIENDLY) && !flee && ce(*ee, hero)) { | |
| 346 /* | |
| 347 * Look all around the player. If there is a fightable | |
| 348 * creature next to both of us, hit it. Otherwise, if | |
| 349 * there is a fightable creature next to the player, try | |
| 350 * to move next to it. | |
| 351 */ | |
| 352 dist = MAXINT; | |
| 353 for (x = hero.x - 1; x <= hero.x + 1; x++) { | |
| 354 if (x < 0 || x >= cols) /* Don't try off the board */ | |
| 355 continue; | |
| 356 for (y = hero.y - 1; y <= hero.y + 1; y++) { | |
| 357 if ((y < 1) || (y >= lines - 2)) /* Stay on the board */ | |
| 358 continue; | |
| 359 | |
| 360 /* Is there a fightable monster here? */ | |
| 361 if (isalpha(mvwinch(mw, y, x)) && | |
| 362 step_ok(y, x, FIGHTOK, tp) && | |
| 363 off(*tp, ISSTONE)) { | |
| 364 thisdist = DISTANCE(er->y, er->x, y, x); | |
| 365 if (thisdist < dist) { | |
| 366 dist = thisdist; | |
| 367 ch_ret.y = y; | |
| 368 ch_ret.x = x; | |
