Logo Search packages:      
Sourcecode: falconseye version File versions  Download package

dig.c

/*    SCCS Id: @(#)dig.c      3.3   2000/04/19  */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"
#include "edog.h"
/* #define DEBUG */     /* turn on for diagnostics */

#ifdef OVLB

static NEARDATA boolean did_dig_msg;

STATIC_DCL boolean NDECL(rm_waslit);
STATIC_DCL void FDECL(mkcavepos, (XCHAR_P,XCHAR_P,int,BOOLEAN_P,BOOLEAN_P));
STATIC_DCL void FDECL(mkcavearea, (BOOLEAN_P));
STATIC_DCL int FDECL(dig_typ, (XCHAR_P,XCHAR_P));
STATIC_DCL int NDECL(dig);
STATIC_DCL schar FDECL(fillholetyp, (int, int));
STATIC_DCL void NDECL(dig_up_grave);


STATIC_OVL boolean
rm_waslit()
{
    register xchar x, y;

    if(levl[u.ux][u.uy].typ == ROOM && levl[u.ux][u.uy].waslit)
      return(TRUE);
    for(x = u.ux-2; x < u.ux+3; x++)
      for(y = u.uy-1; y < u.uy+2; y++)
          if(isok(x,y) && levl[x][y].waslit) return(TRUE);
    return(FALSE);
}

/* Change level topology.  Messes with vision tables and ignores things like
 * boulders in the name of a nice effect.  Vision will get fixed up again
 * immediately after the effect is complete.
 */
STATIC_OVL void
mkcavepos(x, y, dist, waslit, rockit)
    xchar x,y;
    int dist;
    boolean waslit, rockit;
{
    register struct rm *lev;

    if(!isok(x,y)) return;
    lev = &levl[x][y];

    if(rockit) {
      register struct monst *mtmp;

      if(IS_ROCK(lev->typ)) return;
      if(t_at(x, y)) return; /* don't cover the portal */
      if ((mtmp = m_at(x, y)) != 0) /* make sure crucial monsters survive */
          if(!passes_walls(mtmp->data)) rloc(mtmp);
    } else if(lev->typ == ROOM) return;

    unblock_point(x,y); /* make sure vision knows this location is open */

    /* fake out saved state */
    lev->seenv = 0;
    lev->doormask = 0;
    if(dist < 3) lev->lit = (rockit ? FALSE : TRUE);
    if(waslit) lev->waslit = (rockit ? FALSE : TRUE);
    lev->horizontal = FALSE;
    viz_array[y][x] = (dist < 3 ) ?
      (IN_SIGHT|COULD_SEE) : /* short-circuit vision recalc */
      COULD_SEE;
    lev->typ = (rockit ? STONE : ROOM);
    if(dist >= 3)
      impossible("mkcavepos called with dist %d", dist);
    if(Blind)
      feel_location(x, y);
    else newsym(x,y);
}

STATIC_OVL void
mkcavearea(rockit)
register boolean rockit;
{
    int dist;
    xchar xmin = u.ux, xmax = u.ux;
    xchar ymin = u.uy, ymax = u.uy;
    register xchar i;
    register boolean waslit = rm_waslit();

    if(rockit) pline("Crash!  The ceiling collapses around you!");
    else pline("A mysterious force %s cave around you!",
           (levl[u.ux][u.uy].typ == CORR) ? "creates a" : "extends the");
    display_nhwindow(WIN_MESSAGE, TRUE);

    for(dist = 1; dist <= 2; dist++) {
      xmin--; xmax++;

      /* top and bottom */
      if(dist < 2) { /* the area is wider that it is high */
          ymin--; ymax++;
          for(i = xmin+1; i < xmax; i++) {
            mkcavepos(i, ymin, dist, waslit, rockit);
            mkcavepos(i, ymax, dist, waslit, rockit);
          }
      }

      /* left and right */
      for(i = ymin; i <= ymax; i++) {
          mkcavepos(xmin, i, dist, waslit, rockit);
          mkcavepos(xmax, i, dist, waslit, rockit);
      }

      flush_screen(1);  /* make sure the new glyphs shows up */
      delay_output();
    }

    if(!rockit && levl[u.ux][u.uy].typ == CORR) {
      levl[u.ux][u.uy].typ = ROOM;
      if(waslit) levl[u.ux][u.uy].waslit = TRUE;
      newsym(u.ux, u.uy); /* in case player is invisible */
    }

    vision_full_recalc = 1;   /* everything changed */
}

/* When digging into location <x,y>, what are you actually digging into? */
/* result: 1=>statue, 2=>boulder, 3=>door, 0=>other; used as array index */
/* KMH -- Added 4=>tree */
STATIC_OVL int
dig_typ(x, y)
xchar x, y;
{
      return (sobj_at(STATUE, x, y) ? 1 :
            sobj_at(BOULDER, x, y) ? 2 :
            closed_door(x, y) ? 3 :
            IS_TREE(levl[x][y].typ) ? 4: 0);
}

#define BY_YOU          (&youmonst)
#define BY_OBJECT ((struct monst *)0)

boolean
dig_check(madeby, verbose, x, y)
      struct monst      *madeby;
      boolean           verbose;
      int         x, y;
{
      struct trap *ttmp = t_at(x, y);

      if (On_stairs(x, y)) {
          if (x == xdnladder || x == xupladder) {
            if(verbose) pline_The("ladder resists your effort.");
          } else if(verbose) pline_The("stairs are too hard to dig in.");
          return(FALSE);
      } else if (IS_THRONE(levl[x][y].typ) && madeby != BY_OBJECT) {
          if(verbose) pline_The("throne is too hard to break apart.");
          return(FALSE);
      } else if (IS_ALTAR(levl[x][y].typ) && (madeby != BY_OBJECT ||
                        Is_astralevel(&u.uz) || Is_sanctum(&u.uz))) {
          if(verbose) pline_The("altar is too hard to break apart.");
          return(FALSE);
      } else if (Is_airlevel(&u.uz)) {
          if(verbose) You("cannot dig in thin air.");
          return(FALSE);
      } else if (Is_waterlevel(&u.uz)) {
          if(verbose) pline_The("water splashes and subsides.");
          return(FALSE);
      } else if ((IS_WALL(levl[x][y].typ) &&
                  (levl[x][y].wall_info & W_NONDIGGABLE) != 0)
            || (ttmp &&
                  (ttmp->ttyp == MAGIC_PORTAL || !Can_dig_down(&u.uz)))) {
          if(verbose) pline_The("%s here is too hard to dig in.",
                          surface(x,y));
          return(FALSE);
      } else if (sobj_at(BOULDER, x, y)) {
          if(verbose) There("isn't enough room to dig here.");
          return(FALSE);
      } else if (madeby == BY_OBJECT &&
                /* the block against existing traps is mainly to
                   prevent broken wands from turning holes into pits */
                (ttmp || is_pool(x,y) || is_lava(x,y))) {
          /* digging by player handles pools separately */
          return FALSE;
      }
      return(TRUE);
}

