ftp.nice.ch/pub/next/games/strategic/NetHack.s.tar.gz#/NetHackSource/src/mkobj.c

This is mkobj.c in view mode; [Download] [Up]

/*	SCCS Id: @(#)mkobj.c	3.0	89/11/08
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"

static void FDECL(mkbox_cnts,(struct obj *));

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

#ifdef OVLB

const struct icp mkobjprobs[] = {
{10, WEAPON_SYM},
{10, ARMOR_SYM},
{20, FOOD_SYM},
{ 8, TOOL_SYM},
{ 8, GEM_SYM},
#ifdef SPELLS
{16, POTION_SYM},
{16, SCROLL_SYM},
{ 4, SPBOOK_SYM},
#else
{18, POTION_SYM},
{18, SCROLL_SYM},
#endif
{ 4, WAND_SYM},
{ 3, RING_SYM},
{ 1, AMULET_SYM}};

const struct icp boxiprobs[] = {
{18, GEM_SYM},
#ifdef SPELLS
{15, FOOD_SYM},
{20, POTION_SYM},
{20, SCROLL_SYM},
{15, SPBOOK_SYM},
#else
{20, FOOD_SYM},
{25, POTION_SYM},
{25, SCROLL_SYM},
#endif
{ 6, WAND_SYM},
{ 5, RING_SYM},
{ 1, AMULET_SYM}};

#ifdef REINCARNATION
const struct icp rogueprobs[] = {
{12, WEAPON_SYM},
{12, ARMOR_SYM},
{22, FOOD_SYM},
{22, POTION_SYM},
{22, SCROLL_SYM},
{ 5, WAND_SYM},
{ 5, RING_SYM}};
#endif

const struct icp hellprobs[] = {
{20, WEAPON_SYM},
{20, ARMOR_SYM},
{16, FOOD_SYM},
{12, TOOL_SYM},
{10, GEM_SYM},
{ 1, POTION_SYM},
{ 1, SCROLL_SYM},
{ 8, WAND_SYM},
{ 8, RING_SYM},
{ 4, AMULET_SYM}};

static int mksx=0, mksy=0;

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

	mksx = x; mksy = y;
	/* We need to know the X, Y coordinates while creating the object,
	 * to insure shop boxes are empty.
	 * Yes, this is a horrible kludge...
	 */
	otmp = mkobj(let,TRUE);
	otmp->nobj = fobj;
	fobj = otmp;
	place_object(otmp, x, y);
	mksx = mksy = 0;
	return(otmp);
}

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

	mksx = x; mksy = y;
	otmp = mksobj(otyp,TRUE);
	otmp->nobj = fobj;
	place_object(otmp, x, y);
	mksx = mksy = 0;
	return((fobj = otmp));
}

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

	if(let == RANDOM_SYM) {
		const struct icp *iprobs =
#ifdef REINCARNATION
				    (dlevel == rogue_level) ?
				    (const struct icp *)rogueprobs :
#endif
				    Inhell ? (const struct icp *)hellprobs :
				    (const struct icp *)mkobjprobs;

		for(tprob = rnd(100);
		    (tprob -= iprobs->iprob) > 0;
		    iprobs++);
		let = iprobs->ilet;
	}

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

	if(objects[i].oc_olet != let || !objects[i].oc_name)
		panic("probtype error, let=%c i=%d", let, i);

	return(mksobj(i, artif));
}

static void
mkbox_cnts(box)
/* Note: does not check to see if it overloaded the box capacity; usually
 * possible only with corpses in ice boxes.
 */
struct obj *box;
{
	register int n;
	register struct obj *otmp;

	if(in_shop(mksx, mksy)) return; /* boxes are empty in shops */

	switch(box->otyp) {
		case ICE_BOX: 		n = 20; break;
		case CHEST:		n = 5; break;
		case LARGE_BOX:		n = 3; break;
		case SACK:
		case BAG_OF_HOLDING:	n = 1; break;
		default:		n = 0; break;
	}

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

