42bc267ac358b18a3275bd46be52be641158b875
[dragonfly.git] / games / hack / hack.c
1 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2 /* hack.c - version 1.0.3 */
3 /* $FreeBSD: src/games/hack/hack.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */
4 /* $DragonFly: src/games/hack/hack.c,v 1.6 2007/05/13 18:33:55 swildner Exp $ */
5
6 #include "hack.h"
7
8 static void movobj(struct obj *, int, int);
9 #ifdef QUEST
10 static bool rroom(int, int);
11 #endif
12 static int inv_cnt(void);
13
14 /* called on movement:
15  * 1. when throwing ball+chain far away
16  * 2. when teleporting
17  * 3. when walking out of a lit room
18  */
19 void
20 unsee(void)
21 {
22         int x, y;
23         struct rm *lev;
24
25 #ifndef QUEST
26         if (seehx)
27                 seehx = 0;
28         else
29 #endif /* QUEST */
30                 for (x = u.ux - 1; x < u.ux + 2; x++)
31                         for (y = u.uy - 1; y < u.uy + 2; y++) {
32                                 if (!isok(x, y))
33                                         continue;
34                                 lev = &levl[x][y];
35                                 if (!lev->lit && lev->scrsym == '.') {
36                                         lev->scrsym = ' ';
37                                         lev->new = 1;
38                                         on_scr(x, y);
39                                 }
40                         }
41 }
42
43 /* called:
44  *      in hack.eat.c: seeoff(0) - blind after eating rotten food
45  *      in hack.mon.c: seeoff(0) - blinded by a yellow light
46  *      in hack.mon.c: seeoff(1) - swallowed
47  *      in hack.do.c:  seeoff(0) - blind after drinking potion
48  *      in hack.do.c:  seeoff(1) - go up or down the stairs
49  *      in hack.trap.c:seeoff(1) - fall through trapdoor
50  * mode:
51  * 1 to redo @, 0 to leave them *//* 1 means
52  * misc movement, 0 means blindness
53  */
54 void
55 seeoff(bool mode)
56 {
57         int x, y;
58         struct rm *lev;
59
60         if (u.udispl && mode) {
61                 u.udispl = 0;
62                 levl[u.udisx][u.udisy].scrsym = news0(u.udisx, u.udisy);
63         }
64 #ifndef QUEST
65         if (seehx)
66                 seehx = 0;
67         else
68 #endif /* QUEST */
69         if (!mode) {
70                 for (x = u.ux - 1; x < u.ux + 2; x++)
71                         for (y = u.uy - 1; y < u.uy + 2; y++) {
72                                 if (!isok(x, y))
73                                         continue;
74                                 lev = &levl[x][y];
75                                 if (!lev->lit && lev->scrsym == '.')
76                                         lev->seen = 0;
77                         }
78         }
79 }
80
81 void
82 domove(void)
83 {
84         xchar oldx, oldy;
85         struct monst *mtmp = NULL;
86         struct rm *tmpr, *ust;
87         struct trap *trap = NULL;
88         struct obj *otmp;
89
90         u_wipe_engr(rnd(5));
91
92         if (inv_weight() > 0) {
93                 pline("You collapse under your load.");
94                 nomul(0);
95                 return;
96         }
97         if (u.uswallow) {
98                 u.dx = u.dy = 0;
99                 u.ux = u.ustuck->mx;
100                 u.uy = u.ustuck->my;
101         } else {
102                 if (Confusion) {
103                         do {
104                                 confdir();
105                         } while (!isok(u.ux + u.dx, u.uy + u.dy) ||
106                                IS_ROCK(levl[u.ux + u.dx][u.uy + u.dy].typ));
107                 }
108                 if (!isok(u.ux + u.dx, u.uy + u.dy)) {
109                         nomul(0);
110                         return;
111                 }
112         }
113
114         ust = &levl[u.ux][u.uy];
115         oldx = u.ux;
116         oldy = u.uy;
117         if (!u.uswallow &&
118             (trap = t_at(u.ux + u.dx, u.uy + u.dy)) && trap->tseen)
119                 nomul(0);
120         if (u.ustuck && !u.uswallow && (u.ux + u.dx != u.ustuck->mx ||
121                                         u.uy + u.dy != u.ustuck->my)) {
122                 if (dist(u.ustuck->mx, u.ustuck->my) > 2) {
123                         /* perhaps it fled (or was teleported or ... ) */
124                         u.ustuck = 0;
125                 } else {
126                         if (Blind)
127                                 pline("You cannot escape from it!");
128                         else
129                                 pline("You cannot escape from %s!",
130                                     monnam(u.ustuck));
131                         nomul(0);
132                         return;
133                 }
134         }
135         if (u.uswallow || (mtmp = m_at(u.ux + u.dx, u.uy + u.dy))) {
136                 /* attack monster */
137
138                 nomul(0);
139                 gethungry();
140                 if (multi < 0)          /* we just fainted */
141                         return;
142
143                 /* try to attack; note that it might evade */
144                 if (attack(u.uswallow ? u.ustuck : mtmp))
145                         return;
146         }
147         /* not attacking an animal, so we try to move */
148         if (u.utrap) {
149                 if (u.utraptype == TT_PIT) {
150                         pline("You are still in a pit.");
151                         u.utrap--;
152                 } else {
153                         pline("You are caught in a beartrap.");
154                         if ((u.dx && u.dy) || !rn2(5))
155                                 u.utrap--;
156                 }
157                 return;
158         }
159         tmpr = &levl[u.ux + u.dx][u.uy + u.dy];
160         if (IS_ROCK(tmpr->typ) ||
161             (u.dx && u.dy && (tmpr->typ == DOOR || ust->typ == DOOR))) {
162                 flags.move = 0;
163                 nomul(0);
164                 return;
165         }
166         while ((otmp = sobj_at(ENORMOUS_ROCK, u.ux + u.dx, u.uy + u.dy)) != NULL) {
167                 struct trap *ttmp;
168                 xchar rx = u.ux + 2 * u.dx, ry = u.uy + 2 * u.dy;
169                 nomul(0);
170                 if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ) &&
171                     (levl[rx][ry].typ != DOOR || !(u.dx && u.dy)) &&
172                     !sobj_at(ENORMOUS_ROCK, rx, ry)) {
173                         if (m_at(rx, ry)) {
174                                 pline("You hear a monster behind the rock.");
175                                 pline("Perhaps that's why you cannot move it.");
176                                 goto cannot_push;
177                         }
178                         if ((ttmp = t_at(rx, ry)) != NULL)
179                                 switch (ttmp->ttyp) {
180                                 case PIT:
181                                         pline("You push the rock into a pit!");
182                                         deltrap(ttmp);
183                                         delobj(otmp);
184                                         pline("It completely fills the pit!");
185                                         continue;
186                                 case TELEP_TRAP:
187                                         pline("You push the rock and suddenly it disappears!");
188                                         delobj(otmp);
189                                         continue;
190                                 }
191                         if (levl[rx][ry].typ == POOL) {
192                                 levl[rx][ry].typ = ROOM;
193                                 mnewsym(rx, ry);
194                                 prl(rx, ry);
195                                 pline("You push the rock into the water.");
196                                 pline("Now you can cross the water!");
197                                 delobj(otmp);
198                                 continue;
199                         }
200                         otmp->ox = rx;
201                         otmp->oy = ry;
202                         if (cansee(rx, ry))
203                                 atl(rx, ry, otmp->olet);
204                         if (Invisible)
205                                 newsym(u.ux + u.dx, u.uy + u.dy);
206
207                         {
208                                 static long lastmovetime;
209                           /* note: this var contains garbage initially and
210                            * after a restore */
211                                 if (moves > lastmovetime + 2 || moves < lastmovetime)
212                                         pline("With great effort you move the enormous rock.");
213                                 lastmovetime = moves;
214                         }
215                 } else {
216                         pline("You try to move the enormous rock, but in vain.");
217 cannot_push:
218                         if ((!invent || inv_weight() + 90 <= 0) &&
219                             (!u.dx || !u.dy ||
220                              (IS_ROCK(levl[u.ux][u.uy + u.dy].typ)
221                               && IS_ROCK(levl[u.ux + u.dx][u.uy].typ)))) {
222                                 pline("However, you can squeeze yourself into a small opening.");
223                                 break;
224                         } else
225                                 return;
226                 }
227         }
228         if (u.dx && u.dy && IS_ROCK(levl[u.ux][u.uy + u.dy].typ) &&
229             IS_ROCK(levl[u.ux + u.dx][u.uy].typ) &&
230             invent && inv_weight() + 40 > 0) {
231                 pline("You are carrying too much to get through.");
232                 nomul(0);
233                 return;
234         }
235         if (Punished &&
236             DIST(u.ux + u.dx, u.uy + u.dy, uchain->ox, uchain->oy) > 2) {
237                 if (carried(uball)) {
238                         movobj(uchain, u.ux, u.uy);
239                         goto nodrag;
240                 }
241
242                 if (DIST(u.ux + u.dx, u.uy + u.dy, uball->ox, uball->oy) < 3) {
243                         /* leave ball, move chain under/over ball */
244                         movobj(uchain, uball->ox, uball->oy);
245                         goto nodrag;
246                 }
247
248                 if (inv_weight() + (int)uball->owt / 2 > 0) {
249                         pline("You cannot %sdrag the heavy iron ball.",
250                               invent ? "carry all that and also " : "");
251                         nomul(0);
252                         return;
253                 }
254
255                 movobj(uball, uchain->ox, uchain->oy);
256                 unpobj(uball);          /* BAH %% */
257                 uchain->ox = u.ux;
258                 uchain->oy = u.uy;
259                 nomul(-2);
260                 nomovemsg = "";
261 nodrag: ;
262         }
263         u.ux += u.dx;
264         u.uy += u.dy;
265         if (flags.run) {
266                 if (tmpr->typ == DOOR ||
267                     (xupstair == u.ux && yupstair == u.uy) ||
268                     (xdnstair == u.ux && ydnstair == u.uy))
269                         nomul(0);
270         }
271
272         if (tmpr->typ == POOL && !Levitation)
273                 drown();        /* not necessarily fatal */
274
275         if (!Blind) {
276 #ifdef QUEST
277                 setsee();
278 #else
279                 if (ust->lit) {
280                         if (tmpr->lit) {
281                                 if (tmpr->typ == DOOR)
282                                         prl1(u.ux + u.dx, u.uy + u.dy);
283                                 else if (ust->typ == DOOR)
284                                         nose1(oldx - u.dx, oldy - u.dy);
285                         } else {
286                                 unsee();
287                                 prl1(u.ux + u.dx, u.uy + u.dy);
288                         }
289                 } else {
290                         if (tmpr->lit)
291                                 setsee();
292                         else {
293                                 prl1(u.ux + u.dx, u.uy + u.dy);
294                                 if (tmpr->typ == DOOR) {
295                                         if (u.dy) {
296                                                 prl(u.ux - 1, u.uy);
297                                                 prl(u.ux + 1, u.uy);
298                                         } else {
299                                                 prl(u.ux, u.uy - 1);
300                                                 prl(u.ux, u.uy + 1);
301                                         }
302                                 }
303                         }
304                         nose1(oldx - u.dx, oldy - u.dy);
305                 }
306 #endif /* QUEST */
307         } else
308                 pru();
309         if (!flags.nopick)
310                 pickup(1);
311         if (trap)
312                 dotrap(trap);   /* fall into pit, arrow trap, etc. */
313         inshop();
314         if (!Blind)
315                 read_engr_at(u.ux, u.uy);
316 }
317
318 static void
319 movobj(struct obj *obj, int ox, int oy)
320 {
321         /* Some dirty programming to get display right */
322         freeobj(obj);
323         unpobj(obj);
324         obj->nobj = fobj;
325         fobj = obj;
326         obj->ox = ox;
327         obj->oy = oy;
328 }
329
330 int
331 dopickup(void)
332 {
333         if (!g_at(u.ux, u.uy) && !o_at(u.ux, u.uy)) {
334                 pline("There is nothing here to pick up.");
335                 return (0);
336         }
337         if (Levitation) {
338                 pline("You cannot reach the floor.");
339                 return (1);
340         }
341         pickup(0);
342         return (1);
343 }
344
345 void
346 pickup(int all)
347 {
348         struct gold *gold;
349         struct obj *obj, *obj2;
350         int wt;
351
352         if (Levitation)
353                 return;
354         while ((gold = g_at(u.ux, u.uy))) {
355                 pline("%ld gold piece%s.", gold->amount, plur(gold->amount));
356                 u.ugold += gold->amount;
357                 flags.botl = 1;
358                 freegold(gold);
359                 if (flags.run)
360                         nomul(0);
361                 if (Invisible)
362                         newsym(u.ux, u.uy);
363         }
364
365         /* check for more than one object */
366         if (!all) {
367                 int ct = 0;
368
369                 for (obj = fobj; obj; obj = obj->nobj)
370                         if (obj->ox == u.ux && obj->oy == u.uy)
371                                 if (!Punished || obj != uchain)
372                                         ct++;
373                 if (ct < 2)
374                         all++;
375                 else
376                         pline("There are several objects here.");
377         }
378
379         for (obj = fobj; obj; obj = obj2) {
380                 obj2 = obj->nobj;       /* perhaps obj will be picked up */
381                 if (obj->ox == u.ux && obj->oy == u.uy) {
382                         if (flags.run)
383                                 nomul(0);
384
385                         /* do not pick up uchain */
386                         if (Punished && obj == uchain)
387                                 continue;
388
389                         if (!all) {
390                                 char c;
391
392                                 pline("Pick up %s ? [ynaq]", doname(obj));
393                                 while (!strchr("ynaq ", (c = readchar())))
394                                         bell();
395                                 if (c == 'q')
396                                         return;
397                                 if (c == 'n')
398                                         continue;
399                                 if (c == 'a')
400                                         all = 1;
401                         }
402
403                         if (obj->otyp == DEAD_COCKATRICE && !uarmg) {
404                                 pline("Touching the dead cockatrice is a fatal mistake.");
405                                 pline("You turn to stone.");
406                                 killer = "cockatrice cadaver";
407                                 done("died");
408                         }
409
410                         if (obj->otyp == SCR_SCARE_MONSTER) {
411                                 if (!obj->spe)
412                                         obj->spe = 1;
413                                 else {
414                                         /* Note: perhaps the 1st pickup failed: you cannot
415                                          *  carry anymore, and so we never dropped it -
416                                          *  let's assume that treading on it twice also
417                                          *  destroys the scroll */
418                                         pline("The scroll turns to dust as you pick it up.");
419                                         delobj(obj);
420                                         continue;
421                                 }
422                         }
423
424                         wt = inv_weight() + obj->owt;
425                         if (wt > 0) {
426                                 if (obj->quan > 1) {
427                                         /* see how many we can lift */
428                                         int savequan = obj->quan;
429                                         int iw = inv_weight();
430                                         int qq;
431                                         for (qq = 1; qq < savequan; qq++) {
432                                                 obj->quan = qq;
433                                                 if (iw + weight(obj) > 0)
434                                                         break;
435                                         }
436                                         obj->quan = savequan;
437                                         qq--;
438                                         /* we can carry qq of them */
439                                         if (!qq)
440                                                 goto too_heavy;
441                                         pline("You can only carry %s of the %s lying here.",
442                                             (qq == 1) ? "one" : "some",
443                                             doname(obj));
444                                         splitobj(obj, qq);
445                                         /* note: obj2 is set already, so we'll never
446                                          * encounter the other half; if it should be
447                                          * otherwise then write
448                                          *      obj2 = splitobj(obj, qq);
449                                          */
450                                         goto lift_some;
451                                 }
452 too_heavy:
453                                 pline("There %s %s here, but %s.",
454                                       (obj->quan == 1) ? "is" : "are",
455                                       doname(obj),
456                                       !invent ? "it is too heavy for you to lift"
457                                       : "you cannot carry anymore");
458                                 break;
459                         }
460 lift_some:
461                         if (inv_cnt() >= 52) {
462                                 pline("Your knapsack cannot accommodate anymore items.");
463                                 break;
464                         }
465                         if (wt > -5)
466                                 pline("You have a little trouble lifting");
467                         freeobj(obj);
468                         if (Invisible)
469                                 newsym(u.ux, u.uy);
470                         addtobill(obj); /* sets obj->unpaid if necessary */
471                         {
472                                 int pickquan = obj->quan;
473                                 int mergquan;
474                                 if (!Blind)             /* this is done by prinv(), */
475                                         obj->dknown = 1;/* but addinv() needs it */
476                                                         /* already for merging */
477                                 obj = addinv(obj);      /* might merge it with other objects */
478                                 mergquan = obj->quan;
479                                 obj->quan = pickquan;   /* to fool prinv() */
480                                 prinv(obj);
481                                 obj->quan = mergquan;
482                         }
483                 }
484         }
485 }
486
487 /* stop running if we see something interesting */
488 /* turn around a corner if that is the only way we can proceed */
489 /* do not turn left or right twice */
490 void
491 lookaround(void)
492 {
493         int x, y, i, x0, y0, m0, i0 = 9;
494         int corrct = 0, noturn = 0;
495         struct monst *mtmp;
496
497         /* suppress "used before set" message */
498         x0 = y0 = m0 = 0;
499         if (Blind || flags.run == 0)
500                 return;
501         if (flags.run == 1 && levl[u.ux][u.uy].typ == ROOM)
502                 return;
503 #ifdef QUEST
504         if (u.ux0 == u.ux + u.dx && u.uy0 == u.uy + u.dy)
505                 goto stop;
506 #endif /* QUEST */
507         for (x = u.ux - 1; x <= u.ux + 1; x++)
508                 for (y = u.uy - 1; y <= u.uy + 1; y++) {
509                         if (x == u.ux && y == u.uy)
510                                 continue;
511                         if (!levl[x][y].typ)
512                                 continue;
513                         if ((mtmp = m_at(x, y)) && !mtmp->mimic &&
514                             (!mtmp->minvis || See_invisible)) {
515                                 if (!mtmp->mtame ||
516                                     (x == u.ux + u.dx && y == u.uy + u.dy))
517                                         goto stop;
518                         } else          /* invisible M cannot influence us */
519                                 mtmp = 0;
520                         if (x == u.ux - u.dx && y == u.uy - u.dy)
521                                 continue;
522                         switch (levl[x][y].scrsym) {
523                         case '|':
524                         case '-':
525                         case '.':
526                         case ' ':
527                                 break;
528                         case '+':
529                                 if (x != u.ux && y != u.uy)
530                                         break;
531                                 if (flags.run != 1)
532                                         goto stop;
533                         /* fall into next case */
534                         case CORR_SYM:
535 corr:
536                                 if (flags.run == 1 || flags.run == 3) {
537                                         i = DIST(x, y, u.ux + u.dx, u.uy + u.dy);
538                                         if (i > 2)
539                                                 break;
540                                         if (corrct == 1 &&
541                                             DIST(x, y, x0, y0) != 1)
542                                                 noturn = 1;
543                                         if (i < i0) {
544                                                 i0 = i;
545                                                 x0 = x;
546                                                 y0 = y;
547                                                 m0 = mtmp ? 1 : 0;
548                                         }
549                                 }
550                                 corrct++;
551                                 break;
552                         case '^':
553                                 if (flags.run == 1)     /* if you must */
554                                         goto corr;
555                                 if (x == u.ux + u.dx && y == u.uy + u.dy)
556                                         goto stop;
557                                 break;
558                         default:        /* e.g. objects or trap or stairs */
559                                 if (flags.run == 1)
560                                         goto corr;
561                                 if (mtmp)       /* d */
562                                         break;
563 stop:
564                                 nomul(0);
565                                 return;
566                         }
567                 }
568 #ifdef QUEST
569         if (corrct > 0 && (flags.run == 4 || flags.run == 5))
570                 goto stop;
571 #endif /* QUEST */
572         if (corrct > 1 && flags.run == 2)
573                 goto stop;
574         if ((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 &&
575             (corrct == 1 || (corrct == 2 && i0 == 1))) {
576                 /* make sure that we do not turn too far */
577                 if (i0 == 2) {
578                         if (u.dx == y0 - u.uy && u.dy == u.ux - x0)
579                                 i = 2;  /* straight turn right */
580                         else
581                                 i = -2; /* straight turn left */
582                 } else if (u.dx && u.dy) {
583                         if ((u.dx == u.dy && y0 == u.uy) ||
584                             (u.dx != u.dy && y0 != u.uy))
585                                 i = -1; /* half turn left */
586                         else
587                                 i = 1;  /* half turn right */
588                 } else {
589                         if ((x0 - u.ux == y0 - u.uy && !u.dy) ||
590                             (x0 - u.ux != y0 - u.uy && u.dy))
591                                 i = 1;  /* half turn right */
592                         else
593                                 i = -1; /* half turn left */
594                 }
595                 i += u.last_str_turn;
596                 if (i <= 2 && i >= -2) {
597                         u.last_str_turn = i;
598                         u.dx = x0 - u.ux, u.dy = y0 - u.uy;
599                 }
600         }
601 }
602
603 /* something like lookaround, but we are not running */
604 /* react only to monsters that might hit us */
605 bool
606 monster_nearby(void)
607 {
608         int x, y;
609         struct monst *mtmp;
610
611         if (!Blind)
612                 for (x = u.ux - 1; x <= u.ux + 1; x++)
613                         for (y = u.uy - 1; y <= u.uy + 1; y++) {
614                                 if (x == u.ux && y == u.uy)
615                                         continue;
616                                 if ((mtmp = m_at(x, y)) && !mtmp->mimic &&
617                                     !mtmp->mtame &&
618                                     !mtmp->mpeaceful &&
619                                     !strchr("Ea", mtmp->data->mlet) &&
620                                     !mtmp->mfroz && !mtmp->msleep && /* aplvax!jcn */
621                                     (!mtmp->minvis || See_invisible))
622                                         return (1);
623                         }
624         return (0);
625 }
626
627 #ifdef QUEST
628 bool
629 cansee(xchar x, xchar y)
630 {
631         int dx, dy, adx, ady, sdx, sdy, dmax, d;
632
633         if (Blind)
634                 return (0);
635         if (!isok(x, y))
636                 return (0);
637         d = dist(x, y);
638         if (d < 3)
639                 return (1);
640         if (d > u.uhorizon * u.uhorizon)
641                 return (0);
642         if (!levl[x][y].lit)
643                 return (0);
644         dx = x - u.ux;
645         adx = abs(dx);
646         sdx = sgn(dx);
647         dy = y - u.uy;
648         ady = abs(dy);
649         sdy = sgn(dy);
650         if (dx == 0 || dy == 0 || adx == ady) {
651                 dmax = (dx == 0) ? ady : adx;
652                 for (d = 1; d <= dmax; d++)
653                         if (!rroom(sdx * d, sdy * d))
654                                 return (0);
655                 return (1);
656         } else if (ady > adx) {
657                 for (d = 1; d <= ady; d++) {
658                         if (!rroom(sdx * ((d * adx) / ady), sdy * d) ||
659                             !rroom(sdx * ((d * adx - 1) / ady + 1), sdy * d))
660                                 return (0);
661                 }
662                 return (1);
663         } else {
664                 for (d = 1; d <= adx; d++) {
665                         if (!rroom(sdx * d, sdy * ((d * ady) / adx)) ||
666                             !rroom(sdx * d, sdy * ((d * ady - 1) / adx + 1)))
667                                 return (0);
668                 }
669                 return (1);
670         }
671 }
672
673 static bool
674 rroom(int x, int y)
675 {
676         return (IS_ROOM(levl[u.ux + x][u.uy + y].typ));
677 }
678
679 #else
680
681 bool
682 cansee(xchar x, xchar y)
683 {
684         if (Blind || u.uswallow)
685                 return (0);
686         if (dist(x, y) < 3)
687                 return (1);
688         if (levl[x][y].lit && seelx <= x && x <= seehx && seely <= y &&
689             y <= seehy)
690                 return (1);
691         return (0);
692 }
693 #endif /* QUEST */
694
695 int
696 sgn(int a)
697 {
698         return ((a > 0) ? 1 : (a == 0) ? 0 : -1);
699 }
700
701 #ifdef QUEST
702 void
703 setsee(void)
704 {
705         int x, y;
706
707         if (Blind) {
708                 pru();
709                 return;
710         }
711         for (y = u.uy - u.uhorizon; y <= u.uy + u.uhorizon; y++)
712                 for (x = u.ux - u.uhorizon; x <= u.ux + u.uhorizon; x++) {
713                         if (cansee(x, y))
714                                 prl(x, y);
715                 }
716 }
717
718 #else
719
720 void
721 setsee(void)
722 {
723         int x, y;
724
725         if (Blind) {
726                 pru();
727                 return;
728         }
729         if (!levl[u.ux][u.uy].lit) {
730                 seelx = u.ux - 1;
731                 seehx = u.ux + 1;
732                 seely = u.uy - 1;
733                 seehy = u.uy + 1;
734         } else {
735                 for (seelx = u.ux; levl[seelx - 1][u.uy].lit; seelx--) ;
736                 for (seehx = u.ux; levl[seehx + 1][u.uy].lit; seehx++) ;
737                 for (seely = u.uy; levl[u.ux][seely - 1].lit; seely--) ;
738                 for (seehy = u.uy; levl[u.ux][seehy + 1].lit; seehy++) ;
739         }
740         for (y = seely; y <= seehy; y++)
741                 for (x = seelx; x <= seehx; x++)
742                         prl(x, y);
743
744         if (!levl[u.ux][u.uy].lit)      /* seems necessary elsewhere */
745                 seehx = 0;
746         else {
747                 if (seely == u.uy)
748                         for (x = u.ux - 1; x <= u.ux + 1; x++)
749                                 prl(x, seely - 1);
750                 if (seehy == u.uy)
751                         for (x = u.ux - 1; x <= u.ux + 1; x++)
752                                 prl(x, seehy + 1);
753                 if (seelx == u.ux)
754                         for (y = u.uy - 1; y <= u.uy + 1; y++)
755                                 prl(seelx - 1, y);
756                 if (seehx == u.ux)
757                         for (y = u.uy - 1; y <= u.uy + 1; y++)
758                                 prl(seehx + 1, y);
759         }
760 }
761 #endif /* QUEST */
762
763 void
764 nomul(int nval)
765 {
766         if (multi < 0)
767                 return;
768         multi = nval;
769         flags.mv = flags.run = 0;
770 }
771
772 int
773 abon(void)
774 {
775         if (u.ustr == 3)
776                 return (-3);
777         else if (u.ustr < 6)
778                 return (-2);
779         else if (u.ustr < 8)
780                 return (-1);
781         else if (u.ustr < 17)
782                 return (0);
783         else if (u.ustr < 69)   /* up to 18/50 */
784                 return (1);
785         else if (u.ustr < 118)
786                 return (2);
787         else
788                 return (3);
789 }
790
791 int
792 dbon(void)
793 {
794         if (u.ustr < 6)
795                 return (-1);
796         else if (u.ustr < 16)
797                 return (0);
798         else if (u.ustr < 18)
799                 return (1);
800         else if (u.ustr == 18)  /* up to 18 */
801                 return (2);
802         else if (u.ustr < 94)   /* up to 18/75 */
803                 return (3);
804         else if (u.ustr < 109)  /* up to 18/90 */
805                 return (4);
806         else if (u.ustr < 118)  /* up to 18/99 */
807                 return (5);
808         else
809                 return (6);
810 }
811
812 /* may kill you; cause may be poison or monster like 'A' */
813 void
814 losestr(int num)
815 {
816         u.ustr -= num;
817         while (u.ustr < 3) {
818                 u.ustr++;
819                 u.uhp -= 6;
820                 u.uhpmax -= 6;
821         }
822         flags.botl = 1;
823 }
824
825 void
826 losehp(int n, const char *knam)
827 {
828         u.uhp -= n;
829         if (u.uhp > u.uhpmax)
830                 u.uhpmax = u.uhp;       /* perhaps n was negative */
831         flags.botl = 1;
832         if (u.uhp < 1) {
833                 killer = knam;          /* the thing that killed you */
834                 done("died");
835         }
836 }
837
838 void
839 losehp_m(int n, struct monst *mtmp)
840 {
841         u.uhp -= n;
842         flags.botl = 1;
843         if (u.uhp < 1)
844                 done_in_by(mtmp);
845 }
846
847 void
848 losexp(void)    /* hit by V or W */
849 {
850         int num;
851
852         if (u.ulevel > 1)
853                 pline("Goodbye level %u.", u.ulevel--);
854         else
855                 u.uhp = -1;
856         num = rnd(10);
857         u.uhp -= num;
858         u.uhpmax -= num;
859         u.uexp = newuexp();
860         flags.botl = 1;
861 }
862
863 int
864 inv_weight(void)
865 {
866         struct obj *otmp = invent;
867         int wt = (u.ugold + 500) / 1000;
868         int carrcap;
869
870         if (Levitation)         /* pugh@cornell */
871                 carrcap = MAX_CARR_CAP;
872         else {
873                 carrcap = 5 * (((u.ustr > 18) ? 20 : u.ustr) + u.ulevel);
874                 if (carrcap > MAX_CARR_CAP)
875                         carrcap = MAX_CARR_CAP;
876                 if (Wounded_legs & LEFT_SIDE)
877                         carrcap -= 10;
878                 if (Wounded_legs & RIGHT_SIDE)
879                         carrcap -= 10;
880         }
881         while (otmp) {
882                 wt += otmp->owt;
883                 otmp = otmp->nobj;
884         }
885         return (wt - carrcap);
886 }
887
888 static int
889 inv_cnt(void)
890 {
891         struct obj *otmp = invent;
892         int ct = 0;
893
894         while (otmp) {
895                 ct++;
896                 otmp = otmp->nobj;
897         }
898         return (ct);
899 }
900
901 long
902 newuexp(void)
903 {
904         return (10 * (1L << (u.ulevel - 1)));
905 }