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

mkobj.c

/*    SCCS Id: @(#)mkobj.c    3.2   96/05/14    */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"
#include "artifact.h"
#include "prop.h"

STATIC_DCL void FDECL(mkbox_cnts,(struct obj *));
STATIC_DCL void FDECL(obj_timer_checks,(struct obj *, XCHAR_P, XCHAR_P, int));
#ifdef OVL1
static void FDECL(container_weight, (struct obj *));
#ifdef WIZARD
static const char *FDECL(where_name, (int));
static void FDECL(check_contained, (struct obj *,const char *));
#endif
#endif /* OVL1 */

/*#define DEBUG_EFFECTS*/     /* show some messages for debugging */

struct icp {
    int  iprob;         /* probability of an item type */
    char iclass;  /* item class */
};

#ifdef OVL1

const struct icp mkobjprobs[] = {
{10, WEAPON_CLASS},
{10, ARMOR_CLASS},
{20, FOOD_CLASS},
{ 8, TOOL_CLASS},
{ 8, GEM_CLASS},
{16, POTION_CLASS},
{16, SCROLL_CLASS},
{ 4, SPBOOK_CLASS},
{ 4, WAND_CLASS},
{ 3, RING_CLASS},
{ 1, AMULET_CLASS}
};

const struct icp boxiprobs[] = {
{18, GEM_CLASS},
{15, FOOD_CLASS},
{18, POTION_CLASS},
{18, SCROLL_CLASS},
{12, SPBOOK_CLASS},
{ 7, GOLD_CLASS},
{ 6, WAND_CLASS},
{ 5, RING_CLASS},
{ 1, AMULET_CLASS}
};

#ifdef REINCARNATION
const struct icp rogueprobs[] = {
{12, WEAPON_CLASS},
{12, ARMOR_CLASS},
{22, FOOD_CLASS},
{22, POTION_CLASS},
{22, SCROLL_CLASS},
{ 5, WAND_CLASS},
{ 5, RING_CLASS}
};
#endif

const struct icp hellprobs[] = {
{20, WEAPON_CLASS},
{20, ARMOR_CLASS},
{16, FOOD_CLASS},
{12, TOOL_CLASS},
{10, GEM_CLASS},
{ 1, POTION_CLASS},
{ 1, SCROLL_CLASS},
{ 8, WAND_CLASS},
{ 8, RING_CLASS},
{ 4, AMULET_CLASS}
};

struct obj *
mkobj_at(let,x,y, artif)
char let;
int x,y;
boolean artif;
{
      register struct obj *otmp;

      otmp = mkobj(let,artif);
      place_object(otmp, x, y);
      return(otmp);
}

struct obj *
mksobj_at(otyp,x,y,init)
int otyp,x,y;
boolean init;
{
      register struct obj *otmp;

      otmp = mksobj(otyp,init,TRUE);
      place_object(otmp, x, y);
      return(otmp);
}

struct obj *
mkobj(oclass, artif)
char oclass;
boolean artif;
{
      register int tprob, i, prob = rnd(1000);

      if(oclass == RANDOM_CLASS) {
            const struct icp *iprobs =
#ifdef REINCARNATION
                            (Is_rogue_level(&u.uz)) ?
                            (const struct icp *)rogueprobs :
#endif
                            Inhell ? (const struct icp *)hellprobs :
                            (const struct icp *)mkobjprobs;

            for(tprob = rnd(100);
                (tprob -= iprobs->iprob) > 0;
                iprobs++);
            oclass = iprobs->iclass;
      }

      i = bases[(int)oclass];
      while((prob -= objects[i].oc_prob) > 0) i++;

      if(objects[i].oc_class != oclass || !OBJ_NAME(objects[i]))
            panic("probtype error, oclass=%d i=%d", (int) oclass, i);

      return(mksobj(i, TRUE, artif));
}

STATIC_OVL void
mkbox_cnts(box)
struct obj *box;
{
      register int n;
      register struct obj *otmp, *gold = 0;

      box->cobj = (struct obj *) 0;

      switch(box->otyp) {
            case ICE_BOX:           n = 20; break;
            case CHEST:       n = 5; break;
            case LARGE_BOX:         n = 3; break;
            case SACK:
            case OILSKIN_SACK:
                        /* initial inventory: sack starts out empty */
                        if (moves <= 1 && !in_mklev) { n = 0; break; }
                        /*else FALLTHRU*/
            case BAG_OF_HOLDING:    n = 1; break;
            default:          n = 0; break;
      }

      for (n = rn2(n+1); n > 0; n--) {
          if (box->otyp == ICE_BOX) {
            if (!(otmp = mksobj(CORPSE, TRUE, TRUE))) continue;
            /* Note: setting age to 0 is correct.  Age has a different
             * from usual meaning for objects stored in ice boxes. -KAA
             */
            otmp->age = 0L;
            if (otmp->timed) {
                (void) stop_timer(ROT_CORPSE, (genericptr_t)otmp);
                (void) stop_timer(REVIVE_MON, (genericptr_t)otmp);
            }
          } else {
            register int tprob;
            const struct icp *iprobs = boxiprobs;

            for (tprob = rnd(100); (tprob -= iprobs->iprob) > 0; iprobs++)
                ;
            if (!(otmp = mkobj(iprobs->iclass, TRUE))) continue;

            /* handle a couple of special cases */
            if (otmp->oclass == GOLD_CLASS) {
                /* 2.5 x level's usual amount; weight adjusted below */
                otmp->quan = (long)(rnd(level_difficulty()+2) * rnd(75));
                if (gold) {               /* gold already in this box */
                  gold->quan += otmp->quan;     /* merge */
                  dealloc_obj(otmp);      /* note: not yet in any chain */
                  continue;
                } else {
                  gold = otmp;            /* remember this object */
                }
            } else while (otmp->otyp == ROCK) {
                otmp->otyp = rnd_class(DILITHIUM_CRYSTAL, LOADSTONE);
                if (otmp->quan > 2L) otmp->quan = 1L;
                otmp->owt = weight(otmp);
            }
            if (box->otyp == BAG_OF_HOLDING) {
                if (Is_mbag(otmp)) {
                  otmp->otyp = SACK;
                  otmp->spe = 0;
                  otmp->owt = weight(otmp);
                } else while (otmp->otyp == WAN_CANCELLATION)
                      otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING);
            }
          }
          add_to_container(box, otmp);
      }
      if (gold) gold->owt = weight(gold); /* quantity was diddled */
      return;
}

