1 /* NetHack 3.7	vault.c	$NHDT-Date: 1606009006 2020/11/22 01:36:46 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.77 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2011. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 
8 static boolean clear_fcorr(struct monst *, boolean);
9 static void blackout(int, int);
10 static void restfakecorr(struct monst *);
11 static void parkguard(struct monst *);
12 static boolean in_fcorridor(struct monst *, int, int);
13 static boolean find_guard_dest(struct monst *, xchar *, xchar *);
14 static void move_gold(struct obj *, int);
15 static void wallify_vault(struct monst *);
16 static void gd_mv_monaway(struct monst *, int, int);
17 static void gd_pick_corridor_gold(struct monst *, int, int);
18 
19 void
newegd(struct monst * mtmp)20 newegd(struct monst *mtmp)
21 {
22     if (!mtmp->mextra)
23         mtmp->mextra = newmextra();
24     if (!EGD(mtmp)) {
25         EGD(mtmp) = (struct egd *) alloc(sizeof (struct egd));
26         (void) memset((genericptr_t) EGD(mtmp), 0, sizeof (struct egd));
27     }
28 }
29 
30 void
free_egd(struct monst * mtmp)31 free_egd(struct monst *mtmp)
32 {
33     if (mtmp->mextra && EGD(mtmp)) {
34         free((genericptr_t) EGD(mtmp));
35         EGD(mtmp) = (struct egd *) 0;
36     }
37     mtmp->isgd = 0;
38 }
39 
40 /* try to remove the temporary corridor (from vault to rest of map) being
41    maintained by guard 'grd'; if guard is still in it, removal will fail,
42    to be tried again later */
43 static boolean
clear_fcorr(struct monst * grd,boolean forceshow)44 clear_fcorr(struct monst *grd, boolean forceshow)
45 {
46     register int fcx, fcy, fcbeg;
47     struct monst *mtmp;
48     boolean sawcorridor = FALSE,
49             silently = g.program_state.stopprint ? TRUE : FALSE;
50     struct egd *egrd = EGD(grd);
51     struct trap *trap;
52     struct rm *lev;
53 
54     if (!on_level(&egrd->gdlevel, &u.uz))
55         return TRUE;
56 
57     /* note: guard remains on 'fmons' list (alive or dead, at off-map
58        coordinate <0,0>), until temporary corridor from vault back to
59        civilization has been removed */
60     while ((fcbeg = egrd->fcbeg) < egrd->fcend) {
61         fcx = egrd->fakecorr[fcbeg].fx;
62         fcy = egrd->fakecorr[fcbeg].fy;
63         if ((DEADMONSTER(grd) || !in_fcorridor(grd, u.ux, u.uy))
64             && egrd->gddone)
65             forceshow = TRUE;
66         if ((u.ux == fcx && u.uy == fcy && !DEADMONSTER(grd))
67             || (!forceshow && couldsee(fcx, fcy))
68             || (Punished && !carried(uball) && uball->ox == fcx
69                 && uball->oy == fcy))
70             return FALSE;
71 
72         if ((mtmp = m_at(fcx, fcy)) != 0) {
73             if (mtmp->isgd) {
74                 return FALSE;
75             } else {
76                 if (mtmp->mtame)
77                     yelp(mtmp);
78                 if (!rloc(mtmp, TRUE))
79                     m_into_limbo(mtmp);
80             }
81         }
82         lev = &levl[fcx][fcy];
83         if (lev->typ == CORR && cansee(fcx, fcy))
84             sawcorridor = TRUE;
85         lev->typ = egrd->fakecorr[fcbeg].ftyp;
86         if (IS_STWALL(lev->typ)) {
87             /* destroy any trap here (pit dug by you, hole dug via
88                wand while levitating or by monster, bear trap or land
89                mine via object, spun web) when spot reverts to stone */
90             if ((trap = t_at(fcx, fcy)) != 0)
91                 deltrap(trap);
92             /* undo scroll/wand/spell of light affecting this spot */
93             if (lev->typ == STONE)
94                 blackout(fcx, fcy);
95         }
96         map_location(fcx, fcy, 1); /* bypass vision */
97         if (!ACCESSIBLE(lev->typ))
98             block_point(fcx, fcy);
99         g.vision_full_recalc = 1;
100         egrd->fcbeg++;
101     }
102     if (sawcorridor && !silently)
103         pline_The("corridor disappears.");
104     /* only give encased message if hero is still alive (might get here
105        via paygd() -> mongone() -> grddead() when game is over;
106        died: no message, quit: message) */
107     if (IS_ROCK(levl[u.ux][u.uy].typ) && (Upolyd ? u.mh : u.uhp) > 0
108         && !silently)
109         You("are encased in rock.");
110     return TRUE;
111 }
112 
113 /* as a temporary corridor is removed, set stone locations and adjacent
114    spots to unlit; if player used scroll/wand/spell of light while inside
115    the corridor, we don't want the light to reappear if/when a new tunnel
116    goes through the same area */
117 static void
blackout(int x,int y)118 blackout(int x, int y)
119 {
120     struct rm *lev;
121     int i, j;
122 
123     for (i = x - 1; i <= x + 1; ++i)
124         for (j = y - 1; j <= y + 1; ++j) {
125             if (!isok(i, j))
126                 continue;
127             lev = &levl[i][j];
128             /* [possible bug: when (i != x || j != y), perhaps we ought
129                to check whether the spot on the far side is lit instead
130                of doing a blanket blackout of adjacent locations] */
131             if (lev->typ == STONE)
132                 lev->lit = lev->waslit = 0;
133             /* mark <i,j> as not having been seen from <x,y> */
134             unset_seenv(lev, x, y, i, j);
135         }
136 }
137 
138 static void
restfakecorr(struct monst * grd)139 restfakecorr(struct monst *grd)
140 {
141     /* it seems you left the corridor - let the guard disappear */
142     if (clear_fcorr(grd, FALSE)) {
143         grd->isgd = 0; /* dmonsfree() should delete this mon */
144         mongone(grd);
145     }
146 }
147 
148 /* move guard--dead to alive--to <0,0> until temporary corridor is removed */
149 static void
parkguard(struct monst * grd)150 parkguard(struct monst *grd)
151 {
152     /* either guard is dead or will now be treated as if so;
153        monster traversal loops should skip it */
154     if (grd == g.context.polearm.hitmon)
155         g.context.polearm.hitmon = 0;
156     if (grd->mx) {
157         remove_monster(grd->mx, grd->my);
158         newsym(grd->mx, grd->my);
159         place_monster(grd, 0, 0);
160         /* [grd->mx,my just got set to 0,0 by place_monster(), so this
161            just sets EGD(grd)->ogx,ogy to 0,0 too; is that what we want?] */
162         EGD(grd)->ogx = grd->mx;
163         EGD(grd)->ogy = grd->my;
164     }
165 }
166 
167 /* called in mon.c */
168 boolean
grddead(struct monst * grd)169 grddead(struct monst *grd)
170 {
171     boolean dispose = clear_fcorr(grd, TRUE);
172 
173     if (!dispose) {
174         /* destroy guard's gold; drop any other inventory */
175         relobj(grd, 0, FALSE);
176         grd->mhp = 0;
177         parkguard(grd);
178         dispose = clear_fcorr(grd, TRUE);
179     }
180     if (dispose)
181         grd->isgd = 0; /* for dmonsfree() */
182     return dispose;
183 }
184 
185 static boolean
in_fcorridor(struct monst * grd,int x,int y)186 in_fcorridor(struct monst *grd, int x, int y)
187 {
188     register int fci;
189     struct egd *egrd = EGD(grd);
190 
191     for (fci = egrd->fcbeg; fci < egrd->fcend; fci++)
192         if (x == egrd->fakecorr[fci].fx && y == egrd->fakecorr[fci].fy)
193             return TRUE;
194     return FALSE;
195 }
196 
197 struct monst *
findgd(void)198 findgd(void)
199 {
200     register struct monst *mtmp;
201 
202     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
203         if (DEADMONSTER(mtmp))
204             continue;
205         if (mtmp->isgd && on_level(&(EGD(mtmp)->gdlevel), &u.uz))
206             return mtmp;
207     }
208     return (struct monst *) 0;
209 }
210 
211 void
vault_summon_gd(void)212 vault_summon_gd(void)
213 {
214     if (vault_occupied(u.urooms) && !findgd())
215         u.uinvault = (VAULT_GUARD_TIME - 1);
216 }
217 
218 char
vault_occupied(char * array)219 vault_occupied(char *array)
220 {
221     register char *ptr;
222 
223     for (ptr = array; *ptr; ptr++)
224         if (g.rooms[*ptr - ROOMOFFSET].rtype == VAULT)
225             return *ptr;
226     return '\0';
227 }
228 
229 /* hero has teleported out of vault while a guard is active */
230 void
uleftvault(struct monst * grd)231 uleftvault(struct monst *grd)
232 {
233     /* only called if caller has checked vault_occupied() and findgd() */
234     if (!grd || !grd->isgd || DEADMONSTER(grd)) {
235         impossible("escaping vault without guard?");
236         return;
237     }
238     /* if carrying gold and arriving anywhere other than next to the guard,
239        set the guard loose */
240     if ((money_cnt(g.invent) || hidden_gold(TRUE))
241         && um_dist(grd->mx, grd->my, 1)) {
242         if (grd->mpeaceful) {
243             if (canspotmon(grd)) /* see or sense via telepathy */
244                 pline("%s becomes irate.", Monnam(grd));
245             grd->mpeaceful = 0; /* bypass setmangry() */
246         }
247         /* if arriving outside guard's temporary corridor, give the
248            guard an extra move to deliver message(s) and to teleport
249            out of and remove that corridor */
250         if (!in_fcorridor(grd, u.ux, u.uy))
251             (void) gd_move(grd);
252     }
253 }
254 
255 static boolean
find_guard_dest(struct monst * guard,xchar * rx,xchar * ry)256 find_guard_dest(struct monst *guard, xchar *rx, xchar *ry)
257 {
258     register int x, y, dd, lx = 0, ly = 0;
259 
260     for (dd = 2; (dd < ROWNO || dd < COLNO); dd++) {
261         for (y = u.uy - dd; y <= u.uy + dd; ly = y, y++) {
262             if (y < 0 || y > ROWNO - 1)
263                 continue;
264             for (x = u.ux - dd; x <= u.ux + dd; lx = x, x++) {
265                 if (y != u.uy - dd && y != u.uy + dd && x != u.ux - dd)
266                     x = u.ux + dd;
267                 if (x < 1 || x > COLNO - 1)
268                     continue;
269                 if (guard && ((x == guard->mx && y == guard->my)
270                               || (guard->isgd && in_fcorridor(guard, x, y))))
271                     continue;
272                 if (levl[x][y].typ == CORR) {
273                     lx = (x < u.ux) ? x + 1 : (x > u.ux) ? x - 1 : x;
274                     ly = (y < u.uy) ? y + 1 : (y > u.uy) ? y - 1 : y;
275                     if (levl[lx][ly].typ != STONE && levl[lx][ly].typ != CORR)
276                         goto incr_radius;
277                     *rx = x;
278                     *ry = y;
279                     return TRUE;
280                 }
281             }
282         }
283  incr_radius:
284         ;
285     }
286     impossible("Not a single corridor on this level?");
287     tele();
288     return FALSE;
289 }
290 
291 void
invault(void)292 invault(void)
293 {
294     struct monst *guard;
295     boolean gsensed;
296     int trycount, vaultroom = (int) vault_occupied(u.urooms);
297 
298     if (!vaultroom) {
299         u.uinvault = 0;
300         return;
301     }
302     vaultroom -= ROOMOFFSET;
303 
304     guard = findgd();
305     if (++u.uinvault % VAULT_GUARD_TIME == 0 && !guard) {
306         /* if time ok and no guard now. */
307         char buf[BUFSZ];
308         register int x, y, gx, gy;
309         xchar rx, ry;
310         long umoney;
311 
312         /* first find the goal for the guard */
313         if (!find_guard_dest((struct monst *)0, &rx, &ry))
314             return;
315         gx = rx, gy = ry;
316 
317         /* next find a good place for a door in the wall */
318         x = u.ux;
319         y = u.uy;
320         if (levl[x][y].typ != ROOM) { /* player dug a door and is in it */
321             if (levl[x + 1][y].typ == ROOM)
322                 x = x + 1;
323             else if (levl[x][y + 1].typ == ROOM)
324                 y = y + 1;
325             else if (levl[x - 1][y].typ == ROOM)
326                 x = x - 1;
327             else if (levl[x][y - 1].typ == ROOM)
328                 y = y - 1;
329             else if (levl[x + 1][y + 1].typ == ROOM) {
330                 x = x + 1;
331                 y = y + 1;
332             } else if (levl[x - 1][y - 1].typ == ROOM) {
333                 x = x - 1;
334                 y = y - 1;
335             } else if (levl[x + 1][y - 1].typ == ROOM) {
336                 x = x + 1;
337                 y = y - 1;
338             } else if (levl[x - 1][y + 1].typ == ROOM) {
339                 x = x - 1;
340                 y = y + 1;
341             }
342         }
343         while (levl[x][y].typ == ROOM) {
344             register int dx, dy;
345 
346             dx = (gx > x) ? 1 : (gx < x) ? -1 : 0;
347             dy = (gy > y) ? 1 : (gy < y) ? -1 : 0;
348             if (abs(gx - x) >= abs(gy - y))
349                 x += dx;
350             else
351                 y += dy;
352         }
353         if (x == u.ux && y == u.uy) {
354             if (levl[x + 1][y].typ == HWALL || levl[x + 1][y].typ == DOOR)
355                 x = x + 1;
356             else if (levl[x - 1][y].typ == HWALL
357                      || levl[x - 1][y].typ == DOOR)
358                 x = x - 1;
359             else if (levl[x][y + 1].typ == VWALL
360                      || levl[x][y + 1].typ == DOOR)
361                 y = y + 1;
362             else if (levl[x][y - 1].typ == VWALL
363                      || levl[x][y - 1].typ == DOOR)
364                 y = y - 1;
365             else
366                 return;
367         }
368 
369         /* make something interesting happen */
370         if (!(guard = makemon(&mons[PM_GUARD], x, y, MM_EGD)))
371             return;
372         guard->isgd = 1;
373         guard->mpeaceful = 1;
374         set_malign(guard);
375         EGD(guard)->gddone = 0;
376         EGD(guard)->ogx = x;
377         EGD(guard)->ogy = y;
378         assign_level(&(EGD(guard)->gdlevel), &u.uz);
379         EGD(guard)->vroom = vaultroom;
380         EGD(guard)->warncnt = 0;
381 
382         reset_faint(); /* if fainted - wake up */
383         gsensed = !canspotmon(guard);
384         if (!gsensed)
385             pline("Suddenly one of the Vault's %s enters!",
386                   makeplural(pmname(guard->data, Mgender(guard))));
387         else
388             pline("Someone else has entered the Vault.");
389         newsym(guard->mx, guard->my);
390         if (u.uswallow) {
391             /* can't interrogate hero, don't interrogate engulfer */
392             if (!Deaf)
393                 verbalize("What's going on here?");
394             if (gsensed)
395                 pline_The("other presence vanishes.");
396             mongone(guard);
397             return;
398         }
399         if (U_AP_TYPE == M_AP_OBJECT || u.uundetected) {
400             if (U_AP_TYPE == M_AP_OBJECT
401                 && g.youmonst.mappearance != GOLD_PIECE)
402                 if (!Deaf)
403                     verbalize("Hey!  Who left that %s in here?",
404                               mimic_obj_name(&g.youmonst));
405             /* You're mimicking some object or you're hidden. */
406             pline("Puzzled, %s turns around and leaves.", mhe(guard));
407             mongone(guard);
408             return;
409         }
410         if (Strangled || is_silent(g.youmonst.data) || g.multi < 0) {
411             /* [we ought to record whether this this message has already
412                been given in order to vary it upon repeat visits, but
413                discarding the monster and its egd data renders that hard] */
414             if (Deaf)
415                 pline("%s huffs and turns to leave.", noit_Monnam(guard));
416             else
417                 verbalize("I'll be back when you're ready to speak to me!");
418             mongone(guard);
419             return;
420         }
421 
422         stop_occupation(); /* if occupied, stop it *now* */
423         if (g.multi > 0) {
424             nomul(0);
425             unmul((char *) 0);
426         }
427         buf[0] = '\0';
428         trycount = 5;
429         do {
430             getlin(Deaf ? "You are required to supply your name. -"
431                         : "\"Hello stranger, who are you?\" -", buf);
432             (void) mungspaces(buf);
433         } while (!buf[0] && --trycount > 0);
434 
435         if (u.ualign.type == A_LAWFUL
436             /* ignore trailing text, in case player includes rank */
437             && strncmpi(buf, g.plname, (int) strlen(g.plname)) != 0) {
438             adjalign(-1); /* Liar! */
439         }
440 
441         if (!strcmpi(buf, "Croesus") || !strcmpi(buf, "Kroisos")
442             || !strcmpi(buf, "Creosote")) { /* Discworld */
443             if (!g.mvitals[PM_CROESUS].died) {
444                 if (Deaf) {
445                     if (!Blind)
446                         pline("%s waves goodbye.", noit_Monnam(guard));
447                 } else {
448                     verbalize(
449                          "Oh, yes, of course.  Sorry to have disturbed you.");
450                 }
451                 mongone(guard);
452             } else {
453                 setmangry(guard, FALSE);
454                 if (Deaf) {
455                    if (!Blind)
456                         pline("%s mouths something and looks very angry!",
457                               noit_Monnam(guard));
458                 } else {
459                    verbalize(
460                            "Back from the dead, are you?  I'll remedy that!");
461                 }
462                 /* don't want guard to waste next turn wielding a weapon */
463                 if (!MON_WEP(guard)) {
464                     guard->weapon_check = NEED_HTH_WEAPON;
465                     (void) mon_wield_item(guard);
466                 }
467             }
468             return;
469         }
470         if (Deaf)
471             pline("%s doesn't %srecognize you.", noit_Monnam(guard),
472                     (Blind) ? "" : "appear to ");
473         else
474             verbalize("I don't know you.");
475         umoney = money_cnt(g.invent);
476         if (!umoney && !hidden_gold(TRUE)) {
477             if (Deaf)
478                 pline("%s stomps%s.", noit_Monnam(guard),
479                       (Blind) ? "" : " and beckons");
480             else
481                 verbalize("Please follow me.");
482         } else {
483             if (!umoney) {
484                 if (Deaf) {
485                     if (!Blind)
486                         pline("%s glares at you%s.", noit_Monnam(guard),
487                               g.invent ? "r stuff" : "");
488                 } else {
489                    verbalize("You have hidden gold.");
490                 }
491             }
492             if (Deaf) {
493                 if (!Blind)
494                     pline(
495                        "%s holds out %s palm and beckons with %s other hand.",
496                           noit_Monnam(guard), noit_mhis(guard),
497                           noit_mhis(guard));
498             } else {
499                 verbalize(
500                     "Most likely all your gold was stolen from this vault.");
501                 verbalize("Please drop that gold and follow me.");
502             }
503         }
504         EGD(guard)->gdx = gx;
505         EGD(guard)->gdy = gy;
506         EGD(guard)->fcbeg = 0;
507         EGD(guard)->fakecorr[0].fx = x;
508         EGD(guard)->fakecorr[0].fy = y;
509         if (IS_WALL(levl[x][y].typ)) {
510             EGD(guard)->fakecorr[0].ftyp = levl[x][y].typ;
511         } else { /* the initial guard location is a dug door */
512             int vlt = EGD(guard)->vroom;
513             xchar lowx = g.rooms[vlt].lx, hix = g.rooms[vlt].hx;
514             xchar lowy = g.rooms[vlt].ly, hiy = g.rooms[vlt].hy;
515 
516             if (x == lowx - 1 && y == lowy - 1)
517                 EGD(guard)->fakecorr[0].ftyp = TLCORNER;
518             else if (x == hix + 1 && y == lowy - 1)
519                 EGD(guard)->fakecorr[0].ftyp = TRCORNER;
520             else if (x == lowx - 1 && y == hiy + 1)
521                 EGD(guard)->fakecorr[0].ftyp = BLCORNER;
522             else if (x == hix + 1 && y == hiy + 1)
523                 EGD(guard)->fakecorr[0].ftyp = BRCORNER;
524             else if (y == lowy - 1 || y == hiy + 1)
525                 EGD(guard)->fakecorr[0].ftyp = HWALL;
526             else if (x == lowx - 1 || x == hix + 1)
527                 EGD(guard)->fakecorr[0].ftyp = VWALL;
528         }
529         levl[x][y].typ = DOOR;
530         set_doorstate(&levl[x][y], D_NODOOR);
531         unblock_point(x, y); /* doesn't block light */
532         EGD(guard)->fcend = 1;
533         EGD(guard)->warncnt = 1;
534     }
535 }
536 
537 static void
move_gold(struct obj * gold,int vroom)538 move_gold(struct obj *gold, int vroom)
539 {
540     xchar nx, ny;
541 
542     remove_object(gold);
543     newsym(gold->ox, gold->oy);
544     nx = g.rooms[vroom].lx + rn2(2);
545     ny = g.rooms[vroom].ly + rn2(2);
546     place_object(gold, nx, ny);
547     stackobj(gold);
548     newsym(nx, ny);
549 }
550 
551 static void
wallify_vault(struct monst * grd)552 wallify_vault(struct monst *grd)
553 {
554     int x, y, typ;
555     int vlt = EGD(grd)->vroom;
556     char tmp_viz;
557     xchar lox = g.rooms[vlt].lx - 1, hix = g.rooms[vlt].hx + 1,
558           loy = g.rooms[vlt].ly - 1, hiy = g.rooms[vlt].hy + 1;
559     struct monst *mon;
560     struct obj *gold;
561     struct trap *trap;
562     boolean fixed = FALSE;
563     boolean movedgold = FALSE;
564 
565     for (x = lox; x <= hix; x++)
566         for (y = loy; y <= hiy; y++) {
567             /* if not on the room boundary, skip ahead */
568             if (x != lox && x != hix && y != loy && y != hiy)
569                 continue;
570 
571             if (!IS_WALL(levl[x][y].typ) && !in_fcorridor(grd, x, y)) {
572                 if ((mon = m_at(x, y)) != 0 && mon != grd) {
573                     if (mon->mtame)
574                         yelp(mon);
575                     (void) rloc(mon, FALSE);
576                 }
577                 if ((gold = g_at(x, y)) != 0) {
578                     move_gold(gold, EGD(grd)->vroom);
579                     movedgold = TRUE;
580                 }
581                 if ((trap = t_at(x, y)) != 0)
582                     deltrap(trap);
583                 if (x == lox)
584                     typ =
585                         (y == loy) ? TLCORNER : (y == hiy) ? BLCORNER : VWALL;
586                 else if (x == hix)
587                     typ =
588                         (y == loy) ? TRCORNER : (y == hiy) ? BRCORNER : VWALL;
589                 else /* not left or right side, must be top or bottom */
590                     typ = HWALL;
591                 levl[x][y].typ = typ;
592                 levl[x][y].flags = 0;
593                 /*
594                  * hack: player knows walls are restored because of the
595                  * message, below, so show this on the screen.
596                  */
597                 tmp_viz = g.viz_array[y][x];
598                 g.viz_array[y][x] = IN_SIGHT | COULD_SEE;
599                 newsym(x, y);
600                 g.viz_array[y][x] = tmp_viz;
601                 block_point(x, y);
602                 fixed = TRUE;
603             }
604         }
605 
606     if (movedgold || fixed) {
607         if (in_fcorridor(grd, grd->mx, grd->my) || cansee(grd->mx, grd->my))
608             pline("%s whispers an incantation.", noit_Monnam(grd));
609         else
610             You_hear("a distant chant.");
611         if (movedgold)
612             pline("A mysterious force moves the gold into the vault.");
613         if (fixed)
614             pline_The("damaged vault's walls are magically restored!");
615     }
616 }
617 
618 static void
gd_mv_monaway(struct monst * grd,int nx,int ny)619 gd_mv_monaway(struct monst *grd, int nx, int ny)
620 {
621     if (MON_AT(nx, ny) && !(nx == grd->mx && ny == grd->my)) {
622         if (!Deaf)
623             verbalize("Out of my way, scum!");
624         if (!rloc(m_at(nx, ny), FALSE) || MON_AT(nx, ny))
625             m_into_limbo(m_at(nx, ny));
626     }
627 }
628 
629 /* have guard pick gold off the floor, possibly moving to the gold's
630    position before message and back to his current spot after */
631 static void
gd_pick_corridor_gold(struct monst * grd,int goldx,int goldy)632 gd_pick_corridor_gold(struct monst *grd, int goldx, int goldy)
633 {
634     struct obj *gold;
635     coord newcc, bestcc;
636     int gdelta, newdelta, bestdelta, tryct,
637         guardx = grd->mx, guardy = grd->my;
638     boolean under_u = (goldx == u.ux && goldy == u.uy),
639             see_it = cansee(goldx, goldy);
640 
641     if (under_u) {
642         /* Grab the gold from between the hero's feet.
643            If guard is two or more steps away; bring him closer first. */
644         gold = g_at(goldx, goldy);
645         if (!gold) {
646             impossible("vault guard: no gold at hero's feet?");
647             return;
648         }
649         gdelta = distu(guardx, guardy);
650         if (gdelta > 2 && see_it) { /* skip if player won't see it */
651             bestdelta = gdelta;
652             bestcc.x = (xchar) guardx, bestcc.y = (xchar) guardy;
653             tryct = 9;
654             do {
655                 /* pick an available spot nearest the hero and also try
656                    to find the one meeting that criterium which is nearest
657                    the guard's current location */
658                 if (enexto(&newcc, goldx, goldy, grd->data)) {
659                     if ((newdelta = distu(newcc.x, newcc.y)) < bestdelta
660                         || (newdelta == bestdelta
661                             && dist2(newcc.x, newcc.y, guardx, guardy)
662                                < dist2(bestcc.x, bestcc.y, guardx, guardy))) {
663                         bestdelta = newdelta;
664                         bestcc = newcc;
665                     }
666                 }
667             } while (--tryct >= 0);
668 
669             if (bestdelta < gdelta) {
670                 remove_monster(guardx, guardy);
671                 newsym(guardx, guardy);
672                 place_monster(grd, (int) bestcc.x, (int) bestcc.y);
673                 newsym(grd->mx, grd->my);
674             }
675         }
676         obj_extract_self(gold);
677         add_to_minv(grd, gold);
678         newsym(goldx, goldy);
679 
680     /* guard is already at gold's location */
681     } else if (goldx == guardx && goldy == guardy) {
682         mpickgold(grd); /* does a newsym */
683 
684     /* gold is at some third spot, neither guard's nor hero's */
685     } else {
686         /* just for insurance... */
687         gd_mv_monaway(grd, goldx, goldy); /* make room for guard */
688         if (see_it) { /* skip if player won't see the message */
689             remove_monster(grd->mx, grd->my);
690             newsym(grd->mx, grd->my);
691             place_monster(grd, goldx, goldy); /* sets <grd->mx, grd->my> */
692         }
693         mpickgold(grd); /* does a newsym */
694     }
695 
696     if (see_it) { /* cansee(goldx, goldy) */
697         char monnambuf[BUFSZ];
698 
699         Strcpy(monnambuf, Monnam(grd));
700         if (!strcmpi(monnambuf, "It"))
701             Strcpy(monnambuf, "Someone");
702         pline("%s%s picks up the gold%s.", monnambuf,
703               (grd->mpeaceful && EGD(grd)->warncnt > 5)
704                  ? " calms down and" : "",
705               under_u ? " from beneath you" : "");
706     }
707 
708     /* if guard was moved to get the gold, move him back */
709     if (grd->mx != guardx || grd->my != guardy) {
710         remove_monster(grd->mx, grd->my);
711         newsym(grd->mx, grd->my);
712         place_monster(grd, guardx, guardy);
713         newsym(guardx, guardy);
714     }
715     return;
716 }
717 
718 /*
719  * return  1: guard moved,  0: guard didn't,  -1: let m_move do it,  -2: died
720  */
721 int
gd_move(struct monst * grd)722 gd_move(struct monst *grd)
723 {
724     int x, y, nx, ny, m, n;
725     int dx, dy, gx = 0, gy = 0, fci;
726     uchar typ;
727     struct rm *crm;
728     struct fakecorridor *fcp;
729     register struct egd *egrd = EGD(grd);
730     long umoney = 0L;
731     boolean goldincorridor = FALSE, u_in_vault = FALSE, grd_in_vault = FALSE,
732             disappear_msg_seen = FALSE, semi_dead = DEADMONSTER(grd),
733             u_carry_gold = FALSE, newspot = FALSE, see_guard;
734 
735     if (!on_level(&(egrd->gdlevel), &u.uz))
736         return -1;
737     nx = ny = m = n = 0;
738     if (semi_dead || !grd->mx || egrd->gddone) {
739         egrd->gddone = 1;
740         goto cleanup;
741     }
742     debugpline1("gd_move: %s guard", grd->mpeaceful ? "peaceful" : "hostile");
743 
744     u_in_vault = vault_occupied(u.urooms) ? TRUE : FALSE;
745     grd_in_vault = *in_rooms(grd->mx, grd->my, VAULT) ? TRUE : FALSE;
746     if (!u_in_vault && !grd_in_vault)
747         wallify_vault(grd);
748 
749     if (!grd->mpeaceful) {
750         if (!u_in_vault
751             && (grd_in_vault || (in_fcorridor(grd, grd->mx, grd->my)
752                                  && !in_fcorridor(grd, u.ux, u.uy)))) {
753             (void) rloc(grd, TRUE);
754             wallify_vault(grd);
755             if (!in_fcorridor(grd, grd->mx, grd->my))
756                 (void) clear_fcorr(grd, TRUE);
757             goto letknow;
758         }
759         if (!in_fcorridor(grd, grd->mx, grd->my))
760             (void) clear_fcorr(grd, TRUE);
761         return -1;
762     }
763     if (abs(egrd->ogx - grd->mx) > 1 || abs(egrd->ogy - grd->my) > 1)
764         return -1; /* teleported guard - treat as monster */
765 
766     if (egrd->witness) {
767         if (!Deaf)
768             verbalize("How dare you %s that gold, scoundrel!",
769                       (egrd->witness & GD_EATGOLD) ? "consume" : "destroy");
770         egrd->witness = 0;
771         grd->mpeaceful = 0;
772         return -1;
773     }
774 
775     umoney = money_cnt(g.invent);
776     u_carry_gold = (umoney > 0L || hidden_gold(TRUE) > 0L);
777     if (egrd->fcend == 1) {
778         if (u_in_vault && (u_carry_gold || um_dist(grd->mx, grd->my, 1))) {
779             if (egrd->warncnt == 3 && !Deaf)
780                 verbalize("I repeat, %sfollow me!",
781                           u_carry_gold
782                               ? (!umoney ? "drop that hidden gold and "
783                                          : "drop that gold and ")
784                               : "");
785             if (egrd->warncnt == 7) {
786                 m = grd->mx;
787                 n = grd->my;
788                 if (!Deaf)
789                     verbalize("You've been warned, knave!");
790                 mnexto(grd);
791                 levl[m][n].typ = egrd->fakecorr[0].ftyp;
792                 newsym(m, n);
793                 grd->mpeaceful = 0;
794                 return -1;
795             }
796             /* not fair to get mad when (s)he's fainted or paralyzed */
797             if (!is_fainted() && g.multi >= 0)
798                 egrd->warncnt++;
799             return 0;
800         }
801 
802         if (!u_in_vault) {
803             if (u_carry_gold) { /* player teleported */
804                 m = grd->mx;
805                 n = grd->my;
806                 (void) rloc(grd, TRUE);
807                 levl[m][n].typ = egrd->fakecorr[0].ftyp;
808                 newsym(m, n);
809                 grd->mpeaceful = 0;
810  letknow:
811                 if (!cansee(grd->mx, grd->my) || !mon_visible(grd))
812                     You_hear("%s.",
813                              m_carrying(grd, PEA_WHISTLE)
814                                  ? "the shrill sound of a guard's whistle"
815                                  : "angry shouting");
816                 else
817                     You(um_dist(grd->mx, grd->my, 2)
818                             ? "see %s approaching."
819                             : "are confronted by %s.",
820                         /* "an angry guard" */
821                         x_monnam(grd, ARTICLE_A, "angry", 0, FALSE));
822                 return -1;
823             } else {
824                 if (!Deaf)
825                     verbalize("Well, begone.");
826                 egrd->gddone = 1;
827                 goto cleanup;
828             }
829         }
830     }
831 
832     if (egrd->fcend > 1) {
833         if (egrd->fcend > 2 && in_fcorridor(grd, grd->mx, grd->my)
834             && !egrd->gddone && !in_fcorridor(grd, u.ux, u.uy)
835             && levl[egrd->fakecorr[0].fx][egrd->fakecorr[0].fy].typ
836                    == egrd->fakecorr[0].ftyp) {
837             pline("%s, confused, disappears.", noit_Monnam(grd));
838             disappear_msg_seen = TRUE;
839             goto cleanup;
840         }
841         if (u_carry_gold && (in_fcorridor(grd, u.ux, u.uy)
842                              /* cover a 'blind' spot */
843                              || (egrd->fcend > 1 && u_in_vault))) {
844             if (!grd->mx) {
845                 restfakecorr(grd);
846                 return -2;
847             }
848             if (egrd->warncnt < 6) {
849                 egrd->warncnt = 6;
850                 if (Deaf) {
851                     if (!Blind)
852                         pline("%s holds out %s palm demandingly!",
853                               noit_Monnam(grd), noit_mhis(grd));
854                 } else {
855                     verbalize("Drop all your gold, scoundrel!");
856                 }
857                 return 0;
858             } else {
859                 if (Deaf) {
860                     if (!Blind)
861                         pline("%s rubs %s hands with enraged delight!",
862                               noit_Monnam(grd), noit_mhis(grd));
863                 } else {
864                     verbalize("So be it, rogue!");
865                 }
866                 grd->mpeaceful = 0;
867                 return -1;
868             }
869         }
870     }
871     for (fci = egrd->fcbeg; fci < egrd->fcend; fci++)
872         if (g_at(egrd->fakecorr[fci].fx, egrd->fakecorr[fci].fy)) {
873             m = egrd->fakecorr[fci].fx;
874             n = egrd->fakecorr[fci].fy;
875             goldincorridor = TRUE;
876             break;
877         }
878     /* new gold can appear if it was embedded in stone and hero kicks it
879        (on even via wish and drop) so don't assume hero has been warned */
880     if (goldincorridor && !egrd->gddone) {
881         gd_pick_corridor_gold(grd, m, n);
882         if (!grd->mpeaceful)
883             return -1;
884         egrd->warncnt = 5;
885         return 0;
886     }
887     if (um_dist(grd->mx, grd->my, 1) || egrd->gddone) {
888         if (!egrd->gddone && !rn2(10) && !Deaf && !u.uswallow
889             && !(u.ustuck && !sticks(g.youmonst.data)))
890             verbalize("Move along!");
891         restfakecorr(grd);
892         return 0; /* didn't move */
893     }
894     x = grd->mx;
895     y = grd->my;
896 
897     if (u_in_vault)
898         goto nextpos;
899 
900     /* look around (hor & vert only) for accessible places */
901     for (nx = x - 1; nx <= x + 1; nx++)
902         for (ny = y - 1; ny <= y + 1; ny++) {
903             if ((nx == x || ny == y) && (nx != x || ny != y)
904                 && isok(nx, ny)) {
905                 typ = (crm = &levl[nx][ny])->typ;
906                 if (!IS_STWALL(typ) && !IS_POOL(typ)) {
907                     if (in_fcorridor(grd, nx, ny))
908                         goto nextnxy;
909 
910                     if (*in_rooms(nx, ny, VAULT))
911                         continue;
912 
913                     /* seems we found a good place to leave him alone */
914                     egrd->gddone = 1;
915                     if (ACCESSIBLE(typ))
916                         goto newpos;
917 #ifdef STUPID
918                     if (typ == SCORR)
919                         crm->typ = CORR;
920                     else
921                         crm->typ = DOOR;
922 #else
923                     crm->typ = (typ == SCORR) ? CORR : DOOR;
924 #endif
925                     if (crm->typ == DOOR)
926                         set_doorstate(crm, D_NODOOR);
927                     goto proceed;
928                 }
929             }
930  nextnxy:
931             ;
932         }
933  nextpos:
934     nx = x;
935     ny = y;
936     gx = egrd->gdx;
937     gy = egrd->gdy;
938     dx = (gx > x) ? 1 : (gx < x) ? -1 : 0;
939     dy = (gy > y) ? 1 : (gy < y) ? -1 : 0;
940     if (abs(gx - x) >= abs(gy - y))
941         nx += dx;
942     else
943         ny += dy;
944 
945     while ((typ = (crm = &levl[nx][ny])->typ) != STONE) {
946         /* in view of the above we must have IS_WALL(typ) or typ == POOL */
947         /* must be a wall here */
948         if (isok(nx + nx - x, ny + ny - y) && !IS_POOL(typ)
949             && IS_ROOM(levl[nx + nx - x][ny + ny - y].typ)) {
950             crm->typ = DOOR;
951             set_doorstate(crm, D_NODOOR);
952             goto proceed;
953         }
954         if (dy && nx != x) {
955             nx = x;
956             ny = y + dy;
957             continue;
958         }
959         if (dx && ny != y) {
960             ny = y;
961             nx = x + dx;
962             dy = 0;
963             continue;
964         }
965         /* I don't like this, but ... */
966         if (IS_ROOM(typ)) {
967             crm->typ = DOOR;
968             set_doorstate(crm, D_NODOOR);
969             goto proceed;
970         }
971         break;
972     }
973     crm->typ = CORR;
974  proceed:
975     newspot = TRUE;
976     unblock_point(nx, ny); /* doesn't block light */
977     if (cansee(nx, ny))
978         newsym(nx, ny);
979 
980     if ((nx != gx || ny != gy) || (grd->mx != gx || grd->my != gy)) {
981         fcp = &(egrd->fakecorr[egrd->fcend]);
982         if (egrd->fcend++ == FCSIZ)
983             panic("fakecorr overflow");
984         fcp->fx = nx;
985         fcp->fy = ny;
986         fcp->ftyp = typ;
987     } else if (!egrd->gddone) {
988         /* We're stuck, so try to find a new destination. */
989         if (!find_guard_dest(grd, &egrd->gdx, &egrd->gdy)
990             || (egrd->gdx == gx && egrd->gdy == gy)) {
991             pline("%s, confused, disappears.", Monnam(grd));
992             disappear_msg_seen = TRUE;
993             goto cleanup;
994         } else
995             goto nextpos;
996     }
997  newpos:
998     gd_mv_monaway(grd, nx, ny);
999     if (egrd->gddone) {
1000         /* The following is a kludge.  We need to keep    */
1001         /* the guard around in order to be able to make   */
1002         /* the fake corridor disappear as the player      */
1003         /* moves out of it, but we also need the guard    */
1004         /* out of the way.  We send the guard to never-   */
1005         /* never land.  We set ogx ogy to mx my in order  */
1006         /* to avoid a check at the top of this function.  */
1007         /* At the end of the process, the guard is killed */
1008         /* in restfakecorr().                             */
1009  cleanup:
1010         x = grd->mx, y = grd->my;
1011         see_guard = canspotmon(grd);
1012         parkguard(grd); /* move to <0,0> */
1013         wallify_vault(grd);
1014         restfakecorr(grd);
1015         debugpline2("gd_move: %scleanup%s",
1016                     grd->isgd ? "" : "final ",
1017                     grd->isgd ? " attempt" : "");
1018         if (!semi_dead && (in_fcorridor(grd, u.ux, u.uy) || cansee(x, y))) {
1019             if (!disappear_msg_seen && see_guard)
1020                 pline("Suddenly, %s disappears.", noit_mon_nam(grd));
1021             return 1;
1022         }
1023         return -2;
1024     }
1025     egrd->ogx = grd->mx; /* update old positions */
1026     egrd->ogy = grd->my;
1027     remove_monster(grd->mx, grd->my);
1028     place_monster(grd, nx, ny);
1029     if (newspot && g_at(nx, ny)) {
1030         /* if there's gold already here (most likely from mineralize()),
1031            pick it up now so that guard doesn't later think hero dropped
1032            it and give an inappropriate message */
1033         mpickgold(grd);
1034         if (canspotmon(grd))
1035             pline("%s picks up some gold.", Monnam(grd));
1036     } else
1037         newsym(grd->mx, grd->my);
1038     restfakecorr(grd);
1039     return 1;
1040 }
1041 
1042 /* Routine when dying or quitting with a vault guard around */
1043 void
paygd(boolean silently)1044 paygd(boolean silently)
1045 {
1046     register struct monst *grd = findgd();
1047     long umoney = money_cnt(g.invent);
1048     struct obj *coins, *nextcoins;
1049     int gx, gy;
1050     char buf[BUFSZ];
1051 
1052     if (!umoney || !grd)
1053         return;
1054 
1055     if (u.uinvault) {
1056         if (!silently)
1057             Your("%ld %s goes into the Magic Memory Vault.",
1058                  umoney, currency(umoney));
1059         gx = u.ux;
1060         gy = u.uy;
1061     } else {
1062         if (grd->mpeaceful) /* peaceful guard has no "right" to your gold */
1063             goto remove_guard;
1064 
1065         mnexto(grd);
1066         if (!silently)
1067             pline("%s remits your gold to the vault.", Monnam(grd));
1068         gx = g.rooms[EGD(grd)->vroom].lx + rn2(2);
1069         gy = g.rooms[EGD(grd)->vroom].ly + rn2(2);
1070         Sprintf(buf, "To Croesus: here's the gold recovered from %s the %s.",
1071                 g.plname,
1072                 pmname(&mons[u.umonster], flags.female ? FEMALE : MALE));
1073         if (!in_fcorridor(grd, gx, gy)) {
1074             /* don't place a grave in a temporary vault corridor that will be
1075              * removed in a bones file */
1076             make_grave(gx, gy, buf);
1077         }
1078     }
1079     for (coins = g.invent; coins; coins = nextcoins) {
1080         nextcoins = coins->nobj;
1081         if (objects[coins->otyp].oc_class == COIN_CLASS) {
1082             freeinv(coins);
1083             place_object(coins, gx, gy);
1084             stackobj(coins);
1085         }
1086     }
1087  remove_guard:
1088     mongone(grd);
1089     return;
1090 }
1091 
1092 /*
1093  * amount of gold in carried containers
1094  *
1095  * even_if_unknown:
1096  *   True:  all gold
1097  *   False: limit to known contents
1098  */
1099 long
hidden_gold(boolean even_if_unknown)1100 hidden_gold(boolean even_if_unknown)
1101 {
1102     long value = 0L;
1103     struct obj *obj;
1104 
1105     for (obj = g.invent; obj; obj = obj->nobj)
1106         if (Has_contents(obj) && (obj->cknown || even_if_unknown))
1107             value += contained_gold(obj, even_if_unknown);
1108     /* unknown gold stuck inside statues may cause some consternation... */
1109 
1110     return value;
1111 }
1112 
1113 /* prevent "You hear footsteps.." when inappropriate */
1114 boolean
gd_sound(void)1115 gd_sound(void)
1116 {
1117     struct monst *grd = findgd();
1118 
1119     if (vault_occupied(u.urooms))
1120         return FALSE;
1121     else
1122         return (boolean) (grd == (struct monst *) 0);
1123 }
1124 
1125 void
vault_gd_watching(unsigned int activity)1126 vault_gd_watching(unsigned int activity)
1127 {
1128     struct monst *guard = findgd();
1129 
1130     if (guard && guard->mcansee && m_canseeu(guard)) {
1131         if (activity == GD_EATGOLD || activity == GD_DESTROYGOLD)
1132             EGD(guard)->witness = activity;
1133     }
1134 }
1135 
1136 /*vault.c*/
1137