fix 'crash when reviving shopkeeper'
[aNetHack.git] / src / shknam.c
blobb97e902c196fe53531e28ce1cbd34a8b07881cb1
1 /* NetHack 3.6 shknam.c $NHDT-Date: 1454485432 2016/02/03 07:43:52 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.41 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* shknam.c -- initialize a shop */
7 #include "hack.h"
9 STATIC_DCL boolean FDECL(veggy_item, (struct obj * obj, int));
10 STATIC_DCL int NDECL(shkveg);
11 STATIC_DCL void FDECL(mkveggy_at, (int, int));
12 STATIC_DCL void FDECL(mkshobj_at, (const struct shclass *, int, int,
13 BOOLEAN_P));
14 STATIC_DCL void FDECL(nameshk, (struct monst *, const char *const *));
15 STATIC_DCL int FDECL(shkinit, (const struct shclass *, struct mkroom *));
17 #define VEGETARIAN_CLASS (MAXOCLASSES + 1)
20 * Name prefix codes:
21 * dash - female, personal name
22 * underscore _ female, general name
23 * plus + male, personal name
24 * vertical bar | male, general name (implied for most of shktools)
25 * equals = gender not specified, personal name
27 * Personal names do not receive the honorific prefix "Mr." or "Ms.".
30 static const char *const shkliquors[] = {
31 /* Ukraine */
32 "Njezjin", "Tsjernigof", "Ossipewsk", "Gorlowka",
33 /* Belarus */
34 "Gomel",
35 /* N. Russia */
36 "Konosja", "Weliki Oestjoeg", "Syktywkar", "Sablja", "Narodnaja", "Kyzyl",
37 /* Silezie */
38 "Walbrzych", "Swidnica", "Klodzko", "Raciborz", "Gliwice", "Brzeg",
39 "Krnov", "Hradec Kralove",
40 /* Schweiz */
41 "Leuk", "Brig", "Brienz", "Thun", "Sarnen", "Burglen", "Elm", "Flims",
42 "Vals", "Schuls", "Zum Loch", 0
45 static const char *const shkbooks[] = {
46 /* Eire */
47 "Skibbereen", "Kanturk", "Rath Luirc", "Ennistymon",
48 "Lahinch", "Kinnegad", "Lugnaquillia", "Enniscorthy",
49 "Gweebarra", "Kittamagh", "Nenagh", "Sneem",
50 "Ballingeary", "Kilgarvan", "Cahersiveen", "Glenbeigh",
51 "Kilmihil", "Kiltamagh", "Droichead Atha", "Inniscrone",
52 "Clonegal", "Lisnaskea", "Culdaff", "Dunfanaghy",
53 "Inishbofin", "Kesh", 0
56 static const char *const shkarmors[] = {
57 /* Turquie */
58 "Demirci", "Kalecik", "Boyabai", "Yildizeli", "Gaziantep",
59 "Siirt", "Akhalataki", "Tirebolu", "Aksaray", "Ermenak",
60 "Iskenderun", "Kadirli", "Siverek", "Pervari", "Malasgirt",
61 "Bayburt", "Ayancik", "Zonguldak", "Balya", "Tefenni",
62 "Artvin", "Kars", "Makharadze", "Malazgirt", "Midyat",
63 "Birecik", "Kirikkale", "Alaca", "Polatli", "Nallihan",
67 static const char *const shkwands[] = {
68 /* Wales */
69 "Yr Wyddgrug", "Trallwng", "Mallwyd", "Pontarfynach", "Rhaeader",
70 "Llandrindod", "Llanfair-ym-muallt", "Y-Fenni", "Maesteg", "Rhydaman",
71 "Beddgelert", "Curig", "Llanrwst", "Llanerchymedd", "Caergybi",
72 /* Scotland */
73 "Nairn", "Turriff", "Inverurie", "Braemar", "Lochnagar", "Kerloch",
74 "Beinn a Ghlo", "Drumnadrochit", "Morven", "Uist", "Storr",
75 "Sgurr na Ciche", "Cannich", "Gairloch", "Kyleakin", "Dunvegan", 0
78 static const char *const shkrings[] = {
79 /* Hollandse familienamen */
80 "Feyfer", "Flugi", "Gheel", "Havic", "Haynin",
81 "Hoboken", "Imbyze", "Juyn", "Kinsky", "Massis",
82 "Matray", "Moy", "Olycan", "Sadelin", "Svaving",
83 "Tapper", "Terwen", "Wirix", "Ypey",
84 /* Skandinaviske navne */
85 "Rastegaisa", "Varjag Njarga", "Kautekeino", "Abisko", "Enontekis",
86 "Rovaniemi", "Avasaksa", "Haparanda", "Lulea", "Gellivare",
87 "Oeloe", "Kajaani", "Fauske", 0
90 static const char *const shkfoods[] = {
91 /* Indonesia */
92 "Djasinga", "Tjibarusa", "Tjiwidej", "Pengalengan",
93 "Bandjar", "Parbalingga", "Bojolali", "Sarangan",
94 "Ngebel", "Djombang", "Ardjawinangun", "Berbek",
95 "Papar", "Baliga", "Tjisolok", "Siboga",
96 "Banjoewangi", "Trenggalek", "Karangkobar", "Njalindoeng",
97 "Pasawahan", "Pameunpeuk", "Patjitan", "Kediri",
98 "Pemboeang", "Tringanoe", "Makin", "Tipor",
99 "Semai", "Berhala", "Tegal", "Samoe",
103 static const char *const shkweapons[] = {
104 /* Perigord */
105 "Voulgezac", "Rouffiac", "Lerignac", "Touverac", "Guizengeard",
106 "Melac", "Neuvicq", "Vanzac", "Picq", "Urignac",
107 "Corignac", "Fleac", "Lonzac", "Vergt", "Queyssac",
108 "Liorac", "Echourgnac", "Cazelon", "Eypau", "Carignan",
109 "Monbazillac", "Jonzac", "Pons", "Jumilhac", "Fenouilledes",
110 "Laguiolet", "Saujon", "Eymoutiers", "Eygurande", "Eauze",
111 "Labouheyre", 0
114 static const char *const shktools[] = {
115 /* Spmi */
116 "Ymla", "Eed-morra", "Elan Lapinski", "Cubask", "Nieb", "Bnowr Falr",
117 "Sperc", "Noskcirdneh", "Yawolloh", "Hyeghu", "Niskal", "Trahnil",
118 "Htargcm", "Enrobwem", "Kachzi Rellim", "Regien", "Donmyar", "Yelpur",
119 "Nosnehpets", "Stewe", "Renrut", "Senna Hut", "-Zlaw", "Nosalnef",
120 "Rewuorb", "Rellenk", "Yad", "Cire Htims", "Y-crad", "Nenilukah",
121 "Corsh", "Aned", "Dark Eery", "Niknar", "Lapu", "Lechaim",
122 "Rebrol-nek", "AlliWar Wickson", "Oguhmk", "Telloc Cyaj",
123 #ifdef OVERLAY
124 "Erreip", "Nehpets", "Mron", "Snivek", "Kahztiy",
125 #endif
126 #ifdef WIN32
127 "Lexa", "Niod",
128 #endif
129 #ifdef MAC
130 "Nhoj-lee", "Evad\'kh", "Ettaw-noj", "Tsew-mot", "Ydna-s", "Yao-hang",
131 "Tonbar", "Kivenhoug", "Llardom",
132 #endif
133 #ifdef AMIGA
134 "Falo", "Nosid-da\'r", "Ekim-p", "Noslo", "Yl-rednow", "Mured-oog",
135 "Ivrajimsal",
136 #endif
137 #ifdef TOS
138 "Nivram",
139 #endif
140 #ifdef OS2
141 "Nedraawi-nav",
142 #endif
143 #ifdef VMS
144 "Lez-tneg", "Ytnu-haled",
145 #endif
149 static const char *const shklight[] = {
150 /* Romania */
151 "Zarnesti", "Slanic", "Nehoiasu", "Ludus", "Sighisoara", "Nisipitu",
152 "Razboieni", "Bicaz", "Dorohoi", "Vaslui", "Fetesti", "Tirgu Neamt",
153 "Babadag", "Zimnicea", "Zlatna", "Jiu", "Eforie", "Mamaia",
154 /* Bulgaria */
155 "Silistra", "Tulovo", "Panagyuritshte", "Smolyan", "Kirklareli", "Pernik",
156 "Lom", "Haskovo", "Dobrinishte", "Varvara", "Oryahovo", "Troyan",
157 "Lovech", "Sliven", 0
160 static const char *const shkgeneral[] = {
161 /* Suriname */
162 "Hebiwerie", "Possogroenoe", "Asidonhopo", "Manlobbi",
163 "Adjama", "Pakka Pakka", "Kabalebo", "Wonotobo",
164 "Akalapi", "Sipaliwini",
165 /* Greenland */
166 "Annootok", "Upernavik", "Angmagssalik",
167 /* N. Canada */
168 "Aklavik", "Inuvik", "Tuktoyaktuk", "Chicoutimi",
169 "Ouiatchouane", "Chibougamau", "Matagami", "Kipawa",
170 "Kinojevis", "Abitibi", "Maganasipi",
171 /* Iceland */
172 "Akureyri", "Kopasker", "Budereyri", "Akranes",
173 "Bordeyri", "Holmavik", 0
176 static const char *const shkhealthfoods[] = {
177 /* Tibet */
178 "Ga'er", "Zhangmu", "Rikaze", "Jiangji", "Changdu",
179 "Linzhi", "Shigatse", "Gyantse", "Ganden", "Tsurphu",
180 "Lhasa", "Tsedong", "Drepung",
181 /* Hippie names */
182 "=Azura", "=Blaze", "=Breanna", "=Breezy", "=Dharma",
183 "=Feather", "=Jasmine", "=Luna", "=Melody", "=Moonjava",
184 "=Petal", "=Rhiannon", "=Starla", "=Tranquilla", "=Windsong",
185 "=Zennia", "=Zoe", "=Zora", 0
189 * To add new shop types, all that is necessary is to edit the shtypes[]
190 * array. See mkroom.h for the structure definition. Typically, you'll
191 * have to lower some or all of the probability fields in old entries to
192 * free up some percentage for the new type.
194 * The placement type field is not yet used but will be in the near future.
196 * The iprobs array in each entry defines the probabilities for various kinds
197 * of objects to be present in the given shop type. You can associate with
198 * each percentage either a generic object type (represented by one of the
199 * *_CLASS macros) or a specific object (represented by an onames.h define).
200 * In the latter case, prepend it with a unary minus so the code can know
201 * (by testing the sign) whether to use mkobj() or mksobj().
203 const struct shclass shtypes[] = {
204 { "general store",
205 RANDOM_CLASS,
207 D_SHOP,
208 { { 100, RANDOM_CLASS },
209 { 0, 0 },
210 { 0, 0 },
211 { 0, 0 },
212 { 0, 0 },
213 { 0, 0 } },
214 shkgeneral },
215 { "used armor dealership",
216 ARMOR_CLASS,
218 D_SHOP,
219 { { 90, ARMOR_CLASS },
220 { 10, WEAPON_CLASS },
221 { 0, 0 },
222 { 0, 0 },
223 { 0, 0 },
224 { 0, 0 } },
225 shkarmors },
226 { "second-hand bookstore",
227 SCROLL_CLASS,
229 D_SHOP,
230 { { 90, SCROLL_CLASS },
231 { 10, SPBOOK_CLASS },
232 { 0, 0 },
233 { 0, 0 },
234 { 0, 0 },
235 { 0, 0 } },
236 shkbooks },
237 { "liquor emporium",
238 POTION_CLASS,
240 D_SHOP,
241 { { 100, POTION_CLASS },
242 { 0, 0 },
243 { 0, 0 },
244 { 0, 0 },
245 { 0, 0 },
246 { 0, 0 } },
247 shkliquors },
248 { "antique weapons outlet",
249 WEAPON_CLASS,
251 D_SHOP,
252 { { 90, WEAPON_CLASS },
253 { 10, ARMOR_CLASS },
254 { 0, 0 },
255 { 0, 0 },
256 { 0, 0 },
257 { 0, 0 } },
258 shkweapons },
259 { "delicatessen",
260 FOOD_CLASS,
262 D_SHOP,
263 { { 83, FOOD_CLASS },
264 { 5, -POT_FRUIT_JUICE },
265 { 4, -POT_BOOZE },
266 { 5, -POT_WATER },
267 { 3, -ICE_BOX },
268 { 0, 0 } },
269 shkfoods },
270 { "jewelers",
271 RING_CLASS,
273 D_SHOP,
274 { { 85, RING_CLASS },
275 { 10, GEM_CLASS },
276 { 5, AMULET_CLASS },
277 { 0, 0 },
278 { 0, 0 },
279 { 0, 0 } },
280 shkrings },
281 { "quality apparel and accessories",
282 WAND_CLASS,
284 D_SHOP,
285 { { 90, WAND_CLASS },
286 { 5, -LEATHER_GLOVES },
287 { 5, -ELVEN_CLOAK },
288 { 0, 0 } },
289 shkwands },
290 { "hardware store",
291 TOOL_CLASS,
293 D_SHOP,
294 { { 100, TOOL_CLASS },
295 { 0, 0 },
296 { 0, 0 },
297 { 0, 0 },
298 { 0, 0 },
299 { 0, 0 } },
300 shktools },
301 { "rare books",
302 SPBOOK_CLASS,
304 D_SHOP,
305 { { 90, SPBOOK_CLASS },
306 { 10, SCROLL_CLASS },
307 { 0, 0 },
308 { 0, 0 },
309 { 0, 0 },
310 { 0, 0 } },
311 shkbooks },
312 { "health food store",
313 FOOD_CLASS,
315 D_SHOP,
316 { { 70, VEGETARIAN_CLASS },
317 { 20, -POT_FRUIT_JUICE },
318 { 4, -POT_HEALING },
319 { 3, -POT_FULL_HEALING },
320 { 2, -SCR_FOOD_DETECTION },
321 { 1, -LUMP_OF_ROYAL_JELLY } },
322 shkhealthfoods },
323 /* Shops below this point are "unique". That is they must all have a
324 * probability of zero. They are only created via the special level
325 * loader.
327 { "lighting store",
328 TOOL_CLASS,
330 D_SHOP,
331 { { 30, -WAX_CANDLE },
332 { 48, -TALLOW_CANDLE },
333 { 5, -BRASS_LANTERN },
334 { 9, -OIL_LAMP },
335 { 3, -MAGIC_LAMP },
336 { 5, -POT_OIL } },
337 shklight },
338 /* sentinel */
339 { (char *) 0,
343 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
347 #if 0
348 /* validate shop probabilities; otherwise incorrect local changes could
349 end up provoking infinite loops or wild subscripts fetching garbage */
350 void
351 init_shop_selection()
353 register int i, j, item_prob, shop_prob;
355 for (shop_prob = 0, i = 0; i < SIZE(shtypes); i++) {
356 shop_prob += shtypes[i].prob;
357 for (item_prob = 0, j = 0; j < SIZE(shtypes[0].iprobs); j++)
358 item_prob += shtypes[i].iprobs[j].iprob;
359 if (item_prob != 100)
360 panic("item probabilities total to %d for %s shops!",
361 item_prob, shtypes[i].name);
363 if (shop_prob != 100)
364 panic("shop probabilities total to %d!", shop_prob);
366 #endif /*0*/
368 /* decide whether an object or object type is considered vegetarian;
369 for types, items which might go either way are assumed to be veggy */
370 STATIC_OVL boolean
371 veggy_item(obj, otyp)
372 struct obj *obj;
373 int otyp; /* used iff obj is null */
375 int corpsenm;
376 char oclass;
378 if (obj) {
379 /* actual object; will check tin content and corpse species */
380 otyp = (int) obj->otyp;
381 oclass = obj->oclass;
382 corpsenm = obj->corpsenm;
383 } else {
384 /* just a type; caller will have to handle tins and corpses */
385 oclass = objects[otyp].oc_class;
386 corpsenm = PM_LICHEN; /* veggy standin */
389 if (oclass == FOOD_CLASS) {
390 if (objects[otyp].oc_material == VEGGY || otyp == EGG)
391 return TRUE;
392 if (otyp == TIN && corpsenm == NON_PM) /* implies obj is non-null */
393 return (boolean) (obj->spe == 1); /* 0 = empty, 1 = spinach */
394 if (otyp == TIN || otyp == CORPSE)
395 return (boolean) (corpsenm >= LOW_PM
396 && vegetarian(&mons[corpsenm]));
398 return FALSE;
401 STATIC_OVL int
402 shkveg()
404 int i, j, maxprob, prob;
405 char oclass = FOOD_CLASS;
406 int ok[NUM_OBJECTS];
408 j = maxprob = 0;
409 ok[0] = 0; /* lint suppression */
410 for (i = bases[(int) oclass]; i < NUM_OBJECTS; ++i) {
411 if (objects[i].oc_class != oclass)
412 break;
414 if (veggy_item((struct obj *) 0, i)) {
415 ok[j++] = i;
416 maxprob += objects[i].oc_prob;
419 if (maxprob < 1)
420 panic("shkveg no veggy objects");
421 prob = rnd(maxprob);
423 j = 0;
424 i = ok[0];
425 while ((prob -= objects[i].oc_prob) > 0) {
426 j++;
427 i = ok[j];
430 if (objects[i].oc_class != oclass || !OBJ_NAME(objects[i]))
431 panic("shkveg probtype error, oclass=%d i=%d", (int) oclass, i);
432 return i;
435 /* make a random item for health food store */
436 STATIC_OVL void
437 mkveggy_at(sx, sy)
438 int sx, sy;
440 struct obj *obj = mksobj_at(shkveg(), sx, sy, TRUE, TRUE);
442 if (obj && obj->otyp == TIN)
443 set_tin_variety(obj, HEALTHY_TIN);
444 return;
447 /* make an object of the appropriate type for a shop square */
448 STATIC_OVL void
449 mkshobj_at(shp, sx, sy, mkspecl)
450 const struct shclass *shp;
451 int sx, sy;
452 boolean mkspecl;
454 struct monst *mtmp;
455 struct permonst *ptr;
456 int atype;
458 /* 3.6 tribute */
459 if (mkspecl && (!strcmp(shp->name, "rare books")
460 || !strcmp(shp->name, "second-hand bookstore"))) {
461 struct obj *novel = mksobj_at(SPE_NOVEL, sx, sy, FALSE, FALSE);
463 if (novel)
464 context.tribute.bookstock = TRUE;
465 return;
468 if (rn2(100) < depth(&u.uz) && !MON_AT(sx, sy)
469 && (ptr = mkclass(S_MIMIC, 0)) != 0
470 && (mtmp = makemon(ptr, sx, sy, NO_MM_FLAGS)) != 0) {
471 /* note: makemon will set the mimic symbol to a shop item */
472 if (rn2(10) >= depth(&u.uz)) {
473 mtmp->m_ap_type = M_AP_OBJECT;
474 mtmp->mappearance = STRANGE_OBJECT;
476 } else {
477 atype = get_shop_item((int) (shp - shtypes));
478 if (atype == VEGETARIAN_CLASS)
479 mkveggy_at(sx, sy);
480 else if (atype < 0)
481 (void) mksobj_at(-atype, sx, sy, TRUE, TRUE);
482 else
483 (void) mkobj_at(atype, sx, sy, TRUE);
487 /* extract a shopkeeper name for the given shop type */
488 STATIC_OVL void
489 nameshk(shk, nlp)
490 struct monst *shk;
491 const char *const *nlp;
493 int i, trycnt, names_avail;
494 const char *shname = 0;
495 struct monst *mtmp;
496 int name_wanted;
497 s_level *sptr;
499 if (nlp == shkfoods && In_mines(&u.uz) && Role_if(PM_MONK)
500 && (sptr = Is_special(&u.uz)) != 0 && sptr->flags.town) {
501 /* special-case override for minetown food store for monks */
502 nlp = shkhealthfoods;
505 if (nlp == shklight && In_mines(&u.uz) && (sptr = Is_special(&u.uz)) != 0
506 && sptr->flags.town) {
507 /* special-case minetown lighting shk */
508 shname = "+Izchak";
509 shk->female = FALSE;
510 } else {
511 /* We want variation from game to game, without needing the save
512 and restore support which would be necessary for randomization;
513 try not to make too many assumptions about time_t's internals;
514 use ledger_no rather than depth to keep mine town distinct. */
515 int nseed = (int) ((long) ubirthday / 257L);
517 name_wanted = ledger_no(&u.uz) + (nseed % 13) - (nseed % 5);
518 if (name_wanted < 0)
519 name_wanted += (13 + 5);
520 shk->female = name_wanted & 1;
522 for (names_avail = 0; nlp[names_avail]; names_avail++)
523 continue;
525 for (trycnt = 0; trycnt < 50; trycnt++) {
526 if (nlp == shktools) {
527 shname = shktools[rn2(names_avail)];
528 shk->female = 0; /* reversed below for '_' prefix */
529 } else if (name_wanted < names_avail) {
530 shname = nlp[name_wanted];
531 } else if ((i = rn2(names_avail)) != 0) {
532 shname = nlp[i - 1];
533 } else if (nlp != shkgeneral) {
534 nlp = shkgeneral; /* try general names */
535 for (names_avail = 0; nlp[names_avail]; names_avail++)
536 continue;
537 continue; /* next `trycnt' iteration */
538 } else {
539 shname = shk->female ? "-Lucrezia" : "+Dirk";
541 if (*shname == '_' || *shname == '-')
542 shk->female = 1;
543 else if (*shname == '|' || *shname == '+')
544 shk->female = 0;
546 /* is name already in use on this level? */
547 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
548 if (DEADMONSTER(mtmp) || (mtmp == shk) || !mtmp->isshk)
549 continue;
550 if (strcmp(ESHK(mtmp)->shknam, shname))
551 continue;
552 break;
554 if (!mtmp)
555 break; /* new name */
558 (void) strncpy(ESHK(shk)->shknam, shname, PL_NSIZ);
559 ESHK(shk)->shknam[PL_NSIZ - 1] = 0;
562 void
563 neweshk(mtmp)
564 struct monst *mtmp;
566 if (!mtmp->mextra)
567 mtmp->mextra = newmextra();
568 if (!ESHK(mtmp))
569 ESHK(mtmp) = (struct eshk *) alloc(sizeof(struct eshk));
570 (void) memset((genericptr_t) ESHK(mtmp), 0, sizeof(struct eshk));
571 ESHK(mtmp)->bill_p = (struct bill_x *) 0;
574 void
575 free_eshk(mtmp)
576 struct monst *mtmp;
578 if (mtmp->mextra && ESHK(mtmp)) {
579 free((genericptr_t) ESHK(mtmp));
580 ESHK(mtmp) = (struct eshk *) 0;
582 mtmp->isshk = 0;
585 /* create a new shopkeeper in the given room */
586 STATIC_OVL int
587 shkinit(shp, sroom)
588 const struct shclass *shp;
589 struct mkroom *sroom;
591 register int sh, sx, sy;
592 struct monst *shk;
593 struct eshk *eshkp;
595 /* place the shopkeeper in the given room */
596 sh = sroom->fdoor;
597 sx = doors[sh].x;
598 sy = doors[sh].y;
600 /* check that the shopkeeper placement is sane */
601 if (sroom->irregular) {
602 int rmno = (int) ((sroom - rooms) + ROOMOFFSET);
603 if (isok(sx - 1, sy) && !levl[sx - 1][sy].edge
604 && (int) levl[sx - 1][sy].roomno == rmno)
605 sx--;
606 else if (isok(sx + 1, sy) && !levl[sx + 1][sy].edge
607 && (int) levl[sx + 1][sy].roomno == rmno)
608 sx++;
609 else if (isok(sx, sy - 1) && !levl[sx][sy - 1].edge
610 && (int) levl[sx][sy - 1].roomno == rmno)
611 sy--;
612 else if (isok(sx, sy + 1) && !levl[sx][sy + 1].edge
613 && (int) levl[sx][sy + 1].roomno == rmno)
614 sx++;
615 else
616 goto shk_failed;
617 } else if (sx == sroom->lx - 1)
618 sx++;
619 else if (sx == sroom->hx + 1)
620 sx--;
621 else if (sy == sroom->ly - 1)
622 sy++;
623 else if (sy == sroom->hy + 1)
624 sy--;
625 else {
626 shk_failed:
627 #ifdef DEBUG
628 /* Said to happen sometimes, but I have never seen it. */
629 /* Supposedly fixed by fdoor change in mklev.c */
630 if (wizard) {
631 register int j = sroom->doorct;
633 pline("Where is shopdoor?");
634 pline("Room at (%d,%d),(%d,%d).", sroom->lx, sroom->ly, sroom->hx,
635 sroom->hy);
636 pline("doormax=%d doorct=%d fdoor=%d", doorindex, sroom->doorct,
637 sh);
638 while (j--) {
639 pline("door [%d,%d]", doors[sh].x, doors[sh].y);
640 sh++;
642 display_nhwindow(WIN_MESSAGE, FALSE);
644 #endif
645 return -1;
648 if (MON_AT(sx, sy))
649 (void) rloc(m_at(sx, sy), FALSE); /* insurance */
651 /* now initialize the shopkeeper monster structure */
652 if (!(shk = makemon(&mons[PM_SHOPKEEPER], sx, sy, MM_ESHK)))
653 return -1;
654 eshkp = ESHK(shk); /* makemon(...,MM_ESHK) allocates this */
655 shk->isshk = shk->mpeaceful = 1;
656 set_malign(shk);
657 shk->msleeping = 0;
658 shk->mtrapseen = ~0; /* we know all the traps already */
659 eshkp->shoproom = (schar) ((sroom - rooms) + ROOMOFFSET);
660 sroom->resident = shk;
661 eshkp->shoptype = sroom->rtype;
662 assign_level(&eshkp->shoplevel, &u.uz);
663 eshkp->shd = doors[sh];
664 eshkp->shk.x = sx;
665 eshkp->shk.y = sy;
666 eshkp->robbed = eshkp->credit = eshkp->debit = eshkp->loan = 0L;
667 eshkp->following = eshkp->surcharge = eshkp->dismiss_kops = FALSE;
668 eshkp->billct = eshkp->visitct = 0;
669 eshkp->bill_p = (struct bill_x *) 0;
670 eshkp->customer[0] = '\0';
671 mkmonmoney(shk, 1000L + 30L * (long) rnd(100)); /* initial capital */
672 if (shp->shknms == shkrings)
673 (void) mongets(shk, TOUCHSTONE);
674 nameshk(shk, shp->shknms);
676 return sh;
679 /* stock a newly-created room with objects */
680 void
681 stock_room(shp_indx, sroom)
682 int shp_indx;
683 register struct mkroom *sroom;
686 * Someday soon we'll dispatch on the shdist field of shclass to do
687 * different placements in this routine. Currently it only supports
688 * shop-style placement (all squares except a row nearest the first
689 * door get objects).
691 int sx, sy, sh;
692 int stockcount = 0, specialspot = 0;
693 char buf[BUFSZ];
694 int rmno = (int) ((sroom - rooms) + ROOMOFFSET);
695 const struct shclass *shp = &shtypes[shp_indx];
697 /* first, try to place a shopkeeper in the room */
698 if ((sh = shkinit(shp, sroom)) < 0)
699 return;
701 /* make sure no doorways without doors, and no trapped doors, in shops */
702 sx = doors[sroom->fdoor].x;
703 sy = doors[sroom->fdoor].y;
704 if (levl[sx][sy].doormask == D_NODOOR) {
705 levl[sx][sy].doormask = D_ISOPEN;
706 newsym(sx, sy);
708 if (levl[sx][sy].typ == SDOOR) {
709 cvt_sdoor_to_door(&levl[sx][sy]); /* .typ = DOOR */
710 newsym(sx, sy);
712 if (levl[sx][sy].doormask & D_TRAPPED)
713 levl[sx][sy].doormask = D_LOCKED;
715 if (levl[sx][sy].doormask == D_LOCKED) {
716 register int m = sx, n = sy;
718 if (inside_shop(sx + 1, sy))
719 m--;
720 else if (inside_shop(sx - 1, sy))
721 m++;
722 if (inside_shop(sx, sy + 1))
723 n--;
724 else if (inside_shop(sx, sy - 1))
725 n++;
726 Sprintf(buf, "Closed for inventory");
727 make_engr_at(m, n, buf, 0L, DUST);
730 if (context.tribute.enabled && !context.tribute.bookstock) {
732 * Out of the number of spots where we're actually
733 * going to put stuff, randomly single out one in particular.
735 for (sx = sroom->lx; sx <= sroom->hx; sx++)
736 for (sy = sroom->ly; sy <= sroom->hy; sy++) {
737 if (sroom->irregular) {
738 if (levl[sx][sy].edge
739 || (int) levl[sx][sy].roomno != rmno
740 || distmin(sx, sy, doors[sh].x, doors[sh].y) <= 1)
741 continue;
742 } else if ((sx == sroom->lx && doors[sh].x == sx - 1)
743 || (sx == sroom->hx && doors[sh].x == sx + 1)
744 || (sy == sroom->ly && doors[sh].y == sy - 1)
745 || (sy == sroom->hy && doors[sh].y == sy + 1))
746 continue;
747 stockcount++;
749 specialspot = rnd(stockcount);
750 stockcount = 0;
753 for (sx = sroom->lx; sx <= sroom->hx; sx++)
754 for (sy = sroom->ly; sy <= sroom->hy; sy++) {
755 if (sroom->irregular) {
756 if (levl[sx][sy].edge
757 || (int) levl[sx][sy].roomno != rmno
758 || distmin(sx, sy, doors[sh].x, doors[sh].y) <= 1)
759 continue;
760 } else if ((sx == sroom->lx && doors[sh].x == sx - 1)
761 || (sx == sroom->hx && doors[sh].x == sx + 1)
762 || (sy == sroom->ly && doors[sh].y == sy - 1)
763 || (sy == sroom->hy && doors[sh].y == sy + 1))
764 continue;
765 stockcount++;
766 mkshobj_at(shp, sx, sy,
767 ((stockcount) && (stockcount == specialspot)));
771 * Special monster placements (if any) should go here: that way,
772 * monsters will sit on top of objects and not the other way around.
775 level.flags.has_shop = TRUE;
778 /* does shkp's shop stock this item type? */
779 boolean
780 saleable(shkp, obj)
781 struct monst *shkp;
782 struct obj *obj;
784 int i, shp_indx = ESHK(shkp)->shoptype - SHOPBASE;
785 const struct shclass *shp = &shtypes[shp_indx];
787 if (shp->symb == RANDOM_CLASS)
788 return TRUE;
789 for (i = 0; i < SIZE(shtypes[0].iprobs) && shp->iprobs[i].iprob; i++) {
790 /* pseudo-class needs special handling */
791 if (shp->iprobs[i].itype == VEGETARIAN_CLASS) {
792 if (veggy_item(obj, 0))
793 return TRUE;
794 } else if ((shp->iprobs[i].itype < 0)
795 ? shp->iprobs[i].itype == -obj->otyp
796 : shp->iprobs[i].itype == obj->oclass)
797 return TRUE;
799 /* not found */
800 return FALSE;
803 /* positive value: class; negative value: specific object type */
805 get_shop_item(type)
806 int type;
808 const struct shclass *shp = shtypes + type;
809 register int i, j;
811 /* select an appropriate object type at random */
812 for (j = rnd(100), i = 0; (j -= shp->iprobs[i].iprob) > 0; i++)
813 continue;
815 return shp->iprobs[i].itype;
818 /* version of shkname() for beginning of sentence */
819 char *
820 Shknam(mtmp)
821 struct monst *mtmp;
823 char *nam = shkname(mtmp);
825 /* 'nam[]' is almost certainly already capitalized, but be sure */
826 nam[0] = highc(nam[0]);
827 return nam;
830 /* shopkeeper's name, without any visibility constraint; if hallucinating,
831 will yield some other shopkeeper's name (not necessarily one residing
832 in the current game's dungeon, or who keeps same type of shop) */
833 char *
834 shkname(mtmp)
835 struct monst *mtmp;
837 char *nam;
838 unsigned save_isshk = mtmp->isshk;
840 mtmp->isshk = 0; /* don't want mon_nam() calling shkname() */
841 /* get a modifiable name buffer along with fallback result */
842 nam = noit_mon_nam(mtmp);
843 mtmp->isshk = save_isshk;
845 if (!mtmp->isshk) {
846 impossible("shkname: \"%s\" is not a shopkeeper.", nam);
847 } else if (!has_eshk(mtmp)) {
848 panic("shkname: shopkeeper \"%s\" lacks 'eshk' data.", nam);
849 } else {
850 const char *shknm = ESHK(mtmp)->shknam;
852 if (Hallucination && !program_state.gameover) {
853 const char *const *nlp;
854 int num;
856 /* count the number of non-unique shop types;
857 pick one randomly, ignoring shop generation probabilities;
858 pick a name at random from that shop type's list */
859 for (num = 0; num < SIZE(shtypes); num++)
860 if (shtypes[num].prob == 0)
861 break;
862 if (num > 0) {
863 nlp = shtypes[rn2(num)].shknms;
864 for (num = 0; nlp[num]; num++)
865 continue;
866 if (num > 0)
867 shknm = nlp[rn2(num)];
870 /* strip prefix if present */
871 if (!letter(*shknm))
872 ++shknm;
873 Strcpy(nam, shknm);
875 return nam;
878 boolean
879 shkname_is_pname(mtmp)
880 struct monst *mtmp;
882 const char *shknm = ESHK(mtmp)->shknam;
884 return (boolean) (*shknm == '-' || *shknm == '+' || *shknm == '=');
887 boolean
888 is_izchak(shkp, override_hallucination)
889 struct monst *shkp;
890 boolean override_hallucination;
892 const char *shknm;
894 if (Hallucination && !override_hallucination)
895 return FALSE;
896 if (!shkp->isshk)
897 return FALSE;
898 /* outside of town, Izchak becomes just an ordinary shopkeeper */
899 if (!in_town(shkp->mx, shkp->my))
900 return FALSE;
901 shknm = ESHK(shkp)->shknam;
902 /* skip "+" prefix */
903 if (!letter(*shknm))
904 ++shknm;
905 return (boolean) !strcmp(shknm, "Izchak");
908 /*shknam.c*/