int
rndmonnum() /* select a random, common monster type */
{
      register struct permonst *ptr;
      register int      i;

      /* Plan A: get a level-appropriate common monster */
      ptr = rndmonst();
      if (ptr) return(monsndx(ptr));

      /* Plan B: get any common monster */
      do {
          i = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
          ptr = &mons[i];
      } while((ptr->geno & G_NOGEN) || (!Inhell && (ptr->geno & G_HELL)));

      return(i);
}

/*
 * Split obj so that it gets size num. The remainder is put in the object
 * structure delivered by this call.  The object is positioned just
 * following the original in the nobj chain (and nexthere chain when on
 * the floor).
 */
struct obj *
splitobj(obj, num)
struct obj *obj;
long num;
{
      struct obj *otmp;

      if (obj->cobj || num <= 0L || obj->quan < num)
          panic("splitobj");  /* can't split containers */
      otmp = newobj(obj->oxlth + obj->onamelth);
      *otmp = *obj;           /* copies whole structure */
      otmp->o_id = flags.ident++;
      if (!otmp->o_id) otmp->o_id = flags.ident++;    /* ident overflowed */
      otmp->timed = 0;  /* not timed, yet */
      otmp->lamplit = 0;      /* ditto */
      obj->quan = num;
      obj->owt = weight(obj);
      otmp->quan -= num;
      otmp->owt = weight(otmp);     /* -= obj->owt ? */
      obj->nobj = otmp;
      /* Only set nexthere when on the floor, nexthere is also used */
      /* as a back pointer to the container object when contained. */
      if (obj->where == OBJ_FLOOR)
          obj->nexthere = otmp;
      if (obj->oxlth)
          (void)memcpy((genericptr_t)otmp->oextra, (genericptr_t)obj->oextra,
                  obj->oxlth);
      if (obj->onamelth)
          (void)strncpy(ONAME(otmp), ONAME(obj), (int)obj->onamelth);
      if (obj->unpaid) splitbill(obj,otmp);
      if (obj->timed) obj_split_timers(obj, otmp);
      if (obj_sheds_light(obj)) obj_split_light_source(obj, otmp);
      return otmp;
}

/*
 * Create a dummy duplicate to put on shop bill.  The duplicate exists
 * only in the billobjs chain.  This function is used when a shop object
 * is being altered, and a copy of the original is needed for billing
 * purposes.  For example, when eating, where an interruption will yield
 * an object which is different from what it started out as; the "I x"
 * command needs to display the original object.
 */
void
bill_dummy_object(otmp)
register struct obj *otmp;
{
      register struct obj *dummy;

      if (otmp->unpaid)
          subfrombill(otmp, shop_keeper(*u.ushops));
      dummy = newobj(otmp->oxlth + otmp->onamelth);
      *dummy = *otmp;
      dummy->where = OBJ_FREE;
      dummy->o_id = flags.ident++;
      if (!dummy->o_id) dummy->o_id = flags.ident++;  /* ident overflowed */
      dummy->timed = 0;
      if (otmp->oxlth)
          (void)memcpy((genericptr_t)dummy->oextra,
                  (genericptr_t)otmp->oextra, otmp->oxlth);
      if (otmp->onamelth)
          (void)strncpy(ONAME(dummy), ONAME(otmp), (int)otmp->onamelth);
      if (Is_candle(dummy)) dummy->lamplit = 0;
      addtobill(dummy, FALSE, TRUE, TRUE);
}

#endif /* OVL1 */
#ifdef OVLB

static const char dknowns[] = {
            WAND_CLASS, RING_CLASS, POTION_CLASS, SCROLL_CLASS,
            GEM_CLASS, SPBOOK_CLASS, WEAPON_CLASS, TOOL_CLASS, 0
};

