Mercurial > hg > early-roguelike
comparison arogue5/chase.c @ 63:0ed67132cf10
Import Advanced Rogue 5.8 from the Roguelike Restoration Project (r1490)
author | elwin |
---|---|
date | Thu, 09 Aug 2012 22:58:48 +0000 |
parents | |
children | 56e748983fa8 |
comparison
equal
deleted
inserted
replaced
62:0ef99244acb8 | 63:0ed67132cf10 |
---|---|
1 /* | |
2 * Code for one object to chase another | |
3 * | |
4 * Advanced Rogue | |
5 * Copyright (C) 1984, 1985 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 #include <ctype.h> | |
16 #include <limits.h> | |
17 #include "curses.h" | |
18 #include "rogue.h" | |
19 #define MAXINT INT_MAX | |
20 #define MININT INT_MIN | |
21 | |
22 coord ch_ret; /* Where chasing takes you */ | |
23 | |
24 | |
25 | |
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 (on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) || | |
49 tp->t_no_move || | |
50 (rnd(12) < 6)) return(FALSE); | |
51 | |
52 | |
53 /* Initialize the spots as illegal */ | |
54 do { | |
55 spots[--index] = FALSE; | |
56 } while (index > 0); | |
57 | |
58 /* Find a suitable spot next to the player */ | |
59 for (y=hero.y-1; y<hero.y+2; y++) | |
60 for (x=hero.x-1; x<hero.x+2; x++, index++) { | |
61 /* Make sure x coordinate is in range and that we are | |
62 * not at the player's position | |
63 */ | |
64 if (x<0 || x >= COLS || index == 4) continue; | |
65 | |
66 /* Is it OK to move there? */ | |
67 if (step_ok(y, x, NOMONST, tp) && | |
68 (!isatrap(mvwinch(cw, y, x)) || | |
69 rnd(10) >= tp->t_stats.s_intel || | |
70 on(*tp, ISFLY))) { | |
71 /* OK, we can go here. But don't go there if | |
72 * monster can't get at player from there | |
73 */ | |
74 tryp.y = y; | |
75 tryp.x = x; | |
76 if (diag_ok(&tryp, &hero, tp)) { | |
77 spots[index] = TRUE; | |
78 found_one = TRUE; | |
79 } | |
80 } | |
81 } | |
82 | |
83 /* If we found one, go to it */ | |
84 if (found_one) { | |
85 char rch; /* What's really where the creatures moves to */ | |
86 | |
87 /* Find a legal spot */ | |
88 while (spots[index=rnd(9)] == FALSE) continue; | |
89 | |
90 /* Get the coordinates */ | |
91 y = hero.y + (index/3) - 1; | |
92 x = hero.x + (index % 3) - 1; | |
93 | |
94 /* Move the monster from the old space */ | |
95 mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch); | |
96 | |
97 /* Move it to the new space */ | |
98 tp->t_oldch = CCHAR( mvwinch(cw, y, x) ); | |
99 | |
100 /* Display the creature if our hero can see it */ | |
101 if (cansee(y, x) && | |
102 off(*tp, ISINWALL) && | |
103 !invisible(tp)) | |
104 mvwaddch(cw, y, x, tp->t_type); | |
105 | |
106 /* Fix the monster window */ | |
107 mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' '); /* Clear old position */ | |
108 mvwaddch(mw, y, x, tp->t_type); | |
109 | |
110 /* Record the new position */ | |
111 tp->t_pos.y = y; | |
112 tp->t_pos.x = x; | |
113 | |
114 /* If the monster is on a trap, trap it */ | |
115 rch = CCHAR( mvinch(y, x) ); | |
116 if (isatrap(rch)) { | |
117 if (cansee(y, x)) tp->t_oldch = rch; | |
118 be_trapped(tp, &(tp->t_pos)); | |
119 } | |
120 } | |
121 | |
122 return(found_one); | |
123 } | |
124 | |
125 /* | |
126 * Can_shoot determines if the monster (er) has a direct line of shot | |
127 * at the player (ee). If so, it returns the direction in which to shoot. | |
128 */ | |
129 | |
130 coord * | |
131 can_shoot(er, ee) | |
132 register coord *er, *ee; | |
133 { | |
134 static coord shoot_dir; | |
135 | |
136 /* Make sure we are chasing the player */ | |
137 if (!ce((*ee), hero)) return(NULL); | |
138 | |
139 /* | |
140 * They must be in the same room or very close (at door) | |
141 */ | |
142 if (roomin(er) != roomin(&hero) && DISTANCE(er->y,er->x,ee->y,ee->x) > 1) | |
143 return(NULL); | |
144 | |
145 /* Do we have a straight shot? */ | |
146 if (!straight_shot(er->y, er->x, ee->y, ee->x, &shoot_dir)) return(NULL); | |
147 else return(&shoot_dir); | |
148 } | |
149 | |
150 /* | |
151 * chase: | |
152 * Find the spot for the chaser(er) to move closer to the | |
153 * chasee(ee). Returns TRUE if we want to keep on chasing later | |
154 * FALSE if we reach the goal. | |
155 */ | |
156 | |
157 chase(tp, ee, flee, mdead) | |
158 register struct thing *tp; | |
159 register coord *ee; | |
160 bool flee; /* True if destination (ee) is player and monster is running away | |
161 * or the player is in a wall and the monster can't get to it | |
162 */ | |
163 bool *mdead; | |
164 { | |
165 int damage, dist, thisdist, monst_dist = MAXINT; | |
166 struct linked_list *weapon; | |
167 register coord *er = &tp->t_pos; | |
168 coord *shoot_dir; | |
169 char ch, mch; | |
170 bool next_player = FALSE; | |
171 | |
172 if (mdead != NULL) | |
173 *mdead = 0; | |
174 | |
175 /* | |
176 * set the distance from the chas(er) to the chas(ee) here and then | |
177 * we won't have to reset it unless the chas(er) moves (instead of shoots) | |
178 */ | |
179 dist = DISTANCE(er->y, er->x, ee->y, ee->x); | |
180 | |
181 /* | |
182 * If the thing is confused or it can't see the player, | |
183 * let it move randomly. | |
184 */ | |
185 if ((on(*tp, ISHUH) && rnd(10) < 8) || | |
186 (on(player, ISINVIS) && off(*tp, CANSEE))) { /* Player is invisible */ | |
187 /* | |
188 * get a valid random move | |
189 */ | |
190 ch_ret = *rndmove(tp); | |
191 dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x); | |
192 /* | |
193 * check to see if random move takes creature away from player | |
194 * if it does then turn off ISHELD | |
195 */ | |
196 if (dist > 2) { | |
197 if (on(*tp, DIDHOLD)) { | |
198 turn_off(*tp, DIDHOLD); | |
199 turn_on(*tp, CANHOLD); | |
200 if (--hold_count == 0) | |
201 turn_off(player, ISHELD); | |
202 } | |
203 | |
204 /* If monster was suffocating, stop it */ | |
205 if (on(*tp, DIDSUFFOCATE)) { | |
206 turn_off(*tp, DIDSUFFOCATE); | |
207 turn_on(*tp, CANSUFFOCATE); | |
208 extinguish(suffocate); | |
209 } | |
210 } | |
211 } | |
212 | |
213 /* If we can breathe, we may do so */ | |
214 else if (on(*tp, CANBREATHE) && | |
215 (dist < BOLT_LENGTH*BOLT_LENGTH) && | |
216 (shoot_dir = can_shoot(er, ee)) && | |
217 !on(player, ISINWALL) && | |
218 (rnd(100) < 75)) { | |
219 register char *breath = NULL; | |
220 | |
221 damage = tp->t_stats.s_hpt; | |
222 /* Will it breathe at random */ | |
223 if (on(*tp, CANBRANDOM)) { | |
224 /* Turn off random breath */ | |
225 turn_off(*tp, CANBRANDOM); | |
226 | |
227 /* Select type of breath */ | |
228 switch (rnd(10)) { | |
229 case 0: breath = "acid"; | |
230 turn_on(*tp, NOACID); | |
231 when 1: breath = "flame"; | |
232 turn_on(*tp, NOFIRE); | |
233 when 2: breath = "lightning bolt"; | |
234 turn_on(*tp, NOBOLT); | |
235 when 3: breath = "chlorine gas"; | |
236 turn_on(*tp, NOGAS); | |
237 when 4: breath = "ice"; | |
238 turn_on(*tp, NOCOLD); | |
239 when 5: breath = "nerve gas"; | |
240 turn_on(*tp, NOPARALYZE); | |
241 when 6: breath = "sleeping gas"; | |
242 turn_on(*tp, NOSLEEP); | |
243 when 7: breath = "slow gas"; | |
244 turn_on(*tp, NOSLOW); | |
245 when 8: breath = "confusion gas"; | |
246 turn_on(*tp, ISCLEAR); | |
247 when 9: breath = "fear gas"; | |
248 turn_on(*tp, NOFEAR); | |
249 } | |
250 } | |
251 | |
252 /* Or can it breathe acid? */ | |
253 else if (on(*tp, CANBACID)) { | |
254 turn_off(*tp, CANBACID); | |
255 breath = "acid"; | |
256 } | |
257 | |
258 /* Or can it breathe fire */ | |
259 else if (on(*tp, CANBFIRE)) { | |
260 turn_off(*tp, CANBFIRE); | |
261 breath = "flame"; | |
262 } | |
263 | |
264 /* Or can it breathe electricity? */ | |
265 else if (on(*tp, CANBBOLT)) { | |
266 turn_off(*tp, CANBBOLT); | |
267 breath = "lightning bolt"; | |
268 } | |
269 | |
270 /* Or can it breathe gas? */ | |
271 else if (on(*tp, CANBGAS)) { | |
272 turn_off(*tp, CANBGAS); | |
273 breath = "chlorine gas"; | |
274 } | |
275 | |
276 /* Or can it breathe ice? */ | |
277 else if (on(*tp, CANBICE)) { | |
278 turn_off(*tp, CANBICE); | |
279 breath = "ice"; | |
280 } | |
281 | |
282 else if (on(*tp, CANBPGAS)) { | |
283 turn_off(*tp, CANBPGAS); | |
284 breath = "nerve gas"; | |
285 } | |
286 | |
287 /* can it breathe sleeping gas */ | |
288 else if (on(*tp, CANBSGAS)) { | |
289 turn_off(*tp, CANBSGAS); | |
290 breath = "sleeping gas"; | |
291 } | |
292 | |
293 /* can it breathe slow gas */ | |
294 else if (on(*tp, CANBSLGAS)) { | |
295 turn_off(*tp, CANBSLGAS); | |
296 breath = "slow gas"; | |
297 } | |
298 /* can it breathe confusion gas */ | |
299 else if (on(*tp, CANBCGAS)) { | |
300 turn_off(*tp, CANBCGAS); | |
301 breath = "confusion gas"; | |
302 } | |
303 /* can it breathe fear gas */ | |
304 else { | |
305 turn_off(*tp, CANBFGAS); | |
306 breath = "fear gas"; | |
307 } | |
308 | |
309 /* Now breathe -- sets "monst_dead" if it kills someone */ | |
310 *mdead = shoot_bolt( tp, *er, *shoot_dir, FALSE, | |
311 tp->t_index, breath, damage); | |
312 | |
313 ch_ret = *er; | |
314 running = FALSE; | |
315 if (*mdead) return(TRUE); | |
316 } | |
317 | |
318 /* We may shoot missiles if we can */ | |
319 else if (on(*tp, CANMISSILE) && | |
320 (shoot_dir = can_shoot(er, ee)) && | |
321 !on(player, ISINWALL) && | |
322 (rnd(100) < 75)) { | |
323 static struct object missile = | |
324 { | |
325 MISSILE, {0, 0}, "", 0, "", "0d4 " , NULL, 0, WS_MISSILE, 100, 1 | |
326 }; | |
327 | |
328 sprintf(missile.o_hurldmg, "%dd4", tp->t_stats.s_lvl); | |
329 do_motion(&missile, shoot_dir->y, shoot_dir->x, tp); | |
330 hit_monster(unc(missile.o_pos), &missile, tp); | |
331 turn_off(*tp, CANMISSILE); | |
332 ch_ret = *er; | |
333 running = FALSE; | |
334 } | |
335 | |
336 /* We may use a sonic blast if we can */ | |
337 else if (on(*tp, CANSONIC) && | |
338 (dist < BOLT_LENGTH*2) && | |
339 (shoot_dir = can_shoot(er, ee)) && | |
340 !on(player, ISINWALL) && | |
341 (rnd(100) < 50)) { | |
342 static struct object blast = | |
343 { | |
344 MISSILE, {0, 0}, "", 0, "", "150" , NULL, 0, 0, 0, 0 | |
345 }; | |
346 | |
347 turn_off(*tp, CANSONIC); | |
348 do_motion(&blast, shoot_dir->y, shoot_dir->x, tp); | |
349 damage = 150; | |
350 if (save(VS_BREATH, &player, -3)) | |
351 damage /= 2; | |
352 msg ("The %s's sonic blast hits you", monsters[tp->t_index].m_name); | |
353 if ((pstats.s_hpt -= damage) <= 0) | |
354 death(tp->t_index); | |
355 ch_ret = *er; | |
356 running = FALSE; | |
357 } | |
358 /* | |
359 * If we have a special magic item, we might use it. We will restrict | |
360 * this options to uniques with relics for now. | |
361 */ | |
362 else if (on(*tp, ISUNIQUE) && m_use_item(tp, er, ee)) { | |
363 ch_ret = *er; | |
364 running = FALSE; | |
365 } | |
366 /* | |
367 * If we can shoot or throw something, we might do so. | |
368 * If next to player, then 80% prob will fight. | |
369 */ | |
370 else if(on(*tp, CANSHOOT) && | |
371 (shoot_dir = can_shoot(er, ee)) && | |
372 !on(player, ISINWALL) && | |