STATIC_OVL int
dig()
{
      register struct rm *lev;
      register xchar dpx = digging.pos.x, dpy = digging.pos.y;

      lev = &levl[dpx][dpy];
      /* perhaps a nymph stole your pick-axe while you were busy digging */
      /* or perhaps you teleported away */
      if (u.uswallow || !uwep || !is_pick(uwep) ||
          !on_level(&digging.level, &u.uz) ||
          ((digging.down ? (dpx != u.ux || dpy != u.uy)
                     : (distu(dpx,dpy) > 2))))
            return(0);

      if (digging.down) {
          if(!dig_check(BY_YOU, TRUE, u.ux, u.uy)) return(0);
      } else { /* !digging.down */
          if (IS_ROCK(lev->typ) && !may_dig(dpx,dpy) && !dig_typ(dpx, dpy)) {
            pline("This wall is too hard to dig into.");
            return(0);
          }
          if (IS_TREE(lev->typ) && !may_dig(dpx,dpy) && dig_typ(dpx, dpy) == 4) {
            pline("This tree seems to be petrified.");
            return(0);
          }
      }
      if(Fumbling && !rn2(3)) {
            switch(rn2(3)) {
            case 0:  if(!welded(uwep)) {
                       You("fumble and drop your %s.", xname(uwep));
                       dropx(uwep);
                       setuwep((struct obj *)0);
                   } else {
                       pline("Ouch!  Your %s bounces and hits you!",
                        xname(uwep));
                       set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
                   }
                   break;
            case 1:  pline("Bang!  You hit with the broad side of %s!",
                         the(xname(uwep)));
                   break;
            default: Your("swing misses its mark.");
                   break;
            }
            return(0);
      }

      digging.effort += 10 + rn2(5) + abon() +
                     uwep->spe - greatest_erosion(uwep) + u.udaminc;
      if (Race_if(PM_DWARF))
          digging.effort *= 2;
      if (digging.down) {
            register struct trap *ttmp;

            if (digging.effort > 250) {
                (void) dighole(FALSE);
                (void) memset((genericptr_t)&digging, 0, sizeof digging);
                return(0);    /* done with digging */
            }

            if (digging.effort <= 50 ||
                ((ttmp = t_at(dpx,dpy)) != 0 &&
                  (ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT ||
                   ttmp->ttyp == TRAPDOOR || ttmp->ttyp == HOLE)))
                return(1);

            if (IS_ALTAR(lev->typ)) {
                altar_wrath(dpx, dpy);
                angry_priest();
            }

            if (dighole(TRUE)) {    /* make pit at <u.ux,u.uy> */
                digging.level.dnum = 0;
                digging.level.dlevel = -1;
            }
            return(0);
      }

      if (digging.effort > 100) {
            register const char *digtxt, *dmgtxt = (const char*) 0;
            register struct obj *obj;
            register boolean shopedge = *in_rooms(dpx, dpy, SHOPBASE);

            if ((obj = sobj_at(STATUE, dpx, dpy)) != 0) {
                  if (break_statue(obj))
                        digtxt = "The statue shatters.";
                  else
                        /* it was a statue trap; break_statue()
                         * printed a message and updated the screen
                         */
                        digtxt = (char *)0;
            } else if ((obj = sobj_at(BOULDER, dpx, dpy)) != 0) {
                  fracture_rock(obj);
                  digtxt = "The boulder falls apart.";
            } else if (lev->typ == STONE || lev->typ == SCORR ||
                        IS_TREE(lev->typ)) {
                  if(Is_earthlevel(&u.uz)) {
                      if(uwep->blessed && !rn2(3)) {
                        mkcavearea(FALSE);
                        goto cleanup;
                      } else if((uwep->cursed && !rn2(4)) ||
                                (!uwep->blessed && !rn2(6))) {
                        mkcavearea(TRUE);
                        goto cleanup;
                      }
                  }
                  if (IS_TREE(lev->typ)) {
                      digtxt = "You cut down the tree.";
                      lev->typ = ROOM;
                  } else {
                      digtxt = "You succeed in cutting away some rock.";
                      lev->typ = CORR;
                  }
            } else if(IS_WALL(lev->typ)) {
                  if(shopedge) {
                      add_damage(dpx, dpy, 10L * ACURRSTR);
                      dmgtxt = "damage";
                  }
                  if (level.flags.is_maze_lev) {
                      lev->typ = ROOM;
                  } else if (level.flags.is_cavernous_lev) {
                      lev->typ = CORR;
                  } else {
                      lev->typ = DOOR;
                      lev->doormask = D_NODOOR;
                  }
                  digtxt = "You make an opening in the wall.";
            } else if(lev->typ == SDOOR) {
                  cvt_sdoor_to_door(lev); /* ->typ = DOOR */
                  digtxt = "You break through a secret door!";
                  if(!(lev->doormask & D_TRAPPED))
                        lev->doormask = D_BROKEN;
            } else if(closed_door(dpx, dpy)) {
                  digtxt = "You break through the door.";
                  if(shopedge) {
                      add_damage(dpx, dpy, 400L);
                      dmgtxt = "break";
                  }
                  if(!(lev->doormask & D_TRAPPED))
                        lev->doormask = D_BROKEN;
            } else return(0); /* statue or boulder got taken */

            unblock_point(dpx,dpy); /* vision:  can see through */
            if(Blind)
                feel_location(dpx, dpy);
            else
                newsym(dpx, dpy);
            if(digtxt) pline(digtxt);     /* after newsym */
            if(dmgtxt)
                pay_for_damage(dmgtxt);

            if(Is_earthlevel(&u.uz) && !rn2(3)) {
                register struct monst *mtmp;

                switch(rn2(2)) {
                  case 0:
                  mtmp = makemon(&mons[PM_EARTH_ELEMENTAL],
                              dpx, dpy, NO_MM_FLAGS);
                  break;
                  default:
                  mtmp = makemon(&mons[PM_XORN],
                              dpx, dpy, NO_MM_FLAGS);
                  break;
                }
                if(mtmp) pline_The("debris from your digging comes to life!");
            }
            if(IS_DOOR(lev->typ) && (lev->doormask & D_TRAPPED)) {
                  lev->doormask = D_NODOOR;
                  b_trapped("door", 0);
                  newsym(dpx, dpy);
            }
cleanup:
            digging.level.dnum = 0;
            digging.level.dlevel = -1;
            return(0);
      } else {          /* not enough effort has been spent yet */
            static const char *d_target[5] = {
                              "rock", "statue", "boulder", "door", "tree"
            };
            int dig_target = dig_typ(dpx, dpy);

            if (IS_WALL(lev->typ) || dig_target == 3) {
                if(*in_rooms(dpx, dpy, SHOPBASE)) {
                  pline("This %s seems too hard to dig into.",
                        IS_DOOR(lev->typ) ? "door" : "wall");
                  return(0);
                }
            } else if (!IS_ROCK(lev->typ) && !dig_target)
                  return(0); /* statue or boulder got taken */
            if(!did_dig_msg) {
                You("hit the %s with all your might.",
                  d_target[dig_target]);
                did_dig_msg = TRUE;
            }
      }
      return(1);
}