		for(tprob = rnd(100);
		    (tprob -= iprobs->iprob) > 0;
		    iprobs++);
		otmp = mkobj(iprobs->ilet, TRUE);
	    }
	    if (otmp) {
		otmp->cobj = box;
		otmp->nobj = fcobj;
		fcobj = otmp;
		/* inc_cwt(box, otmp); --done by weight() */
	    }
	}
	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 {
	    ptr = &mons[(i = rn2(NUMMONS))];
	} while((ptr->geno & G_NOGEN) || (!Inhell && (ptr->geno & G_HELL)));

	return(i);
}

const char dknowns[] = { WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM,
#ifdef SPELLS
SPBOOK_SYM,
#endif
WEAPON_SYM, 0};

/*ARGSUSED*/
struct obj *
mksobj(otyp, artif)
int otyp;
boolean artif;
{
	int tryct;
	struct obj *otmp;
	char let = objects[otyp].oc_olet;

	otmp = newobj(0);
	*otmp = zeroobj;
	otmp->age = monstermoves;
	otmp->o_id = flags.ident++;
	otmp->quan = 1;
	otmp->olet = let;
	otmp->otyp = otyp;
	otmp->dknown = index(dknowns, let) ? 0 : 1;
	if (!objects[otmp->otyp].oc_uses_known)
		otmp->known = 1;
	switch(let) {
	case WEAPON_SYM:
		otmp->quan = (otmp->otyp <= SHURIKEN) ? rn1(6,6) : 1;
		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);

#ifdef NAMED_ITEMS
		if(artif && !rn2(20)) mkartifact(&otmp);
#endif
		break;
	case FOOD_SYM:
		otmp->oeaten = 0;
		if(otmp->otyp == CORPSE) {
		    /* overridden by mkcorpstat() */
		    do otmp->corpsenm = rndmonnum();
		    while (mons[otmp->corpsenm].geno & G_NOCORPSE);
		    break;
		} else if(otmp->otyp == EGG) {
		    if(!rn2(3)) {		/* "live" eggs */
			register struct permonst *ptr;
			for(tryct = 0;
			    (!(ptr = rndmonst()) ||
			    (!lays_eggs(ptr) && ptr != &mons[PM_KILLER_BEE])) &&
				tryct < 100;
			    tryct++);
			if(tryct < 100)	otmp->corpsenm = monsndx(ptr);
			else		otmp->corpsenm = -1; /* empty */
		    } else		otmp->corpsenm = -1; /* empty */
		} else if(otmp->otyp == TIN) {
		    if(!rn2(6)) {
			otmp->spe = 1;		/* spinach */
			otmp->corpsenm = -1;
		    } else do {
			otmp->corpsenm = rndmonnum();
		    } while (mons[otmp->corpsenm].geno & G_NOCORPSE);
		    blessorcurse(otmp, 10);
		}
#ifdef TUTTI_FRUTTI
		else if (otmp->otyp == SLIME_MOLD)
		    otmp->spe = current_fruit;
#endif
		/* fall into next case */
	case GEM_SYM:
		if (otmp->otyp == LOADSTONE) curse(otmp);
		else if (otmp->otyp == ROCK) otmp->quan = rn1(6,6);
		else if (otmp->otyp != LUCKSTONE && !rn2(6)) otmp->quan = 2;
		else otmp->quan = 1;
		break;
	case TOOL_SYM:
	    switch(otmp->otyp) {
		case LAMP:		otmp->spe = rnd(10);
					blessorcurse(otmp, 5);
					break;
		case MAGIC_LAMP:	otmp->spe = 1;
					otmp->recharged = 0;
					blessorcurse(otmp, 2);
					break;
		case KEY:		/* key # index */
		case SKELETON_KEY:	otmp->spe = rn2(N_LOX);
					break;
		case CHEST:		/* lock # index */
		case LARGE_BOX:		otmp->spe = rn2(N_LOX);
					otmp->olocked = !!(rn2(5));
					otmp->otrapped = !(rn2(10));
		case ICE_BOX:
		case SACK:
		case BAG_OF_HOLDING:	mkbox_cnts(otmp);
					break;
		case MAGIC_MARKER:	otmp->spe = rn1(70,30);
					break;
		case CRYSTAL_BALL:	otmp->spe = rnd(5);
					blessorcurse(otmp, 2);
					break;
		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;
				}
#ifdef MUSIC
		case MAGIC_FLUTE:
		case MAGIC_HARP:
		case FROST_HORN:
		case FIRE_HORN:
		case DRUM_OF_EARTHQUAKE:
					otmp->spe = rn1(5,4);
					break;
#endif /* MUSIC /**/
	    }
	    break;
	case AMULET_SYM:
		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_SYM:
	case CHAIN_SYM:
	case BALL_SYM:
		break;
	case POTION_SYM:
	case SCROLL_SYM:
#ifdef MAIL
		if (otmp->otyp != SCR_MAIL)
#endif
			blessorcurse(otmp, 4);
		break;
#ifdef SPELLS
	case SPBOOK_SYM:
		blessorcurse(otmp, 17);
		break;
#endif
	case ARMOR_SYM:
		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->spe = rne(3);
			otmp->blessed = rn2(2);
		} else	blessorcurse(otmp, 10);
		if(otmp->otyp == DRAGON_SCALE_MAIL)
			otmp->corpsenm = PM_GRAY_DRAGON +
			    rn2(PM_YELLOW_DRAGON-PM_GRAY_DRAGON+1);
		break;
	case WAND_SYM:
#ifdef HARD
		if(otmp->otyp == WAN_WISHING) otmp->spe = rnd(3); else
#else		
		if(otmp->otyp == WAN_WISHING) otmp->spe = 3; else
#endif		
		otmp->spe = rn1(5,
			(objects[otmp->otyp].bits & NODIR) ? 11 : 4);
		blessorcurse(otmp, 17);
		otmp->recharged = 0; /* used to control recharging */
		break;
	case RING_SYM:
		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);
		    }
		    if (otmp->spe < 0 && rn2(5)) curse(otmp);
		} else if(rn2(10) && (otmp->otyp == RIN_TELEPORTATION ||
#ifdef POLYSELF
			  otmp->otyp == RIN_POLYMORPH ||
#endif
			  otmp->otyp == RIN_AGGRAVATE_MONSTER ||
			  otmp->otyp == RIN_HUNGER || !rn2(9))) {
			curse(otmp);
		}
		break;
	case ROCK_SYM:
		switch (otmp->otyp) {
		    case STATUE:
			/* contains book? */
			otmp->spe = (rn2(dlevel/2 + 10) > 10);
			/* overridden by mkcorpstat() */
			otmp->corpsenm = rndmonnum();
		}
		break;
	default:
		impossible("impossible mkobj %d, sym '%c'.", otmp->otyp, let);
		return (struct obj *)0;
	}
	otmp->owt = weight(otmp);
	return(otmp);
}

void
bless(otmp)
register struct obj *otmp;
{
	otmp->cursed = 0;
	otmp->blessed = 1;
	if (otmp->otyp == LUCKSTONE) {
		if (stone_luck(TRUE) >= 0) u.moreluck = LUCKADD;
		else u.moreluck = -LUCKADD;
	}
	return;
}

void
curse(otmp)
register struct obj *otmp;
{
	otmp->blessed = 0;
	otmp->cursed = 1;
	if (otmp->otyp == LUCKSTONE) {
		if (stone_luck(TRUE) >= 0) u.moreluck = LUCKADD;
		else u.moreluck = -LUCKADD;
	}
	return;
}

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

	if(!rn2(chance))
	    if(!rn2(2) || Inhell) { /* in hell, don't usually bless items */
		curse(otmp);
	    } else {
		bless(otmp);
	    }
	return;
}

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

int
letter(c)
int c;
{
	return(('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z'));
}

#endif /* OVLB */
#ifdef OVL0