struct obj *
mksobj(otyp, init, artif)
int otyp;
boolean init;
boolean artif;
{
      int mndx, tryct;
      struct obj *otmp;
      char let = objects[otyp].oc_class;

      otmp = newobj(0);
      *otmp = zeroobj;
      otmp->age = monstermoves;
      otmp->o_id = flags.ident++;
      if (!otmp->o_id) otmp->o_id = flags.ident++;    /* ident overflowed */
      otmp->quan = 1L;
      otmp->oclass = let;
      otmp->otyp = otyp;
      otmp->where = OBJ_FREE;
      otmp->dknown = index(dknowns, let) ? 0 : 1;
      if ((otmp->otyp >= ELVEN_SHIELD && otmp->otyp <= ORCISH_SHIELD) ||
                  otmp->otyp == SHIELD_OF_REFLECTION)
            otmp->dknown = 0;
      if (!objects[otmp->otyp].oc_uses_known)
            otmp->known = 1;
      if (init) switch (let) {
      case WEAPON_CLASS:
            otmp->quan = (otmp->otyp <= SHURIKEN) ? (long) rn1(6,6) : 1L;
            if(!rn2(11)) {
                  otmp->spe = rne(3);
                  otmp->blessed = rn2(2);
            } else if(!rn2(10)) {
                  curse(otmp);
                  otmp->spe = -rne(3);
            } else      blessorcurse(otmp, 10);

            if (artif && !rn2(20))
                otmp = mk_artifact(otmp, (aligntyp)A_NONE);
            break;
      case FOOD_CLASS:
          otmp->oeaten = 0;
          switch(otmp->otyp) {
          case CORPSE:
            /* possibly overridden by mkcorpstat() */
            tryct = 50;
            do otmp->corpsenm = undead_to_corpse(rndmonnum());
            while ((mvitals[otmp->corpsenm].mvflags & G_NOCORPSE) && (--tryct > 0));
            if (tryct == 0) {
            /* perhaps rndmonnum() only wants to make G_NOCORPSE monsters on
               this level; let's create an adventurer's corpse instead, then */
                  otmp->corpsenm = PM_HUMAN;
            }
            start_corpse_timeout(otmp);
            break;
          case EGG:
            otmp->corpsenm = NON_PM;      /* generic egg */
            if (!rn2(3)) for (tryct = 200; tryct > 0; --tryct) {
                mndx = can_be_hatched(rndmonnum());
                if (mndx != NON_PM && !dead_species(mndx, TRUE)) {
                  otmp->corpsenm = mndx;        /* typed egg */
                  attach_egg_hatch_timeout(otmp);
                  break;
                }
            }
            break;
          case TIN:
            otmp->corpsenm = NON_PM;      /* empty (so far) */
            if (!rn2(6))
                otmp->spe = 1;            /* spinach */
            else for (tryct = 200; tryct > 0; --tryct) {
                mndx = undead_to_corpse(rndmonnum());
                if (mons[mndx].cnutrit &&
                      !(mvitals[mndx].mvflags & G_NOCORPSE)) {
                  otmp->corpsenm = mndx;
                  break;
                }
            }
            blessorcurse(otmp, 10);
            break;
          case SLIME_MOLD:
            otmp->spe = current_fruit;
            break;
          }
          if (otmp->otyp == CORPSE) break;
          /* fall into next case */

      case GEM_CLASS:
            if (otmp->otyp == LOADSTONE) curse(otmp);
            else if (otmp->otyp == ROCK) otmp->quan = (long) rn1(6,6);
            else if (otmp->otyp != LUCKSTONE && !rn2(6)) otmp->quan = 2L;
            else otmp->quan = 1L;
            break;
      case TOOL_CLASS:
          switch(otmp->otyp) {
            case TALLOW_CANDLE:
            case WAX_CANDLE:  otmp->spe = 1;
                              otmp->age = 20L * /* 400 or 200 */
                                    (long)objects[otmp->otyp].oc_cost;
                              otmp->lamplit = 0;
                              otmp->quan = 1L +
                                    (long)(rn2(2) ? rn2(7) : 0);
                              blessorcurse(otmp, 5);
                              break;
            case BRASS_LANTERN:
            case OIL_LAMP:          otmp->spe = 1;
                              otmp->age = (long) rn1(500,1000);
                              otmp->lamplit = 0;
                              blessorcurse(otmp, 5);
                              break;
            case MAGIC_LAMP:  otmp->spe = 1;
                              otmp->lamplit = 0;
                              blessorcurse(otmp, 2);
                              break;
            case CHEST:
            case LARGE_BOX:         otmp->olocked = !!(rn2(5));
                              otmp->otrapped = !(rn2(10));
            case ICE_BOX:
            case SACK:
            case OILSKIN_SACK:
            case BAG_OF_HOLDING:    mkbox_cnts(otmp);
                              break;
            case MAGIC_MARKER:      otmp->spe = rn1(70,30);
                              break;
            case CAN_OF_GREASE:     otmp->spe = rnd(25);
                              blessorcurse(otmp, 10);
                              break;
            case CRYSTAL_BALL:      otmp->spe = rnd(5);
                              blessorcurse(otmp, 2);
                              break;
            case HORN_OF_PLENTY:
            case BAG_OF_TRICKS:     otmp->spe = rnd(20);
                              break;
            case FIGURINE:    {     int tryct2 = 0;
                              do
                                  otmp->corpsenm = rndmonnum();
                              while(is_human(&mons[otmp->corpsenm])
                                    && tryct2++ < 30);
                              blessorcurse(otmp, 4);
                              break;
                        }
            case BELL_OF_OPENING:   otmp->spe = 3;
                              break;
            case MAGIC_FLUTE:
            case MAGIC_HARP:
            case FROST_HORN:
            case FIRE_HORN:
            case DRUM_OF_EARTHQUAKE:
                              otmp->spe = rn1(5,4);
                              break;
          }
          break;
      case AMULET_CLASS:
            if(rn2(10) && (otmp->otyp == AMULET_OF_STRANGULATION ||
               otmp->otyp == AMULET_OF_CHANGE ||
               otmp->otyp == AMULET_OF_RESTFUL_SLEEP)) {
                  curse(otmp);
            } else      blessorcurse(otmp, 10);
      case VENOM_CLASS:
      case CHAIN_CLASS:
      case BALL_CLASS:
            break;
      case POTION_CLASS:
            if (otmp->otyp == POT_OIL)
                otmp->age = MAX_OIL_IN_FLASK;   /* amount of oil */
            /* fall through */
      case SCROLL_CLASS:
#ifdef MAIL
            if (otmp->otyp != SCR_MAIL)
#endif
                  blessorcurse(otmp, 4);
            break;
      case SPBOOK_CLASS:
            blessorcurse(otmp, 17);
            break;
      case ARMOR_CLASS:
            if(rn2(10) && (otmp->otyp == FUMBLE_BOOTS ||
               otmp->otyp == LEVITATION_BOOTS ||
               otmp->otyp == HELM_OF_OPPOSITE_ALIGNMENT ||
               otmp->otyp == GAUNTLETS_OF_FUMBLING ||
               !rn2(11))) {
                  curse(otmp);
                  otmp->spe = -rne(3);
            } else if(!rn2(10)) {
                  otmp->blessed = rn2(2);
                  otmp->spe = rne(3);
            } else      blessorcurse(otmp, 10);
            /* simulate lacquered armor for samurai */
            if (Role_is('S') && otmp->otyp == SPLINT_MAIL &&
                (moves <= 1 || In_quest(&u.uz))) {
#ifdef UNIXPC
                  /* optimizer bitfield bug */
                  otmp->oerodeproof = 1;
                  otmp->rknown = 1;
#else
                  otmp->oerodeproof = otmp->rknown = 1;
#endif
            }
            break;
      case WAND_CLASS:
            if(otmp->otyp == WAN_WISHING) otmp->spe = rnd(3); else
            otmp->spe = rn1(5,
                  (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4);
            blessorcurse(otmp, 17);
            otmp->recharged = 0; /* used to control recharging */
            break;
      case RING_CLASS:
            if(objects[otmp->otyp].oc_charged) {
                blessorcurse(otmp, 3);
                if(rn2(10)) {
                  if(rn2(10) && bcsign(otmp))
                      otmp->spe = bcsign(otmp) * rne(3);
                  else otmp->spe = rn2(2) ? rne(3) : -rne(3);
                }
                /* make useless +0 rings much less common */
                if (otmp->spe == 0) otmp->spe = rn2(4) - rn2(3);
                /* negative rings are usually cursed */
                if (otmp->spe < 0 && rn2(5)) curse(otmp);
            } else if(rn2(10) && (otmp->otyp == RIN_TELEPORTATION ||
                    otmp->otyp == RIN_POLYMORPH ||
                    otmp->otyp == RIN_AGGRAVATE_MONSTER ||
                    otmp->otyp == RIN_HUNGER || !rn2(9))) {
                  curse(otmp);
            }
            break;
      case ROCK_CLASS:
            switch (otmp->otyp) {
                case STATUE:
                  /* possibly overridden by mkcorpstat() */
                  otmp->corpsenm = rndmonnum();
                  if (!verysmall(&mons[otmp->corpsenm]) &&
                        rn2(level_difficulty()/2 + 10) > 10)
                      add_to_container(otmp, mkobj(SPBOOK_CLASS,FALSE));
            }
            break;
      case GOLD_CLASS:
            break;      /* do nothing */
      default:
            impossible("impossible mkobj %d, sym '%c'.", otmp->otyp,
                                    objects[otmp->otyp].oc_class);
            return (struct obj *)0;
      }
      /* unique objects may have an associated artifact entry */
      if (objects[otyp].oc_unique && !otmp->oartifact)
          otmp = mk_artifact(otmp, (aligntyp)A_NONE);
      otmp->owt = weight(otmp);
      return(otmp);
}

/*
 * Start a corpse decay or revive timer.  This assumes that the corpse
 * was just dropped and its age is 0.
 */
void
start_corpse_timeout(body)
      struct obj *body;
{
      long when;
      int rot_adjust;
      short action;

#define TAINT_AGE (50L)       /* age when corpses go bad */
#define TROLL_REVIVE_CHANCE 37      /* 1/37 chance for 50 turns ~ 75% chance */
#define ROT_AGE (250L)  /* age when corpses rot away */

      /* lizards don't rot or revive */
      if (body->corpsenm == PM_LIZARD) return;

      action = ROT_CORPSE;          /* default action: rot away */
      when = ROT_AGE;               /* rot away when this old */
      rot_adjust = in_mklev ? 25 : 10;    /* give some variation */
      when += (long)(rnz(rot_adjust) - rot_adjust);

      if (is_rider(&mons[body->corpsenm])) {
            /*
             * Riders always revive.  They have a 1/3 chance per turn
             * of reviving after 12 turns.  Always revive by 500.
             */
            action = REVIVE_MON;
            for (when = 12L; when < 500L; when++)
                if (!rn2(3)) break;

      } else if (mons[body->corpsenm].mlet == S_TROLL) {
            long age;
            for (age = 2; age <= TAINT_AGE; age++)
                if (!rn2(TROLL_REVIVE_CHANCE)) {      /* troll revives */
                  action = REVIVE_MON;
                  when = age;
                  break;
                }
      }

      (void) start_timer(when, TIMER_OBJECT, action, (genericptr_t)body);
}

void
bless(otmp)
register struct obj *otmp;
{
      otmp->cursed = 0;
      otmp->blessed = 1;
      if (otmp->otyp == LUCKSTONE
            || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK)))
          set_moreluck();
      else if (otmp->otyp == BAG_OF_HOLDING)
          otmp->owt = weight(otmp);
      return;
}

