1 | /* SCCS Id: @(#)shk.c 3.3 2000/03/28 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 | #include "eshk.h"
7 |
8 | /*#define DEBUG*/
9 |
10 | #define PAY_SOME 2
11 | #define PAY_BUY 1
12 | #define PAY_CANT 0 /* too poor */
13 | #define PAY_SKIP (-1)
14 | #define PAY_BROKE (-2)
15 |
16 | #ifdef KOPS
17 | STATIC_DCL void FDECL(makekops, (coord *));
18 | STATIC_DCL void FDECL(call_kops, (struct monst *,BOOLEAN_P));
19 | # ifdef OVLB
20 | STATIC_DCL void FDECL(kops_gone, (BOOLEAN_P));
21 | # endif /* OVLB */
22 | #endif /* KOPS */
23 |
24 | #define IS_SHOP(x) (rooms[x].rtype >= SHOPBASE)
25 |
26 | extern const struct shclass shtypes[]; /* defined in shknam.c */
27 |
28 | STATIC_VAR NEARDATA long int followmsg; /* last time of follow message */
29 |
30 | STATIC_DCL void FDECL(setpaid, (struct monst *));
31 | STATIC_DCL long FDECL(addupbill, (struct monst *));
32 | STATIC_DCL void FDECL(pacify_shk, (struct monst *));
33 | STATIC_DCL struct bill_x *FDECL(onbill, (struct obj *, struct monst *, BOOLEAN_P));
34 | STATIC_DCL struct monst *FDECL(next_shkp, (struct monst *, BOOLEAN_P));
35 | STATIC_DCL long FDECL(shop_debt, (struct eshk *));
36 | STATIC_DCL char *FDECL(shk_owns, (char *,struct obj *));
37 | STATIC_DCL char *FDECL(mon_owns, (char *,struct obj *));
38 | STATIC_DCL void FDECL(clear_unpaid,(struct obj *));
39 | STATIC_DCL long FDECL(check_credit, (long, struct monst *));
40 | STATIC_DCL void FDECL(pay, (long, struct monst *));
41 | STATIC_DCL long FDECL(get_cost, (struct obj *, struct monst *));
42 | STATIC_DCL long FDECL(set_cost, (struct obj *, struct monst *));
43 | STATIC_DCL const char *FDECL(shk_embellish, (struct obj *, long));
44 | STATIC_DCL long FDECL(cost_per_charge, (struct monst *,struct obj *,BOOLEAN_P));
45 | STATIC_DCL long FDECL(cheapest_item, (struct monst *));
46 | STATIC_DCL int FDECL(dopayobj, (struct monst *, struct bill_x *,
47 | struct obj **, int, BOOLEAN_P));
48 | STATIC_DCL long FDECL(stolen_container, (struct obj *, struct monst *, long,
49 | BOOLEAN_P));
50 | STATIC_DCL long FDECL(getprice, (struct obj *,BOOLEAN_P));
51 | STATIC_DCL void FDECL(shk_names_obj,
52 | (struct monst *,struct obj *,const char *,long,const char *));
53 | STATIC_DCL struct obj *FDECL(bp_to_obj, (struct bill_x *));
54 | STATIC_DCL boolean FDECL(inherits, (struct monst *, int, BOOLEAN_P));
55 | STATIC_DCL void FDECL(set_repo_loc, (struct eshk *));
56 | STATIC_DCL boolean NDECL(angry_shk_exists);
57 | STATIC_DCL void FDECL(rile_shk, (struct monst *));
58 | STATIC_DCL void FDECL(remove_damage, (struct monst *, BOOLEAN_P));
59 | STATIC_DCL void FDECL(sub_one_frombill, (struct obj *, struct monst *));
60 | STATIC_DCL void FDECL(add_one_tobill, (struct obj *, BOOLEAN_P));
61 | STATIC_DCL void FDECL(dropped_container, (struct obj *, struct monst *,
62 | BOOLEAN_P));
63 | STATIC_DCL void FDECL(add_to_billobjs, (struct obj *));
64 | STATIC_DCL void FDECL(bill_box_content, (struct obj *, BOOLEAN_P, BOOLEAN_P,
65 | struct monst *));
66 |
67 | #ifdef OVLB
68 | /*
69 | invariants: obj->unpaid iff onbill(obj) [unless bp->useup]
70 | obj->quan <= bp->bquan
71 | */
72 |
73 | STATIC_OVL struct monst *
74 | next_shkp(shkp, withbill)
75 | register struct monst *shkp;
76 | register boolean withbill;
77 | {
78 | for (; shkp; shkp = shkp->nmon) {
79 | if (DEADMONSTER(shkp)) continue;
80 | if (shkp->isshk && (ESHK(shkp)->billct || !withbill)) break;
81 | }
82 |
83 | if (shkp) {
84 | if (NOTANGRY(shkp)) {
85 | if (ESHK(shkp)->surcharge) pacify_shk(shkp);
86 | } else {
87 | if (!ESHK(shkp)->surcharge) rile_shk(shkp);
88 | }
89 | }
90 | return(shkp);
91 | }
92 |
93 | char *
94 | shkname(mtmp) /* called in do_name.c */
95 | register struct monst *mtmp;
96 | {
97 | return(ESHK(mtmp)->shknam);
98 | }
99 |
100 | void
101 | shkgone(mtmp) /* called in mon.c */
102 | register struct monst *mtmp;
103 | {
104 | register struct eshk *eshk = ESHK(mtmp);
105 |
106 | if(on_level(&(eshk->shoplevel), &u.uz)) {
107 | remove_damage(mtmp, TRUE);
108 | rooms[eshk->shoproom - ROOMOFFSET].resident
109 | = (struct monst *)0;
110 | if(!search_special(ANY_SHOP))
111 | level.flags.has_shop = 0;
112 | }
113 | /* make sure bill is set only when the
114 | * dead shk is the resident shk. */
115 | if(*u.ushops == eshk->shoproom) {
116 | setpaid(mtmp);
117 | /* dump core when referenced */
118 | ESHK(mtmp)->bill_p = (struct bill_x *) -1000;
119 | u.ushops[0] = '\0';
120 | }
121 | }
122 |
123 | void
124 | set_residency(shkp, zero_out)
125 | register struct monst *shkp;
126 | register boolean zero_out;
127 | {
128 | if (on_level(&(ESHK(shkp)->shoplevel), &u.uz))
129 | rooms[ESHK(shkp)->shoproom - ROOMOFFSET].resident =
130 | (zero_out)? (struct monst *)0 : shkp;
131 | }
132 |
133 | void
134 | replshk(mtmp,mtmp2)
135 | register struct monst *mtmp, *mtmp2;
136 | {
137 | rooms[ESHK(mtmp2)->shoproom - ROOMOFFSET].resident = mtmp2;
138 | if (inhishop(mtmp) && *u.ushops == ESHK(mtmp)->shoproom) {
139 | ESHK(mtmp2)->bill_p = &(ESHK(mtmp2)->bill[0]);
140 | }
141 | }
142 |
143 | /* do shopkeeper specific structure munging -dlc */
144 | void
145 | restshk(shkp, ghostly)
146 | struct monst *shkp;
147 | boolean ghostly;
148 | {
149 | if (u.uz.dlevel) {
150 | struct eshk *eshkp = ESHK(shkp);
151 |
152 | if (eshkp->bill_p != (struct bill_x *) -1000)
153 | eshkp->bill_p = &eshkp->bill[0];
154 | /* shoplevel can change as dungeons move around */
155 | /* savebones guarantees that non-homed shk's will be gone */
156 | if (ghostly) {
157 | assign_level(&eshkp->shoplevel, &u.uz);
158 | if (ANGRY(shkp) && strncmpi(eshkp->customer, plname, PL_NSIZ))
159 | pacify_shk(shkp);
160 | }
161 | }
162 | }
163 |
164 | #endif /* OVLB */
165 | #ifdef OVL3
166 |
167 | /* Clear the unpaid bit on all of the objects in the list. */
168 | STATIC_OVL void
169 | clear_unpaid(list)
170 | register struct obj *list;
171 | {
172 | while (list) {
173 | if (Has_contents(list)) clear_unpaid(list->cobj);
174 | list->unpaid = 0;
175 | list = list->nobj;
176 | }
177 | }
178 | #endif /*OVL3*/
179 | #ifdef OVLB
180 |
181 | STATIC_OVL void
182 | setpaid(shkp) /* either you paid or left the shop or the shopkeeper died */
183 | register struct monst *shkp;
184 | {
185 | register struct obj *obj;
186 | register struct monst *mtmp;
187 |
188 | clear_unpaid(invent);
189 | clear_unpaid(fobj);
190 | clear_unpaid(level.buriedobjlist);
191 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
192 | clear_unpaid(mtmp->minvent);
193 | for(mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon)
194 | clear_unpaid(mtmp->minvent);
195 |
196 | while ((obj = billobjs) != 0) {
197 | obj_extract_self(obj);
198 | dealloc_obj(obj);
199 | }
200 | if(shkp) {
201 | ESHK(shkp)->billct = 0;
202 | ESHK(shkp)->credit = 0L;
203 | ESHK(shkp)->debit = 0L;
204 | ESHK(shkp)->loan = 0L;
205 | }
206 | }
207 |
208 | STATIC_OVL long
209 | addupbill(shkp)
210 | register struct monst *shkp;
211 | {
212 | register int ct = ESHK(shkp)->billct;
213 | register struct bill_x *bp = ESHK(shkp)->bill_p;
214 | register long total = 0L;
215 |
216 | while(ct--){
217 | total += bp->price * bp->bquan;
218 | bp++;
219 | }
220 | return(total);
221 | }
222 |
223 | #endif /* OVLB */
224 | #ifdef OVL1
225 |
226 | #ifdef KOPS
227 | STATIC_OVL void
228 | call_kops(shkp, nearshop)
229 | register struct monst *shkp;
230 | register boolean nearshop;
231 | {
232 | /* Keystone Kops srt@ucla */
233 | register boolean nokops;
234 |
235 | if(!shkp) return;
236 |
237 | if (flags.soundok)
238 | pline("An alarm sounds!");
239 |
240 | nokops = ((mvitals[PM_KEYSTONE_KOP].mvflags & G_GONE) &&
241 | (mvitals[PM_KOP_SERGEANT].mvflags & G_GONE) &&
242 | (mvitals[PM_KOP_LIEUTENANT].mvflags & G_GONE) &&
243 | (mvitals[PM_KOP_KAPTAIN].mvflags & G_GONE));
244 |
245 | if(!angry_guards(!flags.soundok) && nokops) {
246 | if(flags.verbose && flags.soundok)
247 | pline("But no one seems to respond to it.");
248 | return;
249 | }
250 |
251 | if(nokops) return;
252 |
253 | {
254 | coord mm;
255 |
256 | if (nearshop) {
257 | /* Create swarm around you, if you merely "stepped out" */
258 | if (flags.verbose)
259 | pline_The("Keystone Kops appear!");
260 | mm.x = u.ux;
261 | mm.y = u.uy;
262 | makekops(&mm);
263 | return;
264 | }
265 | if (flags.verbose)
266 | pline_The("Keystone Kops are after you!");
267 | /* Create swarm near down staircase (hinders return to level) */
268 | mm.x = xdnstair;
269 | mm.y = ydnstair;
270 | makekops(&mm);
271 | /* Create swarm near shopkeeper (hinders return to shop) */
272 | mm.x = shkp->mx;
273 | mm.y = shkp->my;
274 | makekops(&mm);
275 | }
276 | }
277 | #endif /* KOPS */
278 |
279 | /* x,y is strictly inside shop */
280 | char
281 | inside_shop(x, y)
282 | register xchar x, y;
283 | {
284 | register char rno;
285 |
286 | rno = levl[x][y].roomno;
287 | if ((rno < ROOMOFFSET) || levl[x][y].edge || !IS_SHOP(rno-ROOMOFFSET))
288 | return(NO_ROOM);
289 | else
290 | return(rno);
291 | }
292 |
293 | void
294 | u_left_shop(leavestring, newlev)
295 | register char *leavestring;
296 | register boolean newlev;
297 | {
298 | register struct monst *shkp;
299 | register struct eshk *eshkp;
300 | register long total;
301 |
302 | /*
303 | * IF player
304 | * ((didn't leave outright) AND
305 | * ((he is now strictly-inside the shop) OR
306 | * (he wasn't strictly-inside last turn anyway)))
307 | * THEN (there's nothing to do, so just return)
308 | */
309 | if(!*leavestring &&
310 | (!levl[u.ux][u.uy].edge || levl[u.ux0][u.uy0].edge))
311 | return;
312 |
313 | shkp = shop_keeper(*u.ushops0);
314 |
315 | if(!shkp || !inhishop(shkp))
316 | /* shk died, teleported, changed levels... */
317 | return;
318 |
319 | eshkp = ESHK(shkp);
320 |
321 | if(!eshkp->billct && !eshkp->debit) /* bill is settled */
322 | return;
323 |
324 | if (!*leavestring && shkp->mcanmove && !shkp->msleeping) {
325 | /*
326 | * Player just stepped onto shop-boundary (known from above logic).
327 | * Try to intimidate him into paying his bill
328 | */
329 | verbalize(NOTANGRY(shkp) ?
330 | "%s! Please pay before leaving." :
331 | "%s! Don't you leave without paying!",
332 | plname);
333 | return;
334 | }
335 | total = (addupbill(shkp) + eshkp->debit);
336 | if (eshkp->credit >= total) {
337 | Your("credit of %ld zorkmid%s is used to cover your shopping bill.",
338 | eshkp->credit, plur(eshkp->credit));
339 | total = 0L; /* credit gets cleared by setpaid() */
340 | } else {
341 | You("escaped the shop without paying!");
342 | total -= eshkp->credit;
343 | }
344 | setpaid(shkp);
345 | if (!total) return;
346 |
347 | /* by this point, we know an actual robbery has taken place */
348 | eshkp->robbed += total;
349 | You("stole %ld zorkmid%s worth of merchandise.",
350 | total, plur(total));
351 | if (!Role_if(PM_ROGUE)) /* stealing is unlawful */
352 | adjalign(-sgn(u.ualign.type));
353 |
354 | hot_pursuit(shkp);
355 | #ifdef KOPS
356 | call_kops(shkp, (!newlev && levl[u.ux0][u.uy0].edge));
357 | #else
358 | (void) angry_guards(FALSE);
359 | #endif
360 | }
361 |
362 | void
363 | u_entered_shop(enterstring)
364 | register char *enterstring;
365 | {
366 |
367 | register int rt;
368 | register struct monst *shkp;
369 | register struct eshk *eshkp;
370 | static const char no_shk[] = "This shop appears to be deserted.";
371 | static char empty_shops[5];
372 |
373 | if(!*enterstring)
374 | return;
375 |
376 | if(!(shkp = shop_keeper(*enterstring))) {
377 | if (!index(empty_shops, *enterstring) &&
378 | in_rooms(u.ux, u.uy, SHOPBASE) !=
379 | in_rooms(u.ux0, u.uy0, SHOPBASE))
380 | pline(no_shk);
381 | Strcpy(empty_shops, u.ushops);
382 | u.ushops[0] = '\0';
383 | return;
384 | }
385 |
386 | eshkp = ESHK(shkp);
387 |
388 | if (!inhishop(shkp)) {
389 | /* dump core when referenced */
390 | eshkp->bill_p = (struct bill_x *) -1000;
391 | if (!index(empty_shops, *enterstring))
392 | pline(no_shk);
393 | Strcpy(empty_shops, u.ushops);
394 | u.ushops[0] = '\0';
395 | return;
396 | }
397 |
398 | eshkp->bill_p = &(eshkp->bill[0]);
399 |
400 | if (!eshkp->visitct || strncmpi(eshkp->customer, plname, PL_NSIZ)) {
401 | /* You seem to be new here */
402 | eshkp->visitct = 0;
403 | eshkp->following = 0;
404 | (void) strncpy(eshkp->customer,plname,PL_NSIZ);
405 | pacify_shk(shkp);
406 | }
407 |
408 | if (shkp->msleeping || !shkp->mcanmove || eshkp->following)
409 | return; /* no dialog */
410 |
411 | if (Invis) {
412 | pline("%s senses your presence.", shkname(shkp));
413 | verbalize("Invisible customers are not welcome!");
414 | return;
415 | }
416 |
417 | rt = rooms[*enterstring - ROOMOFFSET].rtype;
418 |
419 | if (ANGRY(shkp)) {
420 | verbalize("So, %s, you dare return to %s %s?!",
421 | plname,
422 | s_suffix(shkname(shkp)),
423 | shtypes[rt - SHOPBASE].name);
424 | } else if (eshkp->robbed) {
425 | pline("%s mutters imprecations against shoplifters.", shkname(shkp));
426 | } else {
427 | verbalize("%s, %s! Welcome%s to %s %s!",
428 | Hello(shkp), plname,
429 | eshkp->visitct++ ? " again" : "",
430 | s_suffix(shkname(shkp)),
431 | shtypes[rt - SHOPBASE].name);
432 | }
433 | /* can't do anything about blocking if teleported in */
434 | if (!inside_shop(u.ux, u.uy)) {
435 | boolean should_block;
436 | int cnt;
437 | const char *tool;
438 | struct obj *pick = carrying(PICK_AXE),
439 | *mattock = carrying(DWARVISH_MATTOCK);
440 |
441 | if (pick || mattock) {
442 | cnt = 1; /* so far */
443 | if (pick && mattock) { /* carrying both types */
444 | tool = "digging tool";
445 | cnt = 2; /* `more than 1' is all that matters */
446 | } else if (pick) {
447 | tool = "pick-axe";
448 | /* hack: `pick' already points somewhere into inventory */
449 | while ((pick = pick->nobj) != 0)
450 | if (pick->otyp == PICK_AXE) ++cnt;
451 | } else { /* assert(mattock != 0) */
452 | tool = "mattock";
453 | while ((mattock = mattock->nobj) != 0)
454 | if (mattock->otyp == DWARVISH_MATTOCK) ++cnt;
455 | }
456 | verbalize(NOTANGRY(shkp) ?
457 | "Will you please leave your %s%s outside?" :
458 | "Leave the %s%s outside.",
459 | tool, plur(cnt));
460 | should_block = TRUE;
461 | #ifdef STEED
462 | } else if (u.usteed) {
463 | verbalize(NOTANGRY(shkp) ?
464 | "Will you please leave %s outside?" :
465 | "Leave %s outside.", y_monnam(u.usteed));
466 | should_block = TRUE;
467 | #endif
468 | } else {
469 | should_block = (Fast && (sobj_at(PICK_AXE, u.ux, u.uy) ||
470 | sobj_at(DWARVISH_MATTOCK, u.ux, u.uy)));
471 | }
472 | if (should_block) (void) dochug(shkp); /* shk gets extra move */
473 | }
474 | return;
475 | }
476 |
477 | /*
478 | Decide whether two unpaid items are mergable; caller is responsible for
479 | making sure they're unpaid and the same type of object; we check the price
480 | quoted by the shopkeeper and also that they both belong to the same shk.
481 | */
482 | boolean same_price(obj1, obj2)
483 | struct obj *obj1, *obj2;
484 | {
485 | register struct monst *shkp1, *shkp2;
486 | register struct bill_x *bp1 = 0, *bp2 = 0;
487 | register boolean are_mergable = FALSE;
488 |
489 | /* look up the first object by finding shk whose bill it's on */
490 | for (shkp1 = next_shkp(fmon, TRUE); shkp1;
491 | shkp1 = next_shkp(shkp1, TRUE))
492 | if ((bp1 = onbill(obj1, shkp1, TRUE)) != 0) break;
493 | /* second object is probably owned by same shk; if not, look harder */
494 | if (shkp1 && (bp2 = onbill(obj2, shkp1, TRUE)) != 0) {
495 | shkp2 = shkp1;
496 | } else {
497 | for (shkp2 = next_shkp(fmon, TRUE); shkp2;
498 | shkp2 = next_shkp(shkp2, TRUE))
499 | if ((bp2 = onbill(obj2, shkp2, TRUE)) != 0) break;
500 | }
501 |
502 | if (!bp1 || !bp2) impossible("same_price: object wasn't on any bill!");
503 | else are_mergable = (shkp1 == shkp2 && bp1->price == bp2->price);
504 | return are_mergable;
505 | }
506 |
507 | /*
508 | * Figure out how much is owed to a given shopkeeper.
509 | * At present, we ignore any amount robbed from the shop, to avoid
510 | * turning the `$' command into a way to discover that the current
511 | * level is bones data which has a shk on the warpath.
512 | */
513 | STATIC_OVL long
514 | shop_debt(eshkp)
515 | struct eshk *eshkp;
516 | {
517 | struct bill_x *bp;
518 | int ct;
519 | long debt = eshkp->debit;
520 |
521 | for (bp = eshkp->bill_p, ct = eshkp->billct; ct > 0; bp++, ct--)
522 | debt += bp->price * bp->bquan;
523 | return debt;
524 | }
525 |
526 | /* called in response to the `$' command */
527 | void
528 | shopper_financial_report()
529 | {
530 | struct monst *shkp, *this_shkp = shop_keeper(inside_shop(u.ux, u.uy));
531 | struct eshk *eshkp;
532 | long amt;
533 | int pass;
534 |
535 | if (this_shkp &&
536 | !(ESHK(this_shkp)->credit || shop_debt(ESHK(this_shkp)))) {
537 | You("have no credit or debt in here.");
538 | this_shkp = 0; /* skip first pass */
539 | }
540 |
541 | /* pass 0: report for the shop we're currently in, if any;
542 | pass 1: report for all other shops on this level. */
543 | for (pass = this_shkp ? 0 : 1; pass <= 1; pass++)
544 | for (shkp = next_shkp(fmon, FALSE);
545 | shkp; shkp = next_shkp(shkp->nmon, FALSE)) {
546 | if ((shkp != this_shkp) ^ pass) continue;
547 | eshkp = ESHK(shkp);
548 | if ((amt = eshkp->credit) != 0)
549 | You("have %ld zorkmid%s credit at %s %s.",
550 | amt, plur(amt), s_suffix(shkname(shkp)),
551 | shtypes[eshkp->shoptype - SHOPBASE].name);
552 | else if (shkp == this_shkp)
553 | You("have no credit in here.");
554 | if ((amt = shop_debt(eshkp)) != 0)
555 | You("owe %s %ld zorkmid%s.",
556 | shkname(shkp), amt, plur(amt));
557 | else if (shkp == this_shkp)
558 | You("don't owe any money here.");
559 | }
560 | }
561 |
562 | #endif /* OVL1 */
563 | #ifdef OVLB
564 |
565 | int
566 | inhishop(mtmp)
567 | register struct monst *mtmp;
568 | {
569 | return(index(in_rooms(mtmp->mx, mtmp->my, SHOPBASE),
570 | ESHK(mtmp)->shoproom) &&
571 | on_level(&(ESHK(mtmp)->shoplevel), &u.uz));
572 | }
573 |
574 | struct monst *
575 | shop_keeper(rmno)
576 | register char rmno;
577 | {
578 | struct monst *shkp = rmno >= ROOMOFFSET ?
579 | rooms[rmno - ROOMOFFSET].resident : 0;
580 |
581 | if (shkp) {
582 | if (NOTANGRY(shkp)) {
583 | if (ESHK(shkp)->surcharge) pacify_shk(shkp);
584 | } else {
585 | if (!ESHK(shkp)->surcharge) rile_shk(shkp);
586 | }
587 | }
588 | return shkp;
589 | }
590 |
591 | boolean
592 | tended_shop(sroom)
593 | register struct mkroom *sroom;
594 | {
595 | register struct monst *mtmp = sroom->resident;
596 |
597 | if (!mtmp)
598 | return(FALSE);
599 | else
600 | return((boolean)(inhishop(mtmp)));
601 | }
602 |
603 | STATIC_OVL struct bill_x *
604 | onbill(obj, shkp, silent)
605 | register struct obj *obj;
606 | register struct monst *shkp;
607 | register boolean silent;
608 | {
609 | if (shkp) {
610 | register struct bill_x *bp = ESHK(shkp)->bill_p;
611 | register int ct = ESHK(shkp)->billct;
612 |
613 | while (--ct >= 0)
614 | if (bp->bo_id == obj->o_id) {
615 | if (!obj->unpaid) pline("onbill: paid obj on bill?");
616 | return bp;
617 | } else bp++;
618 | }
619 | if(obj->unpaid & !silent) pline("onbill: unpaid obj not on bill?");
620 | return (struct bill_x *)0;
621 | }
622 |
623 | /* Delete the contents of the given object. */
624 | void
625 | delete_contents(obj)
626 | register struct obj *obj;
627 | {
628 | register struct obj *curr;
629 |
630 | while ((curr = obj->cobj) != 0) {
631 | obj_extract_self(curr);
632 | obfree(curr, (struct obj *)0);
633 | }
634 | }
635 |
636 | /* called with two args on merge */
637 | void
638 | obfree(obj, merge)
639 | register struct obj *obj, *merge;
640 | {
641 | register struct bill_x *bp;
642 | register struct bill_x *bpm;
643 | register struct monst *shkp;
644 |
645 |
646 | if (obj->otyp == LEASH && obj->leashmon) o_unleash(obj);
647 | if (obj->oclass == FOOD_CLASS) food_disappears(obj);
648 | if (Has_contents(obj)) delete_contents(obj);
649 |
650 | shkp = 0;
651 | if (obj->unpaid) {
652 | /* look for a shopkeeper who owns this object */
653 | for (shkp = next_shkp(fmon, TRUE); shkp;
654 | shkp = next_shkp(shkp, TRUE))
655 | if (onbill(obj, shkp, TRUE)) break;
656 | }
657 | /* sanity check, more or less */
658 | if (!shkp) shkp = shop_keeper(*u.ushops);
659 | /*
660 | * Note: `shkp = shop_keeper(*u.ushops)' used to be
661 | * unconditional. But obfree() is used all over
662 | * the place, so making its behavior be dependent
663 | * upon player location doesn't make much sense.
664 | */
665 |
666 | if ((bp = onbill(obj, shkp, FALSE)) != 0) {
667 | if(!merge){
668 | bp->useup = 1;
669 | obj->unpaid = 0; /* only for doinvbill */
670 | add_to_billobjs(obj);
671 | return;
672 | }
673 | bpm = onbill(merge, shkp, FALSE);
674 | if(!bpm){
675 | /* this used to be a rename */
676 | impossible("obfree: not on bill??");
677 | return;
678 | } else {
679 | /* this was a merger */
680 | bpm->bquan += bp->bquan;
681 | ESHK(shkp)->billct--;
682 | #ifdef DUMB
683 | {
684 | /* DRS/NS 2.2.6 messes up -- Peter Kendell */
685 | int indx = ESHK(shkp)->billct;
686 | *bp = ESHK(shkp)->bill_p[indx];
687 | }
688 | #else
689 | *bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct];
690 | #endif
691 | }
692 | }
693 | dealloc_obj(obj);
694 | }
695 | #endif /* OVLB */
696 | #ifdef OVL3
697 |
698 | STATIC_OVL long
699 | check_credit(tmp, shkp)
700 | long tmp;
701 | register struct monst *shkp;
702 | {
703 | long credit = ESHK(shkp)->credit;
704 |
705 | if(credit == 0L) return(tmp);
706 | if(credit >= tmp) {
707 | pline_The("price is deducted from your credit.");
708 | ESHK(shkp)->credit -=tmp;
709 | tmp = 0L;
710 | } else {
711 | pline_The("price is partially covered by your credit.");
712 | ESHK(shkp)->credit = 0L;
713 | tmp -= credit;
714 | }
715 | return(tmp);
716 | }
717 |
718 | STATIC_OVL void
719 | pay(tmp,shkp)
720 | long tmp;
721 | register struct monst *shkp;
722 | {
723 | long robbed = ESHK(shkp)->robbed;
724 | long balance = ((tmp <= 0L) ? tmp : check_credit(tmp, shkp));
725 |
726 | u.ugold -= balance;
727 | shkp->mgold += balance;
728 | flags.botl = 1;
729 | if(robbed) {
730 | robbed -= tmp;
731 | if(robbed < 0) robbed = 0L;
732 | ESHK(shkp)->robbed = robbed;
733 | }
734 | }
735 | #endif /*OVL3*/
736 | #ifdef OVLB
737 |
738 | /* return shkp to home position */
739 | void
740 | home_shk(shkp, killkops)
741 | register struct monst *shkp;
742 | register boolean killkops;
743 | {
744 | register xchar x = ESHK(shkp)->shk.x, y = ESHK(shkp)->shk.y;
745 |
746 | (void) mnearto(shkp, x, y, TRUE);
747 | level.flags.has_shop = 1;
748 | if (killkops) {
749 | #ifdef KOPS
750 | kops_gone(TRUE);
751 | #else
752 | You_feel("vaguely apprehensive.");
753 | #endif
754 | pacify_guards();
755 | }
756 | }
757 |
758 | STATIC_OVL boolean
759 | angry_shk_exists()
760 | {
761 | register struct monst *shkp;
762 |
763 | for (shkp = next_shkp(fmon, FALSE);
764 | shkp; shkp = next_shkp(shkp->nmon, FALSE))
765 | if (ANGRY(shkp)) return(TRUE);
766 | return(FALSE);
767 | }
768 |
769 | /* remove previously applied surcharge from all billed items */
770 | STATIC_OVL void
771 | pacify_shk(shkp)
772 | register struct monst *shkp;
773 | {
774 | NOTANGRY(shkp) = TRUE; /* make peaceful */
775 | if (ESHK(shkp)->surcharge) {
776 | register struct bill_x *bp = ESHK(shkp)->bill_p;
777 | register int ct = ESHK(shkp)->billct;
778 |
779 | ESHK(shkp)->surcharge = FALSE;
780 | while (ct-- > 0) {
781 | register long reduction = (bp->price + 3L) / 4L;
782 | bp->price -= reduction; /* undo 33% increase */
783 | bp++;
784 | }
785 | }
786 | }
787 |
788 | /* add aggravation surcharge to all billed items */
789 | STATIC_OVL void
790 | rile_shk(shkp)
791 | register struct monst *shkp;
792 | {
793 | NOTANGRY(shkp) = FALSE; /* make angry */
794 | if (!ESHK(shkp)->surcharge) {
795 | register struct bill_x *bp = ESHK(shkp)->bill_p;
796 | register int ct = ESHK(shkp)->billct;
797 |
798 | ESHK(shkp)->surcharge = TRUE;
799 | while (ct-- > 0) {
800 | register long surcharge = (bp->price + 2L) / 3L;
801 | bp->price += surcharge;
802 | bp++;
803 | }
804 | }
805 | }
806 |
807 | void
808 | make_happy_shk(shkp, silentkops)
809 | register struct monst *shkp;
810 | register boolean silentkops;
811 | {
812 | boolean wasmad = ANGRY(shkp);
813 | struct eshk *eshkp = ESHK(shkp);
814 |
815 | pacify_shk(shkp);
816 | eshkp->following = 0;
817 | eshkp->robbed = 0L;
818 | if (!Role_if(PM_ROGUE))
819 | adjalign(sgn(u.ualign.type));
820 | if(!inhishop(shkp)) {
821 | char shk_nam[BUFSZ];
822 | boolean vanished = canseemon(shkp);
823 |
824 | Strcpy(shk_nam, mon_nam(shkp));
825 | if (on_level(&eshkp->shoplevel, &u.uz)) {
826 | home_shk(shkp, FALSE);
827 | /* didn't disappear if shk can still be seen */
828 | if (canseemon(shkp)) vanished = FALSE;
829 | } else {
830 | /* if sensed, does disappear regardless whether seen */
831 | if (sensemon(shkp)) vanished = TRUE;
832 | /* arrive near shop's door */
833 | migrate_to_level(shkp, ledger_no(&eshkp->shoplevel),
834 | MIGR_APPROX_XY, &eshkp->shd);
835 | }
836 | if (vanished)
837 | pline("Satisfied, %s suddenly disappears!", shk_nam);
838 | } else if(wasmad)
839 | pline("%s calms down.", Monnam(shkp));
840 |
841 | if(!angry_shk_exists()) {
842 | #ifdef KOPS
843 | kops_gone(silentkops);
844 | #endif
845 | pacify_guards();
846 | }
847 | }
848 |
849 | void
850 | hot_pursuit(shkp)
851 | register struct monst *shkp;
852 | {
853 | if(!shkp->isshk) return;
854 |
855 | rile_shk(shkp);
856 | ESHK(shkp)->following = 1;
857 | }
858 |
859 | /* used when the shkp is teleported out of his shop,
860 | * or when the player is not on a costly_spot and he
861 | * damages something inside the shop. these conditions
862 | * must be checked by the calling function.
863 | */
864 | void
865 | make_angry_shk(shkp, ox, oy)
866 | register struct monst *shkp;
867 | register xchar ox,oy;
868 | {
869 | xchar sx, sy;
870 | struct eshk *eshkp = ESHK(shkp);
871 |
872 | /* all pending shop transactions are now "past due" */
873 | if (eshkp->billct || eshkp->debit || eshkp->loan || eshkp->credit) {
874 | eshkp->robbed += (addupbill(shkp) + eshkp->debit + eshkp->loan);
875 | eshkp->robbed -= eshkp->credit;
876 | if (eshkp->robbed < 0L) eshkp->robbed = 0L;
877 | /* billct, debit, loan, and credit will be cleared by setpaid */
878 | setpaid(shkp);
879 | }
880 |
881 | /* If you just used a wand of teleportation to send the shk away, you
882 | might not be able to see her any more. Monnam would yield "it",
883 | which makes this message look pretty silly, so temporarily restore
884 | her original location during the call to Monnam. */
885 | sx = shkp->mx, sy = shkp->my;
886 | if (cansee(ox, oy) && !cansee(sx, sy))
887 | shkp->mx = ox, shkp->my = oy;
888 | pline("%s %s!", Monnam(shkp),
889 | !ANGRY(shkp) ? "gets angry" : "is furious");
890 | shkp->mx = sx, shkp->my = sy;
891 | hot_pursuit(shkp);
892 | }
893 |
894 | STATIC_VAR const char no_money[] = "Moreover, you%s have no money.";
895 | STATIC_VAR const char not_enough_money[] =
896 | "Besides, you don't have enough to interest %s.";
897 |
898 | #else
899 | STATIC_VAR const char no_money[];
900 | STATIC_VAR const char not_enough_money[];
901 | #endif /*OVLB*/
902 |
903 | #ifdef OVL3
904 |
905 | STATIC_OVL long
906 | cheapest_item(shkp) /* delivers the cheapest item on the list */
907 | register struct monst *shkp;
908 | {
909 | register int ct = ESHK(shkp)->billct;
910 | register struct bill_x *bp = ESHK(shkp)->bill_p;
911 | register long gmin = (bp->price * bp->bquan);
912 |
913 | while(ct--){
914 | if(bp->price * bp->bquan < gmin)
915 | gmin = bp->price * bp->bquan;
916 | bp++;
917 | }
918 | return(gmin);
919 | }
920 | #endif /*OVL3*/
921 | #ifdef OVL0
922 |
923 | int
924 | dopay()
925 | {
926 | register struct eshk *eshkp;
927 | register struct monst *shkp;
928 | struct monst *nxtm, *resident;
929 | long ltmp;
930 | int pass, tmp, shk_pronoun, sk = 0, seensk = 0;
931 | boolean paid = FALSE, stashed_gold = (hidden_gold() > 0L);
932 |
933 | multi = 0;
934 |
935 | /* find how many shk's there are, how many are in */
936 | /* sight, and are you in a shop room with one. */
937 | nxtm = resident = 0;
938 | for (shkp = next_shkp(fmon, FALSE);
939 | shkp; shkp = next_shkp(shkp->nmon, FALSE)) {
940 | sk++;
941 | if (ANGRY(shkp) && distu(shkp->mx, shkp->my) <= 2) nxtm = shkp;
942 | if (canspotmon(shkp)) seensk++;
943 | if (inhishop(shkp) && (*u.ushops == ESHK(shkp)->shoproom))
944 | resident = shkp;
945 | }
946 |
947 | if (nxtm) { /* Player should always appease an */
948 | shkp = nxtm; /* irate shk standing next to them. */
949 | goto proceed;
950 | }
951 |
952 | if ((!sk && (!Blind || Blind_telepat)) || (!Blind && !seensk)) {
953 | There("appears to be no shopkeeper here to receive your payment.");
954 | return(0);
955 | }
956 |
957 | if(!seensk) {
958 | You_cant("see...");
959 | return(0);
960 | }
961 |
962 | /* the usual case. allow paying at a distance when */
963 | /* inside a tended shop. should we change that? */
964 | if(sk == 1 && resident) {
965 | shkp = resident;
966 | goto proceed;
967 | }
968 |
969 | if (seensk == 1) {
970 | for (shkp = next_shkp(fmon, FALSE);
971 | shkp; shkp = next_shkp(shkp->nmon, FALSE))
972 | if (canspotmon(shkp)) break;
973 | if (shkp != resident && distu(shkp->mx, shkp->my) > 2) {
974 | pline("%s is not near enough to receive your payment.",
975 | Monnam(shkp));
976 | return(0);
977 | }
978 | } else {
979 | struct monst *mtmp;
980 | coord cc;
981 | int cx, cy;
982 |
983 | pline("Pay whom?");
984 | cc.x = u.ux;
985 | cc.y = u.uy;
986 | if (getpos(&cc, TRUE, "the creature you want to pay") < 0)
987 | return 0; /* player pressed ESC */
988 | cx = cc.x;
989 | cy = cc.y;
990 | if(cx < 0) {
991 | pline("Try again...");
992 | return(0);
993 | }
994 | if(u.ux == cx && u.uy == cy) {
995 | You("are generous to yourself.");
996 | return(0);
997 | }
998 | mtmp = m_at(cx, cy);
999 | if(!mtmp) {
1000 | There("is no one there to receive your payment.");
1001 | return(0);
1002 | }
1003 | if(!mtmp->isshk) {
1004 | pline("%s is not interested in your payment.",
1005 | Monnam(mtmp));
1006 | return(0);
1007 | }
1008 | if (mtmp != resident && distu(mtmp->mx, mtmp->my) > 2) {
1009 | pline("%s is too far to receive your payment.",
1010 | Monnam(mtmp));
1011 | return(0);
1012 | }
1013 | shkp = mtmp;
1014 | }
1015 |
1016 | if(!shkp) {
1017 | #ifdef DEBUG
1018 | pline("dopay: null shkp.");
1019 | #endif
1020 | return(0);
1021 | }
1022 | proceed:
1023 |
1024 | if (shkp->msleeping || !shkp->mcanmove) {
1025 | pline("%s %s.", Monnam(shkp),
1026 | rn2(2) ? "seems to be napping" : "doesn't respond");
1027 | return 0;
1028 | }
1029 | eshkp = ESHK(shkp);
1030 | shk_pronoun = pronoun_gender(shkp);
1031 |
1032 | ltmp = eshkp->robbed;
1033 | if(shkp != resident && NOTANGRY(shkp)) {
1034 | if(!ltmp)
1035 | You("do not owe %s anything.", mon_nam(shkp));
1036 | else if(!u.ugold) {
1037 | You("%shave no money.", stashed_gold ? "seem to " : "");
1038 | if(stashed_gold)
1039 | pline("But you have some gold stashed away.");
1040 | } else {
1041 | long ugold = u.ugold;
1042 |
1043 | if(ugold > ltmp) {
1044 | You("give %s the %ld gold piece%s %s asked for.",
1045 | mon_nam(shkp), ltmp, plur(ltmp), he[shk_pronoun]);
1046 | pay(ltmp, shkp);
1047 | } else {
1048 | You("give %s all your%s gold.", mon_nam(shkp),
1049 | stashed_gold ? " openly kept" : "");
1050 | pay(u.ugold, shkp);
1051 | if (stashed_gold) pline("But you have hidden gold!");
1052 | }
1053 | if((ugold < ltmp/2L) || (ugold < ltmp && stashed_gold))
1054 | pline("Unfortunately, %s doesn't look satisfied.",
1055 | he[shk_pronoun]);
1056 | else
1057 | make_happy_shk(shkp, FALSE);
1058 | }
1059 | return(1);
1060 | }
1061 |
1062 | /* ltmp is still eshkp->robbed here */
1063 | if (!eshkp->billct && !eshkp->debit) {
1064 | if(!ltmp && NOTANGRY(shkp)) {
1065 | You("do not owe %s anything.", mon_nam(shkp));
1066 | if (!u.ugold)
1067 | pline(no_money, stashed_gold ? " seem to" : "");
1068 | } else if(ltmp) {
1069 | pline("%s is after blood, not money!", Monnam(shkp));
1070 | if(u.ugold < ltmp/2L ||
1071 | (u.ugold < ltmp && stashed_gold)) {
1072 | if (!u.ugold)
1073 | pline(no_money, stashed_gold ? " seem to" : "");
1074 | else pline(not_enough_money, him[shk_pronoun]);
1075 | return(1);
1076 | }
1077 | pline("But since %s shop has been robbed recently,",
1078 | his[shk_pronoun]);
1079 | pline("you %scompensate %s for %s losses.",
1080 | (u.ugold < ltmp) ? "partially " : "",
1081 | mon_nam(shkp), his[shk_pronoun]);
1082 | pay(u.ugold < ltmp ? u.ugold : ltmp, shkp);
1083 | make_happy_shk(shkp, FALSE);
1084 | } else {
1085 | /* shopkeeper is angry, but has not been robbed --
1086 | * door broken, attacked, etc. */
1087 | pline("%s is after your hide, not your money!",
1088 | Monnam(shkp));
1089 | if(u.ugold < 1000L) {
1090 | if (!u.ugold)
1091 | pline(no_money, stashed_gold ? " seem to" : "");
1092 | else pline(not_enough_money, him[shk_pronoun]);
1093 | return(1);
1094 | }
1095 | You("try to appease %s by giving %s 1000 gold pieces.",
1096 | x_monnam(shkp, ARTICLE_THE, "angry", 0, FALSE),
1097 | him[shk_pronoun]);
1098 | pay(1000L,shkp);
1099 | if (strncmp(eshkp->customer, plname, PL_NSIZ) || rn2(3))
1100 | make_happy_shk(shkp, FALSE);
1101 | else
1102 | pline("But %s is as angry as ever.", mon_nam(shkp));
1103 | }
1104 | return(1);
1105 | }
1106 | if(shkp != resident) {
1107 | impossible("dopay: not to shopkeeper?");
1108 | if(resident) setpaid(resident);
1109 | return(0);
1110 | }
1111 | /* pay debt, if any, first */
1112 | if(eshkp->debit) {
1113 | long dtmp = eshkp->debit;
1114 | long loan = eshkp->loan;
1115 | char sbuf[BUFSZ];
1116 |
1117 | Sprintf(sbuf, "You owe %s %ld zorkmid%s ",
1118 | shkname(shkp), dtmp, plur(dtmp));
1119 | if(loan) {
1120 | if(loan == dtmp)
1121 | Strcat(sbuf, "you picked up in the store.");
1122 | else Strcat(sbuf,
1123 | "for gold picked up and the use of merchandise.");
1124 | } else Strcat(sbuf, "for the use of merchandise.");
1125 | pline(sbuf);
1126 | if (u.ugold + eshkp->credit < dtmp) {
1127 | pline("But you don't%s have enough gold%s.",
1128 | stashed_gold ? " seem to" : "",
1129 | eshkp->credit ? " or credit" : "");
1130 | return(1);
1131 | } else {
1132 | if (eshkp->credit >= dtmp) {
1133 | eshkp->credit -= dtmp;
1134 | eshkp->debit = 0L;
1135 | eshkp->loan = 0L;
1136 | Your("debt is covered by your credit.");
1137 | } else if (!eshkp->credit) {
1138 | u.ugold -= dtmp;
1139 | shkp->mgold += dtmp;
1140 | eshkp->debit = 0L;
1141 | eshkp->loan = 0L;
1142 | You("pay that debt.");
1143 | flags.botl = 1;
1144 | } else {
1145 | dtmp -= eshkp->credit;
1146 | eshkp->credit = 0L;
1147 | u.ugold -= dtmp;
1148 | shkp->mgold += dtmp;
1149 | eshkp->debit = 0L;
1150 | eshkp->loan = 0L;
1151 | pline("That debt is partially offset by your credit.");
1152 | You("pay the remainder.");
1153 | flags.botl = 1;
1154 | }
1155 | paid = TRUE;
1156 | }
1157 | }
1158 | /* now check items on bill */
1159 | if (eshkp->billct) {
1160 | register boolean itemize;
1161 |
1162 | if (!u.ugold && !eshkp->credit) {
1163 | You("%shave no money or credit%s.",
1164 | stashed_gold ? "seem to " : "",
1165 | paid ? " left" : "");
1166 | return(0);
1167 | }
1168 | if ((u.ugold + eshkp->credit) < cheapest_item(shkp)) {
1169 | You("don't have enough money to buy%s the item%s you picked.",
1170 | eshkp->billct > 1 ? " any of" : "", plur(eshkp->billct));
1171 | if(stashed_gold)
1172 | pline("Maybe you have some gold stashed away?");
1173 | return(0);
1174 | }
1175 |
1176 | /* this isn't quite right; it itemizes without asking if the
1177 | * single item on the bill is partly used up and partly unpaid */
1178 | itemize = (eshkp->billct > 1 ? yn("Itemized billing?") == 'y' : 1);
1179 |
1180 | for (pass = 0; pass <= 1; pass++) {
1181 | tmp = 0;
1182 | while (tmp < eshkp->billct) {
1183 | struct obj *otmp;
1184 | register struct bill_x *bp = &(eshkp->bill_p[tmp]);
1185 |
1186 | /* find the object on one of the lists */
1187 | if ((otmp = bp_to_obj(bp)) != 0) {
1188 | /* if completely used up, object quantity is stale;
1189 | restoring it to its original value here avoids
1190 | making the partly-used-up code more complicated */
1191 | if (bp->useup) otmp->quan = bp->bquan;
1192 | } else {
1193 | impossible("Shopkeeper administration out of order.");
1194 | setpaid(shkp); /* be nice to the player */
1195 | return 1;
1196 | }
1197 | if (pass == bp->useup && otmp->quan == bp->bquan) {
1198 | /* pay for used-up items on first pass and others
1199 | * on second, so player will be stuck in the store
1200 | * less often; things which are partly used up
1201 | * are processed on both passes */
1202 | tmp++;
1203 | } else {
1204 | switch (dopayobj(shkp, bp, &otmp, pass, itemize)) {
1205 | case PAY_CANT:
1206 | return 1; /*break*/
1207 | case PAY_BROKE:
1208 | paid = TRUE;
1209 | goto thanks; /*break*/
1210 | case PAY_SKIP:
1211 | tmp++;
1212 | continue; /*break*/
1213 | case PAY_SOME:
1214 | paid = TRUE;
1215 | if (itemize) bot();
1216 | continue; /*break*/
1217 | case PAY_BUY:
1218 | paid = TRUE;
1219 | break;
1220 | }
1221 | if (itemize) bot();
1222 | *bp = eshkp->bill_p[--eshkp->billct];
1223 | }
1224 | }
1225 | }
1226 | thanks:
1227 | if (!itemize)
1228 | update_inventory(); /* Done in dopayobj() if itemize. */
1229 | }
1230 | if(!ANGRY(shkp) && paid)
1231 | verbalize("Thank you for shopping in %s %s!",
1232 | s_suffix(shkname(shkp)),
1233 | shtypes[eshkp->shoptype - SHOPBASE].name);
1234 | return(1);
1235 | }
1236 | #endif /*OVL0*/
1237 | #ifdef OVL3
1238 |
1239 | /* return 2 if used-up portion paid */
1240 | /* 1 if paid successfully */
1241 | /* 0 if not enough money */
1242 | /* -1 if skip this object */
1243 | /* -2 if no money/credit left */
1244 | STATIC_OVL int
1245 | dopayobj(shkp, bp, obj_p, which, itemize)
1246 | register struct monst *shkp;
1247 | register struct bill_x *bp;
1248 | struct obj **obj_p;
1249 | int which; /* 0 => used-up item, 1 => other (unpaid or lost) */
1250 | boolean itemize;
1251 | {
1252 | register struct obj *obj = *obj_p;
1253 | long ltmp, quan, save_quan;
1254 | int buy;
1255 | boolean stashed_gold = (hidden_gold() > 0L),
1256 | consumed = (which == 0);
1257 |
1258 | if(!obj->unpaid && !bp->useup){
1259 | impossible("Paid object on bill??");
1260 | return PAY_BUY;
1261 | }
1262 | if(itemize && u.ugold + ESHK(shkp)->credit == 0L){
1263 | You("%shave no money or credit left.",
1264 | stashed_gold ? "seem to " : "");
1265 | return PAY_BROKE;
1266 | }
1267 | /* we may need to temporarily adjust the object, if part of the
1268 | original quantity has been used up but part remains unpaid */
1269 | save_quan = obj->quan;
1270 | if (consumed) {
1271 | /* either completely used up (simple), or split needed */
1272 | quan = bp->bquan;
1273 | if (quan > obj->quan) /* difference is amount used up */
1274 | quan -= obj->quan;
1275 | } else {
1276 | /* dealing with ordinary unpaid item */
1277 | quan = obj->quan;
1278 | }
1279 | obj->quan = quan; /* to be used by doname() */
1280 | obj->unpaid = 0; /* ditto */
1281 | ltmp = bp->price * quan;
1282 | buy = PAY_BUY; /* flag; if changed then return early */
1283 |
1284 | if (itemize) {
1285 | char qbuf[BUFSZ];
1286 | Sprintf(qbuf,"%s for %ld zorkmid%s. Pay?", quan == 1L ?
1287 | Doname2(obj) : doname(obj), ltmp, plur(ltmp));
1288 | if (yn(qbuf) == 'n') {
1289 | buy = PAY_SKIP; /* don't want to buy */
1290 | } else if (quan < bp->bquan && !consumed) { /* partly used goods */
1291 | obj->quan = bp->bquan - save_quan; /* used up amount */
1292 | verbalize("%s for the other %s before buying %s.",
1293 | ANGRY(shkp) ? "Pay" : "Please pay", xname(obj),
1294 | save_quan > 1L ? "these" : "this one");
1295 | buy = PAY_SKIP; /* shk won't sell */
1296 | }
1297 | }
1298 | if (buy == PAY_BUY && u.ugold + ESHK(shkp)->credit < ltmp) {
1299 | You("don't%s have gold%s enough to pay for %s.",
1300 | stashed_gold ? " seem to" : "",
1301 | (ESHK(shkp)->credit > 0L) ? " or credit" : "",
1302 | doname(obj));
1303 | buy = itemize ? PAY_SKIP : PAY_CANT;
1304 | }
1305 |
1306 | if (buy != PAY_BUY) {
1307 | /* restore unpaid object to original state */
1308 | obj->quan = save_quan;
1309 | obj->unpaid = 1;
1310 | return buy;
1311 | }
1312 |
1313 | pay(ltmp, shkp);
1314 | shk_names_obj(shkp, obj, "bought %s for %ld gold piece%s.%s", ltmp, "");
1315 | obj->quan = save_quan; /* restore original count */
1316 | /* quan => amount just bought, save_quan => remaining unpaid count */
1317 | if (consumed) {
1318 | if (quan != bp->bquan) {
1319 | /* eliminate used-up portion; remainder is still unpaid */
1320 | bp->bquan = obj->quan;
1321 | obj->unpaid = 1;
1322 | bp->useup = 0;
1323 | buy = PAY_SOME;
1324 | } else { /* completely used-up, so get rid of it */
1325 | obj_extract_self(obj);
1326 | /* assert( obj == *obj_p ); */
1327 | dealloc_obj(obj);
1328 | *obj_p = 0; /* destroy pointer to freed object */
1329 | }
1330 | } else if (itemize)
1331 | update_inventory(); /* Done just once in dopay() if !itemize. */
1332 | return buy;
1333 | }
1334 | #endif /*OVL3*/
1335 | #ifdef OVLB
1336 |
1337 | static coord repo_location; /* repossession context */
1338 |
1339 | /* routine called after dying (or quitting) */
1340 | boolean
1341 | paybill(croaked)
1342 | boolean croaked;
1343 | {
1344 | register struct monst *mtmp, *mtmp2, *resident= (struct monst *)0;
1345 | register boolean taken = FALSE;
1346 | register int numsk = 0;
1347 |
1348 | /* this is where inventory will end up if any shk takes it */
1349 | repo_location.x = repo_location.y = 0;
1350 |
1351 | /* give shopkeeper first crack */
1352 | if ((mtmp = shop_keeper(*u.ushops)) && inhishop(mtmp)) {
1353 | numsk++;
1354 | resident = mtmp;
1355 | taken = inherits(resident, numsk, croaked);
1356 | }
1357 | for (mtmp = next_shkp(fmon, FALSE);
1358 | mtmp; mtmp = next_shkp(mtmp2, FALSE)) {
1359 | mtmp2 = mtmp->nmon;
1360 | if (mtmp != resident) {
1361 | /* for bones: we don't want a shopless shk around */
1362 | if(!on_level(&(ESHK(mtmp)->shoplevel), &u.uz))
1363 | mongone(mtmp);
1364 | else {
1365 | numsk++;
1366 | taken |= inherits(mtmp, numsk, croaked);
1367 | }
1368 | }
1369 | }
1370 | if(numsk == 0) return(FALSE);
1371 | return(taken);
1372 | }
1373 |
1374 | STATIC_OVL boolean
1375 | inherits(shkp, numsk, croaked)
1376 | struct monst *shkp;
1377 | int numsk;
1378 | boolean croaked;
1379 | {
1380 | long loss = 0L;
1381 | struct eshk *eshkp = ESHK(shkp);
1382 | boolean take = FALSE, taken = FALSE;
1383 | int roomno = *u.ushops;
1384 |
1385 | /* the simplifying principle is that first-come */
1386 | /* already took everything you had. */
1387 | if(numsk > 1) {
1388 | if(cansee(shkp->mx, shkp->my) && croaked)
1389 | pline("%s %slooks at your corpse%s%s", Monnam(shkp),
1390 | (shkp->msleeping || !shkp->mcanmove) ?
1391 | "wakes up, " : "",
1392 | !rn2(2) ? (shkp->female ? ", shakes her head," :
1393 | ", shakes his head,") : "",
1394 | !inhishop(shkp) ? " and disappears. " : " and sighs.");
1395 | taken = (roomno == eshkp->shoproom);
1396 | goto skip;
1397 | }
1398 |
1399 | /* get one case out of the way: you die in the shop, the */
1400 | /* shopkeeper is peaceful, nothing stolen, nothing owed. */
1401 | if(roomno == eshkp->shoproom && inhishop(shkp) &&
1402 | !eshkp->billct && !eshkp->robbed && !eshkp->debit &&
1403 | NOTANGRY(shkp) && !eshkp->following) {
1404 | if (invent)
1405 | pline("%s gratefully inherits all your possessions.",
1406 | shkname(shkp));
1407 | set_repo_loc(eshkp);
1408 | goto clear;
1409 | }
1410 |
1411 | if (eshkp->billct || eshkp->debit || eshkp->robbed) {
1412 | if (roomno == eshkp->shoproom && inhishop(shkp))
1413 | loss = addupbill(shkp) + eshkp->debit;
1414 | if (loss < eshkp->robbed) loss = eshkp->robbed;
1415 | take = TRUE;
1416 | }
1417 |
1418 | if (eshkp->following || ANGRY(shkp) || take) {
1419 | if (!invent && !u.ugold) goto skip;
1420 |
1421 | if (loss > u.ugold || !loss || roomno == eshkp->shoproom) {
1422 | eshkp->robbed -= u.ugold;
1423 | if (eshkp->robbed < 0L) eshkp->robbed = 0L;
1424 | shkp->mgold += u.ugold;
1425 | u.ugold = 0L;
1426 | flags.botl = 1;
1427 | pline("%s %s%stakes all your possessions.",
1428 | shkname(shkp),
1429 | (shkp->msleeping || !shkp->mcanmove) ?
1430 | "wakes up and " : "",
1431 | (distu(shkp->mx, shkp->my) > 2) ?
1432 | "comes and " : "");
1433 | taken = TRUE;
1434 | /* where to put player's invent (after disclosure) */
1435 | set_repo_loc(eshkp);
1436 | } else {
1437 | shkp->mgold += loss;
1438 | u.ugold -= loss;
1439 | flags.botl = 1;
1440 | pline("%s %sand takes %ld zorkmid%s %sowed %s.",
1441 | Monnam(shkp),
1442 | (shkp->msleeping || !shkp->mcanmove) ?
1443 | "wakes up " : "comes ",
1444 | loss, plur(loss),
1445 | strncmp(eshkp->customer, plname, PL_NSIZ) ?
1446 | "" : "you ",
1447 | shkp->female ? "her" : "him");
1448 | /* shopkeeper has now been paid in full */
1449 | pacify_shk(shkp);
1450 | eshkp->following = 0;
1451 | eshkp->robbed = 0L;
1452 | }
1453 | skip:
1454 | /* in case we create bones */
1455 | shkp->msleeping = 0;
1456 | if (!inhishop(shkp))
1457 | home_shk(shkp, FALSE);
1458 | }
1459 | clear:
1460 | setpaid(shkp);
1461 | return(taken);
1462 | }
1463 |
1464 | STATIC_OVL void
1465 | set_repo_loc(eshkp)
1466 | struct eshk *eshkp;
1467 | {
1468 | register xchar ox, oy;
1469 |
1470 | /* if you're not in this shk's shop room, or if you're in its doorway
1471 | or entry spot, then your gear gets dumped all the way inside */
1472 | if (*u.ushops != eshkp->shoproom ||
1473 | IS_DOOR(levl[u.ux][u.uy].typ) ||
1474 | (u.ux == eshkp->shk.x && u.uy == eshkp->shk.y)) {
1475 | /* shk.x,shk.y is the position immediately in
1476 | * front of the door -- move in one more space
1477 | */
1478 | ox = eshkp->shk.x;
1479 | oy = eshkp->shk.y;
1480 | ox += sgn(ox - eshkp->shd.x);
1481 | oy += sgn(oy - eshkp->shd.y);
1482 | } else { /* already inside this shk's shop */
1483 | ox = u.ux;
1484 | oy = u.uy;
1485 | }
1486 | /* finish_paybill will deposit invent here */
1487 | repo_location.x = ox;
1488 | repo_location.y = oy;
1489 | }
1490 |
1491 | /* called at game exit, after inventory disclosure but before making bones */
1492 | void finish_paybill()
1493 | {
1494 | register struct obj *otmp;
1495 | int ox = repo_location.x,
1496 | oy = repo_location.y;
1497 |
1498 | #if 0 /* don't bother */
1499 | if (ox == 0 && oy == 0) impossible("finish_paybill: no location");
1500 | #endif
1501 | /* transfer all of the character's inventory to the shop floor */
1502 | while ((otmp = invent) != 0) {
1503 | otmp->owornmask = 0L; /* perhaps we should call setnotworn? */
1504 | otmp->lamplit = 0; /* avoid "goes out" msg from freeinv */
1505 | if (rn2(5)) curse(otmp); /* normal bones treatment for invent */
1506 | obj_extract_self(otmp);
1507 | place_object(otmp, ox, oy);
1508 | }
1509 | }
1510 |
1511 | /* find obj on one of the lists */
1512 | STATIC_OVL struct obj *
1513 | bp_to_obj(bp)
1514 | register struct bill_x *bp;
1515 | {
1516 | register struct obj *obj;
1517 | register unsigned int id = bp->bo_id;
1518 |
1519 | if(bp->useup)
1520 | obj = o_on(id, billobjs);
1521 | else
1522 | obj = find_oid(id);
1523 | return obj;
1524 | }
1525 |
1526 | /*
1527 | * Look for o_id on all lists but billobj. Return obj or NULL if not found.
1528 | * Its OK for restore_timers() to call this function, there should not
1529 | * be any timeouts on the billobjs chain.
1530 | */
1531 | struct obj *
1532 | find_oid(id)
1533 | unsigned id;
1534 | {
1535 | struct obj *obj;
1536 | struct monst *mon, *mmtmp[3];
1537 | int i;
1538 |
1539 | /* first check various obj lists directly */
1540 | if ((obj = o_on(id, invent)) != 0) return obj;
1541 | if ((obj = o_on(id, fobj)) != 0) return obj;
1542 | if ((obj = o_on(id, level.buriedobjlist)) != 0) return obj;
1543 | if ((obj = o_on(id, migrating_objs)) != 0) return obj;
1544 |
1545 | /* not found yet; check inventory for members of various monst lists */
1546 | mmtmp[0] = fmon;
1547 | mmtmp[1] = migrating_mons;
1548 | mmtmp[2] = mydogs; /* for use during level changes */
1549 | for (i = 0; i < 3; i++)
1550 | for (mon = mmtmp[i]; mon; mon = mon->nmon)
1551 | if ((obj = o_on(id, mon->minvent)) != 0) return obj;
1552 |
1553 | /* not found at all */
1554 | return (struct obj *)0;
1555 | }
1556 | #endif /*OVLB*/
1557 | #ifdef OVL3
1558 |
1559 | /* calculate the value that the shk will charge for [one of] an object */
1560 | STATIC_OVL long
1561 | get_cost(obj, shkp)
1562 | register struct obj *obj;
1563 | register struct monst *shkp; /* if angry, impose a surcharge */
1564 | {
1565 | register long tmp = getprice(obj, FALSE);
1566 |
1567 | if (!tmp) tmp = 5L;
1568 | /* shopkeeper may notice if the player isn't very knowledgeable -
1569 | especially when gem prices are concerned */
1570 | if (!obj->dknown || !objects[obj->otyp].oc_name_known) {
1571 | if (obj->oclass == GEM_CLASS) {
1572 | /* all gems are priced high - real or not */
1573 | if (objects[obj->otyp].oc_material == GLASS) {
1574 | int i = obj->otyp - LUCKSTONE + JADE + 1;
1575 | /* real gem's cost (worthless gems come
1576 | after jade but before luckstone) */
1577 | tmp = (long) objects[i].oc_cost;
1578 | }
1579 | } else if (!(obj->o_id % 4)) /* arbitrarily impose surcharge */
1580 | tmp += tmp / 3L;
1581 | }
1582 | #ifdef TOURIST
1583 | if ((Role_if(PM_TOURIST) && u.ulevel < (MAXULEV/2))
1584 | || (uarmu && !uarm && !uarmc)) /* touristy shirt visible */
1585 | tmp += tmp / 3L;
1586 | else
1587 | #endif
1588 | if (uarmh && uarmh->otyp == DUNCE_CAP)
1589 | tmp += tmp / 3L;
1590 |
1591 | if (ACURR(A_CHA) > 18) tmp /= 2L;
1592 | else if (ACURR(A_CHA) > 17) tmp -= tmp / 3L;
1593 | else if (ACURR(A_CHA) > 15) tmp -= tmp / 4L;
1594 | else if (ACURR(A_CHA) < 6) tmp *= 2L;
1595 | else if (ACURR(A_CHA) < 8) tmp += tmp / 2L;
1596 | else if (ACURR(A_CHA) < 11) tmp += tmp / 3L;
1597 | if (tmp <= 0L) tmp = 1L;
1598 | else if (obj->oartifact) tmp *= 4L;
1599 | /* anger surcharge should match rile_shk's */
1600 | if (shkp && ESHK(shkp)->surcharge) tmp += (tmp + 2L) / 3L;
1601 | return tmp;
1602 | }
1603 | #endif /*OVL3*/
1604 | #ifdef OVLB
1605 |
1606 | /* returns the price of a container's content. the price
1607 | * of the "top" container is added in the calling functions.
1608 | * a different price quoted for selling as vs. buying.
1609 | */
1610 | long
1611 | contained_cost(obj, shkp, price, usell)
1612 | register struct obj *obj;
1613 | register struct monst *shkp;
1614 | long price;
1615 | register boolean usell;
1616 | {
1617 | register struct obj *otmp;
1618 |
1619 | /* the price of contained objects */
1620 | for (otmp = obj->cobj; otmp; otmp = otmp->nobj) {
1621 | if (otmp->oclass == GOLD_CLASS) continue;
1622 | /* the "top" container is evaluated by caller */
1623 | if (usell) {
1624 | if (saleable(shkp, otmp) &&
1625 | !otmp->unpaid && otmp->oclass != BALL_CLASS &&
1626 | !(otmp->oclass == FOOD_CLASS && otmp->oeaten) &&
1627 | !(Is_candle(otmp) && otmp->age <
1628 | 20L * (long)objects[otmp->otyp].oc_cost))
1629 | price += set_cost(otmp, shkp);
1630 | } else if (!otmp->no_charge) {
1631 | price += get_cost(otmp, shkp) * otmp->quan;
1632 | }
1633 |
1634 | if (Has_contents(otmp))
1635 | price += contained_cost(otmp, shkp, price, usell);
1636 | }
1637 |
1638 | return(price);
1639 | }
1640 |
1641 | long
1642 | contained_gold(obj)
1643 | register struct obj *obj;
1644 | {
1645 | register struct obj *otmp;
1646 | register long value = 0L;
1647 |
1648 | /* accumulate contained gold */
1649 | for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
1650 | if (otmp->oclass == GOLD_CLASS)
1651 | value += otmp->quan;
1652 | else if (Has_contents(otmp))
1653 | value += contained_gold(otmp);
1654 |
1655 | return(value);
1656 | }
1657 |
1658 | STATIC_OVL void
1659 | dropped_container(obj, shkp, sale)
1660 | register struct obj *obj;
1661 | register struct monst *shkp;
1662 | register boolean sale;
1663 | {
1664 | register struct obj *otmp;
1665 |
1666 | /* the "top" container is treated in the calling fn */
1667 | for (otmp = obj->cobj; otmp; otmp = otmp->nobj) {
1668 | if (otmp->oclass == GOLD_CLASS) continue;
1669 |
1670 | if (!otmp->unpaid && !(sale && saleable(shkp, otmp)))
1671 | otmp->no_charge = 1;
1672 |
1673 | if (Has_contents(otmp))
1674 | dropped_container(otmp, shkp, sale);
1675 | }
1676 | }
1677 |
1678 | void
1679 | picked_container(obj)
1680 | register struct obj *obj;
1681 | {
1682 | register struct obj *otmp;
1683 |
1684 | /* the "top" container is treated in the calling fn */
1685 | for (otmp = obj->cobj; otmp; otmp = otmp->nobj) {
1686 | if (otmp->oclass == GOLD_CLASS) continue;
1687 |
1688 | if (otmp->no_charge)
1689 | otmp->no_charge = 0;
1690 |
1691 | if (Has_contents(otmp))
1692 | picked_container(otmp);
1693 | }
1694 | }
1695 | #endif /*OVLB*/
1696 | #ifdef OVL3
1697 |
1698 | /* calculate how much the shk will pay when buying [all of] an object */
1699 | STATIC_OVL long
1700 | set_cost(obj, shkp)
1701 | register struct obj *obj;
1702 | register struct monst *shkp;
1703 | {
1704 | long tmp = getprice(obj, TRUE) * obj->quan;
1705 |
1706 | #ifdef TOURIST
1707 | if ((Role_if(PM_TOURIST) && u.ulevel < (MAXULEV/2))
1708 | || (uarmu && !uarm && !uarmc)) /* touristy shirt visible */
1709 | tmp /= 3L;
1710 | else
1711 | #endif
1712 | if (uarmh && uarmh->otyp == DUNCE_CAP)
1713 | tmp /= 3L;
1714 | else
1715 | tmp /= 2L;
1716 |
1717 | /* shopkeeper may notice if the player isn't very knowledgeable -
1718 | especially when gem prices are concerned */
1719 | if (!obj->dknown || !objects[obj->otyp].oc_name_known) {
1720 | if (obj->oclass == GEM_CLASS) {
1721 | /* different shop keepers give different prices */
1722 | if (objects[obj->otyp].oc_material == GEMSTONE ||
1723 | objects[obj->otyp].oc_material == GLASS) {
1724 | tmp = (obj->otyp % (6 - shkp->m_id % 3));
1725 | tmp = (tmp + 3) * obj->quan;
1726 | }
1727 | } else if (tmp > 1L && !rn2(4))
1728 | tmp -= tmp / 4L;
1729 | }
1730 | return tmp;
1731 | }
1732 |
1733 | #endif /*OVL3*/
1734 | #ifdef OVLB
1735 |
1736 | /* called from doinv(invent.c) for inventory of unpaid objects */
1737 | long
1738 | unpaid_cost(unp_obj)
1739 | register struct obj *unp_obj; /* known to be unpaid */
1740 | {
1741 | register struct bill_x *bp = (struct bill_x *)0;
1742 | register struct monst *shkp;
1743 |
1744 | for(shkp = next_shkp(fmon, TRUE); shkp;
1745 | shkp = next_shkp(shkp->nmon, TRUE))
1746 | if ((bp = onbill(unp_obj, shkp, TRUE)) != 0) break;
1747 |
1748 | /* onbill() gave no message if unexpected problem occurred */
1749 | if(!bp) impossible("unpaid_cost: object wasn't on any bill!");
1750 |
1751 | return bp ? unp_obj->quan * bp->price : 0L;
1752 | }
1753 |
1754 | STATIC_OVL void
1755 | add_one_tobill(obj, dummy)
1756 | register struct obj *obj;
1757 | register boolean dummy;
1758 | {
1759 | register struct monst *shkp;
1760 | register struct bill_x *bp;
1761 | register int bct;
1762 | register char roomno = *u.ushops;
1763 |
1764 | if (!roomno) return;
1765 | if (!(shkp = shop_keeper(roomno))) return;
1766 | if (!inhishop(shkp)) return;
1767 |
1768 | if (onbill(obj, shkp, FALSE) || /* perhaps thrown away earlier */
1769 | (obj->oclass == FOOD_CLASS && obj->oeaten))
1770 | return;
1771 |
1772 | if (ESHK(shkp)->billct == BILLSZ) {
1773 | You("got that for free!");
1774 | return;
1775 | }
1776 |
1777 | /* To recognize objects the shopkeeper is not interested in. -dgk
1778 | */
1779 | if (obj->no_charge) {
1780 | obj->no_charge = 0;
1781 | return;
1782 | }
1783 |
1784 | bct = ESHK(shkp)->billct;
1785 | bp = &(ESHK(shkp)->bill_p[bct]);
1786 | bp->bo_id = obj->o_id;
1787 | bp->bquan = obj->quan;
1788 | if(dummy) { /* a dummy object must be inserted into */
1789 | bp->useup = 1; /* the billobjs chain here. crucial for */
1790 | add_to_billobjs(obj); /* eating floorfood in shop. see eat.c */
1791 | } else bp->useup = 0;
1792 | bp->price = get_cost(obj, shkp);
1793 | ESHK(shkp)->billct++;
1794 | obj->unpaid = 1;
1795 | }
1796 |
1797 | STATIC_OVL void
1798 | add_to_billobjs(obj)
1799 | struct obj *obj;
1800 | {
1801 | if (obj->where != OBJ_FREE)
1802 | panic("add_to_billobjs: obj not free");
1803 | if (obj->timed)
1804 | panic("add_to_billobjs: obj is timed");
1805 |
1806 | obj->nobj = billobjs;
1807 | billobjs = obj;
1808 | obj->where = OBJ_ONBILL;
1809 | }
1810 |
1811 | /* recursive billing of objects within containers. */
1812 | STATIC_OVL void
1813 | bill_box_content(obj, ininv, dummy, shkp)
1814 | register struct obj *obj;
1815 | register boolean ininv, dummy;
1816 | register struct monst *shkp;
1817 | {
1818 | register struct obj *otmp;
1819 |
1820 | for (otmp = obj->cobj; otmp; otmp = otmp->nobj) {
1821 | if (otmp->oclass == GOLD_CLASS) continue;
1822 |
1823 | /* the "top" box is added in addtobill() */
1824 | if (!otmp->no_charge)
1825 | add_one_tobill(otmp, dummy);
1826 | if (Has_contents(otmp))
1827 | bill_box_content(otmp, ininv, dummy, shkp);
1828 | }
1829 |
1830 | }
1831 |
1832 | /* shopkeeper tells you what you bought or sold, sometimes partly IDing it */
1833 | STATIC_OVL void
1834 | shk_names_obj(shkp, obj, fmt, amt, arg)
1835 | struct monst *shkp;
1836 | struct obj *obj;
1837 | const char *fmt; /* "%s %ld %s %s", doname(obj), amt, plur(amt), arg */
1838 | long amt;
1839 | const char *arg;
1840 | {
1841 | char *obj_name, fmtbuf[BUFSZ];
1842 | boolean was_unknown = !obj->dknown;
1843 |
1844 | obj->dknown = TRUE;
1845 | /* Use real name for ordinary weapons/armor, and spell-less
1846 | * scrolls/books (that is, blank and mail), but only if the
1847 | * object is within the shk's area of interest/expertise.
1848 | */
1849 | if (!objects[obj->otyp].oc_magic && saleable(shkp, obj) &&
1850 | (obj->oclass == WEAPON_CLASS || obj->oclass == ARMOR_CLASS ||
1851 | obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS ||
1852 | obj->otyp == MIRROR)) {
1853 | was_unknown |= !objects[obj->otyp].oc_name_known;
1854 | makeknown(obj->otyp);
1855 | }
1856 | obj_name = doname(obj);
1857 | /* Use an alternate message when extra information is being provided */
1858 | if (was_unknown) {
1859 | Sprintf(fmtbuf, "%%s; you %s", fmt);
1860 | obj_name[0] = highc(obj_name[0]);
1861 | pline(fmtbuf, obj_name, (obj->quan > 1) ? "them" : "it",
1862 | amt, plur(amt), arg);
1863 | } else {
1864 | You(fmt, obj_name, amt, plur(amt), arg);
1865 | }
1866 | }
1867 |
1868 | void
1869 | addtobill(obj, ininv, dummy, silent)
1870 | register struct obj *obj;
1871 | register boolean ininv, dummy, silent;
1872 | {
1873 | register struct monst *shkp;
1874 | register char roomno = *u.ushops;
1875 | long ltmp = 0L, cltmp = 0L, gltmp = 0L;
1876 | register boolean container = Has_contents(obj);
1877 |
1878 | if(!*u.ushops) return;
1879 |
1880 | if(!(shkp = shop_keeper(roomno))) return;
1881 |
1882 | if(!inhishop(shkp)) return;
1883 |
1884 | if(/* perhaps we threw it away earlier */
1885 | onbill(obj, shkp, FALSE) ||
1886 | (obj->oclass == FOOD_CLASS && obj->oeaten)
1887 | ) return;
1888 |
1889 | if(ESHK(shkp)->billct == BILLSZ) {
1890 | You("got that for free!");
1891 | return;
1892 | }
1893 |
1894 | if(obj->oclass == GOLD_CLASS) {
1895 | costly_gold(obj->ox, obj->oy, obj->quan);
1896 | return;
1897 | }
1898 |
1899 | if(!obj->no_charge)
1900 | ltmp = get_cost(obj, shkp);
1901 |
1902 | if (obj->no_charge && !container) {
1903 | obj->no_charge = 0;
1904 | return;
1905 | }
1906 |
1907 | if(container) {
1908 | if(obj->cobj == (struct obj *)0) {
1909 | if(obj->no_charge) {
1910 | obj->no_charge = 0;
1911 | return;
1912 | } else {
1913 | add_one_tobill(obj, dummy);
1914 | goto speak;
1915 | }
1916 | } else {
1917 | cltmp += contained_cost(obj, shkp, cltmp, FALSE);
1918 | gltmp += contained_gold(obj);
1919 | }
1920 |
1921 | if(ltmp) add_one_tobill(obj, dummy);
1922 | if(cltmp) bill_box_content(obj, ininv, dummy, shkp);
1923 | picked_container(obj); /* reset contained obj->no_charge */
1924 |
1925 | ltmp += cltmp;
1926 |
1927 | if(gltmp) {
1928 | costly_gold(obj->ox, obj->oy, gltmp);
1929 | if(!ltmp) return;
1930 | }
1931 |
1932 | if(obj->no_charge)
1933 | obj->no_charge = 0;
1934 |
1935 | } else /* i.e., !container */
1936 | add_one_tobill(obj, dummy);
1937 | speak:
1938 | if (shkp->mcanmove && !shkp->msleeping && !silent) {
1939 | char buf[BUFSZ];
1940 |
1941 | if(!ltmp) {
1942 | pline("%s has no interest in %s.", Monnam(shkp),
1943 | the(xname(obj)));
1944 | return;
1945 | }
1946 | Strcpy(buf, "\"For you, ");
1947 | if (ANGRY(shkp)) Strcat(buf, "scum ");
1948 | else {
1949 | static const char *honored[5] = {
1950 | "good", "honored", "most gracious", "esteemed",
1951 | "most renowned and sacred"
1952 | };
1953 | Strcat(buf, honored[rn2(4) + u.uevent.udemigod]);
1954 | if (!is_human(youmonst.data)) Strcat(buf, " creature");
1955 | else
1956 | Strcat(buf, (flags.female) ? " lady" : " sir");
1957 | }
1958 | if(ininv) {
1959 | long quan = obj->quan;
1960 | obj->quan = 1L; /* fool xname() into giving singular */
1961 | pline("%s; only %ld %s %s.\"", buf, ltmp,
1962 | (quan > 1L) ? "per" : "for this", xname(obj));
1963 | obj->quan = quan;
1964 | } else
1965 | pline("%s will cost you %ld zorkmid%s%s.",
1966 | The(xname(obj)), ltmp, plur(ltmp),
1967 | (obj->quan > 1L) ? " each" : "");
1968 | } else if(!silent) {
1969 | if(ltmp) pline_The("list price of %s is %ld zorkmid%s%s.",
1970 | the(xname(obj)), ltmp, plur(ltmp),
1971 | (obj->quan > 1L) ? " each" : "");
1972 | else pline("%s does not notice.", Monnam(shkp));
1973 | }
1974 | }
1975 |
1976 | void
1977 | splitbill(obj, otmp)
1978 | register struct obj *obj, *otmp;
1979 | {
1980 | /* otmp has been split off from obj */
1981 | register struct bill_x *bp;
1982 | register long tmp;
1983 | register struct monst *shkp = shop_keeper(*u.ushops);
1984 |
1985 | if(!shkp || !inhishop(shkp)) {
1986 | impossible("splitbill: no resident shopkeeper??");
1987 | return;
1988 | }
1989 | bp = onbill(obj, shkp, FALSE);
1990 | if(!bp) {
1991 | impossible("splitbill: not on bill?");
1992 | return;
1993 | }
1994 | if(bp->bquan < otmp->quan) {
1995 | impossible("Negative quantity on bill??");
1996 | }
1997 | if(bp->bquan == otmp->quan) {
1998 | impossible("Zero quantity on bill??");
1999 | }
2000 | bp->bquan -= otmp->quan;
2001 |
2002 | if(ESHK(shkp)->billct == BILLSZ) otmp->unpaid = 0;
2003 | else {
2004 | tmp = bp->price;
2005 | bp = &(ESHK(shkp)->bill_p[ESHK(shkp)->billct]);
2006 | bp->bo_id = otmp->o_id;
2007 | bp->bquan = otmp->quan;
2008 | bp->useup = 0;
2009 | bp->price = tmp;
2010 | ESHK(shkp)->billct++;
2011 | }
2012 | }
2013 |
2014 | STATIC_OVL void
2015 | sub_one_frombill(obj, shkp)
2016 | register struct obj *obj;
2017 | register struct monst *shkp;
2018 | {
2019 | register struct bill_x *bp;
2020 |
2021 | if((bp = onbill(obj, shkp, FALSE)) != 0) {
2022 | register struct obj *otmp;
2023 |
2024 | obj->unpaid = 0;
2025 | if(bp->bquan > obj->quan){
2026 | otmp = newobj(0);
2027 | *otmp = *obj;
2028 | bp->bo_id = otmp->o_id = flags.ident++;
2029 | otmp->where = OBJ_FREE;
2030 | otmp->quan = (bp->bquan -= obj->quan);
2031 | otmp->owt = 0; /* superfluous */
2032 | otmp->onamelth = 0;
2033 | otmp->oxlth = 0;
2034 | otmp->oattached = OATTACHED_NOTHING;
2035 | bp->useup = 1;
2036 | add_to_billobjs(otmp);
2037 | return;
2038 | }
2039 | ESHK(shkp)->billct--;
2040 | #ifdef DUMB
2041 | {
2042 | /* DRS/NS 2.2.6 messes up -- Peter Kendell */
2043 | int indx = ESHK(shkp)->billct;
2044 | *bp = ESHK(shkp)->bill_p[indx];
2045 | }
2046 | #else
2047 | *bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct];
2048 | #endif
2049 | return;
2050 | } else if (obj->unpaid) {
2051 | impossible("sub_one_frombill: unpaid object not on bill");
2052 | obj->unpaid = 0;
2053 | }
2054 | }
2055 |
2056 | /* recursive check of unpaid objects within nested containers. */
2057 | void
2058 | subfrombill(obj, shkp)
2059 | register struct obj *obj;
2060 | register struct monst *shkp;
2061 | {
2062 | register struct obj *otmp;
2063 |
2064 | sub_one_frombill(obj, shkp);
2065 |
2066 | if (Has_contents(obj))
2067 | for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {
2068 | if(otmp->oclass == GOLD_CLASS) continue;
2069 |
2070 | if (Has_contents(otmp))
2071 | subfrombill(otmp, shkp);
2072 | else
2073 | sub_one_frombill(otmp, shkp);
2074 | }
2075 | }
2076 |
2077 | #endif /*OVLB*/
2078 | #ifdef OVL3
2079 |
2080 | STATIC_OVL long
2081 | stolen_container(obj, shkp, price, ininv)
2082 | register struct obj *obj;
2083 | register struct monst *shkp;
2084 | long price;
2085 | register boolean ininv;
2086 | {
2087 | register struct obj *otmp;
2088 |
2089 | if(ininv && obj->unpaid)
2090 | price += get_cost(obj, shkp);
2091 | else {
2092 | if(!obj->no_charge)
2093 | price += get_cost(obj, shkp);
2094 | obj->no_charge = 0;
2095 | }
2096 |
2097 | /* the price of contained objects, if any */
2098 | for(otmp = obj->cobj; otmp; otmp = otmp->nobj) {
2099 |
2100 | if(otmp->oclass == GOLD_CLASS) continue;
2101 |
2102 | if (!Has_contents(otmp)) {
2103 | if(ininv) {
2104 | if(otmp->unpaid)
2105 | price += get_cost(otmp, shkp);
2106 | } else {
2107 | if(!otmp->no_charge) {
2108 | if(!(otmp->oclass == BALL_CLASS ||
2109 | (otmp->oclass == FOOD_CLASS && otmp->oeaten) ||
2110 | (Is_candle(otmp) && otmp->age <
2111 | 20L * (long)objects[otmp->otyp].oc_cost))
2112 | ) price += get_cost(otmp, shkp);
2113 | }
2114 | otmp->no_charge = 0;
2115 | }
2116 | } else
2117 | price += stolen_container(otmp, shkp, price, ininv);
2118 | }
2119 |
2120 | return(price);
2121 | }
2122 | #endif /*OVL3*/
2123 | #ifdef OVLB
2124 |
2125 | long
2126 | stolen_value(obj, x, y, peaceful, silent)
2127 | register struct obj *obj;
2128 | register xchar x, y;
2129 | register boolean peaceful, silent;
2130 | {
2131 | register long value = 0L, gvalue = 0L;
2132 | register struct monst *shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
2133 |
2134 | if (!shkp || !inhishop(shkp))
2135 | return (0L);
2136 |
2137 | if(obj->oclass == GOLD_CLASS) {
2138 | gvalue += obj->quan;
2139 | } else if (Has_contents(obj)) {
2140 | register boolean ininv = !!count_unpaid(obj->cobj);
2141 |
2142 | value += stolen_container(obj, shkp, value, ininv);
2143 | if(!ininv) gvalue += contained_gold(obj);
2144 | } else if (!obj->no_charge && saleable(shkp, obj)) {
2145 | value += get_cost(obj, shkp);
2146 | }
2147 |
2148 | if(gvalue + value == 0L) return(0L);
2149 |
2150 | value += gvalue;
2151 |
2152 | if(peaceful) {
2153 | value = check_credit(value, shkp);
2154 | ESHK(shkp)->debit += value;
2155 |
2156 | if(!silent) {
2157 | if(obj->oclass == GOLD_CLASS)
2158 | You("owe %s %ld zorkmids!", mon_nam(shkp), value);
2159 | else You("owe %s %ld zorkmids for %s!",
2160 | mon_nam(shkp),
2161 | value,
2162 | obj->quan > 1L ? "them" : "it");
2163 | }
2164 | } else {
2165 | ESHK(shkp)->robbed += value;
2166 |
2167 | if(!silent) {
2168 | if(cansee(shkp->mx, shkp->my)) {
2169 | if(ESHK(shkp)->customer[0] == 0)
2170 | (void) strncpy(ESHK(shkp)->customer,plname,PL_NSIZ);
2171 | Norep("%s booms: \"%s, you are a thief!\"",
2172 | Monnam(shkp), plname);
2173 | } else Norep("You hear a scream, \"Thief!\"");
2174 | }
2175 | hot_pursuit(shkp);
2176 | (void) angry_guards(FALSE);
2177 | }
2178 | return(value);
2179 | }
2180 |
2181 | /* auto-response flag for/from "sell foo?" 'a' => 'y', 'q' => 'n' */
2182 | static char sell_response = 'a';
2183 | static boolean sell_voluntarily = FALSE;
2184 |
2185 | void
2186 | sellobj_state(deliberate) /* called from dodrop(do.c) and doddrop() */
2187 | boolean deliberate;
2188 | {
2189 | /* If we're deliberately dropping something, there's no automatic
2190 | response to the shopkeeper's "want to sell" query; however, if we
2191 | accidentally drop anything, the shk will buy it/them without asking.
2192 | This retains the old pre-query risk that slippery fingers while in
2193 | shops entailed: you drop it, you've lost it.
2194 | */
2195 | sell_response = deliberate ? '\0' : 'a';
2196 | sell_voluntarily = deliberate;
2197 | }
2198 |
2199 | void
2200 | sellobj(obj, x, y)
2201 | register struct obj *obj;
2202 | xchar x, y;
2203 | {
2204 | register struct monst *shkp;
2205 | register struct eshk *eshkp;
2206 | long ltmp = 0L, cltmp = 0L, gltmp = 0L, offer;
2207 | boolean saleitem, cgold = FALSE, container = Has_contents(obj);
2208 | boolean isgold = (obj->oclass == GOLD_CLASS);
2209 |
2210 | if(!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) ||
2211 | !inhishop(shkp)) return;
2212 | if(!costly_spot(x, y)) return;
2213 | if(!*u.ushops) return;
2214 |
2215 | if(obj->unpaid && !container && !isgold) {
2216 | sub_one_frombill(obj, shkp);
2217 | return;
2218 | }
2219 | if(container) {
2220 | /* find the price of content before subfrombill */
2221 | cltmp += contained_cost(obj, shkp, cltmp, TRUE);
2222 | /* find the value of contained gold */
2223 | gltmp += contained_gold(obj);
2224 | cgold = (gltmp > 0L);
2225 | }
2226 |
2227 | saleitem = saleable(shkp, obj);
2228 | if(!isgold && !obj->unpaid && saleitem)
2229 | ltmp = set_cost(obj, shkp);
2230 |
2231 | offer = ltmp + cltmp;
2232 |
2233 | /* get one case out of the way: nothing to sell, and no gold */
2234 | if(!isgold && (offer + gltmp) == 0L) {
2235 | register boolean unpaid = (obj->unpaid ||
2236 | (container && count_unpaid(obj->cobj)));
2237 |
2238 | if(container) {
2239 | dropped_container(obj, shkp, FALSE);
2240 | if(!obj->unpaid && !saleitem)
2241 | obj->no_charge = 1;
2242 | if(obj->unpaid || count_unpaid(obj->cobj))
2243 | subfrombill(obj, shkp);
2244 | } else obj->no_charge = 1;
2245 |
2246 | if(!unpaid)
2247 | pline("%s seems uninterested.", Monnam(shkp));
2248 | return;
2249 | }
2250 |
2251 | /* you dropped something of your own - probably want to sell it */
2252 | if (shkp->msleeping || !shkp->mcanmove) {
2253 | if (container)
2254 | dropped_container(obj, shkp, TRUE);
2255 | if (!obj->unpaid)
2256 | obj->no_charge = 1;
2257 | if (!shkp->mcanmove) {
2258 | if(ANGRY(shkp) && !rn2(4))
2259 | pline("%s utters a curse.", Monnam(shkp));
2260 | else pline("%s is indisposed.", Monnam(shkp));
2261 | } else if(!rn2(3)) {
2262 | pline("%s snores indifferently.", Monnam(shkp));
2263 | }
2264 | subfrombill(obj, shkp);
2265 | return;
2266 | }
2267 |
2268 | eshkp = ESHK(shkp);
2269 |
2270 | if (ANGRY(shkp)) { /* they become shop-objects, no pay */
2271 | pline("Thank you, scum!");
2272 | subfrombill(obj, shkp);
2273 | return;
2274 | }
2275 |
2276 | if(eshkp->robbed) { /* shkp is not angry? */
2277 | if(isgold) offer = obj->quan;
2278 | else if(cgold) offer += cgold;
2279 | if((eshkp->robbed -= offer < 0L))
2280 | eshkp->robbed = 0L;
2281 | if(offer) verbalize(
2282 | "Thank you for your contribution to restock this recently plundered shop.");
2283 | subfrombill(obj, shkp);
2284 | return;
2285 | }
2286 |
2287 | if(isgold || cgold) {
2288 | if(!cgold) gltmp = obj->quan;
2289 |
2290 | if(eshkp->debit >= gltmp) {
2291 | if(eshkp->loan) { /* you carry shop's gold */
2292 | if(eshkp->loan >= gltmp)
2293 | eshkp->loan -= gltmp;
2294 | else eshkp->loan = 0L;
2295 | }
2296 | eshkp->debit -= gltmp;
2297 | Your("debt is %spaid off.",
2298 | eshkp->debit ? "partially " : "");
2299 | } else {
2300 | long delta = gltmp - eshkp->debit;
2301 |
2302 | eshkp->credit += delta;
2303 | if(eshkp->debit) {
2304 | eshkp->debit = 0L;
2305 | eshkp->loan = 0L;
2306 | Your("debt is paid off.");
2307 | }
2308 | pline("%ld zorkmid%s added to your credit.",
2309 | delta, delta > 1L ? "s are" : " is");
2310 | }
2311 | if(offer) goto move_on;
2312 | else {
2313 | if(!isgold) {
2314 | if (container)
2315 | dropped_container(obj, shkp, FALSE);
2316 | if (!obj->unpaid && !saleitem) obj->no_charge = 1;
2317 | subfrombill(obj, shkp);
2318 | }
2319 | return;
2320 | }
2321 | }
2322 | move_on:
2323 | if((!saleitem && !(container && cltmp > 0L))
2324 | || eshkp->billct == BILLSZ
2325 | || obj->oclass == BALL_CLASS
2326 | || obj->oclass == CHAIN_CLASS || offer == 0L
2327 | || (obj->oclass == FOOD_CLASS && obj->oeaten)
2328 | || (Is_candle(obj) &&
2329 | obj->age < 20L * (long)objects[obj->otyp].oc_cost)) {
2330 | pline("%s seems not interested%s.", Monnam(shkp),
2331 | cgold ? " in the rest" : "");
2332 | if (container)
2333 | dropped_container(obj, shkp, FALSE);
2334 | obj->no_charge = 1;
2335 | return;
2336 | }
2337 |
2338 | if(!shkp->mgold) {
2339 | char c, qbuf[BUFSZ];
2340 | long tmpcr = ((offer * 9L) / 10L) + (offer <= 1L);
2341 |
2342 | if (!sell_voluntarily) {
2343 | c = sell_response = 'y';
2344 | } else if (sell_response != 'n') {
2345 | pline("%s cannot pay you at present.", Monnam(shkp));
2346 | Sprintf(qbuf,
2347 | "Will you accept %ld zorkmid%s in credit for %s?",
2348 | tmpcr, plur(tmpcr), doname(obj));
2349 | /* won't accept 'a' response here */
2350 | c = ynq(qbuf);
2351 | } else /* previously specified "quit" */
2352 | c = 'n';
2353 |
2354 | if (c == 'y') {
2355 | shk_names_obj(shkp, obj, sell_voluntarily ?
2356 | "traded %s for %ld zorkmid%s in %scredit." :
2357 | "relinquish %s and acquire %ld zorkmid%s in %scredit.",
2358 | tmpcr,
2359 | (eshkp->credit > 0L) ? "additional " : "");
2360 | eshkp->credit += tmpcr;
2361 | subfrombill(obj, shkp);
2362 | } else {
2363 | if (c == 'q') sell_response = 'n';
2364 | if (container)
2365 | dropped_container(obj, shkp, FALSE);
2366 | if (!obj->unpaid) obj->no_charge = 1;
2367 | subfrombill(obj, shkp);
2368 | }
2369 | } else {
2370 | char qbuf[BUFSZ];
2371 | boolean short_funds = (offer > shkp->mgold);
2372 |
2373 | if (short_funds) offer = shkp->mgold;
2374 |
2375 | if (!sell_response) {
2376 | Sprintf(qbuf,
2377 | "%s offers%s %ld gold piece%s for%s %s %s. Sell %s?",
2378 | Monnam(shkp), short_funds ? " only" : "",
2379 | offer, plur(offer),
2380 | (!ltmp && cltmp) ? " the contents of" : "",
2381 | obj->unpaid ? "the" : "your", xname(obj),
2382 | (obj->quan == 1L) ? "it" : "them");
2383 | } else qbuf[0] = '\0'; /* just to pacify lint */
2384 |
2385 | switch (sell_response ? sell_response : ynaq(qbuf)) {
2386 | case 'q': sell_response = 'n';
2387 | case 'n': if (container)
2388 | dropped_container(obj, shkp, FALSE);
2389 | if (!obj->unpaid) obj->no_charge = 1;
2390 | subfrombill(obj, shkp);
2391 | break;
2392 | case 'a': sell_response = 'y';
2393 | case 'y': if (container)
2394 | dropped_container(obj, shkp, TRUE);
2395 | if (!obj->unpaid && !saleitem) obj->no_charge = 1;
2396 | subfrombill(obj, shkp);
2397 | pay(-offer, shkp);
2398 | shk_names_obj(shkp, obj, sell_voluntarily ?
2399 | "sold %s for %ld gold piece%s.%s" :
2400 | "relinquish %s and receive %ld gold piece%s in compensation.%s",
2401 | offer, "");
2402 | break;
2403 | default: impossible("invalid sell response");
2404 | }
2405 | }
2406 | }
2407 |
2408 | int
2409 | doinvbill(mode)
2410 | int mode; /* 0: deliver count 1: paged */
2411 | {
2412 | #ifdef __SASC
2413 | void sasc_bug(struct obj *, unsigned);
2414 | #endif
2415 | struct monst *shkp;
2416 | struct eshk *eshkp;
2417 | struct bill_x *bp, *end_bp;
2418 | struct obj *obj;
2419 | long totused;
2420 | char *buf_p;
2421 | winid datawin;
2422 |
2423 | shkp = shop_keeper(*u.ushops);
2424 | if (!shkp || !inhishop(shkp)) {
2425 | if (mode != 0) impossible("doinvbill: no shopkeeper?");
2426 | return 0;
2427 | }
2428 | eshkp = ESHK(shkp);
2429 |
2430 | if (mode == 0) {
2431 | /* count expended items, so that the `I' command can decide
2432 | whether to include 'x' in its prompt string */
2433 | int cnt = !eshkp->debit ? 0 : 1;
2434 |
2435 | for (bp = eshkp->bill_p, end_bp = &eshkp->bill_p[eshkp->billct];
2436 | bp < end_bp; bp++)
2437 | if (bp->useup ||
2438 | ((obj = bp_to_obj(bp)) != 0 && obj->quan < bp->bquan))
2439 | cnt++;
2440 | return cnt;
2441 | }
2442 |
2443 | datawin = create_nhwindow(NHW_MENU);
2444 | putstr(datawin, 0, "Unpaid articles already used up:");
2445 | putstr(datawin, 0, "");
2446 |
2447 | totused = 0L;
2448 | for (bp = eshkp->bill_p, end_bp = &eshkp->bill_p[eshkp->billct];
2449 | bp < end_bp; bp++) {
2450 | obj = bp_to_obj(bp);
2451 | if(!obj) {
2452 | impossible("Bad shopkeeper administration.");
2453 | goto quit;
2454 | }
2455 | if(bp->useup || bp->bquan > obj->quan) {
2456 | long oquan, uquan, thisused;
2457 | unsigned save_unpaid;
2458 |
2459 | save_unpaid = obj->unpaid;
2460 | oquan = obj->quan;
2461 | uquan = (bp->useup ? bp->bquan : bp->bquan - oquan);
2462 | thisused = bp->price * uquan;
2463 | totused += thisused;
2464 | obj->quan = uquan; /* cheat doname */
2465 | obj->unpaid = 0; /* ditto */
2466 | /* Why 'x'? To match `I x', more or less. */
2467 | buf_p = xprname(obj, (char *)0, 'x', FALSE, thisused);
2468 | obj->quan = oquan; /* restore value */
2469 | #ifdef __SASC
2470 | /* SAS/C 6.2 can't cope for some reason */
2471 | sasc_bug(obj,save_unpaid);
2472 | #else
2473 | obj->unpaid = save_unpaid;
2474 | #endif
2475 | putstr(datawin, 0, buf_p);
2476 | }
2477 | }
2478 | if (eshkp->debit) {
2479 | /* additional shop debt which has no itemization available */
2480 | if (totused) putstr(datawin, 0, "");
2481 | totused += eshkp->debit;
2482 | buf_p = xprname((struct obj *)0,
2483 | "usage charges and/or other fees",
2484 | GOLD_SYM, FALSE, eshkp->debit);
2485 | putstr(datawin, 0, buf_p);
2486 | }
2487 | buf_p = xprname((struct obj *)0, "Total:", '*', FALSE, totused);
2488 | putstr(datawin, 0, "");
2489 | putstr(datawin, 0, buf_p);
2490 | display_nhwindow(datawin, FALSE);
2491 | quit:
2492 | destroy_nhwindow(datawin);
2493 | return(0);
2494 | }
2495 |
2496 | #define HUNGRY 2
2497 |
2498 | STATIC_OVL long
2499 | getprice(obj, shk_buying)
2500 | register struct obj *obj;
2501 | boolean shk_buying;
2502 | {
2503 | register long tmp = (long) objects[obj->otyp].oc_cost;
2504 |
2505 | switch(obj->oclass) {
2506 | case FOOD_CLASS:
2507 | /* simpler hunger check, (2-4)*cost */
2508 | if (u.uhs >= HUNGRY && !shk_buying) tmp *= (long) u.uhs;
2509 | if (obj->oeaten) tmp = 0L;
2510 | break;
2511 | case WAND_CLASS:
2512 | if (obj->spe == -1) tmp = 0L;
2513 | break;
2514 | case POTION_CLASS:
2515 | if (obj->otyp == POT_WATER && !obj->blessed && !obj->cursed)
2516 | tmp = 0L;
2517 | break;
2518 | case ARMOR_CLASS:
2519 | case WEAPON_CLASS:
2520 | if (obj->spe > 0) tmp += 10L * (long) obj->spe;
2521 | break;
2522 | case TOOL_CLASS:
2523 | if (Is_candle(obj) &&
2524 | obj->age < 20L * (long)objects[obj->otyp].oc_cost)
2525 | tmp /= 2L;
2526 | break;
2527 | }
2528 | if (obj->oartifact) tmp *= 25L;
2529 | return tmp;
2530 | }
2531 |
2532 | /* shk catches thrown pick-axe */
2533 | int
2534 | shkcatch(obj, x, y)
2535 | register struct obj *obj;
2536 | register xchar x, y;
2537 | {
2538 | register struct monst *shkp;
2539 |
2540 | if (!(shkp = shop_keeper(inside_shop(x, y))) ||
2541 | !inhishop(shkp)) return(0);
2542 |
2543 | if (shkp->mcanmove && !shkp->msleeping &&
2544 | (*u.ushops != ESHK(shkp)->shoproom || !inside_shop(u.ux, u.uy)) &&
2545 | dist2(shkp->mx, shkp->my, x, y) < 3 &&
2546 | /* if it is the shk's pos, you hit and anger him */
2547 | (shkp->mx != x || shkp->my != y)) {
2548 | if (mnearto(shkp, x, y, TRUE))
2549 | verbalize("Out of my way, scum!");
2550 | if (cansee(x, y)) {
2551 | pline("%s nimbly catches %s.",
2552 | Monnam(shkp), the(xname(obj)));
2553 | if (!canspotmon(shkp))
2554 | map_invisible(x, y);
2555 | delay_output();
2556 | mark_synch();
2557 | }
2558 | subfrombill(obj, shkp);
2559 | (void) mpickobj(shkp, obj);
2560 | return(1);
2561 | }
2562 | return(0);
2563 | }
2564 |
2565 | void
2566 | add_damage(x, y, cost)
2567 | register xchar x, y;
2568 | long cost;
2569 | {
2570 | struct damage *tmp_dam;
2571 | char *shops;
2572 |
2573 | if (IS_DOOR(levl[x][y].typ)) {
2574 | struct monst *mtmp;
2575 |
2576 | /* Don't schedule for repair unless it's a real shop entrance */
2577 | for (shops = in_rooms(x, y, SHOPBASE); *shops; shops++)
2578 | if ((mtmp = shop_keeper(*shops)) != 0 &&
2579 | x == ESHK(mtmp)->shd.x && y == ESHK(mtmp)->shd.y)
2580 | break;
2581 | if (!*shops) return;
2582 | }
2583 | for (tmp_dam = level.damagelist; tmp_dam; tmp_dam = tmp_dam->next)
2584 | if (tmp_dam->place.x == x && tmp_dam->place.y == y) {
2585 | tmp_dam->cost += cost;
2586 | return;
2587 | }
2588 | tmp_dam = (struct damage *)alloc((unsigned)sizeof(struct damage));
2589 | tmp_dam->when = monstermoves;
2590 | tmp_dam->place.x = x;
2591 | tmp_dam->place.y = y;
2592 | tmp_dam->cost = cost;
2593 | tmp_dam->typ = levl[x][y].typ;
2594 | tmp_dam->next = level.damagelist;
2595 | level.damagelist = tmp_dam;
2596 | /* If player saw damage, display as a wall forever */
2597 | if (cansee(x, y))
2598 | levl[x][y].seenv = SVALL;
2599 | }
2600 |
2601 | #endif /*OVLB*/
2602 | #ifdef OVL0
2603 |
2604 | /*
2605 | * Do something about damage. Either (!croaked) try to repair it, or
2606 | * (croaked) just discard damage structs for non-shared locations, since
2607 | * they'll never get repaired. Assume that shared locations will get
2608 | * repaired eventually by the other shopkeeper(s). This might be an erroneous
2609 | * assumption (they might all be dead too), but we have no reasonable way of
2610 | * telling that.
2611 | */
2612 | STATIC_OVL
2613 | void
2614 | remove_damage(shkp, croaked)
2615 | register struct monst *shkp;
2616 | register boolean croaked;
2617 | {
2618 | register struct damage *tmp_dam, *tmp2_dam;
2619 | register boolean did_repair = FALSE, saw_door = FALSE;
2620 | register boolean saw_floor = FALSE, stop_picking = FALSE;
2621 | register boolean saw_untrap = FALSE;
2622 | uchar saw_walls = 0;
2623 |
2624 | tmp_dam = level.damagelist;
2625 | tmp2_dam = 0;
2626 | while (tmp_dam) {
2627 | register xchar x = tmp_dam->place.x, y = tmp_dam->place.y;
2628 | char shops[5];
2629 | int disposition;
2630 |
2631 | disposition = 0;
2632 | Strcpy(shops, in_rooms(x, y, SHOPBASE));
2633 | if (index(shops, ESHK(shkp)->shoproom)) {
2634 | if (croaked)
2635 | disposition = (shops[1])? 0 : 1;
2636 | else if (stop_picking)
2637 | disposition = repair_damage(shkp, tmp_dam, FALSE);
2638 | else {
2639 | /* Defer the stop_occupation() until after repair msgs */
2640 | if (closed_door(x, y))
2641 | stop_picking = picking_at(x, y);
2642 | disposition = repair_damage(shkp, tmp_dam, FALSE);
2643 | if (!disposition)
2644 | stop_picking = FALSE;
2645 | }
2646 | }
2647 |
2648 | if (!disposition) {
2649 | tmp2_dam = tmp_dam;
2650 | tmp_dam = tmp_dam->next;
2651 | continue;
2652 | }
2653 |
2654 | if (disposition > 1) {
2655 | did_repair = TRUE;
2656 | if (cansee(x, y)) {
2657 | if (IS_WALL(levl[x][y].typ))
2658 | saw_walls++;
2659 | else if (IS_DOOR(levl[x][y].typ))
2660 | saw_door = TRUE;
2661 | else if (disposition == 3) /* untrapped */
2662 | saw_untrap = TRUE;
2663 | else
2664 | saw_floor = TRUE;
2665 | }
2666 | }
2667 |
2668 | tmp_dam = tmp_dam->next;
2669 | if (!tmp2_dam) {
2670 | free((genericptr_t)level.damagelist);
2671 | level.damagelist = tmp_dam;
2672 | } else {
2673 | free((genericptr_t)tmp2_dam->next);
2674 | tmp2_dam->next = tmp_dam;
2675 | }
2676 | }
2677 | if (!did_repair)
2678 | return;
2679 | if (saw_walls) {
2680 | pline("Suddenly, %s section%s of wall close%s up!",
2681 | (saw_walls == 1) ? "a" : (saw_walls <= 3) ?
2682 | "some" : "several",
2683 | (saw_walls == 1) ? "" : "s", (saw_walls == 1) ? "s" : "");
2684 | if (saw_door)
2685 | pline_The("shop door reappears!");
2686 | if (saw_floor)
2687 | pline_The("floor is repaired!");
2688 | } else {
2689 | if (saw_door)
2690 | pline("Suddenly, the shop door reappears!");
2691 | else if (saw_floor)
2692 | pline("Suddenly, the floor damage is gone!");
2693 | else if (saw_untrap)
2694 | pline("Suddenly, the trap is removed from the floor!");
2695 | else if (inside_shop(u.ux, u.uy) == ESHK(shkp)->shoproom)
2696 | You_feel("more claustrophobic than before.");
2697 | else if (flags.soundok && !rn2(10))
2698 | Norep("The dungeon acoustics noticeably change.");
2699 | }
2700 | if (stop_picking)
2701 | stop_occupation();
2702 | }
2703 |
2704 | /*
2705 | * 0: repair postponed, 1: silent repair (no messages), 2: normal repair
2706 | * 3: untrap
2707 | */
2708 | int
2709 | repair_damage(shkp, tmp_dam, catchup)
2710 | register struct monst *shkp;
2711 | register struct damage *tmp_dam;
2712 | boolean catchup; /* restoring a level */
2713 | {
2714 | register xchar x, y, i;
2715 | xchar litter[9];
2716 | register struct monst *mtmp;
2717 | register struct obj *otmp;
2718 | register struct trap *ttmp;
2719 |
2720 | if ((monstermoves - tmp_dam->when) < REPAIR_DELAY)
2721 | return(0);
2722 | if (shkp->msleeping || !shkp->mcanmove || ESHK(shkp)->following)
2723 | return(0);
2724 | x = tmp_dam->place.x;
2725 | y = tmp_dam->place.y;
2726 | if (!IS_ROOM(tmp_dam->typ)) {
2727 | if (x == u.ux && y == u.uy)
2728 | if (!Passes_walls)
2729 | return(0);
2730 | if (x == shkp->mx && y == shkp->my)
2731 | return(0);
2732 | if ((mtmp = m_at(x, y)) && (!passes_walls(mtmp->data)))
2733 | return(0);
2734 | }
2735 | if ((ttmp = t_at(x, y)) != 0) {
2736 | if (x == u.ux && y == u.uy)
2737 | if (!Passes_walls)
2738 | return(0);
2739 | if (ttmp->ttyp == LANDMINE || ttmp->ttyp == BEAR_TRAP) {
2740 | /* convert to an object */
2741 | otmp = mksobj((ttmp->ttyp == LANDMINE) ? LAND_MINE :
2742 | BEARTRAP, TRUE, FALSE);
2743 | otmp->quan= 1;
2744 | otmp->owt = weight(otmp);
2745 | (void) mpickobj(shkp, otmp);
2746 | }
2747 | deltrap(ttmp);
2748 | newsym(x, y);
2749 | return(3);
2750 | }
2751 | if (IS_ROOM(tmp_dam->typ)) {
2752 | /* No messages if player already filled trap door */
2753 | if (catchup || !ttmp)
2754 | return(1);
2755 | newsym(x, y);
2756 | return(2);
2757 | }
2758 | if (!ttmp && (tmp_dam->typ == levl[x][y].typ) &&
2759 | (!IS_DOOR(tmp_dam->typ) || (levl[x][y].doormask > D_BROKEN)))
2760 | /* No messages if player already replaced shop door */
2761 | return(1);
2762 | levl[x][y].typ = tmp_dam->typ;
2763 | (void) memset((genericptr_t)litter, 0, sizeof(litter));
2764 | if ((otmp = level.objects[x][y]) != 0) {
2765 | /* Scatter objects haphazardly into the shop */
2766 | #define NEED_UPDATE 1
2767 | #define OPEN 2
2768 | #define INSHOP 4
2769 | #define horiz(i) ((i%3)-1)
2770 | #define vert(i) ((i/3)-1)
2771 | for (i = 0; i < 9; i++) {
2772 | if ((i == 4) || (!ZAP_POS(levl[x+horiz(i)][y+vert(i)].typ)))
2773 | continue;
2774 | litter[i] = OPEN;
2775 | if (inside_shop(x+horiz(i),
2776 | y+vert(i)) == ESHK(shkp)->shoproom)
2777 | litter[i] |= INSHOP;
2778 | }
2779 | if (Punished && !u.uswallow &&
2780 | ((uchain->ox == x && uchain->oy == y) ||
2781 | (uball->ox == x && uball->oy == y))) {
2782 | /*
2783 | * Either the ball or chain is in the repair location.
2784 | *
2785 | * Take the easy way out and put ball&chain under hero.
2786 | */
2787 | verbalize("Get your junk out of my wall!");
2788 | unplacebc(); /* pick 'em up */
2789 | placebc(); /* put 'em down */
2790 | }
2791 | while ((otmp = level.objects[x][y]) != 0)
2792 | /* Don't mess w/ boulders -- just merge into wall */
2793 | if ((otmp->otyp == BOULDER) || (otmp->otyp == ROCK)) {
2794 | obj_extract_self(otmp);
2795 | obfree(otmp, (struct obj *)0);
2796 | } else {
2797 | while (!(litter[i = rn2(9)] & INSHOP));
2798 | remove_object(otmp);
2799 | place_object(otmp, x+horiz(i), y+vert(i));
2800 | litter[i] |= NEED_UPDATE;
2801 | }
2802 | }
2803 | if (catchup) return 1; /* repair occurred while off level */
2804 |
2805 | block_point(x, y);
2806 | if(IS_DOOR(tmp_dam->typ)) {
2807 | levl[x][y].doormask = D_CLOSED; /* arbitrary */
2808 | newsym(x, y);
2809 | } else {
2810 | /* don't set doormask - it is (hopefully) the same as it was */
2811 | /* if not, perhaps save it with the damage array... */
2812 |
2813 | if (IS_WALL(tmp_dam->typ) && cansee(x, y)) {
2814 | /* Player sees actual repair process, so they KNOW it's a wall */
2815 | levl[x][y].seenv = SVALL;
2816 | newsym(x, y);
2817 | }
2818 | /* Mark this wall as "repaired". There currently is no code */
2819 | /* to do anything about repaired walls, so don't do it. */
2820 | }
2821 | for (i = 0; i < 9; i++)
2822 | if (litter[i] & NEED_UPDATE)
2823 | newsym(x+horiz(i), y+vert(i));
2824 | return(2);
2825 | #undef NEED_UPDATE
2826 | #undef OPEN
2827 | #undef INSHOP
2828 | #undef vert
2829 | #undef horiz
2830 | }
2831 | #endif /*OVL0*/
2832 | #ifdef OVL3
2833 | /*
2834 | * shk_move: return 1: moved 0: didn't -1: let m_move do it -2: died
2835 | */
2836 | int
2837 | shk_move(shkp)
2838 | register struct monst *shkp;
2839 | {
2840 | register xchar gx,gy,omx,omy;
2841 | register int udist;
2842 | register schar appr;
2843 | register struct eshk *eshkp = ESHK(shkp);
2844 | int z;
2845 | boolean uondoor = FALSE, satdoor, avoid = FALSE, badinv;
2846 |
2847 | omx = shkp->mx;
2848 | omy = shkp->my;
2849 |
2850 | if (inhishop(shkp))
2851 | remove_damage(shkp, FALSE);
2852 |
2853 | if((udist = distu(omx,omy)) < 3 &&
2854 | (shkp->data != &mons[PM_GRID_BUG] || (omx==u.ux || omy==u.uy))) {
2855 | if(ANGRY(shkp) ||
2856 | (Conflict && !resist(shkp, RING_CLASS, 0, 0))) {
2857 | if(Displaced)
2858 | Your("displaced image doesn't fool %s!",
2859 | mon_nam(shkp));
2860 | (void) mattacku(shkp);
2861 | return(0);
2862 | }
2863 | if(eshkp->following) {
2864 | if(strncmp(eshkp->customer, plname, PL_NSIZ)) {
2865 | verbalize("%s, %s! I was looking for %s.",
2866 | Hello(shkp), plname, eshkp->customer);
2867 | eshkp->following = 0;
2868 | return(0);
2869 | }
2870 | if(moves > followmsg+4) {
2871 | verbalize("%s, %s! Didn't you forget to pay?",
2872 | Hello(shkp), plname);
2873 | followmsg = moves;
2874 | if (!rn2(9)) {
2875 | pline("%s doesn't like customers who don't pay.",
2876 | Monnam(shkp));
2877 | rile_shk(shkp);
2878 | }
2879 | }
2880 | if(udist < 2)
2881 | return(0);
2882 | }
2883 | }
2884 |
2885 | appr = 1;
2886 | gx = eshkp->shk.x;
2887 | gy = eshkp->shk.y;
2888 | satdoor = (gx == omx && gy == omy);
2889 | if(eshkp->following || ((z = holetime()) >= 0 && z*z <= udist)){
2890 | if(udist > 4)
2891 | return(-1); /* leave it to m_move */
2892 | gx = u.ux;
2893 | gy = u.uy;
2894 | } else if(ANGRY(shkp)) {
2895 | /* Move towards the hero if the shopkeeper can see him. */
2896 | if(shkp->mcansee && m_canseeu(shkp)) {
2897 | gx = u.ux;
2898 | gy = u.uy;
2899 | }
2900 | avoid = FALSE;
2901 | } else {
2902 | #define GDIST(x,y) (dist2(x,y,gx,gy))
2903 | if (Invis
2904 | #ifdef STEED
2905 | || u.usteed
2906 | #endif
2907 | ) {
2908 | avoid = FALSE;
2909 | } else {
2910 | uondoor = (u.ux == eshkp->shd.x && u.uy == eshkp->shd.y);
2911 | if(uondoor) {
2912 | badinv = (carrying(PICK_AXE) || carrying(DWARVISH_MATTOCK) ||
2913 | (Fast && (sobj_at(PICK_AXE, u.ux, u.uy) ||
2914 | sobj_at(DWARVISH_MATTOCK, u.ux, u.uy))));
2915 | if(satdoor && badinv)
2916 | return(0);
2917 | avoid = !badinv;
2918 | } else {
2919 | avoid = (*u.ushops && distu(gx,gy) > 8);
2920 | badinv = FALSE;
2921 | }
2922 |
2923 | if(((!eshkp->robbed && !eshkp->billct && !eshkp->debit)
2924 | || avoid) && GDIST(omx,omy) < 3) {
2925 | if (!badinv && !onlineu(omx,omy))
2926 | return(0);
2927 | if(satdoor)
2928 | appr = gx = gy = 0;
2929 | }
2930 | }
2931 | }
2932 |
2933 | return(move_special(shkp,inhishop(shkp),
2934 | appr,uondoor,avoid,omx,omy,gx,gy));
2935 | }
2936 |
2937 | #endif /*OVL3*/
2938 | #ifdef OVLB
2939 |
2940 | /* for use in levl_follower (mondata.c) */
2941 | boolean
2942 | is_fshk(mtmp)
2943 | register struct monst *mtmp;
2944 | {
2945 | return((boolean)(mtmp->isshk && ESHK(mtmp)->following));
2946 | }
2947 |
2948 | /* You are digging in the shop. */
2949 | void
2950 | shopdig(fall)
2951 | register int fall;
2952 | {
2953 | register struct monst *shkp = shop_keeper(*u.ushops);
2954 |
2955 | if(!shkp) return;
2956 |
2957 | if(!inhishop(shkp)) {
2958 | if (Role_if(PM_KNIGHT)) {
2959 | You_feel("like a common thief.");
2960 | adjalign(-sgn(u.ualign.type));
2961 | }
2962 | return;
2963 | }
2964 |
2965 | if(!fall) {
2966 | if(u.utraptype == TT_PIT)
2967 | verbalize("Be careful, %s, or you might fall through the floor.",
2968 | flags.female ? "madam" : "sir");
2969 | else
2970 | verbalize("%s, do not damage the floor here!",
2971 | flags.female ? "Madam" : "Sir");
2972 | if (Role_if(PM_KNIGHT)) {
2973 | You_feel("like a common thief.");
2974 | adjalign(-sgn(u.ualign.type));
2975 | }
2976 | } else if(!um_dist(shkp->mx, shkp->my, 5) &&
2977 | !shkp->msleeping && shkp->mcanmove &&
2978 | (ESHK(shkp)->billct || ESHK(shkp)->debit)) {
2979 | register struct obj *obj, *obj2;
2980 |
2981 | if (distu(shkp->mx, shkp->my) > 2) {
2982 | mnexto(shkp);
2983 | /* for some reason the shopkeeper can't come next to you */
2984 | if (distu(shkp->mx, shkp->my) > 2) {
2985 | pline("%s curses you in anger and frustration!",
2986 | shkname(shkp));
2987 | rile_shk(shkp);
2988 | return;
2989 | } else pline("%s leaps, and grabs your backpack!",
2990 | shkname(shkp));
2991 | } else pline("%s grabs your backpack!", shkname(shkp));
2992 |
2993 | for(obj = invent; obj; obj = obj2) {
2994 | obj2 = obj->nobj;
2995 | if(obj->owornmask) continue;
2996 | if(obj->otyp == LEASH && obj->leashmon) continue;
2997 | freeinv(obj);
2998 | subfrombill(obj, shkp);
2999 | (void) add_to_minv(shkp, obj); /* may free obj */
3000 | }
3001 | }
3002 | }
3003 |
3004 | #ifdef KOPS
3005 | STATIC_OVL void
3006 | makekops(mm)
3007 | coord *mm;
3008 | {
3009 | static const short k_mndx[4] = {
3010 | PM_KEYSTONE_KOP, PM_KOP_SERGEANT, PM_KOP_LIEUTENANT, PM_KOP_KAPTAIN
3011 | };
3012 | int k_cnt[4], cnt, mndx, k;
3013 |
3014 | k_cnt[0] = cnt = abs(depth(&u.uz)) + rnd(5);
3015 | k_cnt[1] = (cnt / 3) + 1; /* at least one sarge */
3016 | k_cnt[2] = (cnt / 6); /* maybe a lieutenant */
3017 | k_cnt[3] = (cnt / 9); /* and maybe a kaptain */
3018 |
3019 | for (k = 0; k < 4; k++) {
3020 | if ((cnt = k_cnt[k]) == 0) break;
3021 | mndx = k_mndx[k];
3022 | if (mvitals[mndx].mvflags & G_GONE) continue;
3023 |
3024 | while (cnt--)
3025 | if (enexto(mm, mm->x, mm->y, &mons[mndx]))
3026 | (void) makemon(&mons[mndx], mm->x, mm->y, NO_MM_FLAGS);
3027 | }
3028 | }
3029 | #endif /* KOPS */
3030 |
3031 | void
3032 | pay_for_damage(dmgstr)
3033 | const char *dmgstr;
3034 | {
3035 | register struct monst *shkp = (struct monst *)0;
3036 | char shops_affected[5];
3037 | register boolean uinshp = (*u.ushops != '\0');
3038 | char qbuf[80];
3039 | register xchar x, y;
3040 | boolean dugwall = !strcmp(dmgstr, "dig into") || /* wand */
3041 | !strcmp(dmgstr, "damage"); /* pick-axe */
3042 | struct damage *tmp_dam, *appear_here = 0;
3043 | /* any number >= (80*80)+(24*24) would do, actually */
3044 | long cost_of_damage = 0L;
3045 | unsigned int nearest_shk = 7000, nearest_damage = 7000;
3046 | int picks = 0;
3047 |
3048 | for (tmp_dam = level.damagelist;
3049 | (tmp_dam && (tmp_dam->when == monstermoves));
3050 | tmp_dam = tmp_dam->next) {
3051 | char *shp;
3052 |
3053 | if (!tmp_dam->cost)
3054 | continue;
3055 | cost_of_damage += tmp_dam->cost;
3056 | Strcpy(shops_affected,
3057 | in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE));
3058 | for (shp = shops_affected; *shp; shp++) {
3059 | struct monst *tmp_shk;
3060 | unsigned int shk_distance;
3061 |
3062 | if (!(tmp_shk = shop_keeper(*shp)))
3063 | continue;
3064 | if (tmp_shk == shkp) {
3065 | unsigned int damage_distance =
3066 | distu(tmp_dam->place.x, tmp_dam->place.y);
3067 |
3068 | if (damage_distance < nearest_damage) {
3069 | nearest_damage = damage_distance;
3070 | appear_here = tmp_dam;
3071 | }
3072 | continue;
3073 | }
3074 | if (!inhishop(tmp_shk))
3075 | continue;
3076 | shk_distance = distu(tmp_shk->mx, tmp_shk->my);
3077 | if (shk_distance > nearest_shk)
3078 | continue;
3079 | if ((shk_distance == nearest_shk) && picks) {
3080 | if (rn2(++picks))
3081 | continue;
3082 | } else
3083 | picks = 1;
3084 | shkp = tmp_shk;
3085 | nearest_shk = shk_distance;
3086 | appear_here = tmp_dam;
3087 | nearest_damage = distu(tmp_dam->place.x, tmp_dam->place.y);
3088 | }
3089 | }
3090 |
3091 | if (!cost_of_damage || !shkp)
3092 | return;
3093 |
3094 | x = appear_here->place.x;
3095 | y = appear_here->place.y;
3096 |
3097 | /* not the best introduction to the shk... */
3098 | (void) strncpy(ESHK(shkp)->customer,plname,PL_NSIZ);
3099 |
3100 | /* if the shk is already on the war path, be sure it's all out */
3101 | if(ANGRY(shkp) || ESHK(shkp)->following) {
3102 | hot_pursuit(shkp);
3103 | return;
3104 | }
3105 |
3106 | /* if the shk is not in their shop.. */
3107 | if(!*in_rooms(shkp->mx,shkp->my,SHOPBASE)) {
3108 | if(!cansee(shkp->mx, shkp->my))
3109 | return;
3110 | goto getcad;
3111 | }
3112 |
3113 | if(uinshp) {
3114 | if(um_dist(shkp->mx, shkp->my, 1) &&
3115 | !um_dist(shkp->mx, shkp->my, 3)) {
3116 | pline("%s leaps towards you!", shkname(shkp));
3117 | mnexto(shkp);
3118 | }
3119 | if(um_dist(shkp->mx, shkp->my, 1)) goto getcad;
3120 | } else {
3121 | /*
3122 | * Make shkp show up at the door. Effect: If there is a monster
3123 | * in the doorway, have the hero hear the shopkeeper yell a bit,
3124 | * pause, then have the shopkeeper appear at the door, having
3125 | * yanked the hapless critter out of the way.
3126 | */
3127 | if (MON_AT(x, y)) {
3128 | if(flags.soundok) {
3129 | You_hear("an angry voice:");
3130 | verbalize("Out of my way, scum!");
3131 | wait_synch();
3132 | #if defined(UNIX) || defined(VMS)
3133 | # if defined(SYSV) || defined(ULTRIX) || defined(VMS)
3134 | (void)
3135 | # endif
3136 | sleep(1);
3137 | #endif
3138 | }
3139 | }
3140 | (void) mnearto(shkp, x, y, TRUE);
3141 | }
3142 |
3143 | if((um_dist(x, y, 1) && !uinshp) ||
3144 | (u.ugold + ESHK(shkp)->credit) < cost_of_damage
3145 | || !rn2(50)) {
3146 | if(um_dist(x, y, 1) && !uinshp) {
3147 | pline("%s shouts:", shkname(shkp));
3148 | verbalize("Who dared %s my %s?", dmgstr,
3149 | dugwall ? "shop" : "door");
3150 | } else {
3151 | getcad:
3152 | verbalize("How dare you %s my %s?", dmgstr,
3153 | dugwall ? "shop" : "door");
3154 | }
3155 | hot_pursuit(shkp);
3156 | return;
3157 | }
3158 |
3159 | if(Invis) Your("invisibility does not fool %s!", shkname(shkp));
3160 | Sprintf(qbuf,"\"Cad! You did %ld zorkmids worth of damage!\" Pay? ",
3161 | cost_of_damage);
3162 | if(yn(qbuf) != 'n') {
3163 | cost_of_damage = check_credit(cost_of_damage, shkp);
3164 | u.ugold -= cost_of_damage;
3165 | shkp->mgold += cost_of_damage;
3166 | flags.botl = 1;
3167 | pline("Mollified, %s accepts your restitution.",
3168 | shkname(shkp));
3169 | /* move shk back to his home loc */
3170 | home_shk(shkp, FALSE);
3171 | pacify_shk(shkp);
3172 | } else {
3173 | verbalize("Oh, yes! You'll pay!");
3174 | hot_pursuit(shkp);
3175 | adjalign(-sgn(u.ualign.type));
3176 | }
3177 | }
3178 | #endif /*OVLB*/
3179 | #ifdef OVL0
3180 | /* called in dokick.c when we kick an object that might be in a store */
3181 | boolean
3182 | costly_spot(x, y)
3183 | register xchar x, y;
3184 | {
3185 | register struct monst *shkp;
3186 |
3187 | if (!level.flags.has_shop) return FALSE;
3188 | shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
3189 | if(!shkp || !inhishop(shkp)) return(FALSE);
3190 |
3191 | return((boolean)(inside_shop(x, y) &&
3192 | !(x == ESHK(shkp)->shk.x &&
3193 | y == ESHK(shkp)->shk.y)));
3194 | }
3195 | #endif /*OVL0*/
3196 | #ifdef OVLB
3197 |
3198 | /* called by dotalk(sounds.c) when #chatting; returns obj if location
3199 | contains shop goods and shopkeeper is willing & able to speak */
3200 | struct obj *
3201 | shop_object(x, y)
3202 | register xchar x, y;
3203 | {
3204 | register struct obj *otmp;
3205 | register struct monst *shkp;
3206 |
3207 | if(!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) || !inhishop(shkp))
3208 | return(struct obj *)0;
3209 |
3210 | for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
3211 | if (otmp->oclass != GOLD_CLASS)
3212 | break;
3213 | /* note: otmp might have ->no_charge set, but that's ok */
3214 | return (otmp && costly_spot(x, y) && NOTANGRY(shkp)
3215 | && shkp->mcanmove && !shkp->msleeping)
3216 | ? otmp : (struct obj *)0;
3217 | }
3218 |
3219 | /* give price quotes for all objects linked to this one (ie, on this spot) */
3220 | void
3221 | price_quote(first_obj)
3222 | register struct obj *first_obj;
3223 | {
3224 | register struct obj *otmp;
3225 | char buf[BUFSZ], price[40];
3226 | long cost;
3227 | int cnt = 0;
3228 | winid tmpwin;
3229 | struct monst *shkp = shop_keeper(inside_shop(u.ux, u.uy));
3230 |
3231 | tmpwin = create_nhwindow(NHW_MENU);
3232 | putstr(tmpwin, 0, "Fine goods for sale:");
3233 | putstr(tmpwin, 0, "");
3234 | for (otmp = first_obj; otmp; otmp = otmp->nexthere) {
3235 | if (otmp->oclass == GOLD_CLASS) continue;
3236 | cost = (otmp->no_charge || otmp == uball || otmp == uchain) ? 0L :
3237 | get_cost(otmp, (struct monst *)0);
3238 | if (Has_contents(otmp))
3239 | cost += contained_cost(otmp, shkp, 0L, FALSE);
3240 | if (!cost) {
3241 | Strcpy(price, "no charge");
3242 | } else {
3243 | Sprintf(price, "%ld zorkmid%s%s", cost, plur(cost),
3244 | otmp->quan > 1L ? " each" : "");
3245 | }
3246 | Sprintf(buf, "%s, %s", doname(otmp), price);
3247 | putstr(tmpwin, 0, buf), cnt++;
3248 | }
3249 | if (cnt > 1) {
3250 | display_nhwindow(tmpwin, TRUE);
3251 | } else if (cnt == 1) {
3252 | if (first_obj->no_charge || first_obj == uball || first_obj == uchain){
3253 | pline("%s!", buf); /* buf still contains the string */
3254 | } else {
3255 | /* print cost in slightly different format, so can't reuse buf */
3256 | cost = get_cost(first_obj, (struct monst *)0);
3257 | if (Has_contents(first_obj))
3258 | cost += contained_cost(first_obj, shkp, 0L, FALSE);
3259 | pline("%s, price %ld zorkmid%s%s%s", doname(first_obj),
3260 | cost, plur(cost), first_obj->quan > 1L ? " each" : "",
3261 | shk_embellish(first_obj, cost));
3262 | }
3263 | }
3264 | destroy_nhwindow(tmpwin);
3265 | }
3266 | #endif /*OVLB*/
3267 | #ifdef OVL3
3268 |
3269 | STATIC_OVL const char *
3270 | shk_embellish(itm, cost)
3271 | register struct obj *itm;
3272 | long cost;
3273 | {
3274 | if (!rn2(3)) {
3275 | register int o, choice = rn2(5);
3276 | if (choice == 0) choice = (cost < 100L ? 1 : cost < 500L ? 2 : 3);
3277 | switch (choice) {
3278 | case 4:
3279 | if (cost < 10L) break; else o = itm->oclass;
3280 | if (o == FOOD_CLASS) return ", gourmets' delight!";
3281 | if (objects[itm->otyp].oc_name_known
3282 | ? objects[itm->otyp].oc_magic
3283 | : (o == AMULET_CLASS || o == RING_CLASS ||
3284 | o == WAND_CLASS || o == POTION_CLASS ||
3285 | o == SCROLL_CLASS || o == SPBOOK_CLASS))
3286 | return ", painstakingly developed!";
3287 | return ", superb craftsmanship!";
3288 | case 3: return ", finest quality.";
3289 | case 2: return ", an excellent choice.";
3290 | case 1: return ", a real bargain.";
3291 | default: break;
3292 | }
3293 | } else if (itm->oartifact) {
3294 | return ", one of a kind!";
3295 | }
3296 | return ".";
3297 | }
3298 | #endif /*OVL3*/
3299 | #ifdef OVLB
3300 |
3301 | /* First 4 supplied by Ronen and Tamar, remainder by development team */
3302 | const char *Izchak_speaks[]={
3303 | "%s says: 'These shopping malls give me a headache.'",
3304 | "%s says: 'Slow down. Think clearly.'",
3305 | "%s says: 'You need to take things one at a time.'",
3306 | "%s says: 'I don't like poofy coffee... give me Columbian Supremo.'",
3307 | "%s says that getting the devteam's agreement on anything is difficult.",
3308 | "%s says that he has noticed those who serve their deity will prosper.",
3309 | "%s says: 'Don't try to steal from me - I have friends in high places!'",
3310 | "%s says: 'You may well need something from this shop in the future.'",
3311 | "%s comments about the Valley of the Dead as being a gateway."
3312 | };
3313 |
3314 | void
3315 | shk_chat(shkp)
3316 | register struct monst *shkp;
3317 | {
3318 | register struct eshk *eshk = ESHK(shkp);
3319 |
3320 | if (ANGRY(shkp))
3321 | pline("%s mentions how much %s dislikes %s customers.",
3322 | shkname(shkp), he[shkp->female],
3323 | eshk->robbed ? "non-paying" : "rude");
3324 | else if (eshk->following) {
3325 | if (strncmp(eshk->customer, plname, PL_NSIZ)) {
3326 | verbalize("%s %s! I was looking for %s.",
3327 | Hello(shkp), plname, eshk->customer);
3328 | eshk->following = 0;
3329 | } else {
3330 | verbalize("%s %s! Didn't you forget to pay?", Hello(shkp), plname);
3331 | }
3332 | } else if (eshk->billct) {
3333 | register long total = addupbill(shkp) + eshk->debit;
3334 | pline("%s says that your bill comes to %ld zorkmid%s.",
3335 | shkname(shkp), total, plur(total));
3336 | } else if (eshk->debit)
3337 | pline("%s reminds you that you owe %s %ld zorkmid%s.",
3338 | shkname(shkp), him[shkp->female],
3339 | eshk->debit, plur(eshk->debit));
3340 | else if (eshk->credit)
3341 | pline("%s encourages you to use your %ld zorkmid%s of credit.",
3342 | shkname(shkp), eshk->credit, plur(eshk->credit));
3343 | else if (eshk->robbed)
3344 | pline("%s complains about a recent robbery.", shkname(shkp));
3345 | else if (shkp->mgold < 50)
3346 | pline("%s complains that business is bad.", shkname(shkp));
3347 | else if (shkp->mgold > 4000)
3348 | pline("%s says that business is good.", shkname(shkp));
3349 | else if (strcmp(shkname(shkp), "Izchak") == 0)
3350 | pline(Izchak_speaks[rn2(SIZE(Izchak_speaks))],shkname(shkp));
3351 | else
3352 | pline("%s talks about the problem of shoplifters.",shkname(shkp));
3353 | }
3354 |
3355 | #ifdef KOPS
3356 | STATIC_OVL void
3357 | kops_gone(silent)
3358 | register boolean silent;
3359 | {
3360 | register int cnt = 0;
3361 | register struct monst *mtmp, *mtmp2;
3362 |
3363 | for (mtmp = fmon; mtmp; mtmp = mtmp2) {
3364 | mtmp2 = mtmp->nmon;
3365 | if (mtmp->data->mlet == S_KOP) {
3366 | if (canspotmon(mtmp)) cnt++;
3367 | mongone(mtmp);
3368 | }
3369 | }
3370 | if (cnt && !silent)
3371 | pline_The("Kop%s (disappointed) vanish%s into thin air.",
3372 | plur(cnt), cnt == 1 ? "es" : "");
3373 | }
3374 | #endif /* KOPS */
3375 |
3376 | #endif /*OVLB*/
3377 | #ifdef OVL3
3378 |
3379 | STATIC_OVL long
3380 | cost_per_charge(shkp, otmp, altusage)
3381 | struct monst *shkp;
3382 | struct obj *otmp;
3383 | boolean altusage; /* some items have an "alternate" use with different cost */
3384 | {
3385 | long tmp = 0L;
3386 |
3387 | if(!shkp || !inhishop(shkp)) return(0L); /* insurance */
3388 | tmp = get_cost(otmp, shkp);
3389 |
3390 | /* The idea is to make the exhaustive use of */
3391 | /* an unpaid item more expensive than buying */
3392 | /* it outright. */
3393 | if(otmp->otyp == MAGIC_LAMP) { /* 1 */
3394 | /* normal use (ie, as light source) of a magic lamp never
3395 | degrades its value, but not charging anything would make
3396 | identifcation too easy; charge an amount comparable to
3397 | what is charged for an ordinary lamp (don't bother with
3398 | angry shk surchage) */
3399 | if (!altusage) tmp = (long) objects[OIL_LAMP].oc_cost;
3400 | else tmp += tmp / 3L; /* djinni is being released */
3401 | } else if(otmp->otyp == MAGIC_MARKER) { /* 70 - 100 */
3402 | /* no way to determine in advance */
3403 | /* how many charges will be wasted. */
3404 | /* so, arbitrarily, one half of the */
3405 | /* price per use. */
3406 | tmp /= 2L;
3407 | } else if(otmp->otyp == BAG_OF_TRICKS || /* 1 - 20 */
3408 | otmp->otyp == HORN_OF_PLENTY) {
3409 | tmp /= 5L;
3410 | } else if(otmp->otyp == CRYSTAL_BALL || /* 1 - 5 */
3411 | otmp->otyp == OIL_LAMP || /* 1 - 10 */
3412 | otmp->otyp == BRASS_LANTERN ||
3413 | (otmp->otyp >= MAGIC_FLUTE &&
3414 | otmp->otyp <= DRUM_OF_EARTHQUAKE) || /* 5 - 9 */
3415 | otmp->oclass == WAND_CLASS) { /* 3 - 11 */
3416 | if (otmp->spe > 1) tmp /= 4L;
3417 | } else if (otmp->oclass == SPBOOK_CLASS) {
3418 | tmp -= tmp / 5L;
3419 | } else if (otmp->otyp == CAN_OF_GREASE) {
3420 | tmp /= 10L;
3421 | } else if (otmp->otyp == POT_OIL) {
3422 | tmp /= 5L;
3423 | }
3424 | return(tmp);
3425 | }
3426 | #endif /*OVL3*/
3427 | #ifdef OVLB
3428 |
3429 | /* Charge the player for partial use of an unpaid object.
3430 | *
3431 | * Note that bill_dummy_object() should be used instead
3432 | * when an object is completely used.
3433 | */
3434 | void
3435 | check_unpaid_usage(otmp, altusage)
3436 | struct obj *otmp;
3437 | boolean altusage;
3438 | {
3439 | struct monst *shkp;
3440 | const char *fmt, *arg1, *arg2;
3441 | long tmp;
3442 |
3443 | if (!otmp->unpaid || !*u.ushops ||
3444 | (otmp->spe <= 0 && objects[otmp->otyp].oc_charged))
3445 | return;
3446 | if (!(shkp = shop_keeper(*u.ushops)) || !inhishop(shkp))
3447 | return;
3448 | if ((tmp = cost_per_charge(shkp, otmp, altusage)) == 0L)
3449 | return;
3450 |
3451 | arg1 = arg2 = "";
3452 | if (otmp->oclass == SPBOOK_CLASS) {
3453 | fmt = "%sYou owe%s %ld zorkmids.";
3454 | arg1 = rn2(2) ? "This is no free library, cad! " : "";
3455 | arg2 = ESHK(shkp)->debit > 0L ? " an additional" : "";
3456 | } else if (otmp->otyp == POT_OIL) {
3457 | fmt = "%s%sThat will cost you %ld zorkmids (Yendorian Fuel Tax).";
3458 | } else {
3459 | fmt = "%s%sUsage fee, %ld zorkmids.";
3460 | if (!rn2(3)) arg1 = "Hey! ";
3461 | if (!rn2(3)) arg2 = "Ahem. ";
3462 | }
3463 |
3464 | if (shkp->mcanmove || !shkp->msleeping)
3465 | verbalize(fmt, arg1, arg2, tmp);
3466 | ESHK(shkp)->debit += tmp;
3467 | exercise(A_WIS, TRUE); /* you just got info */
3468 | }
3469 |
3470 | /* for using charges of unpaid objects "used in the normal manner" */
3471 | void
3472 | check_unpaid(otmp)
3473 | struct obj *otmp;
3474 | {
3475 | check_unpaid_usage(otmp, FALSE); /* normal item use */
3476 | }
3477 |
3478 | void
3479 | costly_gold(x, y, amount)
3480 | register xchar x, y;
3481 | register long amount;
3482 | {
3483 | register long delta;
3484 | register struct monst *shkp;
3485 | register struct eshk *eshkp;
3486 |
3487 | if(!costly_spot(x, y)) return;
3488 | /* shkp now guaranteed to exist by costly_spot() */
3489 | shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
3490 |
3491 | eshkp = ESHK(shkp);
3492 | if(eshkp->credit >= amount) {
3493 | if(eshkp->credit > amount)
3494 | Your("credit is reduced by %ld zorkmid%s.",
3495 | amount, plur(amount));
3496 | else Your("credit is erased.");
3497 | eshkp->credit -= amount;
3498 | } else {
3499 | delta = amount - eshkp->credit;
3500 | if(eshkp->credit)
3501 | Your("credit is erased.");
3502 | if(eshkp->debit)
3503 | Your("debt increases by %ld zorkmid%s.",
3504 | delta, plur(delta));
3505 | else You("owe %s %ld zorkmid%s.",
3506 | shkname(shkp), delta, plur(delta));
3507 | eshkp->debit += delta;
3508 | eshkp->loan += delta;
3509 | eshkp->credit = 0L;
3510 | }
3511 | }
3512 |
3513 | /* used in domove to block diagonal shop-exit */
3514 | /* x,y should always be a door */
3515 | boolean
3516 | block_door(x,y)
3517 | register xchar x, y;
3518 | {
3519 | register int roomno = *in_rooms(x, y, SHOPBASE);
3520 | register struct monst *shkp;
3521 |
3522 | if(roomno < 0 || !IS_SHOP(roomno)) return(FALSE);
3523 | if(!IS_DOOR(levl[x][y].typ)) return(FALSE);
3524 | if(roomno != *u.ushops) return(FALSE);
3525 |
3526 | if(!(shkp = shop_keeper((char)roomno)) || !inhishop(shkp))
3527 | return(FALSE);
3528 |
3529 | if(shkp->mx == ESHK(shkp)->shk.x && shkp->my == ESHK(shkp)->shk.y
3530 | /* Actually, the shk should be made to block _any_
3531 | * door, including a door the player digs, if the
3532 | * shk is within a 'jumping' distance.
3533 | */
3534 | && ESHK(shkp)->shd.x == x && ESHK(shkp)->shd.y == y
3535 | && shkp->mcanmove && !shkp->msleeping
3536 | && (ESHK(shkp)->debit || ESHK(shkp)->billct ||
3537 | ESHK(shkp)->robbed)) {
3538 | pline("%s%s blocks your way!", shkname(shkp),
3539 | Invis ? " senses your motion and" : "");
3540 | return(TRUE);
3541 | }
3542 | return(FALSE);
3543 | }
3544 |
3545 | /* used in domove to block diagonal shop-entry */
3546 | /* u.ux, u.uy should always be a door */
3547 | boolean
3548 | block_entry(x,y)
3549 | register xchar x, y;
3550 | {
3551 | register xchar sx, sy;
3552 | register int roomno;
3553 | register struct monst *shkp;
3554 |
3555 | if(!(IS_DOOR(levl[u.ux][u.uy].typ) &&
3556 | levl[u.ux][u.uy].doormask == D_BROKEN)) return(FALSE);
3557 |
3558 | roomno = *in_rooms(x, y, SHOPBASE);
3559 | if(roomno < 0 || !IS_SHOP(roomno)) return(FALSE);
3560 | if(!(shkp = shop_keeper((char)roomno)) || !inhishop(shkp))
3561 | return(FALSE);
3562 |
3563 | if(ESHK(shkp)->shd.x != u.ux || ESHK(shkp)->shd.y != u.uy)
3564 | return(FALSE);
3565 |
3566 | sx = ESHK(shkp)->shk.x;
3567 | sy = ESHK(shkp)->shk.y;
3568 |
3569 | if(shkp->mx == sx && shkp->my == sy
3570 | && shkp->mcanmove && !shkp->msleeping
3571 | && (x == sx-1 || x == sx+1 || y == sy-1 || y == sy+1)
3572 | && (Invis || carrying(PICK_AXE) || carrying(DWARVISH_MATTOCK)
3573 | #ifdef STEED
3574 | || u.usteed
3575 | #endif
3576 | )) {
3577 | pline("%s%s blocks your way!", shkname(shkp),
3578 | Invis ? " senses your motion and" : "");
3579 | return(TRUE);
3580 | }
3581 | return(FALSE);
3582 | }
3583 |
3584 | #endif /* OVLB */
3585 | #ifdef OVL2
3586 |
3587 | char *
3588 | shk_your(buf, obj)
3589 | char *buf;
3590 | struct obj *obj;
3591 | {
3592 | if (!shk_owns(buf, obj) && !mon_owns(buf, obj))
3593 | Strcpy(buf, carried(obj) ? "your" : "the");
3594 | return buf;
3595 | }
3596 |
3597 | char *
3598 | Shk_Your(buf, obj)
3599 | char *buf;
3600 | struct obj *obj;
3601 | {
3602 | (void) shk_your(buf, obj);
3603 | *buf = highc(*buf);
3604 | return buf;
3605 | }
3606 |
3607 | STATIC_OVL char *
3608 | shk_owns(buf, obj)
3609 | char *buf;
3610 | struct obj *obj;
3611 | {
3612 | struct monst *shkp;
3613 | xchar x, y;
3614 |
3615 | if (get_obj_location(obj, &x, &y, 0) &&
3616 | (obj->unpaid ||
3617 | (obj->where==OBJ_FLOOR && !obj->no_charge && costly_spot(x,y)))) {
3618 | shkp = shop_keeper(inside_shop(x, y));
3619 | return strcpy(buf, shkp ? s_suffix(shkname(shkp)) : "the");
3620 | }
3621 | return (char *)0;
3622 | }
3623 |
3624 | STATIC_OVL char *
3625 | mon_owns(buf, obj)
3626 | char *buf;
3627 | struct obj *obj;
3628 | {
3629 | if (obj->where == OBJ_MINVENT)
3630 | return strcpy(buf, s_suffix(mon_nam(obj->ocarry)));
3631 | return (char *)0;
3632 | }
3633 |
3634 | #endif /* OVL2 */
3635 | #ifdef OVLB
3636 |
3637 | #ifdef __SASC
3638 | void
3639 | sasc_bug(struct obj *op, unsigned x){
3640 | op->unpaid=x;
3641 | }
3642 | #endif
3643 |
3644 | #endif /* OVLB */
3645 |
3646 | /*shk.c*/