int
weight(obj)
register struct obj *obj;
{
	register int wt = objects[obj->otyp].oc_weight;

	if (Is_container(obj)) {
		struct obj *contents;
		obj->owt = wt;
		for(contents=fcobj; contents; contents=contents->nobj) {
			if (contents->cobj == obj)
				inc_cwt(obj, contents);
		}
		return obj->owt;
	}
	if (obj->otyp == CORPSE && obj->corpsenm > -1)
		return obj->quan * mons[obj->corpsenm].cwt;
	else if (obj->otyp == STATUE && obj->corpsenm > -1)
		return obj->quan * (mons[obj->corpsenm].cwt * 3 / 2);
	return(wt ? wt*obj->quan : (obj->quan + 1)>>1);
}

#endif /* OVL0 */
#ifdef OVLB

void
mkgold(num,x,y)
long num;
int x, y;
{
	register struct gold *gold;
	register long amount = (num ? num : 1 + (rnd(dlevel+2) * rnd(30)));

	if(levl[x][y].gmask) {
		gold = g_at(x,y);
		gold->amount += amount;
	} else {
		gold = newgold();
		gold->ngold = fgold;
		gold->gx = x;
		gold->gy = y;
		gold->amount = amount;
		fgold = gold;
		levl[x][y].gmask = 1;
		/* do sth with display? */
	}
	return;
}

struct obj *
mkcorpstat(objtype, ptr, x, y)
int objtype;	/* CORPSE or STATUE */
register struct permonst *ptr;
int x, y;
{
	register struct obj *otmp;

	if(objtype != CORPSE && objtype != STATUE)
		impossible("making corpstat type %d", objtype);
	otmp = mksobj_at(objtype, x, y);
	if(otmp)  {
		if(ptr)	otmp->corpsenm = monsndx(ptr);
		else	otmp->corpsenm = rndmonnum();
		otmp->owt = weight(otmp);
	}
	return(otmp);
}

struct obj *
mk_tt_object(objtype, x, y)
int objtype; /* CORPSE or STATUE */
register int x, y;
{
	register struct obj *otmp;

	if(otmp = mksobj_at(objtype,x,y)) {
		if((otmp = tt_oname(otmp)) && objtype == STATUE)
			/* player statues never contain books */
			otmp->spe = 0;
	}
	return(otmp);
}

struct obj *
mk_named_object(objtype, ptr, x, y, nm, lth)
int objtype; /* CORPSE or STATUE */
register struct permonst *ptr;
int x, y;
char * nm;
register int lth;
{
	struct obj *otmp;

	otmp = mkcorpstat(objtype,ptr,x,y);
	if (lth > 0) {
		/* Note: oname() is safe since otmp is first in both chains */
		otmp = oname(otmp, nm, FALSE);
		fobj = otmp;
		level.objects[x][y] = otmp;
	}
	return(otmp);
}

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

	if (otyp == DRAGON_SCALE_MAIL && otmp->corpsenm == PM_RED_DRAGON)
		return FALSE;
	return((objects[otyp].oc_material == WOOD ||
			objects[otyp].oc_material == 0));

}

#ifdef STUPID_CPP
boolean
is_rustprone(otmp)
register struct obj *otmp;
{
	return(objects[otmp->otyp].oc_material == METAL);
}

boolean
is_corrodeable(otmp)
register struct obj *otmp;
{
	return(objects[otmp->otyp].oc_material == COPPER);
}

boolean
OBJ_AT(x, y)
int x, y;
{
	return(level.objects[x][y] != (struct obj *)0);
}
#endif


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

void
place_object(otmp, x, y)
/* put an object on top of the pile at the given location */
register struct obj *otmp;
int x, y;
{
    otmp->nexthere = level.objects[x][y];
    level.objects[x][y] = otmp;

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

void
remove_object(otmp)
register struct obj *otmp;
{
    register struct obj *odel;

    if (otmp == level.objects[otmp->ox][otmp->oy])
	level.objects[otmp->ox][otmp->oy] = otmp->nexthere;
    else
	for (odel = level.objects[otmp->ox][otmp->oy];
						    odel; odel = odel->nexthere)
	    if (odel->nexthere == otmp)
		odel->nexthere = otmp->nexthere;
}

void move_object(otmp, x, y)
register struct obj *otmp;
int x, y;
{
    remove_object(otmp);
    place_object(otmp, x, y);
}

#endif /* OVLB */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.