void
unbless(otmp)
register struct obj *otmp;
{
      otmp->blessed = 0;
      if (otmp->otyp == LUCKSTONE
            || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK)))
          set_moreluck();
      else if (otmp->otyp == BAG_OF_HOLDING)
          otmp->owt = weight(otmp);
}

void
curse(otmp)
register struct obj *otmp;
{
      otmp->blessed = 0;
      otmp->cursed = 1;
      if (otmp->otyp == LUCKSTONE
            || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK)))
          set_moreluck();
      else if (otmp->otyp == BAG_OF_HOLDING)
          otmp->owt = weight(otmp);
      return;
}

void
uncurse(otmp)
register struct obj *otmp;
{
      otmp->cursed = 0;
      if (otmp->otyp == LUCKSTONE
            || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK)))
          set_moreluck();
      else if (otmp->otyp == BAG_OF_HOLDING)
            otmp->owt = weight(otmp);
}

#endif /* OVLB */
#ifdef OVL1

void
blessorcurse(otmp, chance)
register struct obj *otmp;
register int chance;
{
      if(otmp->blessed || otmp->cursed) return;

      if(!rn2(chance)){
          if(!rn2(2)) {
            curse(otmp);
          } else {
            bless(otmp);
          }
      }
      return;
}

#endif /* OVL1 */
#ifdef OVLB

int
bcsign(otmp)
register struct obj *otmp;
{
      return(!!otmp->blessed - !!otmp->cursed);
}

#endif /* OVLB */
#ifdef OVL0

