1 | /* SCCS Id: @(#)hack.c 3.3 2000/04/22 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 |
7 | #ifdef OVL1
8 | static void NDECL(maybe_wail);
9 | #endif /*OVL1*/
10 | STATIC_DCL int NDECL(moverock);
11 | STATIC_DCL int FDECL(still_chewing,(XCHAR_P,XCHAR_P));
12 | #ifdef SINKS
13 | STATIC_DCL void NDECL(dosinkfall);
14 | #endif
15 | STATIC_DCL boolean FDECL(monstinroom, (struct permonst *,int));
16 |
17 | STATIC_DCL void FDECL(move_update, (BOOLEAN_P));
18 |
19 | #define IS_SHOP(x) (rooms[x].rtype >= SHOPBASE)
20 |
21 | #ifdef OVL2
22 |
23 | boolean
24 | revive_nasty(x, y, msg)
25 | int x,y;
26 | const char *msg;
27 | {
28 | register struct obj *otmp, *otmp2;
29 | struct monst *mtmp;
30 | coord cc;
31 | boolean revived = FALSE;
32 |
33 | for(otmp = level.objects[x][y]; otmp; otmp = otmp2) {
34 | otmp2 = otmp->nexthere;
35 | if (otmp->otyp == CORPSE &&
36 | (is_rider(&mons[otmp->corpsenm]) ||
37 | otmp->corpsenm == PM_WIZARD_OF_YENDOR)) {
38 | /* move any living monster already at that location */
39 | if((mtmp = m_at(x,y)) && enexto(&cc, x, y, mtmp->data))
40 | rloc_to(mtmp, cc.x, cc.y);
41 | if(msg) Norep("%s", msg);
42 | revived = revive_corpse(otmp);
43 | }
44 | }
45 |
46 | /* this location might not be safe, if not, move revived monster */
47 | if (revived) {
48 | mtmp = m_at(x,y);
49 | if (mtmp && !goodpos(x, y, mtmp) &&
50 | enexto(&cc, x, y, mtmp->data)) {
51 | rloc_to(mtmp, cc.x, cc.y);
52 | }
53 | /* else impossible? */
54 | }
55 |
56 | return (revived);
57 | }
58 |
59 | STATIC_OVL int
60 | moverock()
61 | {
62 | register xchar rx, ry, sx, sy;
63 | register struct obj *otmp;
64 | register struct trap *ttmp;
65 | register struct monst *mtmp;
66 |
67 | sx = u.ux + u.dx, sy = u.uy + u.dy; /* boulder starting position */
68 | while ((otmp = sobj_at(BOULDER, sx, sy)) != 0) {
69 | /* make sure that this boulder is visible as the top object */
70 | if (otmp != level.objects[sx][sy]) movobj(otmp, sx, sy);
71 |
72 | rx = u.ux + 2 * u.dx; /* boulder destination position */
73 | ry = u.uy + 2 * u.dy;
74 | nomul(0);
75 | if (Levitation || Is_airlevel(&u.uz)) {
76 | if (Blind) feel_location(sx, sy);
77 | You("don't have enough leverage to push %s.", the(xname(otmp)));
78 | /* Give them a chance to climb over it? */
79 | return -1;
80 | }
81 | if (verysmall(youmonst.data)
82 | #ifdef STEED
83 | && !u.usteed
84 | #endif
85 | ) {
86 | if (Blind) feel_location(sx, sy);
87 | pline("You're too small to push that %s.", xname(otmp));
88 | goto cannot_push;
89 | }
90 | if (isok(rx,ry) && !IS_ROCK(levl[rx][ry].typ) &&
91 | (!IS_DOOR(levl[rx][ry].typ) || !(u.dx && u.dy) || (
92 | #ifdef REINCARNATION
93 | !Is_rogue_level(&u.uz) &&
94 | #endif
95 | (levl[rx][ry].doormask & ~D_BROKEN) == D_NODOOR)) &&
96 | !sobj_at(BOULDER, rx, ry)) {
97 | ttmp = t_at(rx, ry);
98 | mtmp = m_at(rx, ry);
99 |
100 | /* KMH -- Sokoban doesn't let you push boulders diagonally */
101 | if (In_sokoban(&u.uz) && u.dx && u.dy) {
102 | if (Blind) feel_location(sx,sy);
103 | pline("%s won't roll diagonally on this %s.",
104 | The(xname(otmp)), surface(sx, sy));
105 | goto cannot_push;
106 | }
107 |
108 | if (revive_nasty(rx, ry, "You sense movement on the other side."))
109 | return (-1);
110 |
111 | if (mtmp && !noncorporeal(mtmp->data) &&
112 | (!mtmp->mtrapped ||
113 | !(ttmp && ((ttmp->ttyp == PIT) ||
114 | (ttmp->ttyp == SPIKED_PIT))))) {
115 | if (canspotmon(mtmp))
116 | pline("There's %s on the other side.", mon_nam(mtmp));
117 | else {
118 | if (Blind) feel_location(sx, sy);
119 | You_hear("a monster behind %s.", the(xname(otmp)));
120 | map_invisible(rx, ry);
121 | }
122 | if (flags.verbose)
123 | pline("Perhaps that's why %s cannot move it.",
124 | #ifdef STEED
125 | u.usteed ? mon_nam(u.usteed) :
126 | #endif
127 | "you");
128 | goto cannot_push;
129 | }
130 |
131 | if (ttmp)
132 | switch(ttmp->ttyp) {
133 | case LANDMINE:
134 | if (rn2(10)) {
135 | pline("KAABLAMM!!! %s triggers %s land mine.",
136 | The(xname(otmp)),
137 | ttmp->madeby_u ? "your" : "a");
138 | obj_extract_self(otmp);
139 | place_object(otmp, rx, ry);
140 | deltrap(ttmp);
141 | del_engr_at(rx,ry);
142 | scatter(rx,ry, 4,
143 | MAY_DESTROY|MAY_HIT|MAY_FRACTURE|VIS_EFFECTS,
144 | (struct obj *)0);
145 | if (cansee(rx,ry)) newsym(rx,ry);
146 | continue;
147 | }
148 | break;
149 | case SPIKED_PIT:
150 | case PIT:
151 | obj_extract_self(otmp);
152 | /* vision kludge to get messages right;
153 | the pit will temporarily be seen even
154 | if this is one among multiple boulders */
155 | if (!Blind) viz_array[ry][rx] |= IN_SIGHT;
156 | if (!flooreffects(otmp, rx, ry, "fall")) {
157 | place_object(otmp, rx, ry);
158 | }
159 | if (mtmp && !Blind) newsym(rx, ry);
160 | continue;
161 | case HOLE:
162 | case TRAPDOOR:
163 | pline("%s %s and plugs a %s in the %s!",
164 | The(xname(otmp)),
165 | (ttmp->ttyp == TRAPDOOR) ? "triggers" : "falls into",
166 | (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole",
167 | surface(rx, ry));
168 | deltrap(ttmp);
169 | delobj(otmp);
170 | bury_objs(rx, ry);
171 | if (cansee(rx,ry)) newsym(rx,ry);
172 | continue;
173 | case LEVEL_TELEP:
174 | case TELEP_TRAP:
175 | #ifdef STEED
176 | if (u.usteed)
177 | pline("%s pushes %s and suddenly it disappears!",
178 | Monnam(u.usteed), the(xname(otmp)));
179 | else
180 | #endif
181 | You("push %s and suddenly it disappears!",
182 | the(xname(otmp)));
183 | if (ttmp->ttyp == TELEP_TRAP)
184 | rloco(otmp);
185 | else {
186 | int newlev = random_teleport_level();
187 | d_level dest;
188 |
189 | if (newlev == depth(&u.uz) || In_endgame(&u.uz))
190 | continue;
191 | obj_extract_self(otmp);
192 | add_to_migration(otmp);
193 | get_level(&dest, newlev);
194 | otmp->ox = dest.dnum;
195 | otmp->oy = dest.dlevel;
196 | otmp->owornmask = (long)MIGR_RANDOM;
197 | }
198 | seetrap(ttmp);
199 | continue;
200 | }
201 | if (closed_door(rx, ry))
202 | goto nopushmsg;
203 | if (boulder_hits_pool(otmp, rx, ry, TRUE))
204 | continue;
205 | /*
206 | * Re-link at top of fobj chain so that pile order is preserved
207 | * when level is restored.
208 | */
209 | if (otmp != fobj) {
210 | remove_object(otmp);
211 | place_object(otmp, otmp->ox, otmp->oy);
212 | }
213 |
214 | {
215 | #ifdef LINT /* static long lastmovetime; */
216 | long lastmovetime;
217 | lastmovetime = 0;
218 | #else
219 | static NEARDATA long lastmovetime;
220 | #endif
221 | /* note: this var contains garbage initially and
222 | after a restore */
223 | #ifdef STEED
224 | if (!u.usteed) {
225 | #endif
226 | if (moves > lastmovetime+2 || moves < lastmovetime)
227 | pline("With %s effort you move %s.",
228 | throws_rocks(youmonst.data) ? "little" : "great",
229 | the(xname(otmp)));
230 | exercise(A_STR, TRUE);
231 | #ifdef STEED
232 | } else
233 | pline("%s moves %s.", Monnam(u.usteed), the(xname(otmp)));
234 | #endif
235 | lastmovetime = moves;
236 | }
237 |
238 | /* Move the boulder *after* the message. */
239 | if (glyph_is_invisible(levl[rx][ry].glyph))
240 | unmap_object(rx, ry);
241 | movobj(otmp, rx, ry); /* does newsym(rx,ry) */
242 | if (Blind) {
243 | feel_location(rx,ry);
244 | feel_location(sx, sy);
245 | } else {
246 | newsym(sx, sy);
247 | }
248 | } else {
249 | nopushmsg:
250 | #ifdef STEED
251 | if (u.usteed)
252 | pline("%s tries to move %s, but cannot.",
253 | Monnam(u.usteed), the(xname(otmp)));
254 | else
255 | #endif
256 | You("try to move %s, but in vain.", the(xname(otmp)));
257 | if (Blind) feel_location(sx, sy);
258 | cannot_push:
259 | if (throws_rocks(youmonst.data)) {
260 | #ifdef STEED
261 | if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) {
262 | You("aren't skilled enough to %s %s from %s.",
263 | (flags.pickup && !In_sokoban(&u.uz))
264 | ? "pick up" : "push aside",
265 | the(xname(otmp)), mon_nam(u.usteed));
266 | } else
267 | #endif
268 | {
269 | pline("However, you can easily %s.",
270 | (flags.pickup && !In_sokoban(&u.uz))
271 | ? "pick it up" : "push it aside");
272 | if (In_sokoban(&u.uz))
273 | change_luck(-1); /* Sokoban guilt */
274 | break;
275 | }
276 | break;
277 | }
278 |
279 | if (
280 | #ifdef STEED
281 | !u.usteed &&
282 | #endif
283 | (((!invent || inv_weight() <= -850) &&
284 | (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][sy].typ)
285 | && IS_ROCK(levl[sx][u.uy].typ))))
286 | || verysmall(youmonst.data))) {
287 | pline("However, you can squeeze yourself into a small opening.");
288 | if (In_sokoban(&u.uz))
289 | change_luck(-1); /* Sokoban guilt */
290 | break;
291 | } else
292 | return (-1);
293 | }
294 | }
295 | return (0);
296 | }
297 |
298 | /*
299 | * still_chewing()
300 | *
301 | * Chew on a wall, door, or boulder. Returns TRUE if still eating, FALSE
302 | * when done.
303 | */
304 | STATIC_OVL int
305 | still_chewing(x,y)
306 | xchar x, y;
307 | {
308 | struct rm *lev = &levl[x][y];
309 | struct obj *boulder = sobj_at(BOULDER,x,y);
310 | const char *digtxt = (char *)0, *dmgtxt = (char *)0;
311 |
312 | if (digging.down) /* not continuing previous dig (w/ pick-axe) */
313 | (void) memset((genericptr_t)&digging, 0, sizeof digging);
314 |
315 | if (!boulder && IS_ROCK(lev->typ) && !may_dig(x,y)) {
316 | You("hurt your teeth on the hard stone.");
317 | nomul(0);
318 | return 1;
319 | } else if (digging.pos.x != x || digging.pos.y != y ||
320 | !on_level(&digging.level, &u.uz)) {
321 | digging.down = FALSE;
322 | digging.chew = TRUE;
323 | digging.pos.x = x;
324 | digging.pos.y = y;
325 | assign_level(&digging.level, &u.uz);
326 | /* solid rock takes more work & time to dig through */
327 | digging.effort = (IS_ROCK(lev->typ) ? 30 : 60) + u.udaminc;
328 | You("start chewing %s %s.",
329 | boulder ? "on a" : "a hole in the",
330 | boulder ? "boulder" : IS_ROCK(lev->typ) ? "rock" : "door");
331 | return 1;
332 | } else if ((digging.effort += (30 + u.udaminc)) <= 100) {
333 | if (flags.verbose)
334 | You("%s chewing on the %s.",
335 | digging.chew ? "continue" : "begin",
336 | boulder ? "boulder" : IS_ROCK(lev->typ) ? "rock" : "door");
337 | digging.chew = TRUE;
338 | return 1;
339 | }
340 |
341 | /* Okay, you've chewed through something */
342 | u.uconduct.food++;
343 | u.uhunger += rnd(20);
344 |
345 | if (boulder) {
346 | delobj(boulder); /* boulder goes bye-bye */
347 | You("eat the boulder."); /* yum */
348 |
349 | /*
350 | * The location could still block because of
351 | * 1. More than one boulder
352 | * 2. Boulder stuck in a wall/stone/door.
353 | *
354 | * [perhaps use does_block() below (from vision.c)]
355 | */
356 | if (IS_ROCK(lev->typ) || closed_door(x,y) || sobj_at(BOULDER,x,y)) {
357 | block_point(x,y); /* delobj will unblock the point */
358 | /* reset dig state */
359 | (void) memset((genericptr_t)&digging, 0, sizeof digging);
360 | return 1;
361 | }
362 |
363 | } else if (IS_WALL(lev->typ)) {
364 | if (*in_rooms(x, y, SHOPBASE)) {
365 | add_damage(x, y, 10L * ACURRSTR);
366 | dmgtxt = "damage";
367 | }
368 | digtxt = "chew a hole in the wall.";
369 | if (level.flags.is_maze_lev) {
370 | lev->typ = ROOM;
371 | } else if (level.flags.is_cavernous_lev) {
372 | lev->typ = CORR;
373 | } else {
374 | lev->typ = DOOR;
375 | lev->doormask = D_NODOOR;
376 | }
377 | } else if (lev->typ == SDOOR) {
378 | if (lev->doormask & D_TRAPPED) {
379 | lev->doormask = D_NODOOR;
380 | b_trapped("secret door", 0);
381 | } else {
382 | digtxt = "chew through the secret door.";
383 | lev->doormask = D_BROKEN;
384 | }
385 | lev->typ = DOOR;
386 |
387 | } else if (IS_DOOR(lev->typ)) {
388 | if (*in_rooms(x, y, SHOPBASE)) {
389 | add_damage(x, y, 400L);
390 | dmgtxt = "break";
391 | }
392 | if (lev->doormask & D_TRAPPED) {
393 | lev->doormask = D_NODOOR;
394 | b_trapped("door", 0);
395 | } else {
396 | digtxt = "chew through the door.";
397 | lev->doormask = D_BROKEN;
398 | }
399 |
400 | } else { /* STONE or SCORR */
401 | digtxt = "chew a passage through the rock.";
402 | lev->typ = CORR;
403 | }
404 |
405 | unblock_point(x, y); /* vision */
406 | newsym(x, y);
407 | if (digtxt) You(digtxt); /* after newsym */
408 | if (dmgtxt) pay_for_damage(dmgtxt);
409 | (void) memset((genericptr_t)&digging, 0, sizeof digging);
410 | return 0;
411 | }
412 |
413 | #endif /* OVL2 */
414 | #ifdef OVLB
415 |
416 | void
417 | movobj(obj, ox, oy)
418 | register struct obj *obj;
419 | register xchar ox, oy;
420 | {
421 | /* optimize by leaving on the fobj chain? */
422 | remove_object(obj);
423 | newsym(obj->ox, obj->oy);
424 | place_object(obj, ox, oy);
425 | newsym(ox, oy);
426 | }
427 |
428 | #ifdef SINKS
429 | static NEARDATA const char fell_on_sink[] = "fell onto a sink";
430 |
431 | STATIC_OVL void
432 | dosinkfall()
433 | {
434 | register struct obj *obj;
435 |
436 | if (is_floater(youmonst.data) || (HLevitation & FROMOUTSIDE)) {
437 | You("wobble unsteadily for a moment.");
438 | } else {
439 | You("crash to the floor!");
440 | losehp((rn1(10, 20 - (int)ACURR(A_CON))),
441 | fell_on_sink, NO_KILLER_PREFIX);
442 | exercise(A_DEX, FALSE);
443 | for(obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
444 | if(obj->oclass == WEAPON_CLASS) {
445 | You("fell on %s.",doname(obj));
446 | losehp(rnd(3), fell_on_sink, NO_KILLER_PREFIX);
447 | exercise(A_CON, FALSE);
448 | }
449 | }
450 |
451 | ELevitation &= ~W_ARTI;
452 | HLevitation &= ~(I_SPECIAL|TIMEOUT);
453 | HLevitation++;
454 | if(uleft && uleft->otyp == RIN_LEVITATION) {
455 | obj = uleft;
456 | Ring_off(obj);
457 | off_msg(obj);
458 | }
459 | if(uright && uright->otyp == RIN_LEVITATION) {
460 | obj = uright;
461 | Ring_off(obj);
462 | off_msg(obj);
463 | }
464 | if(uarmf && uarmf->otyp == LEVITATION_BOOTS) {
465 | obj = uarmf;
466 | (void)Boots_off();
467 | off_msg(obj);
468 | }
469 | HLevitation--;
470 | }
471 | #endif
472 |
473 | boolean
474 | may_dig(x,y)
475 | register xchar x,y;
476 | /* intended to be called only on ROCKs */
477 | {
478 | return (boolean)(!(IS_STWALL(levl[x][y].typ) &&
479 | (levl[x][y].wall_info & W_NONDIGGABLE)));
480 | }
481 |
482 | boolean
483 | may_passwall(x,y)
484 | register xchar x,y;
485 | {
486 | return (boolean)(!(IS_STWALL(levl[x][y].typ) &&
487 | (levl[x][y].wall_info & W_NONPASSWALL)));
488 | }
489 |
490 | #endif /* OVLB */
491 | #ifdef OVL1
492 |
493 | boolean
494 | bad_rock(mdat,x,y)
495 | struct permonst *mdat;
496 | register xchar x,y;
497 | {
498 | return((boolean) ((In_sokoban(&u.uz) && sobj_at(BOULDER,x,y)) ||
499 | (IS_ROCK(levl[x][y].typ)
500 | && (!tunnels(mdat) || needspick(mdat) || !may_dig(x,y))
501 | && !(passes_walls(mdat) && may_passwall(x,y)))));
502 | }
503 |
504 | boolean
505 | invocation_pos(x, y)
506 | xchar x, y;
507 | {
508 | return((boolean)(Invocation_lev(&u.uz) && x == inv_pos.x && y == inv_pos.y));
509 | }
510 |
511 | #endif /* OVL1 */
512 | #ifdef OVL3
513 |
514 | void
515 | domove()
516 | {
517 | register struct monst *mtmp;
518 | register struct rm *tmpr,*ust;
519 | register xchar x,y;
520 | struct trap *trap;
521 | int wtcap;
522 | boolean on_ice;
523 | xchar chainx, chainy, ballx, bally; /* ball&chain new positions */
524 | int bc_control; /* control for ball&chain */
525 | boolean cause_delay = FALSE; /* dragging ball will skip a move */
526 |
527 | u_wipe_engr(rnd(5));
528 |
529 | if(((wtcap = near_capacity()) >= OVERLOADED
530 | || (wtcap > SLT_ENCUMBER &&
531 | (Upolyd ? (u.mh < 5 && u.mh != u.mhmax)
532 | : (u.uhp < 10 && u.uhp != u.uhpmax))))
533 | && !Is_airlevel(&u.uz)) {
534 | if(wtcap < OVERLOADED) {
535 | You("don't have enough stamina to move.");
536 | exercise(A_CON, FALSE);
537 | } else
538 | You("collapse under your load.");
539 | nomul(0);
540 | return;
541 | }
542 | if(u.uswallow) {
543 | u.dx = u.dy = 0;
544 | u.ux = x = u.ustuck->mx;
545 | u.uy = y = u.ustuck->my;
546 | mtmp = u.ustuck;
547 | } else {
548 | if (Is_airlevel(&u.uz) && rn2(4) &&
549 | !Levitation && !Flying) {
550 | switch(rn2(3)) {
551 | case 0:
552 | You("tumble in place.");
553 | exercise(A_DEX, FALSE);
554 | break;
555 | case 1:
556 | You_cant("control your movements very well."); break;
557 | case 2:
558 | pline("It's hard to walk in thin air.");
559 | exercise(A_DEX, TRUE);
560 | break;
561 | }
562 | return;
563 | }
564 |
565 | /* check slippery ice */
566 | on_ice = !Levitation && is_ice(u.ux, u.uy);
567 | if (on_ice) {
568 | static int skates = 0;
569 | if (!skates) skates = find_skates();
570 | if ((uarmf && uarmf->otyp == skates)
571 | || resists_cold(&youmonst) || Flying
572 | || is_floater(youmonst.data) || is_clinger(youmonst.data)
573 | || is_whirly(youmonst.data))
574 | on_ice = FALSE;
575 | else if (!rn2(Cold_resistance ? 3 : 2)) {
576 | HFumbling |= FROMOUTSIDE;
577 | HFumbling &= ~TIMEOUT;
578 | HFumbling += 1; /* slip on next move */
579 | }
580 | }
581 | if (!on_ice && (HFumbling & FROMOUTSIDE))
582 | HFumbling &= ~FROMOUTSIDE;
583 |
584 | x = u.ux + u.dx;
585 | y = u.uy + u.dy;
586 | if(Stunned || (Confusion && !rn2(5))) {
587 | register int tries = 0;
588 |
589 | do {
590 | if(tries++ > 50) {
591 | nomul(0);
592 | return;
593 | }
594 | confdir();
595 | x = u.ux + u.dx;
596 | y = u.uy + u.dy;
597 | } while(!isok(x, y) || bad_rock(youmonst.data, x, y));
598 | }
599 | /* turbulence might alter your actual destination */
600 | if (u.uinwater) {
601 | water_friction();
602 | if (!u.dx && !u.dy) {
603 | nomul(0);
604 | return;
605 | }
606 | x = u.ux + u.dx;
607 | y = u.uy + u.dy;
608 | }
609 | if(!isok(x, y)) {
610 | nomul(0);
611 | return;
612 | }
613 | if((trap = t_at(x, y)) && trap->tseen) {
614 | if(flags.run >= 2) {
615 | nomul(0);
616 | flags.move = 0;
617 | return;
618 | } else
619 | nomul(0);
620 | }
621 |
622 | if (u.ustuck && (x != u.ustuck->mx || y != u.ustuck->my)) {
623 | if (distu(u.ustuck->mx, u.ustuck->my) > 2) {
624 | /* perhaps it fled (or was teleported or ... ) */
625 | u.ustuck = 0;
626 | } else if (sticks(youmonst.data)) {
627 | /* When polymorphed into a sticking monster,
628 | * u.ustuck means it's stuck to you, not you to it.
629 | */
630 | You("release %s.", mon_nam(u.ustuck));
631 | u.ustuck = 0;
632 | } else {
633 | /* If holder is asleep or paralyzed:
634 | * 37.5% chance of getting away,
635 | * 12.5% chance of waking/releasing it;
636 | * otherwise:
637 | * 7.5% chance of getting away.
638 | * [strength ought to be a factor]
639 | */
640 | switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) {
641 | case 0: case 1: case 2:
642 | You("pull free from %s.", mon_nam(u.ustuck));
643 | u.ustuck = 0;
644 | break;
645 | case 3:
646 | if (!u.ustuck->mcanmove) {
647 | /* it's free to move on next turn */
648 | u.ustuck->mfrozen = 1;
649 | u.ustuck->msleeping = 0;
650 | }
651 | /*FALLTHRU*/
652 | default:
653 | You("cannot escape from %s!", mon_nam(u.ustuck));
654 | nomul(0);
655 | return;
656 | }
657 | }
658 | }
659 |
660 | mtmp = m_at(x,y);
661 | if (mtmp) {
662 | /* Don't attack if you're running, and can see it */
663 | /* We should never get here if forcefight */
664 | if (flags.run &&
665 | ((!Blind && mon_visible(mtmp) &&
666 | ((mtmp->m_ap_type != M_AP_FURNITURE &&
667 | mtmp->m_ap_type != M_AP_OBJECT) ||
668 | Protection_from_shape_changers)) ||
669 | sensemon(mtmp))) {
670 | nomul(0);
671 | flags.move = 0;
672 | return;
673 | }
674 | }
675 | }
676 |
677 | u.ux0 = u.ux;
678 | u.uy0 = u.uy;
679 | bhitpos.x = x;
680 | bhitpos.y = y;
681 | tmpr = &levl[x][y];
682 |
683 | /* attack monster */
684 | if(mtmp) {
685 | nomul(0);
686 | /* only attack if we know it's there */
687 | /* or if we used the 'F' command to fight blindly */
688 | /* or if it hides_under, in which case we call attack() to print
689 | * the Wait! message.
690 | * This is different from ceiling hiders, who aren't handled in
691 | * attack().
692 | */
693 |
694 | /* If they used a 'm' command, trying to move onto a monster
695 | * prints the below message and wastes a turn. The exception is
696 | * if the monster is unseen and the player doesn't remember an
697 | * invisible monster--then, we fall through to attack() and
698 | * attack_check(), which still wastes a turn, but prints a
699 | * different message and makes the player remember the monster. */
700 | if(flags.nopick &&
701 | (canspotmon(mtmp) || glyph_is_invisible(levl[x][y].glyph))){
702 | if(mtmp->m_ap_type && !Protection_from_shape_changers
703 | && !sensemon(mtmp))
704 | stumble_onto_mimic(mtmp);
705 | else if (mtmp->mpeaceful)
706 | pline("Pardon me, %s.", m_monnam(mtmp));
707 | else
708 | You("move right into %s.", mon_nam(mtmp));
709 | return;
710 | }
711 | if(flags.forcefight || !mtmp->mundetected || sensemon(mtmp) ||
712 | ((hides_under(mtmp->data) || mtmp->data->mlet == S_EEL) &&
713 | !is_safepet(mtmp))){
714 | gethungry();
715 | if(wtcap >= HVY_ENCUMBER && moves%3) {
716 | if (Upolyd && u.mh > 1) {
717 | u.mh--;
718 | } else if (!Upolyd && u.uhp > 1) {
719 | u.uhp--;
720 | } else {
721 | You("pass out from exertion!");
722 | exercise(A_CON, FALSE);
723 | fall_asleep(-10, FALSE);
724 | }
725 | }
726 | if(multi < 0) return; /* we just fainted */
727 |
728 | /* try to attack; note that it might evade */
729 | /* also, we don't attack tame when _safepet_ */
730 | if(attack(mtmp)) return;
731 | }
732 | }
733 |
734 | /* specifying 'F' with no monster wastes a turn */
735 | if (flags.forcefight ||
736 | /* remembered an 'I' && didn't use a move command */
737 | (glyph_is_invisible(levl[x][y].glyph) && !flags.nopick)) {
738 | You("attack %s.", Underwater ? "empty water" : "thin air");
739 | unmap_object(x, y); /* known empty -- remove 'I' if present */
740 | newsym(x, y);
741 | nomul(0);
742 | return;
743 | }
744 | if (glyph_is_invisible(levl[x][y].glyph)) {
745 | unmap_object(x, y);
746 | newsym(x, y);
747 | }
748 | /* not attacking an animal, so we try to move */
749 | #ifdef STEED
750 | if (u.usteed && !u.usteed->mcanmove && (u.dx || u.dy)) {
751 | pline("%s won't move!", Monnam(u.usteed));
752 | nomul(0);
753 | return;
754 | } else
755 | #endif
756 | if(!youmonst.data->mmove) {
757 | You("are rooted %s.",
758 | Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) ?
759 | "in place" : "to the ground");
760 | nomul(0);
761 | return;
762 | }
763 | if(u.utrap) {
764 | if(u.utraptype == TT_PIT) {
765 | if (!rn2(2) && sobj_at(BOULDER, u.ux, u.uy)) {
766 | Your("%s gets stuck in a crevice.", body_part(LEG));
767 | display_nhwindow(WIN_MESSAGE, FALSE);
768 | clear_nhwindow(WIN_MESSAGE);
769 | You("free your %s.", body_part(LEG));
770 | } else if (!(--u.utrap)) {
771 | You("%s to the edge of the pit.",
772 | (In_sokoban(&u.uz) && Levitation) ?
773 | "struggle against the air currents and float" : "crawl");
774 | fill_pit(u.ux, u.uy);
775 | vision_full_recalc = 1; /* vision limits change */
776 | } else if (flags.verbose)
777 | Norep( (Hallucination && !rn2(5)) ?
778 | "You've fallen, and you can't get up." :
779 | "You are still in a pit." );
780 | } else if (u.utraptype == TT_LAVA) {
781 | if(flags.verbose)
782 | Norep("You are stuck in the lava.");
783 | if(!is_lava(x,y)) {
784 | u.utrap--;
785 | if((u.utrap & 0xff) == 0) {
786 | You("pull yourself to the edge of the lava.");
787 | u.utrap = 0;
788 | }
789 | }
790 | u.umoved = TRUE;
791 | } else if (u.utraptype == TT_WEB) {
792 | if(uwep && uwep->oartifact == ART_STING) {
793 | u.utrap = 0;
794 | pline("Sting cuts through the web!");
795 | return;
796 | }
797 | if(--u.utrap) {
798 | if(flags.verbose)
799 | Norep("You are stuck to the web.");
800 | } else You("disentangle yourself.");
801 | } else if (u.utraptype == TT_INFLOOR) {
802 | if(--u.utrap) {
803 | if(flags.verbose)
804 | Norep("You are stuck in the %s.",
805 | surface(u.ux, u.uy));
806 | } else You("finally wiggle free.");
807 | } else {
808 | if(flags.verbose)
809 | Norep("You are caught in a bear trap.");
810 | if((u.dx && u.dy) || !rn2(5)) u.utrap--;
811 | }
812 | return;
813 | }
814 |
815 |
816 | /*
817 | * Check for physical obstacles. First, the place we are going.
818 | */
819 | if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
820 | if (Blind) feel_location(x,y);
821 | if (Passes_walls && may_passwall(x,y)) {
822 | ; /* do nothing */
823 | } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
824 | /* Eat the rock. */
825 | if (still_chewing(x,y)) return;
826 | } else {
827 | if (Is_stronghold(&u.uz) && is_db_wall(x,y))
828 | pline_The("drawbridge is up!");
829 | flags.move = 0;
830 | nomul(0);
831 | return;
832 | }
833 | } else if (IS_DOOR(tmpr->typ)) {
834 | if (closed_door(x,y)) {
835 | if (Blind) feel_location(x,y);
836 | if (Passes_walls)
837 | ; /* do nothing */
838 | else if (can_ooze(&youmonst))
839 | You("ooze under the door.");
840 | else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
841 | /* Eat the door. */
842 | if (still_chewing(x,y)) return;
843 | } else {
844 | flags.move = 0;
845 | if (amorphous(youmonst.data))
846 | You("try to ooze under the door, but can't squeeze your possessions through.");
847 | else if (x == u.ux || y == u.uy) {
848 | if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) {
849 | pline("Ouch! You bump into a door.");
850 | exercise(A_DEX, FALSE);
851 | } else pline("That door is closed.");
852 | }
853 | nomul(0);
854 | return;
855 | }
856 | } else if (u.dx && u.dy && !Passes_walls
857 | && ((tmpr->doormask & ~D_BROKEN)
858 | #ifdef REINCARNATION
859 | || Is_rogue_level(&u.uz)
860 | #endif
861 | || block_door(x,y))) {
862 | /* Diagonal moves into a door are not allowed. */
863 | if (Blind) feel_location(x,y); /* ?? */
864 | flags.move = 0;
865 | nomul(0);
866 | return;
867 | }
868 | }
869 | if (u.dx && u.dy
870 | && bad_rock(youmonst.data,u.ux,y) && bad_rock(youmonst.data,x,u.uy)) {
871 | /* Move at a diagonal. */
872 | if (In_sokoban(&u.uz)) {
873 | You("cannot pass that way.");
874 | nomul(0);
875 | return;
876 | }
877 | if (bigmonst(youmonst.data)) {
878 | Your("body is too large to fit through.");
879 | nomul(0);
880 | return;
881 | }
882 | if (invent && (inv_weight() + weight_cap() > 600)) {
883 | You("are carrying too much to get through.");
884 | nomul(0);
885 | return;
886 | }
887 | }
888 |
889 | ust = &levl[u.ux][u.uy];
890 |
891 | /* Now see if other things block our way . . */
892 | if (u.dx && u.dy && !Passes_walls
893 | && (IS_DOOR(ust->typ) && ((ust->doormask & ~D_BROKEN)
894 | #ifdef REINCARNATION
895 | || Is_rogue_level(&u.uz)
896 | #endif
897 | || block_entry(x, y))
898 | )) {
899 | /* Can't move at a diagonal out of a doorway with door. */
900 | flags.move = 0;
901 | nomul(0);
902 | return;
903 | }
904 |
905 | if (sobj_at(BOULDER,x,y) && (In_sokoban(&u.uz) || !Passes_walls)) {
906 | if (!(Blind || Hallucination) && (flags.run >= 2)) {
907 | nomul(0);
908 | flags.move = 0;
909 | return;
910 | }
911 | /* tunneling monsters will chew before pushing */
912 | if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
913 | if (still_chewing(x,y)) return;
914 | } else
915 | if (moverock() < 0) return;
916 | }
917 |
918 | /* OK, it is a legal place to move. */
919 |
920 | /* Move ball and chain. */
921 | if (Punished)
922 | if (!drag_ball(x,y, &bc_control, &ballx, &bally, &chainx, &chainy,
923 | &cause_delay))
924 | return;
925 |
926 | /* Check regions entering/leaving */
927 | if (!in_out_region(x,y))
928 | return;
929 |
930 | /* now move the hero */
931 | mtmp = m_at(x, y);
932 | u.ux += u.dx;
933 | u.uy += u.dy;
934 | #ifdef STEED
935 | /* Move your steed, too */
936 | if (u.usteed) {
937 | u.usteed->mx = u.ux;
938 | u.usteed->my = u.uy;
939 | exercise_steed();
940 | }
941 | #endif
942 |
943 | /*
944 | * If safepet at destination then move the pet to the hero's
945 | * previous location using the same conditions as in attack().
946 | * there are special extenuating circumstances:
947 | * (1) if the pet dies then your god angers,
948 | * (2) if the pet gets trapped then your god may disapprove,
949 | * (3) if the pet was already trapped and you attempt to free it
950 | * not only do you encounter the trap but you may frighten your
951 | * pet causing it to go wild! moral: don't abuse this privilege.
952 | *
953 | * Ceiling-hiding pets are skipped by this section of code, to
954 | * be caught by the normal falling-monster code.
955 | */
956 | if (is_safepet(mtmp) && !(is_hider(mtmp->data) && mtmp->mundetected)) {
957 | /* if trapped, there's a chance the pet goes wild */
958 | if (mtmp->mtrapped) {
959 | if (!rn2(mtmp->mtame)) {
960 | mtmp->mtame = mtmp->mpeaceful = mtmp->msleeping = 0;
961 | growl(mtmp);
962 | } else {
963 | yelp(mtmp);
964 | }
965 | }
966 | mtmp->mundetected = 0;
967 | if (mtmp->m_ap_type) seemimic(mtmp);
968 | else if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my);
969 |
970 | if (mtmp->mtrapped &&
971 | (trap = t_at(mtmp->mx, mtmp->my)) != 0 &&
972 | (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT) &&
973 | sobj_at(BOULDER, trap->tx, trap->ty)) {
974 | /* can't swap places with pet pinned in a pit by a boulder */
975 | u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */
976 | } else {
977 | mtmp->mtrapped = 0;
978 | remove_monster(x, y);
979 | place_monster(mtmp, u.ux0, u.uy0);
980 |
981 | /* check for displacing it into pools and traps */
982 | switch (minwater(mtmp) ? 2 : mintrap(mtmp)) {
983 | case 0:
984 | You("%s %s.", mtmp->mtame ? "displaced" : "frightened",
985 | y_monnam(mtmp));
986 | break;
987 | case 1: /* trapped */
988 | case 3: /* changed levels */
989 | /* there's already been a trap message, reinforce it */
990 | abuse_dog(mtmp);
991 | adjalign(-3);
992 | break;
993 | case 2:
994 | /* it may have drowned or died. that's no way to
995 | * treat a pet! your god gets angry.
996 | */
997 | if (rn2(4)) {
998 | You_feel("guilty about losing your pet like this.");
999 | u.ugangr++;
1000 | adjalign(-15);
1001 | }
1002 | break;
1003 | default:
1004 | pline("that's strange, unknown mintrap result!");
1005 | break;
1006 | }
1007 | }
1008 | }
1009 |
1010 | reset_occupations();
1011 | if (flags.run) {
1012 | if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ) ||
1013 | IS_FURNITURE(tmpr->typ))
1014 | nomul(0);
1015 | }
1016 |
1017 | if (hides_under(youmonst.data))
1018 | u.uundetected = OBJ_AT(u.ux, u.uy);
1019 | else if (youmonst.data->mlet == S_EEL)
1020 | u.uundetected = is_pool(u.ux, u.uy) && !Is_waterlevel(&u.uz);
1021 | else if (u.dx || u.dy)
1022 | u.uundetected = 0;
1023 |
1024 | /*
1025 | * Mimics (or whatever) become noticeable if they move and are
1026 | * imitating something that doesn't move. We could extend this
1027 | * to non-moving monsters...
1028 | */
1029 | if ((u.dx || u.dy) && (youmonst.m_ap_type == M_AP_OBJECT
1030 | || youmonst.m_ap_type == M_AP_FURNITURE))
1031 | youmonst.m_ap_type = M_AP_NOTHING;
1032 |
1033 | check_leash(u.ux0,u.uy0);
1034 |
1035 | if(u.ux0 != u.ux || u.uy0 != u.uy) {
1036 | u.umoved = TRUE;
1037 | /* Clean old position -- vision_recalc() will print our new one. */
1038 | newsym(u.ux0,u.uy0);
1039 | /* Since the hero has moved, adjust what can be seen/unseen. */
1040 | vision_recalc(1); /* Do the work now in the recover time. */
1041 | invocation_message();
1042 | }
1043 |
1044 | if (Punished) /* put back ball and chain */
1045 | move_bc(0,bc_control,ballx,bally,chainx,chainy);
1046 |
1047 | spoteffects(TRUE);
1048 |
1049 | /* delay next move because of ball dragging */
1050 | /* must come after we finished picking up, in spoteffects() */
1051 | if (cause_delay) {
1052 | nomul(-2);
1053 | nomovemsg = "";
1054 | }
1055 | }
1056 |
1057 | void
1058 | invocation_message()
1059 | {
1060 | /* a special clue-msg when on the Invocation position */
1061 | if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
1062 | struct obj *otmp = carrying(CANDELABRUM_OF_INVOCATION);
1063 |
1064 | You_feel("a strange vibration under your %s.",
1065 | makeplural(body_part(FOOT)));
1066 | if (otmp && otmp->spe == 7 && otmp->lamplit)
1067 | pline("%s %s!", The(xname(otmp)),
1068 | Blind ? "throbs palpably" : "glows with a strange light");
1069 | }
1070 | }
1071 |
1072 | #endif /* OVL3 */
1073 | #ifdef OVL2
1074 |
1075 | void
1076 | spoteffects(pick)
1077 | boolean pick;
1078 | {
1079 | register struct trap *trap;
1080 | register struct monst *mtmp;
1081 |
1082 | if(u.uinwater) {
1083 | int was_underwater;
1084 |
1085 | if (!is_pool(u.ux,u.uy)) {
1086 | if (Is_waterlevel(&u.uz))
1087 | You("pop into an air bubble.");
1088 | else if (is_lava(u.ux, u.uy))
1089 | You("leave the water..."); /* oops! */
1090 | else
1091 | You("are on solid %s again.",
1092 | is_ice(u.ux, u.uy) ? "ice" : "land");
1093 | }
1094 | else if (Is_waterlevel(&u.uz))
1095 | goto stillinwater;
1096 | else if (Levitation)
1097 | You("pop out of the water like a cork!");
1098 | else if (Flying)
1099 | You("fly out of the water.");
1100 | else if (Wwalking)
1101 | You("slowly rise above the surface.");
1102 | else
1103 | goto stillinwater;
1104 | was_underwater = Underwater && !Is_waterlevel(&u.uz);
1105 | u.uinwater = 0; /* leave the water */
1106 | if (was_underwater) { /* restore vision */
1107 | docrt();
1108 | vision_full_recalc = 1;
1109 | }
1110 | }
1111 | stillinwater:;
1112 | if (!Levitation && !u.ustuck && !Flying) {
1113 | /* limit recursive calls through teleds() */
1114 | if(is_lava(u.ux,u.uy) && lava_effects())
1115 | return;
1116 | if(is_pool(u.ux,u.uy) && !Wwalking && drown())
1117 | return;
1118 | }
1119 | check_special_room(FALSE);
1120 | #ifdef SINKS
1121 | if(IS_SINK(levl[u.ux][u.uy].typ) && Levitation)
1122 | dosinkfall();
1123 | #endif
1124 | if (pick && !in_steed_dismounting)
1125 | (void) pickup(1);
1126 | if ((trap = t_at(u.ux,u.uy)) != 0)
1127 | dotrap(trap); /* fall into pit, arrow trap, etc. */
1128 | if((mtmp = m_at(u.ux, u.uy)) && !u.uswallow) {
1129 | mtmp->mundetected = mtmp->msleeping = 0;
1130 | switch(mtmp->data->mlet) {
1131 | case S_PIERCER:
1132 | pline("%s suddenly drops from the %s!",
1133 | Amonnam(mtmp), ceiling(u.ux,u.uy));
1134 | if(mtmp->mtame) /* jumps to greet you, not attack */
1135 | ;
1136 | else if(uarmh)
1137 | pline("Its blow glances off your helmet.");
1138 | else if (u.uac + 3 <= rnd(20))
1139 | You("are almost hit by %s!",
1140 | x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
1141 | else {
1142 | int dmg;
1143 | You("are hit by %s!",
1144 | x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE));
1145 | dmg = d(4,6);
1146 | if(Half_physical_damage) dmg = (dmg+1) / 2;
1147 | mdamageu(mtmp, dmg);
1148 | }
1149 | break;
1150 | default: /* monster surprises you. */
1151 | if(mtmp->mtame)
1152 | pline("%s jumps near you from the %s.",
1153 | Amonnam(mtmp), ceiling(u.ux,u.uy));
1154 | else if(mtmp->mpeaceful) {
1155 | You("surprise %s!",
1156 | Blind && !sensemon(mtmp) ?
1157 | something : a_monnam(mtmp));
1158 | mtmp->mpeaceful = 0;
1159 | } else
1160 | pline("%s attacks you by surprise!",
1161 | Amonnam(mtmp));
1162 | break;
1163 | }
1164 | mnexto(mtmp); /* have to move the monster */
1165 | }
1166 | }
1167 |
1168 | STATIC_OVL boolean
1169 | monstinroom(mdat,roomno)
1170 | struct permonst *mdat;
1171 | int roomno;
1172 | {
1173 | register struct monst *mtmp;
1174 |
1175 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
1176 | if(!DEADMONSTER(mtmp) && mtmp->data == mdat &&
1177 | index(in_rooms(mtmp->mx, mtmp->my, 0), roomno + ROOMOFFSET))
1178 | return(TRUE);
1179 | return(FALSE);
1180 | }
1181 |
1182 | char *
1183 | in_rooms(x, y, typewanted)
1184 | register xchar x, y;
1185 | register int typewanted;
1186 | {
1187 | static char buf[5];
1188 | char rno, *ptr = &buf[4];
1189 | int typefound, min_x, min_y, max_x, max_y_offset, step;
1190 | register struct rm *lev;
1191 |
1192 | #define goodtype(rno) (!typewanted || \
1193 | ((typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted) || \
1194 | ((typewanted == SHOPBASE) && (typefound > SHOPBASE))) \
1195 |
1196 | switch (rno = levl[x][y].roomno) {
1197 | case NO_ROOM:
1198 | return(ptr);
1199 | case SHARED:
1200 | step = 2;
1201 | break;
1202 | case SHARED_PLUS:
1203 | step = 1;
1204 | break;
1205 | default: /* i.e. a regular room # */
1206 | if (goodtype(rno))
1207 | *(--ptr) = rno;
1208 | return(ptr);
1209 | }
1210 |
1211 | min_x = x - 1;
1212 | max_x = x + 1;
1213 | if (x < 1)
1214 | min_x += step;
1215 | else
1216 | if (x >= COLNO)
1217 | max_x -= step;
1218 |
1219 | min_y = y - 1;
1220 | max_y_offset = 2;
1221 | if (min_y < 0) {
1222 | min_y += step;
1223 | max_y_offset -= step;
1224 | } else
1225 | if ((min_y + max_y_offset) >= ROWNO)
1226 | max_y_offset -= step;
1227 |
1228 | for (x = min_x; x <= max_x; x += step) {
1229 | lev = &levl[x][min_y];
1230 | y = 0;
1231 | if (((rno = lev[y].roomno) >= ROOMOFFSET) &&
1232 | !index(ptr, rno) && goodtype(rno))
1233 | *(--ptr) = rno;
1234 | y += step;
1235 | if (y > max_y_offset)
1236 | continue;
1237 | if (((rno = lev[y].roomno) >= ROOMOFFSET) &&
1238 | !index(ptr, rno) && goodtype(rno))
1239 | *(--ptr) = rno;
1240 | y += step;
1241 | if (y > max_y_offset)
1242 | continue;
1243 | if (((rno = lev[y].roomno) >= ROOMOFFSET) &&
1244 | !index(ptr, rno) && goodtype(rno))
1245 | *(--ptr) = rno;
1246 | }
1247 | return(ptr);
1248 | }
1249 |
1250 | STATIC_OVL void
1251 | move_update(newlev)
1252 | register boolean newlev;
1253 | {
1254 | char *ptr1, *ptr2, *ptr3, *ptr4;
1255 |
1256 | Strcpy(u.urooms0, u.urooms);
1257 | Strcpy(u.ushops0, u.ushops);
1258 | if (newlev) {
1259 | u.urooms[0] = '\0';
1260 | u.uentered[0] = '\0';
1261 | u.ushops[0] = '\0';
1262 | u.ushops_entered[0] = '\0';
1263 | Strcpy(u.ushops_left, u.ushops0);
1264 | return;
1265 | }
1266 | Strcpy(u.urooms, in_rooms(u.ux, u.uy, 0));
1267 |
1268 | for (ptr1 = &u.urooms[0],
1269 | ptr2 = &u.uentered[0],
1270 | ptr3 = &u.ushops[0],
1271 | ptr4 = &u.ushops_entered[0];
1272 | *ptr1; ptr1++) {
1273 | if (!index(u.urooms0, *ptr1))
1274 | *(ptr2++) = *ptr1;
1275 | if (IS_SHOP(*ptr1 - ROOMOFFSET)) {
1276 | *(ptr3++) = *ptr1;
1277 | if (!index(u.ushops0, *ptr1))
1278 | *(ptr4++) = *ptr1;
1279 | }
1280 | }
1281 | *ptr2 = '\0';
1282 | *ptr3 = '\0';
1283 | *ptr4 = '\0';
1284 |
1285 | /* filter u.ushops0 -> u.ushops_left */
1286 | for (ptr1 = &u.ushops0[0], ptr2 = &u.ushops_left[0]; *ptr1; ptr1++)
1287 | if (!index(u.ushops, *ptr1))
1288 | *(ptr2++) = *ptr1;
1289 | *ptr2 = '\0';
1290 | }
1291 |
1292 | void
1293 | check_special_room(newlev)
1294 | register boolean newlev;
1295 | {
1296 | register struct monst *mtmp;
1297 | char *ptr;
1298 |
1299 | move_update(newlev);
1300 |
1301 | if (*u.ushops0)
1302 | u_left_shop(u.ushops_left, newlev);
1303 |
1304 | if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */
1305 | return; /* no entrance messages necessary */
1306 |
1307 | /* Did we just enter a shop? */
1308 | if (*u.ushops_entered)
1309 | u_entered_shop(u.ushops_entered);
1310 |
1311 | for (ptr = &u.uentered[0]; *ptr; ptr++) {
1312 | register int roomno = *ptr - ROOMOFFSET, rt = rooms[roomno].rtype;
1313 |
1314 | /* Did we just enter some other special room? */
1315 | /* vault.c insists that a vault remain a VAULT,
1316 | * and temples should remain TEMPLEs,
1317 | * but everything else gives a message only the first time */
1318 | switch (rt) {
1319 | case ZOO:
1320 | pline("Welcome to David's treasure zoo!");
1321 | break;
1322 | case SWAMP:
1323 | pline("It %s rather %s down here.",
1324 | Blind ? "feels" : "looks",
1325 | Blind ? "humid" : "muddy");
1326 | break;
1327 | case COURT:
1328 | You("enter an opulent throne room!");
1329 | break;
1330 | case LEPREHALL:
1331 | You("enter a leprechaun hall!");
1332 | break;
1333 | case MORGUE:
1334 | if(midnight()) {
1335 | const char *run = locomotion(youmonst.data, "Run");
1336 | pline("%s away! %s away!", run, run);
1337 | } else
1338 | You("have an uncanny feeling...");
1339 | break;
1340 | case BEEHIVE:
1341 | You("enter a giant beehive!");
1342 | break;
1343 | case COCKNEST:
1344 | You("enter a disgusting nest!");
1345 | break;
1346 | case ANTHOLE:
1347 | You("enter an anthole!");
1348 | break;
1349 | case BARRACKS:
1350 | if(monstinroom(&mons[PM_SOLDIER], roomno) ||
1351 | monstinroom(&mons[PM_SERGEANT], roomno) ||
1352 | monstinroom(&mons[PM_LIEUTENANT], roomno) ||
1353 | monstinroom(&mons[PM_CAPTAIN], roomno))
1354 | You("enter a military barracks!");
1355 | else
1356 | You("enter an abandoned barracks.");
1357 | break;
1358 | case DELPHI:
1359 | if(monstinroom(&mons[PM_ORACLE], roomno))
1360 | verbalize("%s, %s, welcome to Delphi!",
1361 | Hello((struct monst *) 0), plname);
1362 | break;
1363 | case TEMPLE:
1364 | intemple(roomno + ROOMOFFSET);
1365 | /* fall through */
1366 | default:
1367 | rt = 0;
1368 | }
1369 |
1370 | if (rt != 0) {
1371 | rooms[roomno].rtype = OROOM;
1372 | if (!search_special(rt)) {
1373 | /* No more room of that type */
1374 | switch(rt) {
1375 | case COURT:
1376 | level.flags.has_court = 0;
1377 | break;
1378 | case SWAMP:
1379 | level.flags.has_swamp = 0;
1380 | break;
1381 | case MORGUE:
1382 | level.flags.has_morgue = 0;
1383 | break;
1384 | case ZOO:
1385 | level.flags.has_zoo = 0;
1386 | break;
1387 | case BARRACKS:
1388 | level.flags.has_barracks = 0;
1389 | break;
1390 | case TEMPLE:
1391 | level.flags.has_temple = 0;
1392 | break;
1393 | case BEEHIVE:
1394 | level.flags.has_beehive = 0;
1395 | break;
1396 | }
1397 | }
1398 | if (rt == COURT || rt == SWAMP || rt == MORGUE || rt == ZOO)
1399 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
1400 | if (!DEADMONSTER(mtmp) && !Stealth && !rn2(3)) mtmp->msleeping = 0;
1401 | }
1402 | }
1403 |
1404 | return;
1405 | }
1406 |
1407 | #endif /* OVL2 */
1408 | #ifdef OVLB
1409 |
1410 | int
1411 | dopickup()
1412 | {
1413 | int count;
1414 | /* awful kludge to work around parse()'s pre-decrement */
1415 | count = (multi || (save_cm && *save_cm == ',')) ? multi + 1 : 0;
1416 | multi = 0; /* always reset */
1417 | /* uswallow case added by GAN 01/29/87 */
1418 | if(u.uswallow) {
1419 | if (is_animal(u.ustuck->data)) {
1420 | You("pick up %s tongue.", s_suffix(mon_nam(u.ustuck)));
1421 | pline("But it's kind of slimy, so you drop it.");
1422 | } else
1423 | You("don't %s anything in here to pick up.",
1424 | Blind ? "feel" : "see");
1425 | return(1);
1426 | }
1427 | if(is_pool(u.ux, u.uy)) {
1428 | if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
1429 | || (Flying && !Breathless)) {
1430 | You("cannot dive into the water to pick things up.");
1431 | return(0);
1432 | } else if (!Underwater) {
1433 | You_cant("even see the bottom, let alone pick up %s.",
1434 | something);
1435 | return(0);
1436 | }
1437 | }
1438 | if (is_lava(u.ux, u.uy)) {
1439 | if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
1440 | || (Flying && !Breathless)) {
1441 | You_cant("reach the bottom to pick things up.");
1442 | return(0);
1443 | } else if (!likes_lava(youmonst.data)) {
1444 | You("would burn to a crisp trying to pick things up.");
1445 | return(0);
1446 | }
1447 | }
1448 | if(!OBJ_AT(u.ux, u.uy)) {
1449 | There("is nothing here to pick up.");
1450 | return(0);
1451 | }
1452 | if (!can_reach_floor()) {
1453 | #ifdef STEED
1454 | if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
1455 | You("aren't skilled enough to reach from %s.",
1456 | mon_nam(u.usteed));
1457 | else
1458 | #endif
1459 | You("cannot reach the %s.", surface(u.ux,u.uy));
1460 | return(0);
1461 | }
1462 | return (pickup(-count));
1463 | }
1464 |
1465 | #endif /* OVLB */
1466 | #ifdef OVL2
1467 |
1468 | /* stop running if we see something interesting */
1469 | /* turn around a corner if that is the only way we can proceed */
1470 | /* do not turn left or right twice */
1471 | void
1472 | lookaround()
1473 | {
1474 | register int x, y, i, x0 = 0, y0 = 0, m0 = 1, i0 = 9;
1475 | register int corrct = 0, noturn = 0;
1476 | register struct monst *mtmp;
1477 | register struct trap *trap;
1478 |
1479 | /* Grid bugs stop if trying to move diagonal, even if blind. Maybe */
1480 | /* they polymorphed while in the middle of a long move. */
1481 | if (u.umonnum == PM_GRID_BUG && u.dx && u.dy) {
1482 | nomul(0);
1483 | return;
1484 | }
1485 |
1486 | if(Blind || flags.run == 0) return;
1487 | for(x = u.ux-1; x <= u.ux+1; x++) for(y = u.uy-1; y <= u.uy+1; y++) {
1488 | if(!isok(x,y)) continue;
1489 |
1490 | if(u.umonnum == PM_GRID_BUG && x != u.ux && y != u.uy) continue;
1491 |
1492 | if(x == u.ux && y == u.uy) continue;
1493 |
1494 | if((mtmp = m_at(x,y)) &&
1495 | mtmp->m_ap_type != M_AP_FURNITURE &&
1496 | mtmp->m_ap_type != M_AP_OBJECT &&
1497 | (!mtmp->minvis || See_invisible) && !mtmp->mundetected) {
1498 | if((flags.run != 1 && !mtmp->mtame)
1499 | || (x == u.ux+u.dx && y == u.uy+u.dy))
1500 | goto stop;
1501 | }
1502 |
1503 | if (levl[x][y].typ == STONE) continue;
1504 | if (x == u.ux-u.dx && y == u.uy-u.dy) continue;
1505 |
1506 | if (IS_ROCK(levl[x][y].typ) || (levl[x][y].typ == ROOM) ||
1507 | IS_AIR(levl[x][y].typ))
1508 | continue;
1509 | else if (closed_door(x,y)) {
1510 | if(x != u.ux && y != u.uy) continue;
1511 | if(flags.run != 1) goto stop;
1512 | goto bcorr;
1513 | } else if (levl[x][y].typ == CORR) {
1514 | bcorr:
1515 | if(levl[u.ux][u.uy].typ != ROOM) {
1516 | if(flags.run == 1 || flags.run == 3) {
1517 | i = dist2(x,y,u.ux+u.dx,u.uy+u.dy);
1518 | if(i > 2) continue;
1519 | if(corrct == 1 && dist2(x,y,x0,y0) != 1)
1520 | noturn = 1;
1521 | if(i < i0) {
1522 | i0 = i;
1523 | x0 = x;
1524 | y0 = y;
1525 | m0 = mtmp ? 1 : 0;
1526 | }
1527 | }
1528 | corrct++;
1529 | }
1530 | continue;
1531 | } else if ((trap = t_at(x,y)) && trap->tseen) {
1532 | if(flags.run == 1) goto bcorr; /* if you must */
1533 | if(x == u.ux+u.dx && y == u.uy+u.dy) goto stop;
1534 | continue;
1535 | } else if (is_pool(x,y) || is_lava(x,y)) {
1536 | /* water and lava only stop you if directly in front, and stop
1537 | * you even if you are running
1538 | */
1539 | if(!Levitation && !Flying && !is_clinger(youmonst.data) &&
1540 | x == u.ux+u.dx && y == u.uy+u.dy)
1541 | /* No Wwalking check; otherwise they'd be able
1542 | * to test boots by trying to SHIFT-direction
1543 | * into a pool and seeing if the game allowed it
1544 | */
1545 | goto stop;
1546 | continue;
1547 | } else { /* e.g. objects or trap or stairs */
1548 | if(flags.run == 1) goto bcorr;
1549 | if(mtmp) continue; /* d */
1550 | if(((x == u.ux - u.dx) && (y != u.uy + u.dy)) ||
1551 | ((y == u.uy - u.dy) && (x != u.ux + u.dx)))
1552 | continue;
1553 | }
1554 | stop:
1555 | nomul(0);
1556 | return;
1557 | } /* end for loops */
1558 |
1559 | if(corrct > 1 && flags.run == 2) goto stop;
1560 | if((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 &&
1561 | (corrct == 1 || (corrct == 2 && i0 == 1))) {
1562 | /* make sure that we do not turn too far */
1563 | if(i0 == 2) {
1564 | if(u.dx == y0-u.uy && u.dy == u.ux-x0)
1565 | i = 2; /* straight turn right */
1566 | else
1567 | i = -2; /* straight turn left */
1568 | } else if(u.dx && u.dy) {
1569 | if((u.dx == u.dy && y0 == u.uy) || (u.dx != u.dy && y0 != u.uy))
1570 | i = -1; /* half turn left */
1571 | else
1572 | i = 1; /* half turn right */
1573 | } else {
1574 | if((x0-u.ux == y0-u.uy && !u.dy) || (x0-u.ux != y0-u.uy && u.dy))
1575 | i = 1; /* half turn right */
1576 | else
1577 | i = -1; /* half turn left */
1578 | }
1579 |
1580 | i += u.last_str_turn;
1581 | if(i <= 2 && i >= -2) {
1582 | u.last_str_turn = i;
1583 | u.dx = x0-u.ux;
1584 | u.dy = y0-u.uy;
1585 | }
1586 | }
1587 | }
1588 |
1589 | /* something like lookaround, but we are not running */
1590 | /* react only to monsters that might hit us */
1591 | int
1592 | monster_nearby()
1593 | {
1594 | register int x,y;
1595 | register struct monst *mtmp;
1596 |
1597 | /* Also see the similar check in dochugw() in monmove.c */
1598 | for(x = u.ux-1; x <= u.ux+1; x++)
1599 | for(y = u.uy-1; y <= u.uy+1; y++) {
1600 | if(!isok(x,y)) continue;
1601 | if(x == u.ux && y == u.uy) continue;
1602 | if((mtmp = m_at(x,y)) &&
1603 | mtmp->m_ap_type != M_AP_FURNITURE &&
1604 | mtmp->m_ap_type != M_AP_OBJECT &&
1605 | (!mtmp->mpeaceful || Hallucination) &&
1606 | (!is_hider(mtmp->data) || !mtmp->mundetected) &&
1607 | !noattacks(mtmp->data) &&
1608 | mtmp->mcanmove && !mtmp->msleeping && /* aplvax!jcn */
1609 | !onscary(u.ux, u.uy, mtmp) &&
1610 | canspotmon(mtmp))
1611 | return(1);
1612 | }
1613 | return(0);
1614 | }
1615 |
1616 | void
1617 | nomul(nval)
1618 | register int nval;
1619 | {
1620 | if(multi < nval) return; /* This is a bug fix by ab@unido */
1621 | u.uinvulnerable = FALSE; /* Kludge to avoid ctrl-C bug -dlc */
1622 | u.usleep = 0;
1623 | multi = nval;
1624 | flags.mv = flags.run = 0;
1625 | }
1626 |
1627 | /* called when a non-movement, multi-turn action has completed */
1628 | void unmul(msg_override)
1629 | const char *msg_override;
1630 | {
1631 | multi = 0; /* caller will usually have done this already */
1632 | if (msg_override) nomovemsg = msg_override;
1633 | else if (!nomovemsg) nomovemsg = You_can_move_again;
1634 | if (*nomovemsg) pline(nomovemsg);
1635 | nomovemsg = 0;
1636 | u.usleep = 0;
1637 | if (afternmv) (*afternmv)();
1638 | afternmv = 0;
1639 | }
1640 |
1641 | #endif /* OVL2 */
1642 | #ifdef OVL1
1643 |
1644 | static void
1645 | maybe_wail()
1646 | {
1647 | static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES,
1648 | SHOCK_RES, FIRE_RES, SLEEP_RES, DISINT_RES,
1649 | TELEPORT_CONTROL, STEALTH, FAST, INVIS };
1650 |
1651 | if (moves <= wailmsg + 50) return;
1652 |
1653 | wailmsg = moves;
1654 | if (Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)) {
1655 | const char *who;
1656 | int i, powercnt;
1657 |
1658 | who = (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ?
1659 | urole.name.m : "Elf";
1660 | if (u.uhp == 1) {
1661 | pline("%s is about to die.", who);
1662 | } else {
1663 | for (i = 0, powercnt = 0; i < SIZE(powers); ++i)
1664 | if (u.uprops[powers[i]].intrinsic & INTRINSIC) ++powercnt;
1665 |
1666 | pline(powercnt >= 4 ? "%s, all your powers will be lost..."
1667 | : "%s, your life force is running out.", who);
1668 | }
1669 | } else {
1670 | You_hear(u.uhp == 1 ? "the wailing of the Banshee..."
1671 | : "the howling of the CwnAnnwn...");
1672 | }
1673 | }
1674 |
1675 | void
1676 | losehp(n, knam, k_format)
1677 | register int n;
1678 | register const char *knam;
1679 | boolean k_format;
1680 | {
1681 | if (Upolyd) {
1682 | u.mh -= n;
1683 | if (u.mhmax < u.mh) u.mhmax = u.mh;
1684 | flags.botl = 1;
1685 | if (u.mh < 1)
1686 | rehumanize();
1687 | else if (n > 0 && u.mh*10 < u.mhmax && Unchanging)
1688 | maybe_wail();
1689 | return;
1690 | }
1691 |
1692 | u.uhp -= n;
1693 | if(u.uhp > u.uhpmax)
1694 | u.uhpmax = u.uhp; /* perhaps n was negative */
1695 | flags.botl = 1;
1696 | if(u.uhp < 1) {
1697 | killer_format = k_format;
1698 | killer = knam; /* the thing that killed you */
1699 | You("die...");
1700 | done(DIED);
1701 | } else if (n > 0 && u.uhp*10 < u.uhpmax) {
1702 | maybe_wail();
1703 | }
1704 | }
1705 |
1706 | int
1707 | weight_cap()
1708 | {
1709 | register long carrcap;
1710 |
1711 | carrcap = (((ACURRSTR + ACURR(A_CON))/2)+1)*50;
1712 | if (Upolyd) {
1713 | /* consistent with can_carry() in mon.c */
1714 | if (youmonst.data->mlet == S_NYMPH)
1715 | carrcap = MAX_CARR_CAP;
1716 | else if (!youmonst.data->cwt)
1717 | carrcap = (carrcap * (long)youmonst.data->msize) / MZ_HUMAN;
1718 | else if (!strongmonst(youmonst.data)
1719 | || (strongmonst(youmonst.data) && (youmonst.data->cwt > WT_HUMAN)))
1720 | carrcap = (carrcap * (long)youmonst.data->cwt / WT_HUMAN);
1721 | }
1722 |
1723 | if (Levitation || Is_airlevel(&u.uz) /* pugh@cornell */
1724 | #ifdef STEED
1725 | || (u.usteed && strongmonst(u.usteed->data))
1726 | #endif
1727 | )
1728 | carrcap = MAX_CARR_CAP;
1729 | else {
1730 | if(carrcap > MAX_CARR_CAP) carrcap = MAX_CARR_CAP;
1731 | if (!Flying) {
1732 | if(EWounded_legs & LEFT_SIDE) carrcap -= 100;
1733 | if(EWounded_legs & RIGHT_SIDE) carrcap -= 100;
1734 | }
1735 | if (carrcap < 0) carrcap = 0;
1736 | }
1737 | return((int) carrcap);
1738 | }
1739 |
1740 | static int wc; /* current weight_cap(); valid after call to inv_weight() */
1741 |
1742 | /* returns how far beyond the normal capacity the player is currently. */
1743 | /* inv_weight() is negative if the player is below normal capacity. */
1744 | int
1745 | inv_weight()
1746 | {
1747 | register struct obj *otmp = invent;
1748 | register int wt;
1749 |
1750 | /* when putting stuff into containers, gold is inserted at the head
1751 | of invent for easier manipulation by askchain & co, but it's also
1752 | retained in u.ugold in order to keep the status line accurate; we
1753 | mustn't add its weight in twice under that circumstance */
1754 | wt = (otmp && otmp->oclass == GOLD_CLASS) ? 0 :
1755 | (int)((u.ugold + 50L) / 100L);
1756 |
1757 | while (otmp) {
1758 | if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data))
1759 | wt += otmp->owt;
1760 | otmp = otmp->nobj;
1761 | }
1762 | wc = weight_cap();
1763 | return (wt - wc);
1764 | }
1765 |
1766 | /*
1767 | * Returns 0 if below normal capacity, or the number of "capacity units"
1768 | * over the normal capacity the player is loaded. Max is 5.
1769 | */
1770 | int
1771 | calc_capacity(xtra_wt)
1772 | int xtra_wt;
1773 | {
1774 | int cap, wt = inv_weight() + xtra_wt;
1775 |
1776 | if (wt <= 0) return UNENCUMBERED;
1777 | if (wc <= 1) return OVERLOADED;
1778 | cap = (wt*2 / wc) + 1;
1779 | return min(cap, OVERLOADED);
1780 | }
1781 |
1782 | int
1783 | near_capacity()
1784 | {
1785 | return calc_capacity(0);
1786 | }
1787 |
1788 | int
1789 | max_capacity()
1790 | {
1791 | int wt = inv_weight();
1792 |
1793 | return (wt - (2 * wc));
1794 | }
1795 |
1796 | boolean
1797 | check_capacity(str)
1798 | const char *str;
1799 | {
1800 | if(near_capacity() >= EXT_ENCUMBER) {
1801 | if(str)
1802 | pline(str);
1803 | else
1804 | You_cant("do that while carrying so much stuff.");
1805 | return 1;
1806 | }
1807 | return 0;
1808 | }
1809 |
1810 | #endif /* OVL1 */
1811 | #ifdef OVLB
1812 |
1813 | int
1814 | inv_cnt()
1815 | {
1816 | register struct obj *otmp = invent;
1817 | register int ct = 0;
1818 |
1819 | while(otmp){
1820 | ct++;
1821 | otmp = otmp->nobj;
1822 | }
1823 | return(ct);
1824 | }
1825 |
1826 | #endif /* OVLB */
1827 |
1828 | /*hack.c*/