/* When will hole be finished? Very rough indication used by shopkeeper. */
int
holetime()
{
      if(occupation != dig || !*u.ushops) return(-1);
      return ((250 - digging.effort) / 20);
}

/* Return typ of liquid to fill a hole with, or ROOM, if no liquid nearby */
STATIC_OVL
schar
fillholetyp(x,y)
int x, y;
{
    register int x1, y1;
    int lo_x = max(1,x-1), hi_x = min(x+1,COLNO-1),
      lo_y = max(0,y-1), hi_y = min(y+1,ROWNO-1);
    int pool_cnt = 0, moat_cnt = 0, lava_cnt = 0;

    for (x1 = lo_x; x1 <= hi_x; x1++)
      for (y1 = lo_y; y1 <= hi_y; y1++)
          if (levl[x1][y1].typ == POOL)
            pool_cnt++;
          else if (levl[x1][y1].typ == MOAT ||
                (levl[x1][y1].typ == DRAWBRIDGE_UP &&
                  (levl[x1][y1].drawbridgemask & DB_UNDER) == DB_MOAT))
            moat_cnt++;
          else if (levl[x1][y1].typ == LAVAPOOL ||
                (levl[x1][y1].typ == DRAWBRIDGE_UP &&
                  (levl[x1][y1].drawbridgemask & DB_UNDER) == DB_LAVA))
            lava_cnt++;
    pool_cnt /= 3;            /* not as much liquid as the others */

    if (lava_cnt > moat_cnt + pool_cnt && rn2(lava_cnt + 1))
      return LAVAPOOL;
    else if (moat_cnt > 0 && rn2(moat_cnt + 1))
      return MOAT;
    else if (pool_cnt > 0 && rn2(pool_cnt + 1))
      return POOL;
    else
      return ROOM;
}