/*
 *  Calculate the weight of the given object.  This will recursively follow
 *  and calculate the weight of any containers.
 *
 *  Note:  It is possible to end up with an incorrect weight if some part
 *       of the code messes with a contained object and doesn't update the
 *       container's weight.
 */
int
weight(obj)
register struct obj *obj;
{
      int wt = objects[obj->otyp].oc_weight;

      if (obj->otyp == LARGE_BOX && obj->spe == 1) /* Schroedinger's Cat */
            wt += mons[PM_HOUSECAT].cwt;
      if (Is_container(obj) || obj->otyp == STATUE) {
            struct obj *contents;
            register int cwt = 0;

            if (obj->otyp == STATUE && obj->corpsenm >= LOW_PM)
                wt = (int)obj->quan *
                   ((int)mons[obj->corpsenm].cwt * 3 / 2);

            for(contents=obj->cobj; contents; contents=contents->nobj)
                  cwt += weight(contents);
            /*
             *  The weight of bags of holding is calculated as the weight
             *  of the bag plus the weight of the bag's contents modified
             *  as follows:
             *
             *    Bag status  Weight of contents
             *    ----------  ------------------
             *    cursed                  2x
             *    blessed                 x/4 + 1
             *    otherwise         x/2 + 1
             *
             *  The macro DELTA_CWT in pickup.c also implements these
             *  weight equations.
             *
             *  Note:  The above checks are performed in the given order.
             *       this means that if an object is both blessed and
             *       cursed (not supposed to happen), it will be treated
             *       as cursed.
             */
            if (obj->otyp == BAG_OF_HOLDING)
                cwt = obj->cursed ? (cwt * 2) :
                              (1 + (cwt / (obj->blessed ? 4 : 2)));

            return wt + cwt;
      }
      if (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM)
            return (int)obj->quan * mons[obj->corpsenm].cwt;
      else if (obj->oclass == GOLD_CLASS)
            return (int)((obj->quan + 50L) / 100L);
      else if (obj->otyp == HEAVY_IRON_BALL && obj->owt != 0)
            return((int)(obj->owt));      /* kludge for "very" heavy iron ball */
      return(wt ? wt*(int)obj->quan : ((int)obj->quan + 1)>>1);
}

#endif /* OVL0 */
#ifdef OVLB

void
mkgold(amount, x, y)
long amount;
int x, y;
{
    register struct obj *gold = g_at(x,y);

    if (amount <= 0L) amount = (long)(1 + rnd(level_difficulty()+2) * rnd(30));
    if (gold) {
      gold->quan += amount;
    } else {
      gold = mksobj_at(GOLD_PIECE,x,y,TRUE);
      gold->quan = amount;
    }
    gold->owt = weight(gold);
}

#endif /* OVLB */
#ifdef OVL1

/* return TRUE if the corpse has special timing */
#define special_corpse(num)  (((num) == PM_LIZARD)          \
                        || (is_rider(&mons[num]))     \
                        || (mons[num].mlet == S_TROLL))

/*
 * OEXTRA note: Passing mtmp causes mtraits to be saved
 * even if ptr passed as well, but ptr is always used for
 * the corpse type (corpsenm). That allows the corpse type
 * to be different from the original monster,
 *    i.e.  vampire -> human corpse
 * yet still allow restoration of the original monster upon
 * resurrection.
 */
struct obj *
mkcorpstat(objtype, mtmp, ptr, x, y, init)
int objtype;      /* CORPSE or STATUE */
struct monst *mtmp;
struct permonst *ptr;
int x, y;
boolean init;
{
      register struct obj *otmp;

      if (objtype != CORPSE && objtype != STATUE)
          impossible("making corpstat type %d", objtype);
      otmp = mksobj_at(objtype, x, y, init);
      if (otmp) {
#ifdef OEXTRA
          if (mtmp) {
            struct obj *otmp2;

            if (!ptr) ptr = mtmp->data;
            /* save_mtraits frees original data pointed to by otmp */
            otmp2 = save_mtraits(otmp, mtmp);
            if (otmp2) otmp = otmp2;
          }
#endif /* OEXTRA */

          /* use the corpse or statue produced by mksobj() as-is
             unless `ptr' is non-null */
          if (ptr) {
            int old_corpsenm = otmp->corpsenm;

            otmp->corpsenm = monsndx(ptr);
            otmp->owt = weight(otmp);
            if (otmp->otyp == CORPSE &&
                  (special_corpse(old_corpsenm) ||
                        special_corpse(otmp->corpsenm))) {
                obj_stop_timers(otmp);
                start_corpse_timeout(otmp);
            }
          }
      }
      return(otmp);
}

#ifdef OEXTRA
struct obj *
save_mtraits(obj, mtmp)
struct obj *obj;
struct monst *mtmp;
{
      struct obj *otmp;
      int lth, namelth;

      lth = sizeof(struct monst) + mtmp->mxlth + mtmp->mnamelth;
      namelth = obj->onamelth ? strlen(ONAME(obj)) + 1 : 0;
      otmp = replobj(obj, lth, (genericptr_t) mtmp, namelth, ONAME(obj));
      if (otmp && otmp->oxlth) {
            struct monst *mtmp2 = (struct monst *)otmp->oextra;
            mtmp2->mnum = otmp->corpsenm;
            otmp->mtraits = 1;            /* mark it */
      }
      return otmp;
}

/* returns a pointer to the monst structure within the obj.
 * Do not use the return value in any monst chains directly!
 */
struct monst *
get_mtraits(obj)
struct obj *obj;
{
      struct monst *mtmp = (struct monst *)0;

      if (obj->oxlth && obj->mtraits == 1) {
            mtmp = (struct monst *)obj->oextra;
            /* save_mtraits() validated mtmp->mnum */
            mtmp->data = &mons[mtmp->mnum];
      }
      return mtmp;
}
#endif /* OEXTRA */

#endif /* OVL1 */
#ifdef OVLB

