Files
nethack/src/shknam.c
nethack.allison 0dc071bee8 mextra changes
Note: The CVS repository was tagged with NETHACK_PRE_MEXTRA
prior to application of this patch to allow easy withdrawal if necessary.

Adds a new mextra structure type that has a set
of pointers to various types of monster structures
including:
   mname, egd, epri, eshk, emin, edog

Replaces the mextra bits in the monst structure
with a single pointer called mtmp->mextra of type
(struct mextra *).
The pointer can be null if there are no additional
structures attached. The mextra structure is not
adjacent to the monst structure.

Reduces the in-memory footprint of the monst that
has no other structures attached, at the cost
of adding 6 extra long ints per monster to
the save file

The new mextra structure has the mextra fields
independent of each other, not overlapping as was
the case with previous NetHack versions.
This patch doesn't do anything to capitalize on
that difference however.

Consolidates vault.h, epri.h, eshk.h, emin.h and edog.h
into mextra.h

Adds a macro for checking for whether a monster has
a name:
	has_name(monst)

This fixes the magic trap panic
   expels() -> spoteffects() -> dotrap() ->
	domagictrap() -> tamedog()
because the monst no longer varies in size so no
replacement is required.
2006-01-06 05:46:03 +00:00

708 lines
22 KiB
C

