From c2a9343367845bc6d46452fa57952ad893a03919 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 14 Sep 2022 15:54:24 -0700 Subject: [PATCH] horn_of_plenty-to-container #tip For tipping purposes, a horn of plenty is treated like a container. But using one as the source container in a container-to-container tip wasn't supported. Implement that. Also, #tip was offering carried bags of tricks as candidate containers to tip some other carried container into. Only do that for ones which aren't known to be bags of tricks (so when type not discovered yet, or specific bag not seen yet due to blindness). --- include/extern.h | 2 +- src/apply.c | 2 +- src/mkobj.c | 11 ++++++++- src/pickup.c | 62 ++++++++++++++++++++++++++++++++---------------- 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/include/extern.h b/include/extern.h index 0adb68192..32b88d989 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1490,7 +1490,7 @@ extern void add_to_buried(struct obj *); extern void dealloc_obj(struct obj *); extern void obj_ice_effects(coordxy, coordxy, boolean); extern long peek_at_iced_corpse_age(struct obj *); -extern int hornoplenty(struct obj *, boolean); +extern int hornoplenty(struct obj *, boolean, struct obj *); extern void obj_sanity_check(void); extern struct obj *obj_nexto(struct obj *); extern struct obj *obj_nexto_xy(struct obj *, coordxy, coordxy, boolean); diff --git a/src/apply.c b/src/apply.c index 0b07f7d56..fc00ac4d8 100644 --- a/src/apply.c +++ b/src/apply.c @@ -4115,7 +4115,7 @@ doapply(void) res = do_play_instrument(obj); break; case HORN_OF_PLENTY: /* not a musical instrument */ - (void) hornoplenty(obj, FALSE); + (void) hornoplenty(obj, FALSE, (struct obj *) 0); break; case LAND_MINE: case BEARTRAP: diff --git a/src/mkobj.c b/src/mkobj.c index 8b59223e9..18ac0fe7e 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2547,7 +2547,8 @@ dealloc_obj(struct obj *obj) int hornoplenty( struct obj *horn, - boolean tipping) /* caller emptying entire contents; affects shop mesgs */ + boolean tipping, /* caller emptying entire contents; affects shop mesgs */ + struct obj *targetbox) /* if non-Null, container to tip into */ { int objcount = 0; @@ -2597,6 +2598,14 @@ hornoplenty( : "Oops! %s to the floor!", The(aobjnam(obj, "slip")), (char *) 0); nhUse(obj); + } else if (targetbox) { + add_to_container(targetbox, obj); + /* add to container doesn't update the weight */ + targetbox->owt = weight(targetbox); + /* item still in magic horn was weightless; when it's now in + a carried container, hero's encumbrance could change */ + if (carried(targetbox)) + (void) encumber_msg(); } else { /* assumes this is taking place at hero's location */ if (!can_reach_floor(TRUE)) { diff --git a/src/pickup.c b/src/pickup.c index 6acc20e21..e5f7f69b6 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -42,7 +42,7 @@ static int menu_loot(int, boolean); static int tip_ok(struct obj *); static int count_containers(struct obj *); static struct obj *tipcontainer_gettarget(struct obj *, boolean *); -static int tipcontainer_checks(struct obj *, boolean); +static int tipcontainer_checks(struct obj *, struct obj *, boolean); static char in_or_out_menu(const char *, struct obj *, boolean, boolean, boolean, boolean); static boolean able_to_loot(coordxy, coordxy, boolean); @@ -3463,9 +3463,9 @@ tipcontainer(struct obj *box) /* or bag */ to reduce the chance of exhausting shk's billing capacity. */ maybeshopgoods = !carried(box) && costly_spot(box->ox, box->oy); - if (tipcontainer_checks(box, FALSE) != TIPCHECK_OK) + if (tipcontainer_checks(box, targetbox, FALSE) != TIPCHECK_OK) return; - if (targetbox && tipcontainer_checks(targetbox, TRUE) != TIPCHECK_OK) + if (targetbox && tipcontainer_checks(targetbox, NULL, TRUE) != TIPCHECK_OK) return; { @@ -3588,16 +3588,22 @@ tipcontainer_gettarget(struct obj *box, boolean *cancelled) char buf[BUFSZ]; menu_item *pick_list = (menu_item *) 0; struct obj dummyobj, *otmp; - int n_conts = count_containers(g.invent); + int n_conts; int clr = 0; - /* we're carrying the box, don't count it as possible target */ - if (box->where == OBJ_INVENT) + /* if tipping a known bag of tricks, don't prompt for destination */ + if (box->otyp == BAG_OF_TRICKS && box->dknown + && objects[box->otyp].oc_name_known) + return (struct obj *) 0; + + n_conts = count_containers(g.invent); + /* when we're carrying the box, don't count it as possible target; + note: 'box' might be a horn so not be included in the count */ + if (carried(box) && Is_container(box)) n_conts--; if (n_conts < 1) { - if (cancelled) - *cancelled = FALSE; + *cancelled = FALSE; return (struct obj *) 0; } @@ -3613,15 +3619,22 @@ tipcontainer_gettarget(struct obj *box, boolean *cancelled) add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - for (otmp = g.invent; otmp; otmp = otmp->nobj) - if (Is_container(otmp) && otmp != box - /* don't include any container that's known to be locked */ - && (!otmp->olocked || !otmp->lknown)) { + for (otmp = g.invent; otmp; otmp = otmp->nobj) { + if (otmp == box) + continue; + /* bag of tricks passes Is_container() test; don't include it if + it is known to be a bag of tricks */ + if (otmp->otyp == BAG_OF_TRICKS && otmp->dknown + && objects[otmp->otyp].oc_name_known) + continue; + /* include any container unless it's known to be locked */ + if (Is_container(otmp) && !(otmp->olocked && otmp->lknown)) { any = cg.zeroany; any.a_obj = otmp; add_menu(win, &nul_glyphinfo, &any, otmp->invlet, 0, ATR_NONE, clr, doname(otmp), MENU_ITEMFLAGS_NONE); } + } Sprintf(buf, "Where to tip the contents of %s", doname(box)); end_menu(win, buf); @@ -3633,8 +3646,7 @@ tipcontainer_gettarget(struct obj *box, boolean *cancelled) otmp = pick_list[1].item.a_obj; if (pick_list) free((genericptr_t) pick_list); - if (cancelled) - *cancelled = (n == -1); + *cancelled = (boolean) (n == -1); if (otmp && otmp != &dummyobj) return otmp; @@ -3645,7 +3657,10 @@ tipcontainer_gettarget(struct obj *box, boolean *cancelled) Returns one of TIPCHECK_foo values. If allowempty if TRUE, return TIPCHECK_OK instead of TIPCHECK_EMPTY. */ static int -tipcontainer_checks(struct obj *box, boolean allowempty) +tipcontainer_checks( + struct obj *box, /* container player wants to tip */ + struct obj *targetbox, /* destination (used here for horn of plenty) */ + boolean allowempty) /* affects result when box is empty */ { /* caveat: this assumes that cknown, lknown, olocked, and otrapped fields haven't been overloaded to mean something special for the @@ -3657,7 +3672,7 @@ tipcontainer_checks(struct obj *box, boolean allowempty) } if (box->olocked) { - pline("It's locked."); + pline("%s is locked.", upstart(thesimpleoname(box))); return TIPCHECK_LOCKED; } else if (box->otrapped) { @@ -3672,11 +3687,18 @@ tipcontainer_checks(struct obj *box, boolean allowempty) return TIPCHECK_TRAPPED; } else if (box->otyp == BAG_OF_TRICKS || box->otyp == HORN_OF_PLENTY) { - boolean bag = box->otyp == BAG_OF_TRICKS; + int res = TIPCHECK_OK; + boolean bag = (box->otyp == BAG_OF_TRICKS); int old_spe = box->spe, seen = 0; - boolean maybeshopgoods = !carried(box) && costly_spot(box->ox, box->oy); + boolean maybeshopgoods = (!carried(box) + && costly_spot(box->ox, box->oy)); coordxy ox = u.ux, oy = u.uy; + if (targetbox + && ((res = tipcontainer_checks(targetbox, NULL, TRUE)) + != TIPCHECK_OK)) + return res; + if (get_obj_location(box, &ox, &oy, 0)) box->ox = ox, box->oy = oy; @@ -3686,7 +3708,7 @@ tipcontainer_checks(struct obj *box, boolean allowempty) (if the latter occurs, force the former...) */ do { if (!(bag ? bagotricks(box, TRUE, &seen) - : hornoplenty(box, TRUE))) + : hornoplenty(box, TRUE, targetbox))) break; } while (box->spe > 0); @@ -3703,7 +3725,7 @@ tipcontainer_checks(struct obj *box, boolean allowempty) } if (maybeshopgoods && !box->no_charge) subfrombill(box, shop_keeper(*in_rooms(ox, oy, SHOPBASE))); - return TIPCHECK_CANNOT; + return TIPCHECK_CANNOT; /* actually means 'already done' */ } else if (SchroedingersBox(box)) { char yourbuf[BUFSZ];