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