void
digactualhole(x, y, madeby, ttyp)
register int      x, y;
struct monst      *madeby;
int ttyp;
{
      struct obj *oldobjs, *newobjs;
      register struct trap *ttmp;
      char surface_type[BUFSZ];
      struct rm *lev = &levl[x][y];
      boolean shopdoor;
      struct monst *mtmp = m_at(x, y);    /* may be madeby */
      boolean madeby_u = (madeby == BY_YOU);
      boolean madeby_obj = (madeby == BY_OBJECT);
      boolean at_u = (x == u.ux) && (y == u.uy);
      boolean wont_fall = Levitation || Flying;

      /* these furniture checks were in dighole(), but wand
         breaking bypasses that routine and calls us directly */
      if (IS_FOUNTAIN(lev->typ)) {
          dogushforth(FALSE);
          lev->looted |= F_WARNED;        /* force dryup */
          dryup(x, y, madeby_u);
          return;
#ifdef SINKS
      } else if (IS_SINK(lev->typ)) {
          breaksink(x, y);
          return;
#endif
      }

      if (ttyp != PIT && !Can_dig_down(&u.uz)) {
          impossible("digactualhole: can't dig %s on this level.",
                   defsyms[trap_to_defsym(ttyp)].explanation);
          ttyp = PIT;
      }

      Strcpy(surface_type, surface(x,y)); /* maketrap() might change it */
      shopdoor = IS_DOOR(lev->typ) && *in_rooms(x, y, SHOPBASE);
      oldobjs = level.objects[x][y];
      ttmp = maketrap(x, y, ttyp);
      if (!ttmp) return;
      newobjs = level.objects[x][y];
      ttmp->tseen = (madeby_u || cansee(x,y));
      ttmp->madeby_u = madeby_u;
      newsym(ttmp->tx,ttmp->ty);

      if (ttyp == PIT) {

          if(madeby_u) {
            You("dig a pit in the %s.", surface_type);
            if (shopdoor) pay_for_damage("ruin");
          } else if (!madeby_obj && canseemon(madeby))
            pline("%s digs a pit in the %s.", Monnam(madeby), surface_type);
          else if (cansee(x, y) && flags.verbose)
            pline("A pit appears in the %s.", surface_type);

          if(at_u) {
            if (!wont_fall) {
                  u.utrap = rn1(4,2);
                  u.utraptype = TT_PIT;
                  vision_full_recalc = 1; /* vision limits change */
            } else
                  u.utrap = 0;
            if (oldobjs != newobjs) /* something unearthed */
                  (void) pickup(1); /* detects pit */
          } else if(mtmp) {
            if(is_flyer(mtmp->data) || is_floater(mtmp->data)) {
                if(canseemon(mtmp))
                  pline("%s %s over the pit.", Monnam(mtmp),
                                         (is_flyer(mtmp->data)) ?
                                         "flies" : "floats");
            } else if(mtmp != madeby)
                (void) mintrap(mtmp);
          }
      } else {    /* was TRAPDOOR now a HOLE*/

          if(madeby_u)
            You("dig a hole through the %s.", surface_type);
          else if(!madeby_obj && canseemon(madeby))
            pline("%s digs a hole through the %s.",
                  Monnam(madeby), surface_type);
          else if(cansee(x, y) && flags.verbose)
            pline("A hole appears in the %s.", surface_type);

          if (at_u) {
            if (!u.ustuck && !wont_fall && !next_to_u()) {
                You("are jerked back by your pet!");
                wont_fall = TRUE;
            }

            /* Floor objects get a chance of falling down.  The case where
             * the hero does NOT fall down is treated here.  The case
             * where the hero does fall down is treated in goto_level().
             */
            if (u.ustuck || wont_fall) {
                if (newobjs)
                  impact_drop((struct obj *)0, x, y, 0);
                if (oldobjs != newobjs)
                  (void) pickup(1);
                if (shopdoor && madeby_u) pay_for_damage("ruin");

            } else {
                d_level newlevel;

                if (*u.ushops && madeby_u)
                  shopdig(1); /* shk might snatch pack */

                You("fall through...");
                /* Earlier checks must ensure that the destination
                 * level exists and is in the present dungeon.
                 */
                newlevel.dnum = u.uz.dnum;
                newlevel.dlevel = u.uz.dlevel + 1;
                goto_level(&newlevel, FALSE, TRUE, FALSE);
                /* messages for arriving in special rooms */
                spoteffects(FALSE);
            }
          } else {
            if (shopdoor && madeby_u) pay_for_damage("ruin");
            if (newobjs)
                impact_drop((struct obj *)0, x, y, 0);
            if (mtmp) {
                 /*[don't we need special sokoban handling here?]*/
                if (is_flyer(mtmp->data) || is_floater(mtmp->data) ||
                    mtmp->data == &mons[PM_WUMPUS] ||
                  (mtmp->wormno && count_wsegs(mtmp) > 5) ||
                  mtmp->data->msize >= MZ_HUGE) return;
                if (mtmp == u.ustuck)     /* probably a vortex */
                      return;       /* temporary? kludge */

                if (teleport_pet(mtmp, FALSE)) {
                  d_level tolevel;

                  if (Is_stronghold(&u.uz)) {
                      assign_level(&tolevel, &valley_level);
                  } else if (Is_botlevel(&u.uz)) {
                      if (canseemon(mtmp))
                        pline("%s avoids the trap.", Monnam(mtmp));
                      return;
                  } else {
                      get_level(&tolevel, depth(&u.uz) + 1);
                  }
                  migrate_to_level(mtmp, ledger_no(&tolevel),
                               MIGR_RANDOM, (coord *)0);
                }
            }
          }
      }
}

/* return TRUE if digging succeeded, FALSE otherwise */
boolean
dighole(pit_only)
boolean pit_only;
{
      register struct trap *ttmp = t_at(u.ux, u.uy);
      struct rm *lev = &levl[u.ux][u.uy];
      struct obj *boulder_here;
      schar typ;
      boolean nohole = !Can_dig_down(&u.uz);

      if ((ttmp && (ttmp->ttyp == MAGIC_PORTAL || nohole)) ||
         (IS_WALL(lev->typ) && (lev->wall_info & W_NONDIGGABLE) != 0)) {
            pline_The("%s here is too hard to dig in.", surface(u.ux,u.uy));

      } else if (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) {
            pline_The("%s sloshes furiously for a moment, then subsides.",
                  is_lava(u.ux, u.uy) ? "lava" : "water");
            wake_nearby();    /* splashing */

      } else if (lev->typ == DRAWBRIDGE_DOWN ||
               (is_drawbridge_wall(u.ux, u.uy) >= 0)) {
            /* drawbridge_down is the platform crossing the moat when the
               bridge is extended; drawbridge_wall is the open "doorway" or
               closed "door" where the portcullis/mechanism is located */
            if (pit_only) {
                pline_The("drawbridge seems too hard to dig through.");
                return FALSE;
            } else {
                int x = u.ux, y = u.uy;
                /* if under the portcullis, the bridge is adjacent */
                (void) find_drawbridge(&x, &y);
                destroy_drawbridge(x, y);
                return TRUE;
            }

      } else if ((boulder_here = sobj_at(BOULDER, u.ux, u.uy)) != 0) {
            if (ttmp && (ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT) &&
                rn2(2)) {
                  pline_The("boulder settles into the pit.");
                  ttmp->ttyp = PIT;  /* crush spikes */
            } else {
                  /*
                   * digging makes a hole, but the boulder immediately
                   * fills it.  Final outcome:  no hole, no boulder.
                   */
                  pline("KADOOM! The boulder falls in!");
                  (void) delfloortrap(ttmp);
            }
            delobj(boulder_here);
            return TRUE;

      } else if (IS_GRAVE(lev->typ)) {        
          digactualhole(u.ux, u.uy, BY_YOU, PIT);
          dig_up_grave();
          return TRUE;
      } else if (lev->typ == DRAWBRIDGE_UP) {
            /* must be floor or ice, other cases handled above */
            /* dig "pit" and let fluid flow in (if possible) */
            typ = fillholetyp(u.ux,u.uy);

            if (typ == ROOM) {
                  /*
                   * We can't dig a hole here since that will destroy
                   * the drawbridge.  The following is a cop-out. --dlc
                   */
                  pline_The("%s here is too hard to dig in.",
                        surface(u.ux, u.uy));
                  return FALSE;
            }

            lev->drawbridgemask &= ~DB_UNDER;
            lev->drawbridgemask |= (typ == LAVAPOOL) ? DB_LAVA : DB_MOAT;

 liquid_flow:
            if (ttmp) (void) delfloortrap(ttmp);
            /* if any objects were frozen here, they're released now */
            unearth_objs(u.ux, u.uy);

            pline("As you dig, the hole fills with %s!",
                  typ == LAVAPOOL ? "lava" : "water");
            if (!Levitation && !Flying) {
                if (typ == LAVAPOOL)
                  (void) lava_effects();
                else if (!Wwalking)
                  (void) drown();
            }
            return TRUE;

      /* the following two are here for the wand of digging */
      } else if (IS_THRONE(lev->typ)) {
            pline_The("throne is too hard to break apart.");

      } else if (IS_ALTAR(lev->typ)) {
            pline_The("altar is too hard to break apart.");

      } else {
            typ = fillholetyp(u.ux,u.uy);

            if (typ != ROOM) {
                  lev->typ = typ;
                  goto liquid_flow;
            }

            /* finally we get to make a hole */
            if (nohole || pit_only)
                  digactualhole(u.ux, u.uy, BY_YOU, PIT);
            else
                  digactualhole(u.ux, u.uy, BY_YOU, HOLE);

            return TRUE;
      }

      return FALSE;
}