/* SCCS Id: @(#)shknam.c 3.5 2006/01/03 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
/* shknam.c -- initialize a shop */
#include "hack.h"
STATIC_DCL boolean FDECL(veggy_item, (struct obj *obj,int));
STATIC_DCL int NDECL(shkveg);
STATIC_DCL void FDECL(mkveggy_at, (int,int));
STATIC_DCL void FDECL(mkshobj_at, (const struct shclass *,int,int));
STATIC_DCL void FDECL(nameshk, (struct monst *,const char * const *));
STATIC_DCL int FDECL(shkinit, (const struct shclass *,struct mkroom *));
#define VEGETARIAN_CLASS (MAXOCLASSES+1)
/*
* Name prefix codes:
* dash _ female, personal name
* underscore _ female, general name
* plus + male, personal name
* vertical bar | male, general name (implied for most of shktools)
* equals = gender not specified, personal name
*
* Personal names do not receive the honorific prefix "Mr." or "Ms.".
*/
static const char * const shkliquors[] = {
/* Ukraine */
"Njezjin", "Tsjernigof", "Ossipewsk", "Gorlowka",
/* Belarus */
"Gomel",
/* N. Russia */
"Konosja", "Weliki Oestjoeg", "Syktywkar", "Sablja",
"Narodnaja", "Kyzyl",
/* Silezie */
"Walbrzych", "Swidnica", "Klodzko", "Raciborz", "Gliwice",
"Brzeg", "Krnov", "Hradec Kralove",
/* Schweiz */
"Leuk", "Brig", "Brienz", "Thun", "Sarnen", "Burglen", "Elm",
"Flims", "Vals", "Schuls", "Zum Loch",
0
};
static const char * const shkbooks[] = {
/* Eire */
"Skibbereen", "Kanturk", "Rath Luirc", "Ennistymon", "Lahinch",
"Kinnegad", "Lugnaquillia", "Enniscorthy", "Gweebarra",
"Kittamagh", "Nenagh", "Sneem", "Ballingeary", "Kilgarvan",
"Cahersiveen", "Glenbeigh", "Kilmihil", "Kiltamagh",
"Droichead Atha", "Inniscrone", "Clonegal", "Lisnaskea",
"Culdaff", "Dunfanaghy", "Inishbofin", "Kesh",
0
};
static const char * const shkarmors[] = {
/* Turquie */
"Demirci", "Kalecik", "Boyabai", "Yildizeli", "Gaziantep",
"Siirt", "Akhalataki", "Tirebolu", "Aksaray", "Ermenak",
"Iskenderun", "Kadirli", "Siverek", "Pervari", "Malasgirt",
"Bayburt", "Ayancik", "Zonguldak", "Balya", "Tefenni",
"Artvin", "Kars", "Makharadze", "Malazgirt", "Midyat",
"Birecik", "Kirikkale", "Alaca", "Polatli", "Nallihan",
0
};
static const char * const shkwands[] = {
/* Wales */
"Yr Wyddgrug", "Trallwng", "Mallwyd", "Pontarfynach",
"Rhaeader", "Llandrindod", "Llanfair-ym-muallt",
"Y-Fenni", "Maesteg", "Rhydaman", "Beddgelert",
"Curig", "Llanrwst", "Llanerchymedd", "Caergybi",
/* Scotland */
"Nairn", "Turriff", "Inverurie", "Braemar", "Lochnagar",
"Kerloch", "Beinn a Ghlo", "Drumnadrochit", "Morven",
"Uist", "Storr", "Sgurr na Ciche", "Cannich", "Gairloch",
"Kyleakin", "Dunvegan",
0
};
static const char * const shkrings[] = {
/* Hollandse familienamen */
"Feyfer", "Flugi", "Gheel", "Havic", "Haynin", "Hoboken",
"Imbyze", "Juyn", "Kinsky", "Massis", "Matray", "Moy",
"Olycan", "Sadelin", "Svaving", "Tapper", "Terwen", "Wirix",
"Ypey",
/* Skandinaviske navne */
"Rastegaisa", "Varjag Njarga", "Kautekeino", "Abisko",
"Enontekis", "Rovaniemi", "Avasaksa", "Haparanda",
"Lulea", "Gellivare", "Oeloe", "Kajaani", "Fauske",
0
};
static const char * const shkfoods[] = {
/* Indonesia */
"Djasinga", "Tjibarusa", "Tjiwidej", "Pengalengan",
"Bandjar", "Parbalingga", "Bojolali", "Sarangan",
"Ngebel", "Djombang", "Ardjawinangun", "Berbek",
"Papar", "Baliga", "Tjisolok", "Siboga", "Banjoewangi",
"Trenggalek", "Karangkobar", "Njalindoeng", "Pasawahan",
"Pameunpeuk", "Patjitan", "Kediri", "Pemboeang", "Tringanoe",
"Makin", "Tipor", "Semai", "Berhala", "Tegal", "Samoe",
0
};
static const char * const shkweapons[] = {
/* Perigord */
"Voulgezac", "Rouffiac", "Lerignac", "Touverac", "Guizengeard",
"Melac", "Neuvicq", "Vanzac", "Picq", "Urignac", "Corignac",
"Fleac", "Lonzac", "Vergt", "Queyssac", "Liorac", "Echourgnac",
"Cazelon", "Eypau", "Carignan", "Monbazillac", "Jonzac",
"Pons", "Jumilhac", "Fenouilledes", "Laguiolet", "Saujon",
"Eymoutiers", "Eygurande", "Eauze", "Labouheyre",
0
};
static const char * const shktools[] = {
/* Spmi */
"Ymla", "Eed-morra", "Cubask", "Nieb", "Bnowr Falr", "Telloc Cyaj",
"Sperc", "Noskcirdneh", "Yawolloh", "Hyeghu", "Niskal", "Trahnil",
"Htargcm", "Enrobwem", "Kachzi Rellim", "Regien", "Donmyar",
"Yelpur", "Nosnehpets", "Stewe", "Renrut", "_Zlaw", "Nosalnef",
"Rewuorb", "Rellenk", "Yad", "Cire Htims", "Y-crad", "Nenilukah",
"Corsh", "Aned",
#ifdef OVERLAY
"Erreip", "Nehpets", "Mron", "Snivek", "Lapu", "Kahztiy",
#endif
#ifdef WIN32
"Lechaim", "Lexa", "Niod",
#endif
#ifdef MAC
"Nhoj-lee", "Evad\'kh", "Ettaw-noj", "Tsew-mot", "Ydna-s",
"Yao-hang", "Tonbar", "Kivenhoug",
#endif
#ifdef AMIGA
"Falo", "Nosid-da\'r", "Ekim-p", "Rebrol-nek", "Noslo", "Yl-rednow",
"Mured-oog", "Ivrajimsal",
#endif
#ifdef TOS
"Nivram",
#endif
#ifdef VMS
"Lez-tneg", "Ytnu-haled", "Niknar",
#endif
0
};
static const char * const shklight[] = {
/* Romania */
"Zarnesti", "Slanic", "Nehoiasu", "Ludus", "Sighisoara", "Nisipitu",
"Razboieni", "Bicaz", "Dorohoi", "Vaslui", "Fetesti", "Tirgu Neamt",
"Babadag", "Zimnicea", "Zlatna", "Jiu", "Eforie", "Mamaia",
/* Bulgaria */
"Silistra", "Tulovo", "Panagyuritshte", "Smolyan", "Kirklareli",
"Pernik", "Lom", "Haskovo", "Dobrinishte", "Varvara", "Oryahovo",
"Troyan", "Lovech", "Sliven",
0
};
static const char * const shkgeneral[] = {
/* Suriname */
"Hebiwerie", "Possogroenoe", "Asidonhopo", "Manlobbi",
"Adjama", "Pakka Pakka", "Kabalebo", "Wonotobo",
"Akalapi", "Sipaliwini",
/* Greenland */
"Annootok", "Upernavik", "Angmagssalik",
/* N. Canada */
"Aklavik", "Inuvik", "Tuktoyaktuk",
"Chicoutimi", "Ouiatchouane", "Chibougamau",
"Matagami", "Kipawa", "Kinojevis",
"Abitibi", "Maganasipi",
/* Iceland */
"Akureyri", "Kopasker", "Budereyri", "Akranes", "Bordeyri",
"Holmavik",
0
};
static const char * const shkhealthfoods[] = {
/* Tibet */
"Ga'er", "Zhangmu", "Rikaze", "Jiangji",
"Changdu", "Linzhi", "Shigatse", "Gyantse",
"Ganden", "Tsurphu", "Lhasa", "Tsedong",
"Drepung",
/* Hippie names */
"=Azura", "=Blaze", "=Breanna", "=Breezy",
"=Dharma", "=Feather", "=Jasmine", "=Luna",
"=Melody", "=Moonjava", "=Petal", "=Rhiannon",
"=Starla", "=Tranquilla", "=Windsong", "=Zennia",
"=Zoe", "=Zora",
0
};
/*
* To add new shop types, all that is necessary is to edit the shtypes[] array.
* See mkroom.h for the structure definition. Typically, you'll have to lower
* some or all of the probability fields in old entries to free up some
* percentage for the new type.
*
* The placement type field is not yet used but will be in the near future.
*
* The iprobs array in each entry defines the probabilities for various kinds
* of objects to be present in the given shop type. You can associate with
* each percentage either a generic object type (represented by one of the
* *_CLASS macros) or a specific object (represented by an onames.h define).
* In the latter case, prepend it with a unary minus so the code can know
* (by testing the sign) whether to use mkobj() or mksobj().
*/
const struct shclass shtypes[] = {
{"general store", RANDOM_CLASS, 42, D_SHOP,
{{100, RANDOM_CLASS}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
shkgeneral},
{"used armor dealership", ARMOR_CLASS, 14, D_SHOP,
{{90, ARMOR_CLASS}, {10, WEAPON_CLASS}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
shkarmors},
{"second-hand bookstore", SCROLL_CLASS, 10, D_SHOP,
{{90, SCROLL_CLASS}, {10, SPBOOK_CLASS}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
shkbooks},
{"liquor emporium", POTION_CLASS, 10, D_SHOP,
{{100, POTION_CLASS}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
shkliquors},
{"antique weapons outlet", WEAPON_CLASS, 5, D_SHOP,
{{90, WEAPON_CLASS}, {10, ARMOR_CLASS}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
shkweapons},
{"delicatessen", FOOD_CLASS, 5, D_SHOP,
{{83, FOOD_CLASS}, {5, -POT_FRUIT_JUICE}, {4, -POT_BOOZE},
{5, -POT_WATER}, {3, -ICE_BOX}, {0, 0}},
shkfoods},
{"jewelers", RING_CLASS, 3, D_SHOP,
{{85, RING_CLASS}, {10, GEM_CLASS}, {5, AMULET_CLASS},
{0, 0}, {0, 0}, {0, 0}},
shkrings},
{"quality apparel and accessories", WAND_CLASS, 3, D_SHOP,
{{90, WAND_CLASS}, {5, -LEATHER_GLOVES}, {5, -ELVEN_CLOAK}, {0, 0}},
shkwands},
{"hardware store", TOOL_CLASS, 3, D_SHOP,
{{100, TOOL_CLASS}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
shktools},
{"rare books", SPBOOK_CLASS, 3, D_SHOP,
{{90, SPBOOK_CLASS}, {10, SCROLL_CLASS}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
shkbooks},
{"health food store", FOOD_CLASS, 2, D_SHOP,
{{70, VEGETARIAN_CLASS}, {20, -POT_FRUIT_JUICE}, {4, -POT_HEALING},
{3, -POT_FULL_HEALING}, {2, -SCR_FOOD_DETECTION}, {1, -LUMP_OF_ROYAL_JELLY}},
shkhealthfoods},
/* Shops below this point are "unique". That is they must all have a
* probability of zero. They are only created via the special level
* loader.
*/
{"lighting store", TOOL_CLASS, 0, D_SHOP,
{{30, -WAX_CANDLE}, {48, -TALLOW_CANDLE}, {5, -BRASS_LANTERN},
{9, -OIL_LAMP}, {3, -MAGIC_LAMP}, {5, -POT_OIL}},
shklight},
{(char *)0, 0, 0, 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0}
};
#if 0
/* validate shop probabilities; otherwise incorrect local changes could
end up provoking infinite loops or wild subscripts fetching garbage */
void
init_shop_selection()
{
register int i, j, item_prob, shop_prob;
for (shop_prob = 0, i = 0; i < SIZE(shtypes); i++) {
shop_prob += shtypes[i].prob;
for (item_prob = 0, j = 0; j < SIZE(shtypes[0].iprobs); j++)
item_prob += shtypes[i].iprobs[j].iprob;
if (item_prob != 100)
panic("item probabilities total to %d for %s shops!",
item_prob, shtypes[i].name);
}
if (shop_prob != 100)
panic("shop probabilities total to %d!", shop_prob);
}
#endif /*0*/
/* decide whether an object or object type is considered vegetarian;
for types, items which might go either way are assumed to be veggy */
STATIC_OVL boolean
veggy_item(obj, otyp)
struct obj *obj;
int otyp; /* used iff obj is null */
{
int corpsenm;
char oclass;
if (obj) {
/* actual object; will check tin content and corpse species */
otyp = (int) obj->otyp;
oclass = obj->oclass;
corpsenm = obj->corpsenm;
} else {
/* just a type; caller will have to handle tins and corpses */
oclass = objects[otyp].oc_class;
corpsenm = PM_LICHEN; /* veggy standin */
}
if (oclass == FOOD_CLASS) {
if (objects[otyp].oc_material == VEGGY || otyp == EGG)
return TRUE;
if (otyp == TIN && corpsenm == NON_PM) /* implies obj is non-null */
return (obj->spe == 1); /* 0 = empty, 1 = spinach */
if (otyp == TIN || otyp == CORPSE)
return (corpsenm >= LOW_PM && vegetarian(&mons[corpsenm]));
}
return FALSE;
}
STATIC_OVL int
shkveg()
{
int i, j, maxprob, prob;
char oclass = FOOD_CLASS;
int ok[NUM_OBJECTS];
j = maxprob = 0;
for (i = bases[(int)oclass]; i < NUM_OBJECTS; ++i) {
if (objects[i].oc_class != oclass) break;
if (veggy_item((struct obj *)0, i)) {
ok[j++] = i;
maxprob += objects[i].oc_prob;
}
}
if (maxprob < 1) panic("shkveg no veggy objects");
prob = rnd(maxprob);
j = 0;
i = ok[0];
while((prob -= objects[i].oc_prob) > 0) {
j++;
i = ok[j];
}
if(objects[i].oc_class != oclass || !OBJ_NAME(objects[i]))
panic("shkveg probtype error, oclass=%d i=%d", (int) oclass, i);
return i;
}
/* make a random item for health food store */
STATIC_OVL void
mkveggy_at(sx, sy)
int sx, sy;
{
struct obj *obj = mksobj_at(shkveg(), sx, sy, TRUE, TRUE);
if (obj && obj->otyp == TIN)
set_tin_variety(obj, HEALTHY_TIN);
return;
}
STATIC_OVL void
mkshobj_at(shp, sx, sy)
/* make an object of the appropriate type for a shop square */
const struct shclass *shp;
int sx, sy;
{
struct monst *mtmp;
int atype;
struct permonst *ptr;
if (rn2(100) < depth(&u.uz) &&
!MON_AT(sx, sy) && (ptr = mkclass(S_MIMIC,0)) &&
(mtmp = makemon(ptr,sx,sy,NO_MM_FLAGS)) != 0) {
/* note: makemon will set the mimic symbol to a shop item */
if (rn2(10) >= depth(&u.uz)) {
mtmp->m_ap_type = M_AP_OBJECT;
mtmp->mappearance = STRANGE_OBJECT;
}
} else {
atype = get_shop_item(shp - shtypes);
if (atype == VEGETARIAN_CLASS)
mkveggy_at(sx, sy);
else if (atype < 0)
(void) mksobj_at(-atype, sx, sy, TRUE, TRUE);
else
(void) mkobj_at(atype, sx, sy, TRUE);
}
}
/* extract a shopkeeper name for the given shop type */
STATIC_OVL void
nameshk(shk, nlp)
struct monst *shk;
const char * const *nlp;
{
int i, trycnt, names_avail;
const char *shname = 0;
struct monst *mtmp;
int name_wanted;
s_level *sptr;
if (nlp == shkfoods && In_mines(&u.uz) && Role_if(PM_MONK)
&& (sptr = Is_special(&u.uz)) != 0 && sptr->flags.town) {
/* special-case override for minetown food store for monks */
nlp = shkhealthfoods;
}
if (nlp == shklight && In_mines(&u.uz)
&& (sptr = Is_special(&u.uz)) != 0 && sptr->flags.town) {
/* special-case minetown lighting shk */
shname = "+Izchak";
shk->female = FALSE;
} else {
/* We want variation from game to game, without needing the save
and restore support which would be necessary for randomization;
try not to make too many assumptions about time_t's internals;
use ledger_no rather than depth to keep mine town distinct. */
int nseed = (int)((long)u.ubirthday / 257L);
name_wanted = ledger_no(&u.uz) + (nseed % 13) - (nseed % 5);
if (name_wanted < 0) name_wanted += (13 + 5);
shk->female = name_wanted & 1;
for (names_avail = 0; nlp[names_avail]; names_avail++)
continue;
for (trycnt = 0; trycnt < 50; trycnt++) {
if (nlp == shktools) {
shname = shktools[rn2(names_avail)];
shk->female = 0; /* reversed below for '_' prefix */
} else if (name_wanted < names_avail) {
shname = nlp[name_wanted];
} else if ((i = rn2(names_avail)) != 0) {
shname = nlp[i - 1];
} else if (nlp != shkgeneral) {
nlp = shkgeneral; /* try general names */
for (names_avail = 0; nlp[names_avail]; names_avail++)
continue;
continue; /* next `trycnt' iteration */
} else {
shname = shk->female ? "-Lucrezia" : "+Dirk";
}
if (*shname == '_' || *shname == '-') shk->female = 1;
else if (*shname == '|' || *shname == '+') shk->female = 0;
/* is name already in use on this level? */
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
if (DEADMONSTER(mtmp) || (mtmp == shk) || !mtmp->isshk) continue;
if (strcmp(ESHK(mtmp)->shknam, shname)) continue;
break;
}
if (!mtmp) break; /* new name */
}
}
(void) strncpy(ESHK(shk)->shknam, shname, PL_NSIZ);
ESHK(shk)->shknam[PL_NSIZ-1] = 0;
}
void
neweshk(mtmp)
struct monst *mtmp;
{
if (!mtmp->mextra) mtmp->mextra = newmextra();
if (!ESHK(mtmp)) {
ESHK(mtmp) = (struct eshk *)alloc(sizeof(struct eshk));
(void) memset((genericptr_t) ESHK(mtmp), 0, sizeof(struct eshk));
}
}
void
free_eshk(mtmp)
struct monst *mtmp;
{
if (mtmp->mextra && ESHK(mtmp)) {
free((genericptr_t) ESHK(mtmp));
ESHK(mtmp) = (struct eshk *)0;
}
mtmp->isshk = 0;
}
STATIC_OVL int
shkinit(shp, sroom) /* create a new shopkeeper in the given room */
const struct shclass *shp;
struct mkroom *sroom;
{
register int sh, sx, sy;
struct monst *shk;
/* place the shopkeeper in the given room */
sh = sroom->fdoor;
sx = doors[sh].x;
sy = doors[sh].y;
/* check that the shopkeeper placement is sane */
if(sroom->irregular) {
int rmno = (sroom - rooms) + ROOMOFFSET;
if (isok(sx-1,sy) && !levl[sx-1][sy].edge &&
(int) levl[sx-1][sy].roomno == rmno) sx--;
else if (isok(sx+1,sy) && !levl[sx+1][sy].edge &&
(int) levl[sx+1][sy].roomno == rmno) sx++;
else if (isok(sx,sy-1) && !levl[sx][sy-1].edge &&
(int) levl[sx][sy-1].roomno == rmno) sy--;
else if (isok(sx,sy+1) && !levl[sx][sy+1].edge &&
(int) levl[sx][sy+1].roomno == rmno) sx++;
else goto shk_failed;
}
else if(sx == sroom->lx-1) sx++;
else if(sx == sroom->hx+1) sx--;
else if(sy == sroom->ly-1) sy++;
else if(sy == sroom->hy+1) sy--; else {
shk_failed:
#ifdef DEBUG
# ifdef WIZARD
/* Said to happen sometimes, but I have never seen it. */
/* Supposedly fixed by fdoor change in mklev.c */
if(wizard) {
register int j = sroom->doorct;
pline("Where is shopdoor?");
pline("Room at (%d,%d),(%d,%d).",
sroom->lx, sroom->ly, sroom->hx, sroom->hy);
pline("doormax=%d doorct=%d fdoor=%d",
doorindex, sroom->doorct, sh);
while(j--) {
pline("door [%d,%d]", doors[sh].x, doors[sh].y);
sh++;
}
display_nhwindow(WIN_MESSAGE, FALSE);
}
# endif
#endif
return(-1);
}
if(MON_AT(sx, sy)) (void) rloc(m_at(sx, sy), FALSE); /* insurance */
/* now initialize the shopkeeper monster structure */
if(!(shk = makemon(&mons[PM_SHOPKEEPER], sx, sy, MM_ESHK)))
return(-1);
shk->isshk = shk->mpeaceful = 1;
set_malign(shk);
shk->msleeping = 0;
shk->mtrapseen = ~0; /* we know all the traps already */
ESHK(shk)->shoproom = (sroom - rooms) + ROOMOFFSET;
sroom->resident = shk;
ESHK(shk)->shoptype = sroom->rtype;
assign_level(&(ESHK(shk)->shoplevel), &u.uz);
ESHK(shk)->shd = doors[sh];
ESHK(shk)->shk.x = sx;
ESHK(shk)->shk.y = sy;
ESHK(shk)->robbed = 0L;
ESHK(shk)->credit = 0L;
ESHK(shk)->debit = 0L;
ESHK(shk)->loan = 0L;
ESHK(shk)->visitct = 0;
ESHK(shk)->following = 0;
ESHK(shk)->billct = 0;
#ifndef GOLDOBJ
shk->mgold = 1000L + 30L*(long)rnd(100); /* initial capital */
#else
mkmonmoney(shk, 1000L + 30L*(long)rnd(100)); /* initial capital */
#endif
if (shp->shknms == shkrings)
(void) mongets(shk, TOUCHSTONE);
nameshk(shk, shp->shknms);
return(sh);
}
/* stock a newly-created room with objects */
void
stock_room(shp_indx, sroom)
int shp_indx;
register struct mkroom *sroom;
{
/*
* Someday soon we'll dispatch on the shdist field of shclass to do
* different placements in this routine. Currently it only supports
* shop-style placement (all squares except a row nearest the first
* door get objects).
*/
register int sx, sy, sh;
char buf[BUFSZ];
int rmno = (sroom - rooms) + ROOMOFFSET;
const struct shclass *shp = &shtypes[shp_indx];
/* first, try to place a shopkeeper in the room */
if ((sh = shkinit(shp, sroom)) < 0)
return;
/* make sure no doorways without doors, and no */
/* trapped doors, in shops. */
sx = doors[sroom->fdoor].x;
sy = doors[sroom->fdoor].y;
if(levl[sx][sy].doormask == D_NODOOR) {
levl[sx][sy].doormask = D_ISOPEN;
newsym(sx,sy);
}
if(levl[sx][sy].typ == SDOOR) {
cvt_sdoor_to_door(&levl[sx][sy]); /* .typ = DOOR */
newsym(sx,sy);
}
if(levl[sx][sy].doormask & D_TRAPPED)
levl[sx][sy].doormask = D_LOCKED;
if(levl[sx][sy].doormask == D_LOCKED) {
register int m = sx, n = sy;
if(inside_shop(sx+1,sy)) m--;
else if(inside_shop(sx-1,sy)) m++;
if(inside_shop(sx,sy+1)) n--;
else if(inside_shop(sx,sy-1)) n++;
Sprintf(buf, "Closed for inventory");
make_engr_at(m, n, buf, 0L, DUST);
}
for(sx = sroom->lx; sx <= sroom->hx; sx++)
for(sy = sroom->ly; sy <= sroom->hy; sy++) {
if(sroom->irregular) {
if (levl[sx][sy].edge || (int) levl[sx][sy].roomno != rmno ||
distmin(sx, sy, doors[sh].x, doors[sh].y) <= 1)
continue;
} else if((sx == sroom->lx && doors[sh].x == sx-1) ||
(sx == sroom->hx && doors[sh].x == sx+1) ||
(sy == sroom->ly && doors[sh].y == sy-1) ||
(sy == sroom->hy && doors[sh].y == sy+1)) continue;
mkshobj_at(shp, sx, sy);
}
/*
* Special monster placements (if any) should go here: that way,
* monsters will sit on top of objects and not the other way around.
*/
level.flags.has_shop = TRUE;
}
/* does shkp's shop stock this item type? */
boolean
saleable(shkp, obj)
struct monst *shkp;
struct obj *obj;
{
int i, shp_indx = ESHK(shkp)->shoptype - SHOPBASE;
const struct shclass *shp = &shtypes[shp_indx];
if (shp->symb == RANDOM_CLASS)
return TRUE;
for (i = 0; i < SIZE(shtypes[0].iprobs) && shp->iprobs[i].iprob; i++) {
/* pseudo-class needs special handling */
if (shp->iprobs[i].itype == VEGETARIAN_CLASS) {
if (veggy_item(obj, 0)) return TRUE;
} else if ((shp->iprobs[i].itype < 0) ?
shp->iprobs[i].itype == - obj->otyp :
shp->iprobs[i].itype == obj->oclass)
return TRUE;
}
/* not found */
return FALSE;
}
/* positive value: class; negative value: specific object type */
int
get_shop_item(type)
int type;
{
const struct shclass *shp = shtypes+type;
register int i,j;
/* select an appropriate object type at random */
for(j = rnd(100), i = 0; (j -= shp->iprobs[i].iprob) > 0; i++)
continue;
return shp->iprobs[i].itype;
}
const char *
shkname(mtmp)
struct monst *mtmp;
{
const char *shknm = ESHK(mtmp)->shknam;
if (Hallucination && !program_state.gameover) {
const char * const *nlp;
int num;
/* count the number of non-unique shop types;
pick one randomly, ignoring shop generation probabilities;
pick a name at random from that shop type's list */
for (num = 0; num < SIZE(shtypes); num++)
if (shtypes[num].prob == 0) break;
if (num > 0) {
nlp = shtypes[rn2(num)].shknms;
for (num = 0; nlp[num]; num++) continue;
if (num > 0) shknm = nlp[rn2(num)];
}
}
/* strip prefix if present */
if (!letter(*shknm)) ++shknm;
return shknm;
}
boolean
shkname_is_pname(mtmp)
struct monst *mtmp;
{
const char *shknm = ESHK(mtmp)->shknam;
return (*shknm == '-' || *shknm == '+' || *shknm == '=');
}
/*shknam.c*/