/* make an object named after someone listed in the scoreboard file */
struct obj *
mk_tt_object(objtype, x, y)
int objtype; /* CORPSE or STATUE */
register int x, y;
{
      register struct obj *otmp, *otmp2;
      boolean initialize_it;

      /* player statues never contain books */
      initialize_it = (objtype != STATUE);
      if ((otmp = mksobj_at(objtype, x, y, initialize_it)) != 0) {
          /* tt_oname will return null if the scoreboard is empty */
          if ((otmp2 = tt_oname(otmp)) != 0) otmp = otmp2;
      }
      return(otmp);
}

/* make a new corpse or statue, uninitialized if a statue (i.e. no books) */
struct obj *
mk_named_object(objtype, ptr, x, y, nm)
int objtype;      /* CORPSE or STATUE */
struct permonst *ptr;
int x, y;
const char *nm;
{
      struct obj *otmp;

      otmp = mkcorpstat(objtype, (struct monst *)0, ptr,
                        x, y, (boolean)(objtype != STATUE));
      if (nm)
            otmp = oname(otmp, nm);
      return(otmp);
}

boolean
is_flammable(otmp)
register struct obj *otmp;
{
      int otyp = otmp->otyp;

      if (objects[otyp].oc_oprop == FIRE_RES) return FALSE;

      return((boolean)(objects[otyp].oc_material <= WOOD &&
                  objects[otyp].oc_material != LIQUID));
}

#endif /* OVLB */
#ifdef OVL1

/*
 * These routines maintain the single-linked lists headed in level.objects[][]
 * and threaded through the nexthere fields in the object-instance structure.
 */

/* put the object at the given location */
void
place_object(otmp, x, y)
register struct obj *otmp;
int x, y;
{
    register struct obj *otmp2 = level.objects[x][y];

    if (otmp->where != OBJ_FREE)
      panic("place_object: obj not free");

    if (otmp->otyp == BOULDER) block_point(x,y);      /* vision */

    /* obj goes under boulders */
    if (otmp2 && (otmp2->otyp == BOULDER)) {
      otmp->nexthere = otmp2->nexthere;
      otmp2->nexthere = otmp;
    } else {
      otmp->nexthere = otmp2;
      level.objects[x][y] = otmp;
    }

    /* set the new object's location */
    otmp->ox = x;
    otmp->oy = y;

    otmp->where = OBJ_FLOOR;

    /* add to floor chain */
    otmp->nobj = fobj;
    fobj = otmp;
    if (otmp->timed) obj_timer_checks(otmp, x, y, 0);
}

#define ON_ICE(a) ((a)->recharged)
#define ROT_ICE_ADJUSTMENT 2  /* rotting on ice takes 2 times as long */

/* If ice was affecting any objects correct that now
 * Also used for starting ice effects too. [zap.c]
 */
void
obj_ice_effects(x, y, do_buried)
int x, y;
boolean do_buried;
{
      struct obj *otmp;

      for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) {
            if (otmp->timed) obj_timer_checks(otmp, x, y, 0);
      }
      if (do_buried) {
          for (otmp = level.buriedobjlist; otmp; otmp = otmp->nobj) {
            if (otmp->ox == x && otmp->oy == y) {
                  if (otmp->timed) obj_timer_checks(otmp, x, y, 0);
            }
          }
      }
}

/*
 * Returns an obj->age for a corpse object on ice, that would be the
 * actual obj->age if the corpse had just been lifted from the ice.
 * This is useful when just using obj->age in a check or calculation because
 * rot timers pertaining to the object don't have to be stopped and
 * restarted etc.
 */
long
peek_at_iced_corpse_age(otmp)
struct obj *otmp;
{
    long age, retval = otmp->age;
    
    if (otmp->otyp == CORPSE && ON_ICE(otmp)) {
      /* Adjust the age; must be same as obj_timer_checks() for off ice*/
      age = monstermoves - otmp->age;
      retval = otmp->age + (age / ROT_ICE_ADJUSTMENT);
#ifdef DEBUG_EFFECTS
      pline_The("%s age has ice modifications:otmp->age = %ld, returning %ld.",
            s_suffix(doname(otmp)),otmp->age, retval);
      pline("Effective age of corpse: %ld.",
            monstermoves - retval);
#endif
    }
    return retval;
}

static void
obj_timer_checks(otmp, x, y, force)
struct obj *otmp;
xchar x, y;
int force;  /* 0 = no force so do checks, <0 = force off, >0 force on */
{
    long tleft = 0L;
    short action = ROT_CORPSE;
    boolean restart_timer = FALSE;
    boolean on_floor = (otmp->where == OBJ_FLOOR);
    boolean buried = (otmp->where == OBJ_BURIED);

    /* Check for corpses just placed on or in ice */
    if (otmp->otyp == CORPSE && (on_floor || buried) && is_ice(x,y)) {
      tleft = stop_timer(action, (genericptr_t)otmp);
      if (tleft == 0L) {
            action = REVIVE_MON;
            tleft = stop_timer(action, (genericptr_t)otmp);
      } 
      if (tleft != 0L) {
          long age;
          
          tleft = tleft - monstermoves;
          /* mark the corpse as being on ice */
          ON_ICE(otmp) = 1;
#ifdef DEBUG_EFFECTS
          pline("%s is now on ice at %d,%d.", The(xname(otmp)),x,y);
#endif
          /* Adjust the time remaining */
          tleft *= ROT_ICE_ADJUSTMENT;
          restart_timer = TRUE;
          /* Adjust the age; must be same as in obj_ice_age() */
          age = monstermoves - otmp->age;
          otmp->age = monstermoves - (age * ROT_ICE_ADJUSTMENT);
      }
    }
    /* Check for corpses coming off ice */
    else if ((force < 0) ||
           (otmp->otyp == CORPSE && ON_ICE(otmp) &&
           ((on_floor && !is_ice(x,y)) || !on_floor))) {
      tleft = stop_timer(action, (genericptr_t)otmp);
      if (tleft == 0L) {
            action = REVIVE_MON;
            tleft = stop_timer(action, (genericptr_t)otmp);
      }
      if (tleft != 0L) {
            long age;

            tleft = tleft - monstermoves;
            ON_ICE(otmp) = 0;
#ifdef DEBUG_EFFECTS
            pline("%s is no longer on ice at %d,%d.", The(xname(otmp)),x,y);
#endif
            /* Adjust the remaining time */
            tleft /= ROT_ICE_ADJUSTMENT;
            restart_timer = TRUE;
            /* Adjust the age */
            age = monstermoves - otmp->age;
            otmp->age = otmp->age + (age / ROT_ICE_ADJUSTMENT);
      }
    }
    /* now re-start the timer with the appropriate modifications */ 
    if (restart_timer)
      (void) start_timer(tleft, TIMER_OBJECT, action, (genericptr_t)otmp);
}

