billr@saab.CNA.TEK.COM (Bill Randle) (07/12/90)
Submitted-by: Izchak Miller <izchak@linc.cis.upenn.edu> Posting-number: Volume 10, Issue 54 Archive-name: nethack3p9/Part09 Supersedes: NetHack3: Volume 7, Issue 56-93 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 9 (of 56)." # Contents: others/random.c src/shk.c # Wrapped by billr@saab on Wed Jul 11 17:11:02 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'others/random.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'others/random.c'\" else echo shar: Extracting \"'others/random.c'\" \(13466 characters\) sed "s/^X//" >'others/random.c' <<'END_OF_FILE' X/* X * Copyright (c) 1983 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that the above copyright notice and this paragraph are X * duplicated in all such forms and that any documentation, X * advertising materials, and other materials related to such X * distribution and use acknowledge that the software was developed X * by the University of California, Berkeley. The name of the X * University may not be used to endorse or promote products derived X * from this software without specific prior written permission. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X */ X X/* Several minor changes were made for the NetHack distribution to satisfy X * non-BSD compilers (by definition BSD compilers do not need to compile X * this file for NetHack). These changes consisted of: X * - changing the sccsid conditions to nested ifdefs from defined()s X * to accommodate stupid preprocessors X * - giving srandom() type void instead of allowing it to default to int X * - making the first return in initstate() return a value consistent X * with its type (instead of no value) X * - ANSI function prototyping in extern.h - therefore include hack.h X * instead of stdio.h and remove separate declaration of random() from X * the beginning of function srandom X */ X X#ifdef LIBC_SCCS X# ifndef lint Xstatic char sccsid[] = "@(#)random.c 5.5 (Berkeley) 7/6/88"; X# endif X#endif /* LIBC_SCCS and not lint */ X X#include "hack.h" X X/* X * random.c: X * An improved random number generation package. In addition to the standard X * rand()/srand() like interface, this package also has a special state info X * interface. The initstate() routine is called with a seed, an array of X * bytes, and a count of how many bytes are being passed in; this array is then X * initialized to contain information for random number generation with that X * much state information. Good sizes for the amount of state information are X * 32, 64, 128, and 256 bytes. The state can be switched by calling the X * setstate() routine with the same array as was initiallized with initstate(). X * By default, the package runs with 128 bytes of state information and X * generates far better random numbers than a linear congruential generator. X * If the amount of state information is less than 32 bytes, a simple linear X * congruential R.N.G. is used. X * Internally, the state information is treated as an array of longs; the X * zeroeth element of the array is the type of R.N.G. being used (small X * integer); the remainder of the array is the state information for the X * R.N.G. Thus, 32 bytes of state information will give 7 longs worth of X * state information, which will allow a degree seven polynomial. (Note: the X * zeroeth word of state information also has some other information stored X * in it -- see setstate() for details). X * The random number generation technique is a linear feedback shift register X * approach, employing trinomials (since there are fewer terms to sum up that X * way). In this approach, the least significant bit of all the numbers in X * the state table will act as a linear feedback shift register, and will have X * period 2^deg - 1 (where deg is the degree of the polynomial being used, X * assuming that the polynomial is irreducible and primitive). The higher X * order bits will have longer periods, since their values are also influenced X * by pseudo-random carries out of the lower bits. The total period of the X * generator is approximately deg*(2**deg - 1); thus doubling the amount of X * state information has a vast influence on the period of the generator. X * Note: the deg*(2**deg - 1) is an approximation only good for large deg, X * when the period of the shift register is the dominant factor. With deg X * equal to seven, the period is actually much longer than the 7*(2**7 - 1) X * predicted by this formula. X */ X X X X/* X * For each of the currently supported random number generators, we have a X * break value on the amount of state information (you need at least this X * many bytes of state info to support this random number generator), a degree X * for the polynomial (actually a trinomial) that the R.N.G. is based on, and X * the separation between the two lower order coefficients of the trinomial. X */ X X#define TYPE_0 0 /* linear congruential */ X#define BREAK_0 8 X#define DEG_0 0 X#define SEP_0 0 X X#define TYPE_1 1 /* x**7 + x**3 + 1 */ X#define BREAK_1 32 X#define DEG_1 7 X#define SEP_1 3 X X#define TYPE_2 2 /* x**15 + x + 1 */ X#define BREAK_2 64 X#define DEG_2 15 X#define SEP_2 1 X X#define TYPE_3 3 /* x**31 + x**3 + 1 */ X#define BREAK_3 128 X#define DEG_3 31 X#define SEP_3 3 X X#define TYPE_4 4 /* x**63 + x + 1 */ X#define BREAK_4 256 X#define DEG_4 63 X#define SEP_4 1 X X X/* X * Array versions of the above information to make code run faster -- relies X * on fact that TYPE_i == i. X */ X X#define MAX_TYPES 5 /* max number of types above */ X Xstatic int degrees[ MAX_TYPES ] = { DEG_0, DEG_1, DEG_2, X DEG_3, DEG_4 }; X Xstatic int seps[ MAX_TYPES ] = { SEP_0, SEP_1, SEP_2, X SEP_3, SEP_4 }; X X X X/* X * Initially, everything is set up as if from : X * initstate( 1, &randtbl, 128 ); X * Note that this initialization takes advantage of the fact that srandom() X * advances the front and rear pointers 10*rand_deg times, and hence the X * rear pointer which starts at 0 will also end up at zero; thus the zeroeth X * element of the state information, which contains info about the current X * position of the rear pointer is just X * MAX_TYPES*(rptr - state) + TYPE_3 == TYPE_3. X */ X Xstatic long randtbl[ DEG_3 + 1 ] = { TYPE_3, X 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, X 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb, X 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd, X 0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, X 0xda672e2a, 0x1588ca88, 0xe369735d, 0x904f35f7, X 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, X 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, X 0xf5ad9d0e, 0x8999220b, 0x27fb47b9 }; X X/* X * fptr and rptr are two pointers into the state info, a front and a rear X * pointer. These two pointers are always rand_sep places aparts, as they cycle X * cyclically through the state information. (Yes, this does mean we could get X * away with just one pointer, but the code for random() is more efficient this X * way). The pointers are left positioned as they would be from the call X * initstate( 1, randtbl, 128 ) X * (The position of the rear pointer, rptr, is really 0 (as explained above X * in the initialization of randtbl) because the state table pointer is set X * to point to randtbl[1] (as explained below). X */ X Xstatic long *fptr = &randtbl[ SEP_3 + 1 ]; Xstatic long *rptr = &randtbl[ 1 ]; X X X X/* X * The following things are the pointer to the state information table, X * the type of the current generator, the degree of the current polynomial X * being used, and the separation between the two pointers. X * Note that for efficiency of random(), we remember the first location of X * the state information, not the zeroeth. Hence it is valid to access X * state[-1], which is used to store the type of the R.N.G. X * Also, we remember the last location, since this is more efficient than X * indexing every time to find the address of the last element to see if X * the front and rear pointers have wrapped. X */ X Xstatic long *state = &randtbl[ 1 ]; X Xstatic int rand_type = TYPE_3; Xstatic int rand_deg = DEG_3; Xstatic int rand_sep = SEP_3; X Xstatic long *end_ptr = &randtbl[ DEG_3 + 1 ]; X X X X/* X * srandom: X * Initialize the random number generator based on the given seed. If the X * type is the trivial no-state-information type, just remember the seed. X * Otherwise, initializes state[] based on the given "seed" via a linear X * congruential generator. Then, the pointers are set to known locations X * that are exactly rand_sep places apart. Lastly, it cycles the state X * information a given number of times to get rid of any initial dependencies X * introduced by the L.C.R.N.G. X * Note that the initialization of randtbl[] for default usage relies on X * values produced by this routine. X */ X Xvoid Xsrandom( x ) X X unsigned x; X{ X register int i, j; X X if( rand_type == TYPE_0 ) { X state[ 0 ] = x; X } X else { X j = 1; X state[ 0 ] = x; X for( i = 1; i < rand_deg; i++ ) { X state[i] = 1103515245*state[i - 1] + 12345; X } X fptr = &state[ rand_sep ]; X rptr = &state[ 0 ]; X for( i = 0; i < 10*rand_deg; i++ ) random(); X } X} X X X X/* X * initstate: X * Initialize the state information in the given array of n bytes for X * future random number generation. Based on the number of bytes we X * are given, and the break values for the different R.N.G.'s, we choose X * the best (largest) one we can and set things up for it. srandom() is X * then called to initialize the state information. X * Note that on return from srandom(), we set state[-1] to be the type X * multiplexed with the current value of the rear pointer; this is so X * successive calls to initstate() won't lose this information and will X * be able to restart with setstate(). X * Note: the first thing we do is save the current state, if any, just like X * setstate() so that it doesn't matter when initstate is called. X * Returns a pointer to the old state. X */ X Xchar * Xinitstate( seed, arg_state, n ) X X unsigned seed; /* seed for R. N. G. */ X char *arg_state; /* pointer to state array */ X int n; /* # bytes of state info */ X{ X register char *ostate = (char *)( &state[ -1 ] ); X X if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; X else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; X if( n < BREAK_1 ) { X if( n < BREAK_0 ) { X fprintf( stderr, "initstate: not enough state (%d bytes) with which to do jack; ignored.\n", n ); X return (char *)0; X } X rand_type = TYPE_0; X rand_deg = DEG_0; X rand_sep = SEP_0; X } X else { X if( n < BREAK_2 ) { X rand_type = TYPE_1; X rand_deg = DEG_1; X rand_sep = SEP_1; X } X else { X if( n < BREAK_3 ) { X rand_type = TYPE_2; X rand_deg = DEG_2; X rand_sep = SEP_2; X } X else { X if( n < BREAK_4 ) { X rand_type = TYPE_3; X rand_deg = DEG_3; X rand_sep = SEP_3; X } X else { X rand_type = TYPE_4; X rand_deg = DEG_4; X rand_sep = SEP_4; X } X } X } X } X state = &( ( (long *)arg_state )[1] ); /* first location */ X end_ptr = &state[ rand_deg ]; /* must set end_ptr before srandom */ X srandom( seed ); X if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; X else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; X return( ostate ); X} X X X X/* X * setstate: X * Restore the state from the given state array. X * Note: it is important that we also remember the locations of the pointers X * in the current state information, and restore the locations of the pointers X * from the old state information. This is done by multiplexing the pointer X * location into the zeroeth word of the state information. X * Note that due to the order in which things are done, it is OK to call X * setstate() with the same state as the current state. X * Returns a pointer to the old state information. X */ X Xchar * Xsetstate( arg_state ) X X char *arg_state; X{ X register long *new_state = (long *)arg_state; X register int type = new_state[0]%MAX_TYPES; X register int rear = new_state[0]/MAX_TYPES; X char *ostate = (char *)( &state[ -1 ] ); X X if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; X else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; X switch( type ) { X case TYPE_0: X case TYPE_1: X case TYPE_2: X case TYPE_3: X case TYPE_4: X rand_type = type; X rand_deg = degrees[ type ]; X rand_sep = seps[ type ]; X break; X X default: X fprintf( stderr, "setstate: state info has been munged; not changed.\n" ); X } X state = &new_state[ 1 ]; X if( rand_type != TYPE_0 ) { X rptr = &state[ rear ]; X fptr = &state[ (rear + rand_sep)%rand_deg ]; X } X end_ptr = &state[ rand_deg ]; /* set end_ptr too */ X return( ostate ); X} X X X X/* X * random: X * If we are using the trivial TYPE_0 R.N.G., just do the old linear X * congruential bit. Otherwise, we do our fancy trinomial stuff, which is the X * same in all ther other cases due to all the global variables that have been X * set up. The basic operation is to add the number at the rear pointer into X * the one at the front pointer. Then both pointers are advanced to the next X * location cyclically in the table. The value returned is the sum generated, X * reduced to 31 bits by throwing away the "least random" low bit. X * Note: the code takes advantage of the fact that both the front and X * rear pointers can't wrap on the same call by not testing the rear X * pointer if the front one has wrapped. X * Returns a 31-bit random number. X */ X Xlong Xrandom() X{ X long i; X X if( rand_type == TYPE_0 ) { X i = state[0] = ( state[0]*1103515245 + 12345 )&0x7fffffff; X } X else { X *fptr += *rptr; X i = (*fptr >> 1)&0x7fffffff; /* chucking least random bit */ X if( ++fptr >= end_ptr ) { X fptr = state; X ++rptr; X } X else { X if( ++rptr >= end_ptr ) rptr = state; X } X } X return( i ); X} X END_OF_FILE if test 13466 -ne `wc -c <'others/random.c'`; then echo shar: \"'others/random.c'\" unpacked with wrong size! fi # end of 'others/random.c' fi if test -f 'src/shk.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/shk.c'\" else echo shar: Extracting \"'src/shk.c'\" \(42124 characters\) sed "s/^X//" >'src/shk.c' <<'END_OF_FILE' X/* SCCS Id: @(#)shk.c 3.0 89/11/27 X/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ X/* NetHack may be freely redistributed. See license for details. */ X X#define MONATTK_H /* comment line for pre-compiled headers */ X/* block some unused #defines to avoid overloading some cpp's */ X#include "hack.h" X X#include "eshk.h" X X X# ifdef KOPS XSTATIC_DCL int FDECL(makekops, (coord *)); X# ifdef OVLB Xstatic void NDECL(kops_gone); X# endif /* OVLB */ X#endif /* KOPS */ X X#define NOTANGRY(mon) mon->mpeaceful X#define ANGRY(mon) !NOTANGRY(mon) X X/* Descriptor of current shopkeeper. Note that the bill need not be X per-shopkeeper, since it is valid only when in a shop. */ XSTATIC_VAR struct monst NEARDATA *shopkeeper; XSTATIC_VAR struct bill_x NEARDATA *bill; XSTATIC_VAR int NEARDATA shlevel; /* level of this shopkeeper */ X/* struct obj *billobjs; /* objects on bill with bp->useup */ X /* only accessed here and by save & restore */ XSTATIC_VAR long int NEARDATA total; /* filled by addupbill() */ XSTATIC_VAR long int NEARDATA followmsg; /* last time of follow message */ X XSTATIC_DCL void NDECL(setpaid); XSTATIC_DCL void NDECL(addupbill); XSTATIC_DCL boolean FDECL(monstinroom, (struct permonst *,int)); XSTATIC_DCL void FDECL(findshk, (int)); X X#ifdef OVLB X Xstatic struct bill_x * FDECL(onbill, (struct obj *)); Xstatic long FDECL(check_credit, (long,struct monst *)); Xstatic void FDECL(pay, (long,struct monst *)); Xstatic unsigned FDECL(get_cost, (struct obj *)); Xstatic unsigned FDECL(cost_per_charge, (struct obj *)); Xstatic int FDECL(dopayobj, (struct bill_x *)), FDECL(getprice, (struct obj *)); Xstatic struct obj *FDECL(bp_to_obj, (struct bill_x *)); X X/* X invariants: obj->unpaid iff onbill(obj) [unless bp->useup] X obj->quan <= bp->bquan X */ X Xchar * Xshkname(mtmp) /* called in do_name.c */ Xregister struct monst *mtmp; X{ X return(ESHK(mtmp)->shknam); X} X Xvoid Xshkdead(mtmp) /* called in mon.c */ Xregister struct monst *mtmp; X{ X register struct eshk *eshk = ESHK(mtmp); X X if(eshk->shoplevel == dlevel) X rooms[eshk->shoproom].rtype = OROOM; X if(mtmp == shopkeeper) { X setpaid(); X shopkeeper = 0; X bill = (struct bill_x *) -1000; /* dump core when referenced */ X } X} X Xvoid Xreplshk(mtmp,mtmp2) Xregister struct monst *mtmp, *mtmp2; X{ X if(mtmp == shopkeeper) { X shopkeeper = mtmp2; X bill = &(ESHK(shopkeeper)->bill[0]); X } X} X XSTATIC_OVL void Xsetpaid(){ /* caller has checked that shopkeeper exists */ X /* either we paid or left the shop or he just died */ X register struct obj *obj; X register struct monst *mtmp; X for(obj = invent; obj; obj = obj->nobj) X obj->unpaid = 0; X for(obj = fobj; obj; obj = obj->nobj) X obj->unpaid = 0; X for(obj = fcobj; obj; obj = obj->nobj) X obj->unpaid = 0; X for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) X for(obj = mtmp->minvent; obj; obj = obj->nobj) X obj->unpaid = 0; X for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon) X for(obj = mtmp->minvent; obj; obj = obj->nobj) X obj->unpaid = 0; X while(obj = billobjs){ X billobjs = obj->nobj; X free((genericptr_t) obj); X } X if(shopkeeper) { X ESHK(shopkeeper)->billct = 0; X ESHK(shopkeeper)->credit = 0L; X ESHK(shopkeeper)->debit = 0L; X } X} X XSTATIC_OVL void Xaddupbill(){ /* delivers result in total */ X /* caller has checked that shopkeeper exists */ X register int ct = ESHK(shopkeeper)->billct; X register struct bill_x *bp = bill; X total = 0; X while(ct--){ X total += bp->price * bp->bquan; X bp++; X } X} X XSTATIC_OVL boolean Xmonstinroom(mdat,roomno) Xstruct permonst *mdat; Xint roomno; X{ X register struct monst *mtmp; X X for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) X if(mtmp->data == mdat && inroom(mtmp->mx,mtmp->my) == roomno) X return(TRUE); X return(FALSE); X} X X#endif /* OVLB */ X#ifdef OVL1 X Xint Xinshop() { X register int roomno = inroom(u.ux,u.uy); X X /* Did we just leave a shop? */ X if(u.uinshop && X (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { X X /* This is part of the bugfix for shopkeepers not having their X * bill paid. As reported by ab@unido -dgk X * I made this standard due to the KOPS code below. -mrs X */ X if(shopkeeper) { X if(ESHK(shopkeeper)->billct || ESHK(shopkeeper)->debit) { X if(inroom(shopkeeper->mx, shopkeeper->my) X == u.uinshop - 1) /* ab@unido */ X You("escaped the shop without paying!"); X addupbill(); X total += ESHK(shopkeeper)->debit; X You("stole %ld zorkmid%s worth of merchandise.", X total, plur(total)); X ESHK(shopkeeper)->robbed += total; X ESHK(shopkeeper)->credit = 0L; X ESHK(shopkeeper)->debit = 0L; X if (pl_character[0] != 'R') /* stealing is unlawful */ X adjalign(-sgn(u.ualigntyp)); X setpaid(); X if((rooms[ESHK(shopkeeper)->shoproom].rtype == SHOPBASE) X == (rn2(3) == 0)) X ESHK(shopkeeper)->following = 1; X#ifdef KOPS X { /* Keystone Kops srt@ucla */ X coord mm; X X if (flags.soundok) X pline("An alarm sounds throughout the dungeon!"); X if(flags.verbose) { X if((mons[PM_KEYSTONE_KOP].geno & G_GENOD) && X (mons[PM_KOP_SERGEANT].geno & G_GENOD) && X (mons[PM_KOP_LIEUTENANT].geno & G_GENOD) && X (mons[PM_KOP_KAPTAIN].geno & G_GENOD)) { X if (flags.soundok) X pline("But no one seems to respond to it."); X } else X pline("The Keystone Kops are after you!"); X } X /* Create a swarm near the staircase */ X mm.x = xdnstair; X mm.y = ydnstair; X (void) makekops(&mm); X /* Create a swarm near the shopkeeper */ X mm.x = shopkeeper->mx; X mm.y = shopkeeper->my; X (void) makekops(&mm); X } X#endif X } X shopkeeper = 0; X shlevel = 0; X } X u.uinshop = 0; X } X X /* Did we just enter a zoo of some kind? */ X /* This counts everything except shops and vaults X -- vault.c insists that a vault remain a VAULT */ X if(roomno >= 0) { X register int rt = rooms[roomno].rtype; X register struct monst *mtmp; X X switch (rt) { X case ZOO: X pline("Welcome to David's treasure zoo!"); X break; X case SWAMP: X pline("It looks rather muddy down here."); X break; X#ifdef THRONES X case COURT: X You("enter an opulent throne room!"); X break; X#endif X case MORGUE: X if(midnight()) X pline("Run away! Run away!"); X else X You("have an uncanny feeling..."); X break; X case BEEHIVE: X You("enter a giant beehive!"); X break; X#ifdef ARMY X case BARRACKS: X if(monstinroom(&mons[PM_SOLDIER], roomno) || X monstinroom(&mons[PM_SERGEANT], roomno) || X monstinroom(&mons[PM_LIEUTENANT], roomno) || X monstinroom(&mons[PM_CAPTAIN], roomno)) X You("enter a military barracks!"); X else You("enter an abandoned barracks."); X break; X#endif X#ifdef ORACLE X case DELPHI: X if(monstinroom(&mons[PM_ORACLE], roomno)) X pline("\"Hello, %s, welcome to Delphi!\"", plname); X break; X#endif X default: X rt = 0; X } X X if(rt != 0) { X rooms[roomno].rtype = OROOM; X if(rt==COURT || rt==SWAMP || rt==MORGUE || rt==ZOO) X for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) X /* was if(rt != ZOO || !rn2(3)) -- why should ZOO X be different from COURT or MORGUE? */ X if(!Stealth && !rn2(3)) X mtmp->msleep = 0; X } X } X#if defined(ALTARS) && defined(THEOLOGY) X if(roomno >= 0 && rooms[roomno].rtype == TEMPLE) { X intemple(); X } X#endif X /* Did we just enter a shop? */ X if(roomno >= 0 && rooms[roomno].rtype >= SHOPBASE) { X register int rt = rooms[roomno].rtype; X X if(shlevel != dlevel || !shopkeeper X || ESHK(shopkeeper)->shoproom != roomno) X findshk(roomno); X if(!shopkeeper) { X rooms[roomno].rtype = OROOM; X u.uinshop = 0; X } else if(!u.uinshop){ X if(!ESHK(shopkeeper)->visitct || X strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ)) { X /* He seems to be new here */ X ESHK(shopkeeper)->visitct = 0; X ESHK(shopkeeper)->following = 0; X (void) strncpy(ESHK(shopkeeper)->customer,plname,PL_NSIZ); X NOTANGRY(shopkeeper) = 1; X } X if(!ESHK(shopkeeper)->following && inhishop(shopkeeper)) { X if(Invis) { X pline("%s senses your presence.", shkname(shopkeeper)); X verbalize("Invisible customers are not welcome!"); X } X else X if(ANGRY(shopkeeper)) X pline("\"So, %s, you dare return to %s's %s?!\"", X plname, X shkname(shopkeeper), X shtypes[rt - SHOPBASE].name); X else X if(ESHK(shopkeeper)->robbed) X pline("\"Beware, %s! I am upset about missing stock!\"", X plname); X else X pline("\"Hello, %s! Welcome%s to %s's %s!\"", X plname, X ESHK(shopkeeper)->visitct++ ? " again" : "", X shkname(shopkeeper), X shtypes[rt - SHOPBASE].name); X if(carrying(PICK_AXE) != (struct obj *)0 && !Invis) { X verbalize(NOTANGRY(shopkeeper) ? X "Will you please leave your pick-axe outside?" : X "Leave the pick-axe outside."); X if(dochug(shopkeeper)) { X u.uinshop = 0; /* he died moving */ X return(0); X } X } X } X u.uinshop = (unsigned int)(roomno + 1); X } X } X return (int)u.uinshop; X} X X#endif /* OVL1 */ X#ifdef OVLB X Xint Xinhishop(mtmp) Xregister struct monst *mtmp; X{ X return((ESHK(mtmp)->shoproom == inroom(mtmp->mx, mtmp->my) && X ESHK(mtmp)->shoplevel == dlevel)); X} X X#ifdef SOUNDS Xboolean Xtended_shop(sroom) Xstruct mkroom *sroom; X{ X register struct monst *mtmp; X X for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) X if(mtmp->isshk && &rooms[ESHK(mtmp)->shoproom] == sroom X && inhishop(mtmp)) return(TRUE); X return(FALSE); X} X#endif X XSTATIC_OVL void Xfindshk(roomno) Xregister int roomno; X{ X register struct monst *mtmp; X X for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) X if(mtmp->isshk && ESHK(mtmp)->shoproom == roomno X && ESHK(mtmp)->shoplevel == dlevel) { X shopkeeper = mtmp; X bill = &(ESHK(shopkeeper)->bill[0]); X shlevel = dlevel; X if(ANGRY(shopkeeper) && X strncmp(ESHK(shopkeeper)->customer,plname,PL_NSIZ)) X NOTANGRY(shopkeeper) = 1; X /* billobjs = 0; -- this is wrong if we save in a shop */ X /* (and it is harmless to have too many things in billobjs) */ X return; X } X shopkeeper = 0; X shlevel = 0; X bill = (struct bill_x *) -1000; /* dump core when referenced */ X} X Xstatic struct bill_x * Xonbill(obj) Xregister struct obj *obj; X{ X register struct bill_x *bp; X if(!shopkeeper) return (struct bill_x *)0; X for(bp = bill; bp < &bill[ESHK(shopkeeper)->billct]; bp++) X if(bp->bo_id == obj->o_id) { X if(!obj->unpaid) pline("onbill: paid obj on bill?"); X return(bp); X } X if(obj->unpaid) pline("onbill: unpaid obj not on bill?"); X return (struct bill_x *)0; X} X X/* called with two args on merge */ Xvoid Xobfree(obj, merge) Xregister struct obj *obj, *merge; X{ X register struct bill_x *bp = onbill(obj); X register struct bill_x *bpm; X if(bp) { X if(!merge){ X bp->useup = 1; X obj->unpaid = 0; /* only for doinvbill */ X obj->nobj = billobjs; X billobjs = obj; X return; X } X bpm = onbill(merge); X if(!bpm){ X /* this used to be a rename */ X impossible("obfree: not on bill??"); X return; X } else { X /* this was a merger */ X bpm->bquan += bp->bquan; X ESHK(shopkeeper)->billct--; X *bp = bill[ESHK(shopkeeper)->billct]; X } X } X free((genericptr_t) obj); X} X Xstatic long Xcheck_credit(tmp, shkp) Xlong tmp; Xregister struct monst *shkp; X{ X long credit = ESHK(shkp)->credit; X X if(credit == 0L) return(tmp); X if(credit >= tmp) { X pline("The price is deducted from your credit."); X ESHK(shkp)->credit -=tmp; X tmp = 0L; X } else { X pline("The price is partially covered by your credit."); X ESHK(shkp)->credit = 0L; X tmp -= credit; X } X return(tmp); X} X Xstatic void Xpay(tmp,shkp) Xlong tmp; Xregister struct monst *shkp; X{ X long robbed = ESHK(shkp)->robbed; X long balance = ((tmp <= 0L) ? tmp : check_credit(tmp, shkp)); X X u.ugold -= balance; X shkp->mgold += balance; X flags.botl = 1; X if(robbed) { X robbed -= tmp; X if(robbed < 0) robbed = 0L; X ESHK(shkp)->robbed = robbed; X } X} X X/* return shkp to home position */ Xvoid Xhome_shk(shkp) Xregister struct monst *shkp; X{ X register xchar x = ESHK(shkp)->shk.x, y = ESHK(shkp)->shk.y; X if(MON_AT(x, y)) X mnearto(m_at(x,y), x, y, FALSE); X remove_monster(shkp->mx, shkp->my); X place_monster(shkp, x, y); X unpmon(shkp); X} X Xvoid Xmake_happy_shk(shkp) Xstruct monst *shkp; X{ X register boolean wasmad = ANGRY(shkp); X X NOTANGRY(shkp) = 1; X ESHK(shkp)->following = 0; X ESHK(shkp)->robbed = 0L; X if (pl_character[0] != 'R') X adjalign(sgn(u.ualigntyp)); X if(!inhishop(shkp)) { X pline("Satisfied, %s suddenly disappears!", mon_nam(shkp)); X if(ESHK(shkp)->shoplevel == dlevel) X home_shk(shkp); X else X fall_down(shkp, ESHK(shkp)->shoplevel); X } else if(wasmad) X pline("%s calms down.", Monnam(shkp)); X#ifdef KOPS X kops_gone(); X#endif X} X Xstatic const char no_money[] = "Moreover, you have no money."; X Xint Xdopay() X{ X long ltmp; X register struct bill_x *bp; X register struct monst *shkp; X int pass, tmp; X X multi = 0; X (void) inshop(); X for(shkp = fmon; shkp; shkp = shkp->nmon) X if(shkp->isshk && dist(shkp->mx,shkp->my) < 3) X break; X if(!shkp && u.uinshop && inhishop(shopkeeper)) X shkp = shopkeeper; X X if(!shkp) { X pline("There is nobody here to receive your payment."); X return(0); X } X ltmp = ESHK(shkp)->robbed; X if(shkp != shopkeeper && NOTANGRY(shkp)) { X if(!ltmp) X You("do not owe %s anything.", mon_nam(shkp)); X else if(!u.ugold) X You("have no money."); X else { X long ugold = u.ugold; X X if(ugold > ltmp) { X You("give %s the %ld gold piece%s %s asked for.", X mon_nam(shkp), ltmp, plur(ltmp), X ESHK(shkp)->ismale ? "he" : "she"); X pay(ltmp, shkp); X } else { X You("give %s all your gold.", mon_nam(shkp)); X pay(u.ugold, shkp); X } X if(ugold < ltmp/2L) X pline("Unfortunately, %s doesn't look satisfied.", X ESHK(shkp)->ismale ? "he" : "she"); X else X make_happy_shk(shkp); X } X return(1); X } X X /* ltmp is still ESHK(shkp)->robbed here */ X if(!ESHK(shkp)->billct && !ESHK(shkp)->debit) { X if(!ltmp && NOTANGRY(shkp)) { X You("do not owe %s anything.", mon_nam(shkp)); X if(!u.ugold) pline(no_money); X } else if(ltmp) { X pline("%s is after blood, not money!", mon_nam(shkp)); X if(u.ugold < ltmp/2L) { X if(!u.ugold) pline(no_money); X else pline("Besides, you don't have enough to interest %s.", X ESHK(shkp)->ismale ? "him" : "her"); X return(1); X } X pline("But since %s shop has been robbed recently,", X ESHK(shkp)->ismale ? "his" : "her"); X pline("you %scompensate %s for %s losses.", X (u.ugold < ltmp) ? "partially " : "", X mon_nam(shkp), X ESHK(shkp)->ismale ? "his" : "her"); X pay(u.ugold < ltmp ? u.ugold : ltmp, shkp); X make_happy_shk(shkp); X } else { X /* shopkeeper is angry, but has not been robbed -- X * door broken, attacked, etc. */ X pline("%s is after your hide, not your money!", X mon_nam(shkp)); X if(u.ugold < 1000L) { X if(!u.ugold) pline(no_money); X else X pline("Besides, you don't have enough to interest %s.", X ESHK(shkp)->ismale ? "him" : "her"); X return(1); X } X You("try to appease %s by giving %s 1000 gold pieces.", X a_monnam(shkp, "angry"), X ESHK(shkp)->ismale ? "him" : "her"); X pay(1000L,shkp); X if(strncmp(ESHK(shkp)->customer, plname, PL_NSIZ) X || rn2(3)) X make_happy_shk(shkp); X else X pline("But %s is as angry as ever.", Monnam(shkp)); X } X return(1); X } X if(shkp != shopkeeper) { X impossible("dopay: not to shopkeeper?"); X if(shopkeeper) setpaid(); X return(0); X } X /* pay debt, if any, first */ X if(ESHK(shopkeeper)->debit) { X You("owe %s %ld zorkmid%s for the use of merchandise.", X shkname(shopkeeper), ESHK(shopkeeper)->debit, X plur(ESHK(shopkeeper)->debit)); X if(u.ugold + ESHK(shopkeeper)->credit < X ESHK(shopkeeper)->debit) { X pline("But you don't have enough gold%s.", X ESHK(shopkeeper)->credit ? " or credit" : ""); X return(1); X } else { X long dtmp = ESHK(shopkeeper)->debit; X X if(ESHK(shopkeeper)->credit >= dtmp) { X ESHK(shopkeeper)->credit -= dtmp; X ESHK(shopkeeper)->debit = 0L; X Your("debt is covered by your credit."); X } else if(!ESHK(shopkeeper)->credit) { X u.ugold -= dtmp; X shopkeeper->mgold += dtmp; X ESHK(shopkeeper)->debit = 0L; X You("pay that debt."); X flags.botl = 1; X } else { X dtmp -= ESHK(shopkeeper)->credit; X ESHK(shopkeeper)->credit = 0L; X u.ugold -= dtmp; X shopkeeper->mgold += dtmp; X ESHK(shopkeeper)->debit = 0L; X pline("That debt is partially offset by your credit."); X You("pay the remainder."); X flags.botl = 1; X } X } X } X for(pass = 0; pass <= 1; pass++) { X tmp = 0; X while(tmp < ESHK(shopkeeper)->billct) { X bp = &bill[tmp]; X if(!pass && !bp->useup) { X tmp++; X continue; X } X if(!dopayobj(bp)) return(1); X#ifdef MSDOS X *bp = bill[--ESHK(shopkeeper)->billct]; X#else X bill[tmp] = bill[--ESHK(shopkeeper)->billct]; X#endif /* MSDOS /**/ X } X } X if(!ANGRY(shopkeeper)) X pline("\"Thank you for shopping in %s's %s!\"", X shkname(shopkeeper), X shtypes[rooms[ESHK(shopkeeper)->shoproom].rtype - SHOPBASE].name); X return(1); X} X X/* return 1 if paid successfully */ X/* 0 if not enough money */ X/* -1 if object could not be found (but was paid) */ Xstatic int Xdopayobj(bp) Xregister struct bill_x *bp; X{ X register struct obj *obj; X long ltmp; X X /* find the object on one of the lists */ X obj = bp_to_obj(bp); X X if(!obj) { X impossible("Shopkeeper administration out of order."); X setpaid(); /* be nice to the player */ X return(0); X } X X if(!obj->unpaid && !bp->useup){ X impossible("Paid object on bill??"); X return(1); X } X obj->unpaid = 0; X ltmp = bp->price * bp->bquan; X if(ANGRY(shopkeeper)) ltmp += ltmp/3L; X if(u.ugold + ESHK(shopkeeper)->credit < ltmp){ X You("don't have gold%s enough to pay for %s.", X (ESHK(shopkeeper)->credit > 0L) ? " or credit" : "", X doname(obj)); X obj->unpaid = 1; X return(0); X } X pay(ltmp, shopkeeper); X You("bought %s for %ld gold piece%s.", X doname(obj), ltmp, plur(ltmp)); X if(bp->useup) { X register struct obj *otmp = billobjs; X if(obj == billobjs) X billobjs = obj->nobj; X else { X while(otmp && otmp->nobj != obj) otmp = otmp->nobj; X if(otmp) otmp->nobj = obj->nobj; X else pline("Error in shopkeeper administration."); X } X free((genericptr_t) obj); X } X return(1); X} X X/* routine called after dying (or quitting) with nonempty bill or upset shk */ Xboolean Xpaybill(){ X register struct monst *mtmp, *mtmp2; X register long loss = 0L; X register struct obj *otmp; X register xchar ox, oy; X register boolean take = FALSE; X register boolean taken = FALSE; X X for(mtmp = fmon; mtmp; mtmp = mtmp2) { X mtmp2 = mtmp->nmon; X if(mtmp->isshk) { X /* for bones: we don't want a shopless shk around */ X if(ESHK(mtmp)->shoplevel != dlevel) mongone(mtmp); X else shopkeeper = mtmp; X } X } X X if(!shopkeeper) goto clear; X X /* get one case out of the way: you die in the shop, the */ X /* shopkeeper is peaceful, nothing stolen, nothing owed. */ X if(in_shop(u.ux,u.uy) && !IS_DOOR(levl[u.ux][u.uy].typ) && X !ESHK(shopkeeper)->billct && !ESHK(shopkeeper)->robbed && X !ESHK(shopkeeper)->debit && inhishop(shopkeeper) && X NOTANGRY(shopkeeper) && !ESHK(shopkeeper)->following) { X pline("%s gratefully inherits all your possessions.", X Monnam(shopkeeper)); X goto clear; X } X X if(ESHK(shopkeeper)->billct || ESHK(shopkeeper)->debit || X ESHK(shopkeeper)->robbed) { X addupbill(); X total += ESHK(shopkeeper)->debit; X loss = ((total >= ESHK(shopkeeper)->robbed) ? total : X ESHK(shopkeeper)->robbed); X take = TRUE; X } X X if(ESHK(shopkeeper)->following || ANGRY(shopkeeper) || take) { X if((loss > u.ugold) || !loss) { X pline("%s %sand takes all your possessions.", X Monnam(shopkeeper), X (shopkeeper->msleep || shopkeeper->mfrozen) ? X "wakes up " : "comes "); X taken = TRUE; X shopkeeper->mgold += u.ugold; X u.ugold = 0L; X /* in case bones: make it be for real... */ X if(!in_shop(u.ux, u.uy) || IS_DOOR(levl[u.ux][u.uy].typ)) { X /* shk.x,shk.y is the position immediately in X * front of the door -- move in one more space X */ X ox = ESHK(shopkeeper)->shk.x; X oy = ESHK(shopkeeper)->shk.y; X ox += sgn(ox - ESHK(shopkeeper)->shd.x); X oy += sgn(oy - ESHK(shopkeeper)->shd.y); X } else { X ox = u.ux; X oy = u.uy; X } X X if (invent) { X for(otmp = invent; otmp; otmp = otmp->nobj) X place_object(otmp, ox, oy); X X /* add to main object list at end so invent is X still good */ X if (fobj) { X otmp = fobj; X while(otmp->nobj) X otmp = otmp->nobj; X otmp->nobj = invent; X } else X fobj = invent; X } X } else { X u.ugold -= loss; X shopkeeper->mgold += loss; X pline("%s %sand takes %ld zorkmid%s %sowed %s.", X Monnam(shopkeeper), X (shopkeeper->msleep || shopkeeper->mfrozen) ? X "wakes up " : "comes ", X loss, X plur(loss), X strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ) ? "" : "you ", X ESHK(shopkeeper)->ismale ? "him" : "her"); X } X X /* in case we create bones */ X if(!inhishop(shopkeeper)) X home_shk(shopkeeper); X } Xclear: X setpaid(); X return(taken); X} X X/* find obj on one of the lists */ Xstatic struct obj * Xbp_to_obj(bp) Xregister struct bill_x *bp; X{ X register struct obj *obj; X register struct monst *mtmp; X register unsigned int id = bp->bo_id; X X if(bp->useup) X obj = o_on(id, billobjs); X else if(!(obj = o_on(id, invent)) && X !(obj = o_on(id, fobj)) && X !(obj = o_on(id, fcobj))) { X for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) X if(obj = o_on(id, mtmp->minvent)) X break; X for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon) X if(obj = o_on(id, mtmp->minvent)) X break; X } X return(obj); X} X Xstatic unsigned Xget_cost(obj) Xregister struct obj *obj; X{ X register unsigned tmp; X X tmp = getprice(obj); X if (!tmp) tmp = 5; X if (ANGRY(shopkeeper) || X (pl_character[0] == 'T' && u.ulevel < (MAXULEV/2)) X#ifdef SHIRT X || (uarmu && !uarm) /* wearing just a Hawaiian shirt */ X#endif X ) X tmp += tmp/3; X if (ACURR(A_CHA) > 18) tmp /= 2; X else if (ACURR(A_CHA) > 17) tmp = (tmp * 2)/3; X else if (ACURR(A_CHA) > 15) tmp = (tmp * 3)/4; X else if (ACURR(A_CHA) < 6) tmp *= 2; X else if (ACURR(A_CHA) < 8) tmp = (tmp * 3)/2; X else if (ACURR(A_CHA) < 11) tmp = (tmp * 4)/3; X if (!tmp) return 1; X return(tmp); X} X X X/* called in hack.c when we pickup an object */ Xvoid Xaddtobill(obj, ininv) Xregister struct obj *obj; Xregister boolean ininv; X{ X register struct bill_x *bp; X char buf[40]; X if(!shopkeeper || !inhishop(shopkeeper)) return; X X if(!costly_spot(obj->ox,obj->oy) || /* either pickup or kick */ X onbill(obj) /* perhaps we threw it away earlier */ X ) return; X if(ESHK(shopkeeper)->billct == BILLSZ) { X You("got that for free!"); X return; X } X /* To recognize objects the shopkeeper is not interested in. -dgk X */ X if (obj->no_charge) { X obj->no_charge = 0; X return; X } X bp = &bill[ESHK(shopkeeper)->billct]; X bp->bo_id = obj->o_id; X bp->bquan = obj->quan; X bp->useup = 0; X bp->price = get_cost(obj); X if(!shopkeeper->msleep && !shopkeeper->mfrozen) { X Strcpy(buf, "\"For you, "); X if (ANGRY(shopkeeper)) Strcat(buf, "scum "); X else { X switch(rnd(4) X#ifdef HARD X + u.udemigod X#endif X ) { X case 1: Strcat(buf, "good"); X break; X case 2: Strcat(buf, "honored"); X break; X case 3: Strcat(buf, "most gracious"); X break; X case 4: Strcat(buf, "esteemed"); X break; X case 5: if (u.ualigntyp == U_CHAOTIC) Strcat(buf, "un"); X Strcat(buf, "holy"); X break; X } X#ifdef POLYSELF X if(!is_human(uasmon)) Strcat(buf, " creature"); X else X#endif X Strcat(buf, (flags.female) ? " lady" : " sir"); X } X obj->dknown = 1; /* after all, the shk is telling you what it is */ X if(ininv) { X obj->quan = 1; /* fool xname() into giving singular */ X pline("%s; only %d %s %s.\"", buf, bp->price, X (bp->bquan > 1) ? "per" : "for this", xname(obj)); X obj->quan = bp->bquan; X } else pline("The %s will cost you %d zorkmid%s%s.", X xname(obj), bp->price, plur((long)bp->price), X (bp->bquan > 1) ? " each" : ""); X } else pline("The list price of %s is %d zorkmid%s%s.", X xname(obj), bp->price, plur((long)bp->price), X (bp->bquan > 1) ? " each" : ""); X ESHK(shopkeeper)->billct++; X obj->unpaid = 1; X} X Xvoid Xsplitbill(obj, otmp) Xregister struct obj *obj, *otmp; X{ X /* otmp has been split off from obj */ X register struct bill_x *bp; X register int tmp; X bp = onbill(obj); X if(!bp) { X impossible("splitbill: not on bill?"); X return; X } X if(bp->bquan < otmp->quan) { X impossible("Negative quantity on bill??"); X } X if(bp->bquan == otmp->quan) { X impossible("Zero quantity on bill??"); X } X bp->bquan -= otmp->quan; X X if(ESHK(shopkeeper)->billct == BILLSZ) otmp->unpaid = 0; X else { X tmp = bp->price; X bp = &bill[ESHK(shopkeeper)->billct]; X bp->bo_id = otmp->o_id; X bp->bquan = otmp->quan; X bp->useup = 0; X bp->price = tmp; X ESHK(shopkeeper)->billct++; X } X} X Xvoid Xsubfrombill(obj) Xregister struct obj *obj; X{ X register struct bill_x *bp; X X if(!shopkeeper) return; X X if((bp = onbill(obj)) != 0) { X register struct obj *otmp; X X obj->unpaid = 0; X if(bp->bquan > obj->quan){ X otmp = newobj(0); X *otmp = *obj; X bp->bo_id = otmp->o_id = flags.ident++; X otmp->quan = (bp->bquan -= obj->quan); X otmp->owt = 0; /* superfluous */ X otmp->onamelth = 0; X bp->useup = 1; X otmp->nobj = billobjs; X billobjs = otmp; X return; X } X ESHK(shopkeeper)->billct--; X *bp = bill[ESHK(shopkeeper)->billct]; X return; X } else if (obj->unpaid) { X impossible("subfrombill: unpaid object not on bill"); X obj->unpaid = 0; X } X} X Xvoid Xsellobj(obj) Xregister struct obj *obj; X{ X long ltmp; X X if(!costly_spot(u.ux,u.uy)) X return; X if(obj->unpaid) { X subfrombill(obj); X return; X } X /* you dropped something of your own - probably want to sell it */ X if(shopkeeper->msleep || !shopkeeper->mcanmove || !inhishop(shopkeeper)) X return; X ltmp = (long) getprice(obj) * (long) obj->quan; X if (ANGRY(shopkeeper) || (pl_character[0] == 'T' && u.ulevel < (MAXULEV/2)) X#ifdef SHIRT X || (uarmu && !uarm) /* wearing just a Hawaiian shirt */ X#endif X ) { X ltmp /= 3L; X NOTANGRY(shopkeeper) = 1; X } else ltmp /= 2L; X if(ESHK(shopkeeper)->billct == BILLSZ X || !saleable(rooms[ESHK(shopkeeper)->shoproom].rtype-SHOPBASE, obj) X || obj->olet == BALL_SYM || ltmp == 0L X || (obj->olet == FOOD_SYM && obj->oeaten)) { X pline("%s seems not interested.", Monnam(shopkeeper)); X obj->no_charge = 1; X return; X } X if(ESHK(shopkeeper)->robbed) { X if((ESHK(shopkeeper)->robbed -= ltmp) < 0L) X ESHK(shopkeeper)->robbed = 0L; Xverbalize("Thank you for your contribution to restock this recently plundered shop."); X return; X } X if(ltmp > shopkeeper->mgold) X ltmp = shopkeeper->mgold; X pay(-ltmp, shopkeeper); X if(!ltmp) { X pline("%s gladly accepts %s but cannot pay you at present.", X Monnam(shopkeeper), doname(obj)); X obj->no_charge = 1; X } else X You("sold %s for %ld gold piece%s.", doname(obj), ltmp, X plur(ltmp)); X} X Xint Xdoinvbill(mode) Xint mode; /* 0: deliver count 1: paged */ X{ X register struct bill_x *bp; X register struct obj *obj; X#ifdef __GNULINT__ X long totused, thisused = 0L; X/* possibly a bug in the GCC; clearly thisused is always set before use */ X#else X long totused, thisused; X#endif X char buf[BUFSZ]; X X if(mode == 0) { X register int cnt = 0; X X if(shopkeeper) X for(bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) X if(bp->useup || X ((obj = bp_to_obj(bp)) && obj->quan < bp->bquan)) X cnt++; X return(cnt); X } X X if(!shopkeeper) { X impossible("doinvbill: no shopkeeper?"); X return(0); X } X X set_pager(0); X if(page_line("Unpaid articles already used up:") || page_line("")) X goto quit; X X totused = 0L; X for(bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) { X obj = bp_to_obj(bp); X if(!obj) { X impossible("Bad shopkeeper administration."); X goto quit; X } X if(bp->useup || bp->bquan > obj->quan) { X register int cnt; X register unsigned oquan, uquan; X X oquan = obj->quan; X uquan = (bp->useup ? bp->bquan : bp->bquan - oquan); X thisused = bp->price * uquan; X totused += thisused; X obj->quan = uquan; /* cheat doname */ X Sprintf(buf, "x - %s", doname(obj)); X obj->quan = oquan; /* restore value */ X for(cnt = 0; buf[cnt]; cnt++); X while(cnt < 50) X buf[cnt++] = ' '; X Sprintf(&buf[cnt], " %5ld zorkmid%s", thisused, plur(thisused)); X if(page_line(buf)) X goto quit; X } X } X Sprintf(buf, "Total:%50ld zorkmid%s", totused, plur(totused)); X if(page_line("") || page_line(buf)) X goto quit; X set_pager(1); X return(0); Xquit: X set_pager(2); X return(0); X} X X#define HUNGRY 2 Xstatic int Xgetprice(obj) Xregister struct obj *obj; X{ X register int tmp = objects[obj->otyp].oc_cost; X X switch(obj->olet) { X case AMULET_SYM: X if(obj->otyp == AMULET_OF_YENDOR) { X /* don't let the player get rich selling fakes */ X tmp = (obj->spe < 0 ? 0 : 3500); X } X break; X case FOOD_SYM: X /* simpler hunger check, (2-4)*cost */ X if (u.uhs >= HUNGRY) tmp *= u.uhs; X if (obj->oeaten) tmp = eaten_stat(tmp, obj); /* partly eaten */ X break; X case WAND_SYM: X if (obj->spe == -1) tmp = 0; X break; X case POTION_SYM: X if (obj->otyp == POT_WATER && !obj->blessed && !obj->cursed) X tmp = 0; X break; X case ARMOR_SYM: X case WEAPON_SYM: X if (obj->spe > 0) tmp += 10 * obj->spe; X break; X case CHAIN_SYM: X pline("Strange... carrying a chain?"); X break; X } X return(tmp); X} X Xint Xshkcatch(obj) Xregister struct obj *obj; X{ X register struct monst *shkp = shopkeeper; X X if(obj->otyp != PICK_AXE) return(0); X if(u.uinshop && shkp && shkp->mcanmove && !shkp->msleep && X inroom(u.ux+u.dx, u.uy+u.dy) + 1 == u.uinshop && X shkp->mx == ESHK(shkp)->shk.x && shkp->my == ESHK(shkp)->shk.y && X u.ux == ESHK(shkp)->shd.x && u.uy == ESHK(shkp)->shd.y) { X pline("%s nimbly catches the %s.", Monnam(shkp), xname(obj)); X obj->nobj = shkp->minvent; X shkp->minvent = obj; X subfrombill(obj); X return(1); X } X return(0); X} X X/* X * shk_move: return 1: he moved 0: he didn't -1: let m_move do it -2: died X */ Xint Xshk_move(shkp) Xregister struct monst *shkp; X{ X register xchar gx,gy,omx,omy; X register int udist; X register schar appr; X int z; X schar shkroom; X boolean uondoor = FALSE, satdoor, avoid = FALSE, badinv; X X omx = shkp->mx; X omy = shkp->my; X X if((udist = dist(omx,omy)) < 3 && X (shkp->data != &mons[PM_GRID_BUG] || (omx==u.ux || omy==u.uy))) { X if(ANGRY(shkp)) { X if(Displaced) X Your("displaced image doesn't fool %s!", X Monnam(shkp)); X (void) mattacku(shkp); X return(0); X } X if(ESHK(shkp)->following) { X if(strncmp(ESHK(shkp)->customer, plname, PL_NSIZ)) { X pline("\"Hello, %s! I was looking for %s.\"", X plname, ESHK(shkp)->customer); X ESHK(shkp)->following = 0; X return(0); X } X if(moves > followmsg+4) { X pline("\"Hello, %s! Didn't you forget to pay?\"", X plname); X followmsg = moves; X#ifdef HARD X if (!rn2(4)) { X pline ("%s doesn't like customers who don't pay.", Monnam(shkp)); X NOTANGRY(shkp) = 0; X } X#endif X } X if(udist < 2) X return(0); X } X } X X shkroom = inroom(omx,omy); X appr = 1; X gx = ESHK(shkp)->shk.x; X gy = ESHK(shkp)->shk.y; X satdoor = (gx == omx && gy == omy); X if(ESHK(shkp)->following || ((z = holetime()) >= 0 && z*z <= udist)){ X gx = u.ux; X gy = u.uy; X if(shkroom < 0 || shkroom != inroom(u.ux,u.uy)) X if(udist > 4) X return(-1); /* leave it to m_move */ X } else if(ANGRY(shkp)) { X long saveBlind = Blinded; X struct obj *saveUblindf = ublindf; X Blinded = 0; X ublindf = (struct obj *)0; X if(shkp->mcansee && !Invis && cansee(omx,omy)) { X gx = u.ux; X gy = u.uy; X } X Blinded = saveBlind; X ublindf = saveUblindf; X avoid = FALSE; X } else { X#define GDIST(x,y) (dist2(x,y,gx,gy)) X if(Invis) X avoid = FALSE; X else { X uondoor = (u.ux == ESHK(shkp)->shd.x && X u.uy == ESHK(shkp)->shd.y); X if(uondoor) { X if((ESHK(shkp)->billct || ESHK(shkp)->debit) X && inhishop(shkp)) X pline(NOTANGRY(shkp) ? X "\"Hello, %s! Will you please pay before leaving?\"" : X "\"Hey, %s! Don't leave without paying!\"", X plname); X badinv = (!!carrying(PICK_AXE)); X if(satdoor && badinv) X return(0); X avoid = !badinv; X } else { X avoid = (u.uinshop && dist(gx,gy) > 8); X badinv = FALSE; X } X X if(((!ESHK(shkp)->robbed && !ESHK(shkp)->billct && X !ESHK(shkp)->debit) || avoid) X && GDIST(omx,omy) < 3) { X if(!badinv && !online(omx,omy)) X return(0); X if(satdoor) X appr = gx = gy = 0; X } X } X } X X return(move_special(shkp,shkroom,appr,uondoor,avoid,omx,omy,gx,gy)); X} X X#endif /* OVLB */ X#ifdef OVL0 X Xint Xonline(x,y) /* New version to speed things up. X * Compiler dependant, may not always work. X */ Xregister xchar x, y; X{ X return((x-=u.ux) == 0 || (y-=u.uy) == 0 || x == y || (x+=y) == 0); X} X X/* Original version, just in case... X *online(x,y) { X * return(x==u.ux || y==u.uy || (x-u.ux)*(x-u.ux) == (y-u.uy)*(y-u.uy)); X *} X */ X X#endif /* OVL0 */ X#ifdef OVLB X X/* for use in levl_follower (mondata.c) */ Xboolean Xis_fshk(mtmp) Xregister struct monst *mtmp; X{ X return(mtmp->isshk && ESHK(mtmp)->following); X} X X/* He is digging in the shop. */ Xvoid Xshopdig(fall) Xregister int fall; X{ X if(!shopkeeper) return; X if(!inhishop(shopkeeper)) { X if (pl_character[0] == 'K') adjalign(-sgn(u.ualigntyp)); X return; X } X X if(!fall) { X if(u.utraptype == TT_PIT) X pline("\"Be careful, %s, or you might fall through the floor.\"", X flags.female ? "madam" : "sir"); X else X pline("\"%s, do not damage the floor here!\"", X flags.female ? "Madam" : "Sir"); X if (pl_character[0] == 'K') adjalign(-sgn(u.ualigntyp)); X } else X if(!um_dist(shopkeeper->mx, shopkeeper->my, 5) && X !shopkeeper->msleep && shopkeeper->mcanmove && X (ESHK(shopkeeper)->billct || ESHK(shopkeeper)->debit)) { X register struct obj *obj, *obj2; X X if(dist(shopkeeper->mx, shopkeeper->my) > 2) { X mnexto(shopkeeper); X /* for some reason he can't come next to you */ X if(dist(shopkeeper->mx, shopkeeper->my) > 2) { X pline("%s curses you in anger and frustration!", X shkname(shopkeeper)); X NOTANGRY(shopkeeper) = 0; X return; X } else pline("%s leaps, and grabs your backpack!", X shkname(shopkeeper)); X } else pline("%s grabs your backpack!", shkname(shopkeeper)); X X for(obj = invent; obj; obj = obj2) { X obj2 = obj->nobj; X if(obj->owornmask) continue; X#ifdef WALKIES X if(obj->otyp == LEASH && obj->leashmon) continue; X#endif X freeinv(obj); X obj->nobj = shopkeeper->minvent; X shopkeeper->minvent = obj; X subfrombill(obj); X } X } X} X X#ifdef KOPS XSTATIC_OVL int Xmakekops(mm) /* returns the number of (all types of) Kops made */ Xcoord *mm; X{ X register int cnt = dlevel + rnd(5); X register int scnt = (cnt / 3) + 1; /* at least one sarge */ X register int lcnt = (cnt / 6); /* maybe a lieutenant */ X register int kcnt = (cnt / 9); /* and maybe a kaptain */ X X while(cnt--) { X if (enexto(mm, mm->x, mm->y, &mons[PM_KEYSTONE_KOP])) X (void) makemon(&mons[PM_KEYSTONE_KOP], mm->x, mm->y); X } X while(scnt--) { X if (enexto(mm, mm->x, mm->y, &mons[PM_KOP_SERGEANT])) X (void) makemon(&mons[PM_KOP_SERGEANT], mm->x, mm->y); X } X while(lcnt--) { X if (enexto(mm, mm->x, mm->y, &mons[PM_KOP_LIEUTENANT])) X (void) makemon(&mons[PM_KOP_LIEUTENANT], mm->x, mm->y); X } X while(kcnt--) { X if (enexto(mm, mm->x, mm->y, &mons[PM_KOP_KAPTAIN])) X (void) makemon(&mons[PM_KOP_KAPTAIN], mm->x, mm->y); X } X return(cnt + scnt + lcnt + kcnt); X} X#endif X X#endif /* OVLB */ X#ifdef OVL1 X Xboolean Xin_shop(x,y) Xregister int x, y; X{ X register int roomno = inroom(x, y); X X if (roomno < 0) return(FALSE); X return (IS_SHOP(rooms[roomno])); X} X X#endif /* OVL1 */ X#ifdef OVLB X Xvoid Xpay_for_door(x,y,dmgstr) Xint x, y; Xconst char *dmgstr; X{ X struct monst *mtmp; X int roomno = inroom(x, y); X long damage; X boolean uinshp = in_shop(u.ux, u.uy); X X /* make sure this function is not used in the wrong place */ X if(!(IS_DOOR(levl[x][y].typ) && in_shop(x, y))) return; X X findshk(roomno); X X if(!shopkeeper) return; X X /* not the best introduction to the shk... */ X (void) strncpy(ESHK(shopkeeper)->customer,plname,PL_NSIZ); X X /* if he is already on the war path, be sure it's all out */ X if(ANGRY(shopkeeper) || ESHK(shopkeeper)->following) { X NOTANGRY(shopkeeper) = 0; X ESHK(shopkeeper)->following = 1; X return; X } X X /* if he's not in his shop.. */ X if(!in_shop(shopkeeper->mx ,shopkeeper->my)) { X if(!cansee(shopkeeper->mx, shopkeeper->my)) return; X goto gethim; X } X X if(uinshp) { X if(um_dist(shopkeeper->mx, shopkeeper->my, 1) && X !um_dist(shopkeeper->mx, shopkeeper->my, 3)) { X pline("%s leaps towards you!", shkname(shopkeeper)); X mnexto(shopkeeper); X } X if(um_dist(shopkeeper->mx, shopkeeper->my, 1)) goto gethim; X } else { X /* if a !shopkeeper shows up at the door, move him */ X if((mtmp = m_at(x, y)) && mtmp != shopkeeper) { X if(flags.soundok) { X You("hear an angry voice:"); X verbalize("Out of my way, scum!"); X (void) fflush(stdout); X#if defined(SYSV) || defined(ULTRIX) || defined(VMS) X (void) X#endif X#if defined(UNIX) || defined(VMS) X sleep(1); X#endif X } X mnearto(mtmp, x, y, FALSE); X } X X /* make shk show up at the door */ X remove_monster(shopkeeper->mx, shopkeeper->my); X place_monster(shopkeeper, x, y); X pmon(shopkeeper); X } X X if(!strcmp(dmgstr, "destroy")) damage = 400L; X else damage = (long)(ACURR(A_STR) > 18) ? 400 : 20 * ACURR(A_STR); X X if((um_dist(x, y, 1) && !uinshp) || X (u.ugold + ESHK(shopkeeper)->credit) < damage X || !rn2(50)) { X if(um_dist(x, y, 1) && !uinshp) { X pline("%s shouts:", shkname(shopkeeper)); X pline("\"Who dared %s my door?\"", dmgstr); X } X else Xgethim: X pline("\"How dare you %s my door?\"", dmgstr); X X NOTANGRY(shopkeeper) = 0; X ESHK(shopkeeper)->following = 1; X return; X } X X if(Invis) Your("invisibility does not fool %s!", shkname(shopkeeper)); X pline("\"Cad! You did %ld zorkmids worth of damage!\" Pay? ", damage); X if(yn() != 'n') { X damage = check_credit(damage, shopkeeper); X u.ugold -= damage; X shopkeeper->mgold += damage; X flags.botl = 1; X pline("Mollified, %s accepts your restitution.", X shkname(shopkeeper)); X /* move shk back to his home loc */ X home_shk(shopkeeper); X NOTANGRY(shopkeeper) = 1; X } else { X verbalize("Oh, yes! You'll pay!"); X ESHK(shopkeeper)->following = 1; X NOTANGRY(shopkeeper) = 0; X adjalign(-sgn(u.ualigntyp)); X } X} X X/* called in dokick.c when we kick an object in a store */ Xboolean Xcostly_spot(x, y) Xregister int x, y; X{ X register struct monst *shkp = shopkeeper; X X if(!shkp) return(FALSE); X X return(in_shop(x, y) && levl[x][y].typ != DOOR && X !(x == ESHK(shkp)->shk.x && y == ESHK(shkp)->shk.y)); X} X X#ifdef KOPS Xstatic void Xkops_gone() X{ X register int cnt = 0; X register struct monst *mtmp, *mtmp2; X X /* turn off automatic resurrection of kops */ X allow_kops = FALSE; X X for(mtmp = fmon; mtmp; mtmp = mtmp2) { X mtmp2 = mtmp->nmon; X if(mtmp->data->mlet == S_KOP) { X mongone(mtmp); X cnt++; X } X } X if(cnt) pline("The Kops (disappointed) disappear into thin air."); X allow_kops = TRUE; X} X#endif X Xstatic unsigned Xcost_per_charge(otmp) Xregister struct obj *otmp; X{ X register unsigned tmp = get_cost(otmp); X X /* The idea is to make the exhaustive use of */ X /* an unpaid item more expensive than buying */ X /* outright. */ X if(otmp->otyp == MAGIC_LAMP) { /* 1 */ X tmp += (tmp/3); X } else if(otmp->otyp == MAGIC_MARKER) { /* 70 - 100 */ X /* no way to determine in advance */ X /* how many charges will be wasted. */ X /* so, arbitrarily, one half of the */ X /* price per use. */ X tmp = (tmp/2); X } else if(otmp->otyp == BAG_OF_TRICKS) { /* 1 - 20 */ X tmp = (tmp/5); X } else if(otmp->otyp == CRYSTAL_BALL || /* 1 - 5 */ X otmp->otyp == LAMP || /* 1-10 */ X#ifdef MUSIC X (otmp->otyp >= MAGIC_FLUTE && X otmp->otyp <= DRUM_OF_EARTHQUAKE) || /* 5 - 9 */ X#endif X otmp->olet == WAND_SYM) { /* 3 - 11 */ X if(otmp->spe > 1) tmp = (tmp/4); X } X else return(0); X return(tmp); X} X X/* for using charges of unpaid objects */ Xvoid Xcheck_unpaid(otmp) Xregister struct obj *otmp; X{ X if(!in_shop(u.ux, u.uy)) return; X X if(otmp->spe <= 0) return; X X if(otmp->unpaid) { X ESHK(shopkeeper)->debit += cost_per_charge(otmp); X } X} X Xboolean Xblock_door(x,y) /* used in domove to block diagonal shop-exit */ Xregister int x, y; X{ X register int roomno = inroom(x, y); X X if(!in_shop(u.ux, u.uy)) return(FALSE); X X if(!IS_DOOR(levl[x][y].typ)) return(FALSE); X X if(roomno != inroom(u.ux,u.uy)) return(FALSE); X X findshk(roomno); X X if(inhishop(shopkeeper) X && shopkeeper->mx == ESHK(shopkeeper)->shk.x X && shopkeeper->my == ESHK(shopkeeper)->shk.y X /* Actually, the shk should be made to block _any_ */ X /* door, including a door the player digs, if the */ X /* shk is within a 'jumping' distance. */ X && ESHK(shopkeeper)->shd.x == x && ESHK(shopkeeper)->shd.y == y X && shopkeeper->mcanmove && !shopkeeper->msleep X && (ESHK(shopkeeper)->debit || ESHK(shopkeeper)->billct || X ESHK(shopkeeper)->robbed)) { X pline("%s%s blocks your way!", shkname(shopkeeper), X Invis ? " senses your motion and" : ""); X return(TRUE); X } X return(FALSE); X} X Xboolean Xblock_entry(x,y) /* used in domove to block diagonal shop-entry */ Xregister int x, y; X{ X register int sx, sy, roomno = inroom(x, y); X X if(roomno != inroom(u.ux,u.uy)) return(FALSE); X X if(!(in_shop(u.ux, u.uy) && IS_DOOR(levl[u.ux][u.uy].typ) && X levl[u.ux][u.uy].doormask == D_BROKEN)) return(FALSE); X X findshk(roomno); X if(!inhishop(shopkeeper)) return(FALSE); X X if(ESHK(shopkeeper)->shd.x != u.ux || ESHK(shopkeeper)->shd.y != u.uy) X return(FALSE); X X sx = ESHK(shopkeeper)->shk.x; X sy = ESHK(shopkeeper)->shk.y; X X if(shopkeeper->mx == sx && shopkeeper->my == sy X && shopkeeper->mcanmove && !shopkeeper->msleep X && in_shop(x, y) X && (x == sx-1 || x == sx+1 || y == sy-1 || y == sy+1) X && (Invis || carrying(PICK_AXE)) X ) { X pline("%s%s blocks your way!", shkname(shopkeeper), X Invis ? " senses your motion and" : ""); X return(TRUE); X } X return(FALSE); X} X X#endif /* OVLB */ END_OF_FILE if test 42124 -ne `wc -c <'src/shk.c'`; then echo shar: \"'src/shk.c'\" unpacked with wrong size! fi # end of 'src/shk.c' fi echo shar: End of archive 9 \(of 56\). cp /dev/null ark9isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 56 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0