STATIC_OVL void
dig_up_grave()
{
      struct obj *otmp;


      /* Grave-robbing is frowned upon... */
      exercise(A_WIS, FALSE);
      if (Role_if(PM_ARCHEOLOGIST)) {
          adjalign(-sgn(u.ualign.type)*3);
          You_feel("like a despicable grave-robber!");
      } else if (Role_if(PM_SAMURAI)) {
          adjalign(-sgn(u.ualign.type));
          You("disturb the honorable dead!");
      } else if ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10)) {
          adjalign(-sgn(u.ualign.type));
          You("have violated the sanctity of this grave!");
      }

      switch (rn2(5)) {
      case 0:
      case 1:
          You("unearth a corpse.");
          if (!!(otmp = mk_tt_object(CORPSE, u.ux, u.uy)))
            otmp->age -= 100;       /* this is an *OLD* corpse */;
          break;
      case 2:
          if (!Blind) pline(Hallucination ? "Dude!  The living dead!" :
                  "The grave's owner is very upset!");
          (void) makemon(mkclass(S_ZOMBIE,0), u.ux, u.uy, NO_MM_FLAGS);
          break;
      case 3:
          if (!Blind) pline(Hallucination ? "I want my mummy!" :
                  "You've disturbed a tomb!");
          (void) makemon(mkclass(S_MUMMY,0), u.ux, u.uy, NO_MM_FLAGS);
          break;
      default:
          /* No corpse */
          pline_The("grave seems unused.  Strange....");
          break;
      }
      levl[u.ux][u.uy].typ = ROOM;
      del_engr_at(u.ux, u.uy);
      newsym(u.ux,u.uy);
      return;
}

int
use_pick_axe(obj)
struct obj *obj;
{
      char dirsyms[12];
      char qbuf[QBUFSZ];
      register char *dsp = dirsyms;
      register struct rm *lev;
      register int rx, ry;
      int dig_target, res = 0;
      register const char *sdp;
      if(iflags.num_pad) sdp = ndir; else sdp = sdir; /* DICE workaround */

      if (obj != uwep) {
          if (!wield_tool(obj)) return(0);
          else res = 1;
      }
      if (u.utrap && u.utraptype == TT_WEB) {
          pline("%s you can't dig while entangled in a web.",
              /* res==0 => no prior message;
                 res==1 => just got "You now wield a pick-axe." message */
              !res ? "Unfortunately," : "But");
          return res;
      }

      while(*sdp) {
            (void) movecmd(*sdp);   /* sets u.dx and u.dy and u.dz */
            rx = u.ux + u.dx;
            ry = u.uy + u.dy;
            if(u.dz > 0 || (u.dz == 0 && isok(rx, ry) &&
                (IS_ROCK(levl[rx][ry].typ) || dig_typ(rx, ry))))
                  *dsp++ = *sdp;
            sdp++;
      }
      *dsp = 0;
      Sprintf(qbuf, "In what direction do you want to dig? [%s]", dirsyms);
      if(!getdir(qbuf))
            return(res);
      if (u.uswallow && attack(u.ustuck)) {
            ;  /* return(1) */
      } else if (Underwater) {
            pline("Turbulence torpedoes your digging attempts.");
      } else if(u.dz < 0) {
            if(Levitation)
                  You("don't have enough leverage.");
            else
                  You_cant("reach the %s.",ceiling(u.ux,u.uy));
      } else if(!u.dx && !u.dy && !u.dz) {
            char buf[BUFSZ];
            int dam;

            dam = rnd(2) + dbon() + obj->spe;
            if (dam <= 0) dam = 1;
            You("hit yourself with %s.", yname(uwep));
            /* self_pronoun() won't work twice in a sentence */
            Strcpy(buf, self_pronoun("killed %sself with %%s pick-axe",
                  "him"));
            losehp(dam, self_pronoun(buf, "his"), NO_KILLER_PREFIX);
            flags.botl=1;
            return(1);
      } else if(u.dz == 0) {
            if(Stunned || (Confusion && !rn2(5))) confdir();
            rx = u.ux + u.dx;
            ry = u.uy + u.dy;
            if(!isok(rx, ry)) {
                  pline("Clash!");
                  return(1);
            }
            lev = &levl[rx][ry];
            if(MON_AT(rx, ry) && attack(m_at(rx, ry)))
                  return(1);
            dig_target = dig_typ(rx, ry);
            if (!IS_ROCK(lev->typ) && !dig_target) {
                  /* ACCESSIBLE or POOL */
                  struct trap *trap = t_at(rx, ry);

                  if (trap && trap->ttyp == WEB) {
                      if (!trap->tseen) {
                        seetrap(trap);
                        There("is a spider web there!");
                      }
                      Your("%s becomes entangled in the web.",
                        aobjnam(obj, (char *)0));
                      /* you ought to be able to let go; tough luck */
                      /* (maybe `move_into_trap()' would be better) */
                      nomul(-d(2,2));
                      nomovemsg = "You pull free.";
                  } else
                      You("swing your %s through thin air.",
                        aobjnam(obj, (char *)0));
            } else {
                  static const char *d_action[5] = {
                                    "digging",
                                    "chipping the statue",
                                    "hitting the boulder",
                                    "chopping at the door",
                                    "cutting the tree"
                  };
                  if (digging.pos.x != rx || digging.pos.y != ry ||
                      !on_level(&digging.level, &u.uz) || digging.down) {
                      digging.down = digging.chew = FALSE;
                      digging.pos.x = rx;
                      digging.pos.y = ry;
                      assign_level(&digging.level, &u.uz);
                      digging.effort = 0;
                      You("start %s.", d_action[dig_target]);
                  } else {
                      You("%s %s.", digging.chew ? "begin" : "continue",
                              d_action[dig_target]);
                      digging.chew = FALSE;
                  }
                  did_dig_msg = FALSE;
                  set_occupation(dig, "digging", 0);
            }
      } else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) {
            /* it must be air -- water checked above */
            You("swing your %s through thin air.", aobjnam(obj, (char *)0));
      } else if (!can_reach_floor()) {
            You_cant("reach the %s.", surface(u.ux,u.uy));
      } else if (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) {
            /* Monsters which swim also happen not to be able to dig */
            You("cannot stay under%s long enough.",
                        is_pool(u.ux, u.uy) ? "water" : " the lava");
      } else {
            if (digging.pos.x != u.ux || digging.pos.y != u.uy ||
                  !on_level(&digging.level, &u.uz) || !digging.down) {
                digging.chew = FALSE;
                digging.down = TRUE;
                digging.pos.x = u.ux;
                digging.pos.y = u.uy;
                assign_level(&digging.level, &u.uz);
                digging.effort = 0;
                You("start digging downward.");
                if (*u.ushops) shopdig(0);
            } else
                You("continue digging downward.");
            did_dig_msg = FALSE;
            set_occupation(dig, "digging", 0);
      }
      return(1);
}

