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