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