#endif /* OVLB */
#ifdef OVL0

/* Return TRUE if monster died, FALSE otherwise.  Called from m_move(). */
boolean
mdig_tunnel(mtmp)
register struct monst *mtmp;
{
      register struct rm *here;
      int pile = rnd(12);

      here = &levl[mtmp->mx][mtmp->my];
      if (here->typ == SDOOR)
          cvt_sdoor_to_door(here);  /* ->typ = DOOR */
      if (IS_TREE(here->typ))
            /* KMH -- Trees shouldn't create piles */
            pile = 0;

      /* Eats away door if present & closed or locked */
      if (closed_door(mtmp->mx, mtmp->my)) {
          if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE))
            add_damage(mtmp->mx, mtmp->my, 0L);
          unblock_point(mtmp->mx, mtmp->my);    /* vision */
          if (here->doormask & D_TRAPPED) {
            here->doormask = D_NODOOR;
            if (mb_trapped(mtmp)) { /* mtmp is killed */
                newsym(mtmp->mx, mtmp->my);
                return TRUE;
            }
          } else {
            if (!rn2(3) && flags.verbose) /* not too often.. */
                You_feel("an unexpected draft.");
            here->doormask = D_BROKEN;
          }
          newsym(mtmp->mx, mtmp->my);
          return FALSE;
      } else
      if (!IS_ROCK(here->typ)) /* no dig */
          return FALSE;

      /* Only rock and walls fall through to this point. */
      if ((here->wall_info & W_NONDIGGABLE) != 0) {
          impossible("mdig_tunnel:  %s at (%d,%d) is undiggable",
                   (IS_WALL(here->typ) ? "wall" : "stone"),
                   (int) mtmp->mx, (int) mtmp->my);
          return FALSE; /* still alive */
      }

      if (IS_WALL(here->typ)) {
          /* KMH -- Okay on arboreal levels (room walls are still stone) */
          if (flags.soundok && flags.verbose && !rn2(5))
            You_hear("crashing rock.");
          if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE))
            add_damage(mtmp->mx, mtmp->my, 0L);
          if (level.flags.is_maze_lev) {
            here->typ = ROOM;
          } else if (level.flags.is_cavernous_lev) {
            here->typ = CORR;
          } else {
            here->typ = DOOR;
            here->doormask = D_NODOOR;
          }
      } else
          /* KMH -- Added support for trees */
          here->typ = level.flags.arboreal ? ROOM : CORR;

      if (pile && pile < 5)   /* leave behind some rocks? */
          (void) mksobj_at((pile == 1) ? BOULDER : ROCK,
                       mtmp->mx, mtmp->my, TRUE);
      newsym(mtmp->mx, mtmp->my);
      if (!sobj_at(BOULDER, mtmp->mx, mtmp->my))
          unblock_point(mtmp->mx, mtmp->my);    /* vision */

      return FALSE;
}

#endif /* OVL0 */
#ifdef OVL3

