1 /* SCCS Id: @(#)shknam.c 3.3 97/05/25 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 /* shknam.c -- initialize a shop */
6
7 #include "hack.h"
8 #include "eshk.h"
9
10 #ifndef OVLB
11 extern const struct shclass shtypes[];
12
13 #else
14
15 STATIC_DCL void FDECL(mkshobj_at, (const struct shclass *,int,int));
16 STATIC_DCL void FDECL(nameshk, (struct monst *,const char **));
17 STATIC_DCL int FDECL(shkinit, (const struct shclass *,struct mkroom *));
18
19 static const char *shkliquors[] = {
20 /* Ukraine */
21 "Njezjin", "Tsjernigof", "Gomel", "Ossipewsk", "Gorlowka",
22 /* N. Russia */
23 "Konosja", "Weliki Oestjoeg", "Syktywkar", "Sablja",
24 "Narodnaja", "Kyzyl",
25 /* Silezie */
26 "Walbrzych", "Swidnica", "Klodzko", "Raciborz", "Gliwice",
27 "Brzeg", "Krnov", "Hradec Kralove",
28 /* Schweiz */
29 "Leuk", "Brig", "Brienz", "Thun", "Sarnen", "Burglen", "Elm",
30 "Flims", "Vals", "Schuls", "Zum Loch",
31 0
32 };
33
34 static const char *shkbooks[] = {
35 /* Eire */
36 "Skibbereen", "Kanturk", "Rath Luirc", "Ennistymon", "Lahinch",
37 "Kinnegad", "Lugnaquillia", "Enniscorthy", "Gweebarra",
38 "Kittamagh", "Nenagh", "Sneem", "Ballingeary", "Kilgarvan",
39 "Cahersiveen", "Glenbeigh", "Kilmihil", "Kiltamagh",
40 "Droichead Atha", "Inniscrone", "Clonegal", "Lisnaskea",
41 "Culdaff", "Dunfanaghy", "Inishbofin", "Kesh",
42 0
43 };
44
45 static const char *shkarmors[] = {
46 /* Turquie */
47 "Demirci", "Kalecik", "Boyabai", "Yildizeli", "Gaziantep",
48 "Siirt", "Akhalataki", "Tirebolu", "Aksaray", "Ermenak",
49 "Iskenderun", "Kadirli", "Siverek", "Pervari", "Malasgirt",
50 "Bayburt", "Ayancik", "Zonguldak", "Balya", "Tefenni",
51 "Artvin", "Kars", "Makharadze", "Malazgirt", "Midyat",
52 "Birecik", "Kirikkale", "Alaca", "Polatli", "Nallihan",
53 0
54 };
55
56 static const char *shkwands[] = {
57 /* Wales */
58 "Yr Wyddgrug", "Trallwng", "Mallwyd", "Pontarfynach",
59 "Rhaeader", "Llandrindod", "Llanfair-ym-muallt",
60 "Y-Fenni", "Measteg", "Rhydaman", "Beddgelert",
61 "Curig", "Llanrwst", "Llanerchymedd", "Caergybi",
62 /* Scotland */
63 "Nairn", "Turriff", "Inverurie", "Braemar", "Lochnagar",
64 "Kerloch", "Beinn a Ghlo", "Drumnadrochit", "Morven",
65 "Uist", "Storr", "Sgurr na Ciche", "Cannich", "Gairloch",
66 "Kyleakin", "Dunvegan",
67 0
68 };
69
70 static const char *shkrings[] = {
71 /* Hollandse familienamen */
72 "Feyfer", "Flugi", "Gheel", "Havic", "Haynin", "Hoboken",
73 "Imbyze", "Juyn", "Kinsky", "Massis", "Matray", "Moy",
74 "Olycan", "Sadelin", "Svaving", "Tapper", "Terwen", "Wirix",
75 "Ypey",
76 /* Skandinaviske navne */
77 "Rastegaisa", "Varjag Njarga", "Kautekeino", "Abisko",
78 "Enontekis", "Rovaniemi", "Avasaksa", "Haparanda",
79 "Lulea", "Gellivare", "Oeloe", "Kajaani", "Fauske",
80 0
81 };
82
83 static const char *shkfoods[] = {
84 /* Indonesia */
85 "Djasinga", "Tjibarusa", "Tjiwidej", "Pengalengan",
86 "Bandjar", "Parbalingga", "Bojolali", "Sarangan",
87 "Ngebel", "Djombang", "Ardjawinangun", "Berbek",
88 "Papar", "Baliga", "Tjisolok", "Siboga", "Banjoewangi",
89 "Trenggalek", "Karangkobar", "Njalindoeng", "Pasawahan",
90 "Pameunpeuk", "Patjitan", "Kediri", "Pemboeang", "Tringanoe",
91 "Makin", "Tipor", "Semai", "Berhala", "Tegal", "Samoe",
92 0
93 };
94
95 static const char *shkweapons[] = {
96 /* Perigord */
97 "Voulgezac", "Rouffiac", "Lerignac", "Touverac", "Guizengeard",
98 "Melac", "Neuvicq", "Vanzac", "Picq", "Urignac", "Corignac",
99 "Fleac", "Lonzac", "Vergt", "Queyssac", "Liorac", "Echourgnac",
100 "Cazelon", "Eypau", "Carignan", "Monbazillac", "Jonzac",
101 "Pons", "Jumilhac", "Fenouilledes", "Laguiolet", "Saujon",
102 "Eymoutiers", "Eygurande", "Eauze", "Labouheyre",
103 0
104 };
105
106 static const char *shktools[] = {
107 /* Spmi */
108 "Ymla", "Eed-morra", "Cubask", "Nieb", "Bnowr Falr", "Telloc Cyaj",
109 "Sperc", "Noskcirdneh", "Yawolloh", "Hyeghu", "Niskal", "Trahnil",
110 "Htargcm", "Enrobwem", "Kachzi Rellim", "Regien", "Donmyar",
111 "Yelpur", "Nosnehpets", "Stewe", "Renrut", "_Zlaw", "Nosalnef",
112 "Rewuorb", "Rellenk", "Yad", "Cire Htims", "Y-crad", "Nenilukah",
113 "Corsh", "Aned",
114 #ifdef OVERLAY
115 "Erreip", "Nehpets", "Mron", "Snivek", "Lapu", "Kahztiy",
116 #endif
117 #ifdef WIN32
118 "Lechaim",
119 #endif
120 #ifdef MAC
121 "Nhoj-lee", "Evad\'kh", "Ettaw-noj", "Tsew-mot", "Ydna-s",
122 "Yao-hang", "Tonbar", "Kivenhoug",
123 #endif
124 #ifdef AMIGA
125 "Falo", "Nosid-da\'r", "Ekim-p", "Rebrol-nek", "Noslo", "Yl-rednow",
126 "Mured-oog", "Ivrajimsal",
127 #endif
128 #ifdef TOS
129 "Nivram",
130 #endif
131 #ifdef VMS
132 "Lez-tneg", "Ytnu-haled", "Niknar",
133 #endif
134 0
135 };
136
137 static const char *shklight[] = {
138 /* Romania */
139 "Zarnesti", "Slanic", "Nehoiasu", "Ludus", "Sighisoara", "Nisipitu",
140 "Razboieni", "Bicaz", "Dorohoi", "Vaslui", "Fetesti", "Tirgu Neamt",
141 "Babadag", "Zimnicea", "Zlatna", "Jiu", "Eforie", "Mamaia",
142 /* Bulgaria */
143 "Silistra", "Tulovo", "Panagyuritshte", "Smolyan", "Kirklareli",
144 "Pernik", "Lom", "Haskovo", "Dobrinishte", "Varvara", "Oryahovo",
145 "Troyan", "Lovech", "Sliven",
146 0
147 };
148
149 static const char *shkgeneral[] = {
150 /* Suriname */
151 "Hebiwerie", "Possogroenoe", "Asidonhopo", "Manlobbi",
152 "Adjama", "Pakka Pakka", "Kabalebo", "Wonotobo",
153 "Akalapi", "Sipaliwini",
154 /* Greenland */
155 "Annootok", "Upernavik", "Angmagssalik",
156 /* N. Canada */
157 "Aklavik", "Inuvik", "Tuktoyaktuk",
158 "Chicoutimi", "Ouiatchouane", "Chibougamau",
159 "Matagami", "Kipawa", "Kinojevis",
160 "Abitibi", "Maganasipi",
161 /* Iceland */
162 "Akureyri", "Kopasker", "Budereyri", "Akranes", "Bordeyri",
163 "Holmavik",
164 0
165 };
166
167 /*
168 * To add new shop types, all that is necessary is to edit the shtypes[] array.
169 * See mkroom.h for the structure definition. Typically, you'll have to lower
170 * some or all of the probability fields in old entries to free up some
171 * percentage for the new type.
172 *
173 * The placement type field is not yet used but will be in the near future.
174 *
175 * The iprobs array in each entry defines the probabilities for various kinds
176 * of objects to be present in the given shop type. You can associate with
177 * each percentage either a generic object type (represented by one of the
178 * *_CLASS macros) or a specific object (represented by an onames.h define).
179 * In the latter case, prepend it with a unary minus so the code can know
180 * (by testing the sign) whether to use mkobj() or mksobj().
181 */
182
183 const struct shclass shtypes[] = {
184 {"general store", RANDOM_CLASS, 44,
185 D_SHOP, {{100, RANDOM_CLASS}, {0, 0}, {0, 0}}, shkgeneral},
186 {"used armor dealership", ARMOR_CLASS, 14,
187 D_SHOP, {{90, ARMOR_CLASS}, {10, WEAPON_CLASS}, {0, 0}},
188 shkarmors},
189 {"second-hand bookstore", SCROLL_CLASS, 10, D_SHOP,
190 {{90, SCROLL_CLASS}, {10, SPBOOK_CLASS}, {0, 0}}, shkbooks},
191 {"liquor emporium", POTION_CLASS, 10, D_SHOP,
192 {{100, POTION_CLASS}, {0, 0}, {0, 0}}, shkliquors},
193 {"antique weapons outlet", WEAPON_CLASS, 5, D_SHOP,
194 {{90, WEAPON_CLASS}, {10, ARMOR_CLASS}, {0, 0}}, shkweapons},
195 {"delicatessen", FOOD_CLASS, 5, D_SHOP,
196 {{83, FOOD_CLASS}, {5, -POT_FRUIT_JUICE}, {4, -POT_BOOZE},
197 {5, -POT_WATER}, {3, -ICE_BOX}}, shkfoods},
198 {"jewelers", RING_CLASS, 3, D_SHOP,
199 {{85, RING_CLASS}, {10, GEM_CLASS}, {5, AMULET_CLASS}, {0, 0}},
200 shkrings},
201 {"quality apparel and accessories", WAND_CLASS, 3, D_SHOP,
202 {{90, WAND_CLASS}, {5, -LEATHER_GLOVES}, {5, -ELVEN_CLOAK}, {0, 0}},
203 shkwands},
204 {"hardware store", TOOL_CLASS, 3, D_SHOP,
205 {{100, TOOL_CLASS}, {0, 0}, {0, 0}}, shktools},
206 /* Actually shktools is ignored; the code specifically chooses a
207 * random implementor name (along with candle shops having
208 * random shopkeepers)
209 */
210 {"rare books", SPBOOK_CLASS, 3, D_SHOP,
211 {{90, SPBOOK_CLASS}, {10, SCROLL_CLASS}, {0, 0}}, shkbooks},
212 /* Shops below this point are "unique". That is they must all have a
213 * probability of zero. They are only created via the special level
214 * loader.
215 */
216 {"lighting store", TOOL_CLASS, 0, D_SHOP,
217 {{32, -WAX_CANDLE}, {50, -TALLOW_CANDLE},
218 {5, -BRASS_LANTERN}, {10, -OIL_LAMP}, {3, -MAGIC_LAMP}}, shklight},
219 {(char *)0, 0, 0, 0, {{0, 0}, {0, 0}, {0, 0}}, 0}
220 };
221
222 #if 0
223 /* validate shop probabilities; otherwise incorrect local changes could
224 end up provoking infinite loops or wild subscripts fetching garbage */
225 void
226 init_shop_selection()
227 {
228 register int i, j, item_prob, shop_prob;
229
230 for (shop_prob = 0, i = 0; i < SIZE(shtypes); i++) {
231 shop_prob += shtypes[i].prob;
232 for (item_prob = 0, j = 0; j < SIZE(shtypes[0].iprobs); j++)
233 item_prob += shtypes[i].iprobs[j].iprob;
234 if (item_prob != 100)
235 panic("item probabilities total to %d for %s shops!",
236 item_prob, shtypes[i].name);
237 }
238 if (shop_prob != 100)
239 panic("shop probabilities total to %d!", shop_prob);
240 }
241 #endif /*0*/
242
243 STATIC_OVL void
mkshobj_at(shp,sx,sy)244 mkshobj_at(shp, sx, sy)
245 /* make an object of the appropriate type for a shop square */
246 const struct shclass *shp;
247 int sx, sy;
248 {
249 register struct monst *mtmp;
250 int atype;
251 struct permonst *ptr;
252
253 if (rn2(100) < depth(&u.uz) &&
254 !MON_AT(sx, sy) && (ptr = mkclass(S_MIMIC,0)) &&
255 (mtmp=makemon(ptr,sx,sy,NO_MM_FLAGS))) {
256 /* note: makemon will set the mimic symbol to a shop item */
257 if (rn2(10) >= depth(&u.uz)) {
258 mtmp->m_ap_type = M_AP_OBJECT;
259 mtmp->mappearance = STRANGE_OBJECT;
260 }
261 } else if ((atype = get_shop_item(shp - shtypes)) < 0)
262 (void) mksobj_at(-atype, sx, sy, TRUE);
263 else (void) mkobj_at(atype, sx, sy, TRUE);
264 }
265
266 /* extract a shopkeeper name for the given shop type */
267 STATIC_OVL void
nameshk(shk,nlp)268 nameshk(shk, nlp)
269 struct monst *shk;
270 const char *nlp[];
271 {
272 int i, trycnt, names_avail;
273 const char *shname = 0;
274 struct monst *mtmp;
275 int name_wanted;
276 s_level *sptr;
277
278 if (nlp == shklight && In_mines(&u.uz)
279 && (sptr = Is_special(&u.uz)) != 0 && sptr->flags.town) {
280 /* special-case minetown lighting shk */
281 shname = "Izchak";
282 shk->female = FALSE;
283 } else {
284 /* We want variation from game to game, without needing the save
285 and restore support which would be necessary for randomization;
286 try not to make too many assumptions about time_t's internals;
287 use ledger_no rather than depth to keep mine town distinct. */
288 int nseed = (int)((long)u.ubirthday / 257L);
289
290 name_wanted = ledger_no(&u.uz) + (nseed % 13) - (nseed % 5);
291 if (name_wanted < 0) name_wanted += (13 + 5);
292 shk->female = name_wanted & 1;
293
294 for (names_avail = 0; nlp[names_avail]; names_avail++)
295 continue;
296
297 for (trycnt = 0; trycnt < 50; trycnt++) {
298 if (nlp == shktools) {
299 shname = shktools[rn2(names_avail)];
300 shk->female = (*shname == '_');
301 if (shk->female) shname++;
302 } else if (name_wanted < names_avail) {
303 shname = nlp[name_wanted];
304 } else if ((i = rn2(names_avail)) != 0) {
305 shname = nlp[i - 1];
306 } else if (nlp != shkgeneral) {
307 nlp = shkgeneral; /* try general names */
308 for (names_avail = 0; nlp[names_avail]; names_avail++)
309 continue;
310 continue; /* next `trycnt' iteration */
311 } else {
312 shname = shk->female ? "Lucrezia" : "Dirk";
313 }
314
315 /* is name already in use on this level? */
316 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
317 if (DEADMONSTER(mtmp) || (mtmp == shk) || !mtmp->isshk) continue;
318 if (strcmp(ESHK(mtmp)->shknam, shname)) continue;
319 break;
320 }
321 if (!mtmp) break; /* new name */
322 }
323 }
324 (void) strncpy(ESHK(shk)->shknam, shname, PL_NSIZ);
325 ESHK(shk)->shknam[PL_NSIZ-1] = 0;
326 }
327
328 STATIC_OVL int
shkinit(shp,sroom)329 shkinit(shp, sroom) /* create a new shopkeeper in the given room */
330 const struct shclass *shp;
331 struct mkroom *sroom;
332 {
333 register int sh, sx, sy;
334 struct monst *shk;
335
336 /* place the shopkeeper in the given room */
337 sh = sroom->fdoor;
338 sx = doors[sh].x;
339 sy = doors[sh].y;
340
341 /* check that the shopkeeper placement is sane */
342 if(sroom->irregular) {
343 int rmno = (sroom - rooms) + ROOMOFFSET;
344 if (isok(sx-1,sy) && !levl[sx-1][sy].edge &&
345 (int) levl[sx-1][sy].roomno == rmno) sx--;
346 else if (isok(sx+1,sy) && !levl[sx+1][sy].edge &&
347 (int) levl[sx+1][sy].roomno == rmno) sx++;
348 else if (isok(sx,sy-1) && !levl[sx][sy-1].edge &&
349 (int) levl[sx][sy-1].roomno == rmno) sy--;
350 else if (isok(sx,sy+1) && !levl[sx][sy+1].edge &&
351 (int) levl[sx][sy+1].roomno == rmno) sx++;
352 else goto shk_failed;
353 }
354 else if(sx == sroom->lx-1) sx++;
355 else if(sx == sroom->hx+1) sx--;
356 else if(sy == sroom->ly-1) sy++;
357 else if(sy == sroom->hy+1) sy--; else {
358 shk_failed:
359 #ifdef DEBUG
360 # ifdef WIZARD
361 /* Said to happen sometimes, but I have never seen it. */
362 /* Supposedly fixed by fdoor change in mklev.c */
363 if(wizard) {
364 register int j = sroom->doorct;
365
366 pline("Where is shopdoor?");
367 pline("Room at (%d,%d),(%d,%d).",
368 sroom->lx, sroom->ly, sroom->hx, sroom->hy);
369 pline("doormax=%d doorct=%d fdoor=%d",
370 doorindex, sroom->doorct, sh);
371 while(j--) {
372 pline("door [%d,%d]", doors[sh].x, doors[sh].y);
373 sh++;
374 }
375 display_nhwindow(WIN_MESSAGE, FALSE);
376 }
377 # endif
378 #endif
379 return(-1);
380 }
381
382 if(MON_AT(sx, sy)) rloc(m_at(sx, sy)); /* insurance */
383
384 /* now initialize the shopkeeper monster structure */
385 if(!(shk = makemon(&mons[PM_SHOPKEEPER], sx, sy, NO_MM_FLAGS)))
386 return(-1);
387 shk->isshk = shk->mpeaceful = 1;
388 set_malign(shk);
389 shk->msleeping = 0;
390 shk->mtrapseen = ~0; /* we know all the traps already */
391 ESHK(shk)->shoproom = (sroom - rooms) + ROOMOFFSET;
392 sroom->resident = shk;
393 ESHK(shk)->shoptype = sroom->rtype;
394 assign_level(&(ESHK(shk)->shoplevel), &u.uz);
395 ESHK(shk)->shd = doors[sh];
396 ESHK(shk)->shk.x = sx;
397 ESHK(shk)->shk.y = sy;
398 ESHK(shk)->robbed = 0L;
399 ESHK(shk)->credit = 0L;
400 ESHK(shk)->debit = 0L;
401 ESHK(shk)->loan = 0L;
402 ESHK(shk)->visitct = 0;
403 ESHK(shk)->following = 0;
404 ESHK(shk)->billct = 0;
405 shk->mgold = 1000L + 30L*(long)rnd(100); /* initial capital */
406 nameshk(shk, shp->shknms);
407
408 return(sh);
409 }
410
411 /* stock a newly-created room with objects */
412 void
stock_room(shp_indx,sroom)413 stock_room(shp_indx, sroom)
414 int shp_indx;
415 register struct mkroom *sroom;
416 {
417 /*
418 * Someday soon we'll dispatch on the shdist field of shclass to do
419 * different placements in this routine. Currently it only supports
420 * shop-style placement (all squares except a row nearest the first
421 * door get objects).
422 */
423 register int sx, sy, sh;
424 char buf[BUFSZ];
425 int rmno = (sroom - rooms) + ROOMOFFSET;
426 const struct shclass *shp = &shtypes[shp_indx];
427
428 /* first, try to place a shopkeeper in the room */
429 if ((sh = shkinit(shp, sroom)) < 0)
430 return;
431
432 /* make sure no doorways without doors, and no */
433 /* trapped doors, in shops. */
434 sx = doors[sroom->fdoor].x;
435 sy = doors[sroom->fdoor].y;
436
437 if(levl[sx][sy].doormask == D_NODOOR) {
438 levl[sx][sy].doormask = D_ISOPEN;
439 newsym(sx,sy);
440 }
441 if(levl[sx][sy].typ == SDOOR) {
442 cvt_sdoor_to_door(&levl[sx][sy]); /* .typ = DOOR */
443 newsym(sx,sy);
444 }
445 if(levl[sx][sy].doormask & D_TRAPPED)
446 levl[sx][sy].doormask = D_LOCKED;
447
448 if(levl[sx][sy].doormask == D_LOCKED) {
449 register int m = sx, n = sy;
450
451 if(inside_shop(sx+1,sy)) m--;
452 else if(inside_shop(sx-1,sy)) m++;
453 if(inside_shop(sx,sy+1)) n--;
454 else if(inside_shop(sx,sy-1)) n++;
455 Sprintf(buf, "Closed for inventory");
456 make_engr_at(m, n, buf, 0L, DUST);
457 }
458
459 for(sx = sroom->lx; sx <= sroom->hx; sx++)
460 for(sy = sroom->ly; sy <= sroom->hy; sy++) {
461 if(sroom->irregular) {
462 if (levl[sx][sy].edge || (int) levl[sx][sy].roomno != rmno ||
463 distmin(sx, sy, doors[sh].x, doors[sh].y) <= 1)
464 continue;
465 } else if((sx == sroom->lx && doors[sh].x == sx-1) ||
466 (sx == sroom->hx && doors[sh].x == sx+1) ||
467 (sy == sroom->ly && doors[sh].y == sy-1) ||
468 (sy == sroom->hy && doors[sh].y == sy+1)) continue;
469 mkshobj_at(shp, sx, sy);
470 }
471
472 /*
473 * Special monster placements (if any) should go here: that way,
474 * monsters will sit on top of objects and not the other way around.
475 */
476
477 level.flags.has_shop = TRUE;
478 }
479
480 #endif /* OVLB */
481 #ifdef OVL0
482
483 /* does shkp's shop stock this item type? */
484 boolean
saleable(shkp,obj)485 saleable(shkp, obj)
486 struct monst *shkp;
487 struct obj *obj;
488 {
489 int i, shp_indx = ESHK(shkp)->shoptype - SHOPBASE;
490 const struct shclass *shp = &shtypes[shp_indx];
491
492 if (shp->symb == RANDOM_CLASS) return TRUE;
493 else for (i = 0; i < SIZE(shtypes[0].iprobs) && shp->iprobs[i].iprob; i++)
494 if (shp->iprobs[i].itype < 0 ?
495 shp->iprobs[i].itype == - obj->otyp :
496 shp->iprobs[i].itype == obj->oclass) return TRUE;
497 /* not found */
498 return FALSE;
499 }
500
501 /* positive value: class; negative value: specific object type */
502 int
get_shop_item(type)503 get_shop_item(type)
504 int type;
505 {
506 const struct shclass *shp = shtypes+type;
507 register int i,j;
508
509 /* select an appropriate object type at random */
510 for(j = rnd(100), i = 0; (j -= shp->iprobs[i].iprob) > 0; i++)
511 continue;
512
513 return shp->iprobs[i].itype;
514 }
515
516 #endif /* OVL0 */
517
518 /*shknam.c*/
519