#undef ON_ICE
#undef ROT_ICE_ADJUSTMENT

void
remove_object(otmp)
register struct obj *otmp;
{
    xchar x = otmp->ox;
    xchar y = otmp->oy;

    if (otmp->where != OBJ_FLOOR)
      panic("remove_object: obj not on floor");
    if (otmp->otyp == BOULDER) unblock_point(x,y); /* vision */
    extract_nexthere(otmp, &level.objects[x][y]);
    extract_nobj(otmp, &fobj);
    if (otmp->timed) obj_timer_checks(otmp,x,y,0);
}

/* throw away all of a monster's inventory */
void
discard_minvent(mtmp)
struct monst *mtmp;
{
    struct obj *otmp;

    while ((otmp = mtmp->minvent) != 0) {
      obj_extract_self(otmp);
      obfree(otmp, (struct obj *)0);      /* dealloc_obj() isn't sufficient */
    }
}

/*
 * Free obj from whatever list it is on in preperation of deleting it or
 * moving it elsewhere.  This will perform all high-level consequences
 * involved with removing the item.  E.g. if the object is in the hero's
 * inventory and confers heat resistance, the hero will lose it.
 *
 * Object positions:
 *    OBJ_FREE    not on any list
 *    OBJ_FLOOR   fobj, level.locations[][] chains (use remove_object)
 *    OBJ_CONTAINED     cobj chain of container object
 *    OBJ_INVENT  hero's invent chain (use freeinv)
 *    OBJ_MINVENT monster's invent chain
 *    OBJ_MIGRATING     migrating chain
 *    OBJ_BURIED  level.buriedobjs chain
 *    OBJ_ONBILL  on billobjs chain
 */
void
obj_extract_self(obj)
    struct obj *obj;
{
    switch (obj->where) {
      case OBJ_FREE:
          break;
      case OBJ_FLOOR:
          remove_object(obj);
          break;
      case OBJ_CONTAINED:
          extract_nobj(obj, &obj->ocontainer->cobj);
          container_weight(obj->ocontainer);
          break;
      case OBJ_INVENT:
          freeinv(obj);
          break;
      case OBJ_MINVENT:
          extract_nobj(obj, &obj->ocarry->minvent);
          break;
      case OBJ_MIGRATING:
          extract_nobj(obj, &migrating_objs);
          break;
      case OBJ_BURIED:
          extract_nobj(obj, &level.buriedobjlist);
          break;
      case OBJ_ONBILL:
          extract_nobj(obj, &billobjs);
          break;
      default:
          panic("obj_extract_self");
          break;
    }
}


/* Extract the given object from the chain, following nobj chain. */
void
extract_nobj(obj, head_ptr)
    struct obj *obj, **head_ptr;
{
    struct obj *curr, *prev;

    curr = *head_ptr;
    for (prev = (struct obj *) 0; curr; prev = curr, curr = curr->nobj) {
      if (curr == obj) {
          if (prev)
            prev->nobj = curr->nobj;
          else
            *head_ptr = curr->nobj;
          break;
      }
    }
    if (!curr) panic("extract_nobj: object lost");
    obj->where = OBJ_FREE;
}


/*
 * Extract the given object from the chain, following nexthere chain.
 *
 * This does not set obj->where, this function is expected to be called
 * in tandem with extract_nobj, which does set it.
 */
void
extract_nexthere(obj, head_ptr)
    struct obj *obj, **head_ptr;
{
    struct obj *curr, *prev;

    curr = *head_ptr;
    for (prev = (struct obj *) 0; curr; prev = curr, curr = curr->nexthere) {
      if (curr == obj) {
          if (prev)
            prev->nexthere = curr->nexthere;
          else
            *head_ptr = curr->nexthere;
          break;
      }
    }
    if (!curr) panic("extract_nexthere: object lost");
}


void
add_to_minv(mon, obj)
    struct monst *mon;
    struct obj *obj;
{
    struct obj *otmp;

    if (obj->where != OBJ_FREE)
      panic("add_to_minv: obj not free");

    /* merge if possible */
    for (otmp = mon->minvent; otmp; otmp = otmp->nobj)
      if (merged(&otmp, &obj))
          return;
    /* else insert; don't bother forcing it to end of chain */
    obj->where = OBJ_MINVENT;
    obj->ocarry = mon;
    obj->nobj = mon->minvent;
    mon->minvent = obj;
}

void
add_to_container(container, obj)
    struct obj *container, *obj;
{
    if (obj->where != OBJ_FREE)
      panic("add_to_container: obj not free");

    obj->where = OBJ_CONTAINED;
    obj->ocontainer = container;
    obj->nobj = container->cobj;
    container->cobj = obj;
}

void
add_to_migration(obj)
    struct obj *obj;
{
    if (obj->where != OBJ_FREE)
      panic("add_to_migration: obj not free");

    obj->where = OBJ_MIGRATING;
    obj->nobj = migrating_objs;
    migrating_objs = obj;
}

void
add_to_buried(obj)
    struct obj *obj;
{
    if (obj->where != OBJ_FREE)
      panic("add_to_buried: obj not free");