/* digging via wand zap or spell cast */
void
zap_dig()
{
      struct rm *room;
      struct monst *mtmp;
      struct obj *otmp;
      int zx, zy, digdepth;
      boolean shopdoor, shopwall, maze_dig;
      /*
       * Original effect (approximately):
       * from CORR: dig until we pierce a wall
       * from ROOM: pierce wall and dig until we reach
       * an ACCESSIBLE place.
       * Currently: dig for digdepth positions;
       * also down on request of Lennart Augustsson.
       */

      if (u.uswallow) {
          mtmp = u.ustuck;

          if (!is_whirly(mtmp->data)) {
            if (is_animal(mtmp->data))
                You("pierce %s stomach wall!", s_suffix(mon_nam(mtmp)));
            mtmp->mhp = 1;          /* almost dead */
            expels(mtmp, mtmp->data, !is_animal(mtmp->data));
          }
          return;
      } /* swallowed */

      if (u.dz) {
          if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !Underwater) {
            if (u.dz < 0 || On_stairs(u.ux, u.uy)) {
                if (On_stairs(u.ux, u.uy))
                  pline_The("beam bounces off the %s and hits the %s.",
                        (u.ux == xdnladder || u.ux == xupladder) ?
                        "ladder" : "stairs", ceiling(u.ux, u.uy));
                You("loosen a rock from the %s.", ceiling(u.ux, u.uy));
                pline("It falls on your %s!", body_part(HEAD));
                losehp(rnd((uarmh && is_metallic(uarmh)) ? 2 : 6),
                     "falling rock", KILLED_BY_AN);
                if ((otmp = mksobj_at(ROCK, u.ux, u.uy, FALSE)) != 0) {
                  (void)xname(otmp);      /* set dknown, maybe bknown */
                  stackobj(otmp);
                }
                if (Invisible) newsym(u.ux, u.uy);
            } else {
                (void) dighole(FALSE);
            }
          }
          return;
      } /* up or down */

      /* normal case: digging across the level */
      shopdoor = shopwall = FALSE;
      maze_dig = level.flags.is_maze_lev && !Is_earthlevel(&u.uz);
      zx = u.ux + u.dx;
      zy = u.uy + u.dy;
      digdepth = rn1(18, 8);
      tmp_at(DISP_BEAM, cmap_to_glyph(S_digbeam));
      while (--digdepth >= 0) {
          if (!isok(zx,zy)) break;
          room = &levl[zx][zy];
          tmp_at(zx,zy);
          delay_output();     /* wait a little bit */
          if (closed_door(zx, zy) || room->typ == SDOOR) {
            if (*in_rooms(zx,zy,SHOPBASE)) {
                add_damage(zx, zy, 400L);
                shopdoor = TRUE;
            }
            if (room->typ == SDOOR)
                room->typ = DOOR;
            else if (cansee(zx, zy))
                pline_The("door is razed!");
            room->doormask = D_NODOOR;
            unblock_point(zx,zy); /* vision */
            digdepth -= 2;
            if (maze_dig) break;
          } else if (maze_dig) {
            if (IS_WALL(room->typ)) {
                if (!(room->wall_info & W_NONDIGGABLE)) {
                  if (*in_rooms(zx,zy,SHOPBASE)) {
                      add_damage(zx, zy, 200L);
                      shopwall = TRUE;
                  }
                  room->typ = ROOM;
                  unblock_point(zx,zy); /* vision */
                } else if (!Blind)
                  pline_The("wall glows then fades.");
                break;
            } else if (room->typ == STONE || room->typ == SCORR) {
                if (!(room->wall_info & W_NONDIGGABLE)) {
                  room->typ = CORR;
                  unblock_point(zx,zy); /* vision */
                } else if (!Blind)
                  pline_The("rock glows then fades.");
                break;
            }
          } else if (IS_ROCK(room->typ)) {
            if (!may_dig(zx,zy)) break;
            if (IS_WALL(room->typ) || room->typ == SDOOR) {
                if (*in_rooms(zx,zy,SHOPBASE)) {
                  add_damage(zx, zy, 200L);
                  shopwall = TRUE;
                }
                if (level.flags.is_cavernous_lev) {
                  room->typ = CORR;
                } else {
                  room->typ = DOOR;
                  room->doormask = D_NODOOR;
                }
                digdepth -= 2;
            } else {    /* IS_ROCK but not IS_WALL or SDOOR */
                room->typ = CORR;
                digdepth--;
            }
            unblock_point(zx,zy); /* vision */
          }
          zx += u.dx;
          zy += u.dy;
      } /* while */
      tmp_at(DISP_END,0);     /* closing call */
      if (shopdoor || shopwall)
          pay_for_damage(shopdoor ? "destroy" : "dig into");
      return;
}

/* move objects from fobj/nexthere lists to buriedobjlist, keeping position */
/* information */
struct obj *
bury_an_obj(otmp)
      struct obj *otmp;
{
      struct obj *otmp2;
      boolean under_ice;

#ifdef DEBUG
      pline("bury_an_obj: %s", xname(otmp));
#endif
      if (otmp == uball)
            unpunish();
      /* after unpunish(), or might get deallocated chain */
      otmp2 = otmp->nexthere;
      /*
       * obj_resists(,0,0) prevents Rider corpses from being buried.
       * It also prevents The Amulet and invocation tools from being
       * buried.  Since they can't be confined to bags and statues,
       * it makes sense that they can't be buried either, even though
       * the real reason there (direct accessibility when carried) is
       * completely different.
       */
      if (otmp == uchain || obj_resists(otmp, 0, 0))
            return(otmp2);

      if (otmp->otyp == LEASH && otmp->leashmon != 0)
            o_unleash(otmp);

      if (otmp->lamplit && otmp->otyp != POT_OIL)
            end_burn(otmp, TRUE);

      obj_extract_self(otmp);

      under_ice = is_ice(otmp->ox, otmp->oy);
      if (otmp->otyp == ROCK && !under_ice) {
            /* merges into burying material */
            obfree(otmp, (struct obj *)0);
            return(otmp2);
      }
      /*
       * Start a rot on organic material.  Not corpses -- they
       * are already handled.
       */
      if (otmp->otyp == CORPSE) {
          ;       /* should cancel timer if under_ice */
      } else if ((under_ice ? otmp->oclass == POTION_CLASS : is_organic(otmp))
            && !obj_resists(otmp, 5, 95)) {
          (void) start_timer((under_ice ? 0L : 250L) + (long)rnd(250),
                         TIMER_OBJECT, ROT_ORGANIC, (genericptr_t)otmp);
      }
      add_to_buried(otmp);
      return(otmp2);
}

void
bury_objs(x, y)
int x, y;
{
      struct obj *otmp, *otmp2;

#ifdef DEBUG
      if(level.objects[x][y] != (struct obj *)0)
            pline("bury_objs: at %d, %d", x, y);
#endif
      for (otmp = level.objects[x][y]; otmp; otmp = otmp2)
            otmp2 = bury_an_obj(otmp);

      /* don't expect any engravings here, but just in case */
      del_engr_at(x, y);
      newsym(x, y);
}