    obj->where = OBJ_BURIED;
    obj->nobj = level.buriedobjlist;
    level.buriedobjlist = obj;
}

/* Recalculate the weight of this container and all of _its_ containers. */
static void
container_weight(container)
    struct obj *container;
{
    container->owt = weight(container);
    if (container->where == OBJ_CONTAINED)
      container_weight(container->ocontainer);
/*
    else if (container->where == OBJ_INVENT)
      recalculate load delay here ???
*/
}

/*
 * Deallocate the object.  _All_ objects should be run through here for
 * them to be deallocated.
 */
void
dealloc_obj(obj)
    struct obj *obj;
{
    if (obj->where != OBJ_FREE)
      panic("dealloc_obj: obj not free");

    /* free up any timers attached to the object */
    if (obj->timed)
      obj_stop_timers(obj);

    /*
     * Free up any light sources attached to the object.
     *
     * We may want to just call del_light_source() without any
     * checks (requires a code change there).  Otherwise this
     * list must track all objects that can have a light source
     * attached to it (and also requires lamplit to be set).
     */
    if (obj_sheds_light(obj))
      del_light_source(LS_OBJECT, (genericptr_t) obj);

    free((genericptr_t) obj);
}

#ifdef WIZARD
/* Check all object lists for consistency. */
void
obj_sanity_check()
{
    int x, y;
    struct obj *obj;
    struct monst *mon;
    const char *mesg;
    char obj_address[20], mon_address[20];  /* room for formatted pointers */

    mesg = "fobj sanity";
    for (obj = fobj; obj; obj = obj->nobj) {
      if (obj->where != OBJ_FLOOR) {
          pline("%s obj %s %s@(%d,%d): %s\n", mesg,
            fmt_ptr((genericptr_t)obj, obj_address),
            where_name(obj->where),
            obj->ox, obj->oy, doname(obj));
      }
      check_contained(obj, mesg);
    }

    mesg = "location sanity";
    for (x = 0; x < COLNO; x++)
      for (y = 0; y < ROWNO; y++)
          for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
            if (obj->where != OBJ_FLOOR) {
                pline("%s obj %s %s@(%d,%d): %s\n", mesg,
                  fmt_ptr((genericptr_t)obj, obj_address),
                  where_name(obj->where),
                  obj->ox, obj->oy, doname(obj));
            }

    mesg = "invent sanity";
    for (obj = invent; obj; obj = obj->nobj) {
      if (obj->where != OBJ_INVENT) {
          pline("%s obj %s %s: %s\n", mesg,
            fmt_ptr((genericptr_t)obj, obj_address),
            where_name(obj->where), doname(obj));
      }
      check_contained(obj, mesg);
    }

    mesg = "migrating sanity";
    for (obj = migrating_objs; obj; obj = obj->nobj) {
      if (obj->where != OBJ_MIGRATING) {
          pline("%s obj %s %s: %s\n", mesg,
            fmt_ptr((genericptr_t)obj, obj_address),
            where_name(obj->where), doname(obj));
      }
      check_contained(obj, mesg);
    }

    mesg = "buried sanity";
    for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
      if (obj->where != OBJ_BURIED) {
          pline("%s obj %s %s: %s\n", mesg,
            fmt_ptr((genericptr_t)obj, obj_address),
            where_name(obj->where), doname(obj));
      }
      check_contained(obj, mesg);
    }

    mesg = "bill sanity";
    for (obj = billobjs; obj; obj = obj->nobj) {
      if (obj->where != OBJ_ONBILL) {
          pline("%s obj %s %s: %s\n", mesg,
            fmt_ptr((genericptr_t)obj, obj_address),
            where_name(obj->where), doname(obj));
      }
      /* shouldn't be a full container on the bill */
      if (obj->cobj) {
          pline("%s obj %s contains something! %s\n", mesg,
            fmt_ptr((genericptr_t)obj, obj_address),
            doname(obj));
      }
    }

    mesg = "minvent sanity";
    for (mon = fmon; mon; mon = mon->nmon)
      for (obj = mon->minvent; obj; obj = obj->nobj) {
          if (obj->where != OBJ_MINVENT) {
            pline("%s obj %s %s: %s\n", mesg,
                  fmt_ptr((genericptr_t)obj, obj_address),
                  where_name(obj->where), doname(obj));
          }
          if (obj->ocarry != mon) {
            pline("%s obj %s (%s) not held by mon %s (%s)\n", mesg,
                  fmt_ptr((genericptr_t)obj, obj_address),
                  doname(obj),
                  fmt_ptr((genericptr_t)mon, mon_address),
                  mon_nam(mon));
          }
          check_contained(obj, mesg);
      }
}

/* This must stay consistent with the defines in obj.h. */
static const char *obj_state_names[NOBJ_STATES] = {
      "free",           "floor",    "contained",      "invent",
      "minvent",  "migrating",      "buried",   "onbill"
};

static const char *
where_name(where)
    int where;
{
    return (where<0 || where>=NOBJ_STATES) ? "unknown" : obj_state_names[where];
}

/* obj sanity check: check objs contained by container */
static void
check_contained(container, mesg)
    struct obj *container;
    const char *mesg;
{
    struct obj *obj;
    char obj1_address[20], obj2_address[20];

    for (obj = container->cobj; obj; obj = obj->nobj) {
      if (obj->where != OBJ_CONTAINED)
          pline("contained %s obj %s: %s\n", mesg,
            fmt_ptr((genericptr_t)obj, obj1_address),
            where_name(obj->where));
      else if (obj->ocontainer != container)
          pline("%s obj %s not in container %s\n", mesg,
            fmt_ptr((genericptr_t)obj, obj1_address),
            fmt_ptr((genericptr_t)container, obj2_address));
    }
}
#endif /* WIZARD */

#endif /* OVL1 */

/*mkobj.c*/

Generated by  Doxygen 1.6.0   Back to index