/* move objects from buriedobjlist to fobj/nexthere lists */
void
unearth_objs(x, y)
int x, y;
{
      struct obj *otmp, *otmp2;

#ifdef DEBUG
      pline("unearth_objs: at %d, %d", x, y);
#endif
      for (otmp = level.buriedobjlist; otmp; otmp = otmp2) {
            otmp2 = otmp->nobj;
            if (otmp->ox == x && otmp->oy == y) {
                obj_extract_self(otmp);
                if (otmp->timed)
                  (void) stop_timer(ROT_ORGANIC, (genericptr_t)otmp);
                place_object(otmp, x, y);
                stackobj(otmp);
            }
      }
      del_engr_at(x, y);
      newsym(x, y);
}

/*
 * The organic material has rotted away while buried.  As an expansion,
 * we could add add partial damage.  A damage count is kept in the object
 * and every time we are called we increment the count and reschedule another
 * timeout.  Eventually the object rots away.
 *
 * This is used by buried objects other than corpses.  When a container rots
 * away, any contents become newly buried objects.
 */
/* ARGSUSED */
void
rot_organic(arg, timeout)
genericptr_t arg;
long timeout;     /* unused */
{
      struct obj *obj = (struct obj *) arg;

      while (Has_contents(obj)) {
          /* We don't need to place contained object on the floor
             first, but we do need to update its map coordinates. */
          obj->cobj->ox = obj->ox,  obj->cobj->oy = obj->oy;
          /* Everything which can be held in a container can also be
             buried, so bury_an_obj's use of obj_extract_self insures
             that Has_contents(obj) will eventually become false. */
          (void)bury_an_obj(obj->cobj);
      }
      obj_extract_self(obj);
      obfree(obj, (struct obj *) 0);
}

/*
 * Called when a corpse has rotted completely away.
 */
void
rot_corpse(arg, timeout)
genericptr_t arg;
long timeout;     /* unused */
{
      xchar x = 0, y = 0;
      struct obj *obj = (struct obj *) arg;
      boolean on_floor = obj->where == OBJ_FLOOR,
            in_invent = obj->where == OBJ_INVENT;

      if (on_floor) {
          x = obj->ox;
          y = obj->oy;
      } else if (in_invent) {
          if (flags.verbose)
            Your("%s%s rot%s away%c",
                 obj == uwep ? "wielded " : "", corpse_xname(obj, FALSE),
                 obj->quan == 1L ? "s" : "", obj == uwep ? '!' : '.');
          if (obj == uwep) {
            uwepgone(); /* now bare handed */
            stop_occupation();
          } else if (obj == uswapwep) {
            uswapwepgone();
            stop_occupation();
          } else if (obj == uquiver) {
            uqwepgone();
            stop_occupation();
          }
      } else if (obj->where == OBJ_MINVENT && obj->owornmask) {
          if (obj == MON_WEP(obj->ocarry)) {
            obj->owornmask &= ~W_WEP;
            MON_NOWEP(obj->ocarry);
          }
      }
      rot_organic(arg, timeout);
      if (on_floor) newsym(x, y);
      else if (in_invent) update_inventory();
}

#if 0
void
bury_monst(mtmp)
struct monst *mtmp;
{
#ifdef DEBUG
      pline("bury_monst: %s", mon_nam(mtmp));
#endif
      if(canseemon(mtmp)) {
          if(is_flyer(mtmp->data) || is_floater(mtmp->data)) {
            pline_The("%s opens up, but %s is not swallowed!",
                  surface(mtmp->mx, mtmp->my), mon_nam(mtmp));
            return;
          } else
              pline_The("%s opens up and swallows %s!",
                  surface(mtmp->mx, mtmp->my), mon_nam(mtmp));
      }

      mtmp->mburied = TRUE;
      wakeup(mtmp);                 /* at least give it a chance :-) */
      newsym(mtmp->mx, mtmp->my);
}

void
bury_you()
{
#ifdef DEBUG
      pline("bury_you");
#endif
    if (!Levitation && !Flying) {
      if(u.uswallow)
          You_feel("a sensation like falling into a trap!");
      else
          pline_The("%s opens beneath you and you fall in!",
              surface(u.ux, u.uy));

      u.uburied = TRUE;
      if(!Strangled && !Breathless) Strangled = 6;
      under_ground(1);
    }
}

void
unearth_you()
{
#ifdef DEBUG
      pline("unearth_you");
#endif
      u.uburied = FALSE;
      under_ground(0);
      if(!uamul || uamul->otyp != AMULET_OF_STRANGULATION)
            Strangled = 0;
      vision_recalc(0);
}

void
escape_tomb()
{
#ifdef DEBUG
      pline("escape_tomb");
#endif
      if ((Teleportation || can_teleport(youmonst.data)) &&
          (Teleport_control || rn2(3) < Luck+2)) {
            You("attempt a teleport spell.");
            (void) dotele();  /* calls unearth_you() */
      } else if(u.uburied) { /* still buried after 'port attempt */
            boolean good;

            if(amorphous(youmonst.data) || Passes_walls ||
               noncorporeal(youmonst.data) || unsolid(youmonst.data) ||
               (tunnels(youmonst.data) && !needspick(youmonst.data))) {

                You("%s up through the %s.",
                  (tunnels(youmonst.data) && !needspick(youmonst.data)) ?
                   "try to tunnel" : (amorphous(youmonst.data)) ?
                   "ooze" : "phase", surface(u.ux, u.uy));

                if(tunnels(youmonst.data) && !needspick(youmonst.data))
                  good = dighole(TRUE);
                else good = TRUE;
                if(good) unearth_you();
            }
      }
}

void
bury_obj(otmp)
struct obj *otmp;
{

#ifdef DEBUG
      pline("bury_obj");
#endif
      if(cansee(otmp->ox, otmp->oy))
         pline_The("objects on the %s tumble into a hole!",
            surface(otmp->ox, otmp->oy));

      bury_objs(otmp->ox, otmp->oy);
}
#endif

#ifdef DEBUG
void
wiz_debug_cmd() /* in this case, bury everything at your loc and around */
{
      int x, y;

      for (x = u.ux - 1; x <= u.ux + 1; x++)
          for (y = u.uy - 1; y <= u.uy + 1; y++)
            if (isok(x,y)) bury_objs(x,y);
}

#endif /* DEBUG */
#endif /* OVL3 */

/*dig.c*/

Generated by  Doxygen 1.6.0   Back to index