diff --git a/include/extern.h b/include/extern.h index a24ca64f3..e1f96be64 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1011,7 +1011,8 @@ E boolean NDECL(wearing_armor); E boolean FDECL(is_worn, (struct obj *)); E struct obj *FDECL(g_at, (int, int)); E boolean FDECL(splittable, (struct obj *)); -E struct obj *FDECL(getobj, (const char *, const char *)); +E int FDECL(any_obj_ok, (struct obj *)); +E struct obj *FDECL(getobj, (const char *, int (*)(OBJ_P), unsigned int)); E int FDECL(ggetobj, (const char *, int (*)(OBJ_P), int, BOOLEAN_P, unsigned *)); E int FDECL(askchain, (struct obj **, const char *, int, int (*)(OBJ_P), @@ -2124,7 +2125,7 @@ E char *FDECL(apron_text, (struct obj *, char *)); E const char *FDECL(candy_wrapper_text, (struct obj *)); E void FDECL(assign_candy_wrapper, (struct obj *)); E int NDECL(doread); -E boolean FDECL(is_chargeable, (struct obj *)); +E int FDECL(charge_ok, (struct obj *)); E void FDECL(recharge, (struct obj *, int)); E int FDECL(seffects, (struct obj *)); E void FDECL(drop_boulder_on_player, diff --git a/include/hack.h b/include/hack.h index ca8b855c4..b245d9a6e 100644 --- a/include/hack.h +++ b/include/hack.h @@ -478,6 +478,40 @@ enum bodypart_types { #define MON_POLE_DIST 5 /* How far monsters can use pole-weapons */ #define PET_MISSILE_RANGE2 36 /* Square of distance within which pets shoot */ +/* flags passed to getobj() to control how it responds to player input */ +#define GETOBJ_NOFLAGS 0x0 +#define GETOBJ_ALLOWCNT 0x1 /* is a count allowed with this command? */ +#define GETOBJ_PROMPT 0x2 /* should it force a prompt for input? (prevents it + exiting early with "You don't have anything to + foo" if nothing in inventory is valid) */ + +/* values returned from getobj() callback functions */ +enum getobj_callback_returns { + /* generally invalid - can't be used for this purpose. will give a "silly + * thing" message if the player tries to pick it, unless a more specific + * failure message is in getobj itself - e.g. "You cannot foo gold". */ + GETOBJ_EXCLUDE = -2, + /* invalid because it is an inaccessible or unwanted piece of gear, but + * psuedo-valid for the purposes of allowing the player to select it and + * getobj to return it if there is a prompt instead of getting "silly + * thing", in order for the getobj caller to present a specific failure + * message. Other than that, the only thing this does differently from + * GETOBJ_EXCLUDE is that it inserts an "else" in "You don't have anything + * else to foo". */ + GETOBJ_EXCLUDE_INACCESS = -1, + /* invalid for purposes of not showing a prompt if nothing is valid but + * psuedo-valid for selecting - identical to GETOBJ_EXCLUDE_INACCESS but + * without the "else" in "You don't have anything else to foo". */ + GETOBJ_EXCLUDE_SELECTABLE = 0, + /* valid - invlet not presented in the summary or the ? menu as a + * recommendation, but is selectable if the player enters it anyway. Used + * for objects that are actually valid but unimportantly so, such as shirts + * for reading. */ + GETOBJ_DOWNPLAY = 1, + /* valid - will be shown in summary and ? menu */ + GETOBJ_SUGGEST = 2, +}; + /* * option setting restrictions */ diff --git a/include/objclass.h b/include/objclass.h index cdfc9b625..7339d41bb 100644 --- a/include/objclass.h +++ b/include/objclass.h @@ -166,10 +166,6 @@ enum obj_class_types { /* for mkobj() use ONLY! odd '-SPBOOK_CLASS' is in case of unsigned enums */ #define SPBOOK_no_NOVEL (0 - (int) SPBOOK_CLASS) -#define ALLOW_COUNT (MAXOCLASSES + 1) /* Can be used in the object class */ -#define ALL_CLASSES (MAXOCLASSES + 2) /* input to getobj(). */ -#define ALLOW_NONE (MAXOCLASSES + 3) - #define BURNING_OIL (MAXOCLASSES + 1) /* Can be used as input to explode. */ #define MON_EXPLODE (MAXOCLASSES + 2) /* Exploding monster (e.g. gas spore) */ diff --git a/src/apply.c b/src/apply.c index fdc179e52..0d00e3236 100644 --- a/src/apply.c +++ b/src/apply.c @@ -18,25 +18,28 @@ static void FDECL(use_candelabrum, (struct obj *)); static void FDECL(use_candle, (struct obj **)); static void FDECL(use_lamp, (struct obj *)); static void FDECL(light_cocktail, (struct obj **)); +static int FDECL(rub_ok, (struct obj *)); static void FDECL(display_jump_positions, (int)); static void FDECL(use_tinning_kit, (struct obj *)); static void FDECL(use_figurine, (struct obj **)); +static int FDECL(grease_ok, (struct obj *)); static void FDECL(use_grease, (struct obj *)); static void FDECL(use_trap, (struct obj *)); +static int FDECL(touchstone_ok, (struct obj *)); static void FDECL(use_stone, (struct obj *)); static int NDECL(set_trap); /* occupation callback */ static int FDECL(use_whip, (struct obj *)); static void FDECL(display_polearm_positions, (int)); static int FDECL(use_pole, (struct obj *)); static int FDECL(use_cream_pie, (struct obj *)); +static int FDECL(jelly_ok, (struct obj *)); static int FDECL(use_royal_jelly, (struct obj *)); static int FDECL(use_grapple, (struct obj *)); static int FDECL(do_break_wand, (struct obj *)); +static int FDECL(apply_ok, (struct obj *)); static int FDECL(flip_through_book, (struct obj *)); static boolean FDECL(figurine_location_checks, (struct obj *, coord *, BOOLEAN_P)); -static void FDECL(add_class, (char *, CHAR_P)); -static void FDECL(setapplyclasses, (char *)); static boolean FDECL(check_jump, (genericptr_t, int, int)); static boolean FDECL(is_valid_jump_pos, (int, int, int, BOOLEAN_P)); static boolean FDECL(get_valid_jump_position, (int, int)); @@ -1552,9 +1555,22 @@ struct obj **optr; *optr = obj; } -static NEARDATA const char - cuddly[] = { TOOL_CLASS, GEM_CLASS, 0 }, - cuddlier[] = { TOOL_CLASS, GEM_CLASS, FOOD_CLASS, 0 }; +/* getobj callback for object to be rubbed - not selecting a secondary object to + * rub on a gray stone or rub jelly on */ +static int +rub_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP + || obj->otyp == BRASS_LANTERN || is_graystone(obj) + || obj->otyp == LUMP_OF_ROYAL_JELLY) + return GETOBJ_SUGGEST; + + return GETOBJ_EXCLUDE; +} int dorub() @@ -1565,7 +1581,7 @@ dorub() You("aren't able to rub anything without hands."); return 0; } - obj = getobj(carrying(LUMP_OF_ROYAL_JELLY) ? cuddlier : cuddly, "rub"); + obj = getobj("rub", rub_ok, GETOBJ_NOFLAGS); if (!obj) { /* pline1(Never_mind); -- handled by getobj() */ return 0; @@ -2331,7 +2347,24 @@ struct obj **optr; *optr = 0; } -static NEARDATA const char lubricables[] = { ALL_CLASSES, ALLOW_NONE, 0 }; +/* getobj callback for object to apply grease to */ +static int +grease_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_SUGGEST; + + if (obj->oclass == COIN_CLASS) + return GETOBJ_EXCLUDE; + + if (inaccessible_equipment(obj, (const char *) 0, FALSE)) + return GETOBJ_EXCLUDE_INACCESS; + + /* Possible extension: don't suggest greasing objects which are already + * greased. */ + return GETOBJ_SUGGEST; +} static void use_grease(obj) @@ -2357,7 +2390,7 @@ struct obj *obj; dropx(obj); return; } - otmp = getobj(lubricables, "grease"); + otmp = getobj("grease", grease_ok, GETOBJ_PROMPT); if (!otmp) return; if (inaccessible_equipment(otmp, "grease", FALSE)) @@ -2386,31 +2419,52 @@ struct obj *obj; update_inventory(); } +/* getobj callback for object to rub on a known touchstone */ +static int +touchstone_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + /* Gold being suggested as a rub target is questionable - it fits the + * real-world historic use of touchstones, but doesn't do anything + * significant in the game. */ + if (obj->oclass == COIN_CLASS) + return GETOBJ_SUGGEST; + + /* don't suggest identified gems */ + if (obj->oclass == GEM_CLASS + && !(obj->dknown && objects[obj->otyp].oc_name_known)) + return GETOBJ_SUGGEST; + + return GETOBJ_DOWNPLAY; +} + + /* touchstones - by Ken Arnold */ static void use_stone(tstone) struct obj *tstone; { static const char scritch[] = "\"scritch, scritch\""; - static const char allowall[3] = { COIN_CLASS, ALL_CLASSES, 0 }; - static const char coins_gems[3] = { COIN_CLASS, GEM_CLASS, 0 }; struct obj *obj; boolean do_scratch; - const char *streak_color, *choices; + const char *streak_color; char stonebuf[QBUFSZ]; int oclass; + boolean known; /* in case it was acquired while blinded */ if (!Blind) tstone->dknown = 1; + known = (tstone->otyp == TOUCHSTONE && tstone->dknown + && objects[TOUCHSTONE].oc_name_known); + Sprintf(stonebuf, "rub on the stone%s", plur(tstone->quan)); /* when the touchstone is fully known, don't bother listing extra junk as likely candidates for rubbing */ - choices = (tstone->otyp == TOUCHSTONE && tstone->dknown - && objects[TOUCHSTONE].oc_name_known) - ? coins_gems - : allowall; - Sprintf(stonebuf, "rub on the stone%s", plur(tstone->quan)); - if ((obj = getobj(choices, stonebuf)) == 0) + if ((obj = getobj(stonebuf, known ? touchstone_ok : any_obj_ok, + GETOBJ_PROMPT)) == 0) return; if (obj == tstone && obj->quan == 1L) { @@ -3180,11 +3234,21 @@ struct obj *obj; return 0; } +/* getobj callback for object to rub royal jelly on */ +static int +jelly_ok(obj) +struct obj *obj; +{ + if (obj && obj->otyp == EGG) + return GETOBJ_SUGGEST; + + return GETOBJ_EXCLUDE; +} + static int use_royal_jelly(obj) struct obj *obj; { - static const char allowall[2] = { ALL_CLASSES, 0 }; int oldcorpsenm; unsigned was_timed; struct obj *eobj; @@ -3196,7 +3260,7 @@ struct obj *obj; freeinv(obj); /* right now you can rub one royal jelly on an entire stack of eggs */ - eobj = getobj(allowall, "rub the royal jelly on"); + eobj = getobj("rub the royal jelly on", jelly_ok, GETOBJ_PROMPT); if (!eobj) { addinv(obj); /* put the unused lump back; if it came from * a split, it should merge back */ @@ -3624,64 +3688,53 @@ discard_broken_wand: return 1; } -static void -add_class(cl, class) -char *cl; -char class; +/* getobj callback for object to apply - this is more complex than most other + * callbacks because there are a lot of appliables */ +static int +apply_ok(obj) +struct obj *obj; { - char tmp[2]; + if (!obj) + return GETOBJ_EXCLUDE; - tmp[0] = class; - tmp[1] = '\0'; - Strcat(cl, tmp); -} + /* all tools, all wands (breaking), all spellbooks (flipping through - + * including blank/novel/Book of the Dead) */ + if (obj->oclass == TOOL_CLASS || obj->oclass == WAND_CLASS + || obj->oclass == SPBOOK_CLASS) + return GETOBJ_SUGGEST; -static const char tools[] = { TOOL_CLASS, WEAPON_CLASS, WAND_CLASS, 0 }; + /* certain weapons */ + if (obj->oclass == WEAPON_CLASS + && (is_pick(obj) || is_axe(obj) || is_pole(obj) + || obj->otyp == BULLWHIP)) + return GETOBJ_SUGGEST; -/* augment tools[] if various items are carried */ -static void -setapplyclasses(class_list) -char class_list[]; -{ - register struct obj *otmp; - int otyp; - boolean knowoil, knowtouchstone; - boolean addpotions, addstones, addfood, addspellbooks; + /* only applicable potion is oil, and it will only be offered as a choice + * when already discovered */ + if (obj->otyp == POT_OIL && obj->dknown + && objects[obj->otyp].oc_name_known) + return GETOBJ_SUGGEST; - knowoil = objects[POT_OIL].oc_name_known; - knowtouchstone = objects[TOUCHSTONE].oc_name_known; - addpotions = addstones = addfood = addspellbooks = FALSE; - for (otmp = g.invent; otmp; otmp = otmp->nobj) { - otyp = otmp->otyp; - if (otyp == POT_OIL - || (otmp->oclass == POTION_CLASS - && (!otmp->dknown - || (!knowoil && !objects[otyp].oc_name_known)))) - addpotions = TRUE; - if (otyp == TOUCHSTONE - || (is_graystone(otmp) - && (!otmp->dknown - || (!knowtouchstone && !objects[otyp].oc_name_known)))) - addstones = TRUE; - if (otyp == CREAM_PIE || otyp == EUCALYPTUS_LEAF - || otyp == LUMP_OF_ROYAL_JELLY) - addfood = TRUE; - if (otmp->oclass == SPBOOK_CLASS) - addspellbooks = TRUE; + /* certain foods */ + if (obj->otyp == CREAM_PIE || obj->otyp == EUCALYPTUS_LEAF + || obj->otyp == LUMP_OF_ROYAL_JELLY) + return GETOBJ_SUGGEST; + + if (is_graystone(obj)) { + /* The only case where we don't suggest a gray stone is if we KNOW it + * isn't a touchstone. */ + if (!obj->dknown) + return GETOBJ_SUGGEST; + + if (obj->otyp != TOUCHSTONE + && (objects[TOUCHSTONE].oc_name_known + || objects[obj->otyp].oc_name_known)) + return GETOBJ_EXCLUDE; + + return GETOBJ_SUGGEST; } - class_list[0] = '\0'; - if (addpotions || addstones) - add_class(class_list, ALL_CLASSES); - Strcat(class_list, tools); - if (addpotions) - add_class(class_list, POTION_CLASS); - if (addstones) - add_class(class_list, GEM_CLASS); - if (addfood) - add_class(class_list, FOOD_CLASS); - if (addspellbooks) - add_class(class_list, SPBOOK_CLASS); + return GETOBJ_EXCLUDE; } /* the 'a' command */ @@ -3690,7 +3743,6 @@ doapply() { struct obj *obj; register int res = 1; - char class_list[MAXOCLASSES + 2]; if (nohands(g.youmonst.data)) { You("aren't able to use or apply tools in your current form."); @@ -3699,8 +3751,7 @@ doapply() if (check_capacity((char *) 0)) return 0; - setapplyclasses(class_list); /* tools[] */ - obj = getobj(class_list, "use or apply"); + obj = getobj("use or apply", apply_ok, GETOBJ_NOFLAGS); if (!obj) return 0; diff --git a/src/artifact.c b/src/artifact.c index b9bca5126..e2433c8c1 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -20,6 +20,7 @@ static boolean FDECL(bane_applies, (const struct artifact *, struct monst *)); static int FDECL(spec_applies, (const struct artifact *, struct monst *)); +static int FDECL(invoke_ok, (struct obj *)); static int FDECL(arti_invoke, (struct obj *)); static boolean FDECL(Mb_hit, (struct monst * magr, struct monst *mdef, struct obj *, int *, int, BOOLEAN_P, char *)); @@ -1411,9 +1412,28 @@ int dieroll; /* needed for Magicbane and vorpal blades */ return FALSE; } -static NEARDATA const char recharge_type[] = { ALLOW_COUNT, ALL_CLASSES, 0 }; -static NEARDATA const char invoke_types[] = { ALL_CLASSES, 0 }; -/* #invoke: an "ugly check" filters out most objects */ +/* getobj callback for object to be invoked */ +static int +invoke_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + /* artifacts and other special items */ + if (obj->oartifact || objects[obj->otyp].oc_unique + || (obj->otyp == FAKE_AMULET_OF_YENDOR && !obj->known)) + return GETOBJ_SUGGEST; + + /* synonym for apply, though actually invoking it will do different things + * depending if it's a regular crystal ball, an artifact one that has an + * invoke power, and a (theoretical) artifact one that doesn't have an + * invoke power */ + if (obj->otyp == CRYSTAL_BALL) + return GETOBJ_SUGGEST; + + return GETOBJ_EXCLUDE; +} /* the #invoke command */ int @@ -1421,7 +1441,7 @@ doinvoke() { struct obj *obj; - obj = getobj(invoke_types, "invoke"); + obj = getobj("invoke", invoke_ok, GETOBJ_PROMPT); if (!obj) return 0; if (!retouch_object(&obj, FALSE)) @@ -1516,7 +1536,8 @@ struct obj *obj; break; } case CHARGE_OBJ: { - struct obj *otmp = getobj(recharge_type, "charge"); + struct obj *otmp = getobj("charge", charge_ok, + GETOBJ_PROMPT | GETOBJ_ALLOWCNT); boolean b_effect; if (!otmp) { diff --git a/src/detect.c b/src/detect.c index 4c4419417..8dcf00b88 100644 --- a/src/detect.c +++ b/src/detect.c @@ -29,6 +29,10 @@ static int FDECL(mfind0, (struct monst *, BOOLEAN_P)); static int FDECL(reveal_terrain_getglyph, (int, int, int, unsigned, int, int)); +/* wildcard class for clear_stale_map - this used to be used as a getobj() input + * but it's no longer used for that function */ +#define ALL_CLASSES (MAXOCLASSES + 1) + /* bring hero out from underwater or underground or being engulfed; return True iff any change occurred */ static boolean diff --git a/src/do.c b/src/do.c index 241d1d5df..bc3af874f 100644 --- a/src/do.c +++ b/src/do.c @@ -19,18 +19,15 @@ static NHFILE *NDECL(currentlevel_rewrite); static void NDECL(final_level); /* static boolean FDECL(badspot, (XCHAR_P,XCHAR_P)); */ -static NEARDATA const char drop_types[] = { ALLOW_COUNT, COIN_CLASS, - ALL_CLASSES, 0 }; - /* 'd' command: drop one inventory item */ int dodrop() { - int result, i = (g.invent) ? 0 : (SIZE(drop_types) - 1); + int result; if (*u.ushops) sellobj_state(SELL_DELIBERATE); - result = drop(getobj(&drop_types[i], "drop")); + result = drop(getobj("drop", any_obj_ok, GETOBJ_PROMPT)); if (*u.ushops) sellobj_state(SELL_NORMAL); if (result) diff --git a/src/do_name.c b/src/do_name.c index 4958c600b..f0374b931 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -21,6 +21,8 @@ static void FDECL(auto_describe, (int, int)); static void NDECL(do_mgivenname); static boolean FDECL(alreadynamed, (struct monst *, char *, char *)); static void FDECL(do_oname, (struct obj *)); +static int FDECL(name_ok, (struct obj *)); +static int FDECL(call_ok, (struct obj *)); static char *FDECL(docall_xname, (struct obj *)); static void NDECL(namefloorobj); @@ -1334,18 +1336,53 @@ const char *name; return obj; } -static NEARDATA const char callable[] = { - SCROLL_CLASS, POTION_CLASS, WAND_CLASS, RING_CLASS, AMULET_CLASS, - GEM_CLASS, SPBOOK_CLASS, ARMOR_CLASS, TOOL_CLASS, VENOM_CLASS, 0 -}; - boolean objtyp_is_callable(i) int i; { - return (boolean) (objects[i].oc_uname - || (OBJ_DESCR(objects[i]) - && index(callable, objects[i].oc_class))); + if (objects[i].oc_uname) + return TRUE; + + switch(objects[i].oc_class) { + case SCROLL_CLASS: + case POTION_CLASS: + case WAND_CLASS: + case RING_CLASS: + case AMULET_CLASS: + case GEM_CLASS: + case SPBOOK_CLASS: + case ARMOR_CLASS: + case TOOL_CLASS: + case VENOM_CLASS: + if (OBJ_DESCR(objects[i])) + return TRUE; + break; + default: + break; + } + return FALSE; +} + +/* getobj callback for object to name (specific item) - anything but gold */ +static int +name_ok(obj) +struct obj *obj; +{ + if (!obj || obj->oclass == COIN_CLASS) + return GETOBJ_EXCLUDE; + + return GETOBJ_SUGGEST; +} + +/* getobj callback for object to call (name its type) */ +static int +call_ok(obj) +struct obj *obj; +{ + if (!obj || !objtyp_is_callable(obj->otyp)) + return GETOBJ_EXCLUDE; + + return GETOBJ_SUGGEST; } /* C and #name commands - player can name monster or object or type of obj */ @@ -1356,7 +1393,7 @@ docallcmd() winid win; anything any; menu_item *pick_list = 0; - char ch, allowall[2]; + char ch; /* if player wants a,b,c instead of i,o when looting, do that here too */ boolean abc = flags.lootabc; @@ -1406,14 +1443,12 @@ docallcmd() do_mgivenname(); break; case 'i': /* name an individual object in inventory */ - allowall[0] = ALL_CLASSES; - allowall[1] = '\0'; - obj = getobj(allowall, "name"); + obj = getobj("name", name_ok, GETOBJ_PROMPT); if (obj) do_oname(obj); break; case 'o': /* name a type of object in inventory */ - obj = getobj(callable, "call"); + obj = getobj("call", call_ok, GETOBJ_NOFLAGS); if (obj) { /* behave as if examining it in inventory; this might set dknown if it was picked up @@ -1423,7 +1458,7 @@ docallcmd() if (!obj->dknown) { You("would never recognize another one."); #if 0 - } else if (!objtyp_is_callable(obj->otyp)) { + } else if (!call_ok(obj)) { You("know those as well as you ever will."); #endif } else { @@ -1591,7 +1626,7 @@ namefloorobj() pline("%s %s to call you \"%s.\"", The(buf), use_plural ? "decide" : "decides", unames[rn2_on_display_rng(SIZE(unames))]); - } else if (!objtyp_is_callable(obj->otyp)) { + } else if (!call_ok(obj)) { pline("%s %s can't be assigned a type name.", use_plural ? "Those" : "That", buf); } else if (!obj->dknown) { diff --git a/src/do_wear.c b/src/do_wear.c index 044b363d6..ebdf6102c 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -42,6 +42,11 @@ static int FDECL(armor_or_accessory_off, (struct obj *)); static int FDECL(accessory_or_armor_on, (struct obj *)); static void FDECL(already_wearing, (const char *)); static void FDECL(already_wearing2, (const char *, const char *)); +static int FDECL(equip_ok, (struct obj *, BOOLEAN_P, BOOLEAN_P)); +static int FDECL(puton_ok, (struct obj *)); +static int FDECL(remove_ok, (struct obj *)); +static int FDECL(wear_ok, (struct obj *)); +static int FDECL(takeoff_ok, (struct obj *)); /* plural "fingers" or optionally "gloves" */ const char * @@ -1446,14 +1451,6 @@ struct obj *stolenobj; /* no message if stolenobj is already being doffing */ return result; } -/* both 'clothes' and 'accessories' now include both armor and accessories; - TOOL_CLASS is for eyewear, FOOD_CLASS is for MEAT_RING */ -static NEARDATA const char clothes[] = { - ARMOR_CLASS, RING_CLASS, AMULET_CLASS, TOOL_CLASS, FOOD_CLASS, 0 -}; -static NEARDATA const char accessories[] = { - RING_CLASS, AMULET_CLASS, TOOL_CLASS, FOOD_CLASS, ARMOR_CLASS, 0 -}; static NEARDATA int Narmorpieces, Naccessories; /* assign values to Narmorpieces and Naccessories */ @@ -1575,7 +1572,7 @@ dotakeoff() return 0; } if (Narmorpieces != 1 || ParanoidRemove) - otmp = getobj(clothes, "take off"); + otmp = getobj("take off", takeoff_ok, GETOBJ_NOFLAGS); if (!otmp) return 0; @@ -1594,7 +1591,7 @@ doremring() return 0; } if (Naccessories != 1 || ParanoidRemove) - otmp = getobj(accessories, "remove"); + otmp = getobj("remove", remove_ok, GETOBJ_NOFLAGS); if (!otmp) return 0; @@ -2159,7 +2156,7 @@ dowear() You("are already wearing a full complement of armor."); return 0; } - otmp = getobj(clothes, "wear"); + otmp = getobj("wear", wear_ok, GETOBJ_NOFLAGS); return otmp ? accessory_or_armor_on(otmp) : 0; } @@ -2178,7 +2175,7 @@ doputon() (ublindf->otyp == LENSES) ? "some lenses" : "a blindfold"); return 0; } - otmp = getobj(accessories, "put on"); + otmp = getobj("put on", puton_ok, GETOBJ_NOFLAGS); return otmp ? accessory_or_armor_on(otmp) : 0; } @@ -2934,4 +2931,84 @@ boolean only_if_known_cursed; /* ignore covering unless known to be cursed */ return FALSE; } +/* not a getobj callback - unifies code among the other four getobj callbacks */ +static int +equip_ok(obj, removing, accessory) +struct obj *obj; +boolean removing; +boolean accessory; +{ + boolean is_worn; + long dummymask = 0; + + if (!obj) + return GETOBJ_EXCLUDE; + + /* ignore for putting on if already worn, or removing if not already worn */ + is_worn = ((obj->owornmask & (W_ARMOR | W_ACCESSORY)) != 0); + if (removing ^ is_worn) + return GETOBJ_EXCLUDE_INACCESS; + + /* exclude most object classes outright */ + if (obj->oclass != ARMOR_CLASS && obj->oclass != RING_CLASS + && obj->oclass != AMULET_CLASS) { + /* ... except for a few wearable exceptions outside these classes */ + if (obj->otyp != MEAT_RING && obj->otyp != BLINDFOLD + && obj->otyp != TOWEL && obj->otyp != LENSES) + return GETOBJ_EXCLUDE; + } + + /* armor with 'P' or 'R' or accessory with 'W' or 'T' */ + if (accessory ^ (obj->oclass != ARMOR_CLASS)) + return GETOBJ_DOWNPLAY; + + /* armor we can't wear, e.g. from polyform */ + if (obj->oclass == ARMOR_CLASS && !removing && + !canwearobj(obj, &dummymask, FALSE)) + return GETOBJ_DOWNPLAY; + + /* Possible extension: downplay items (both accessories and armor) which + * can't be worn because the slot is filled with something else. */ + + /* removing inaccessible equipment */ + if (removing && inaccessible_equipment(obj, (const char *) 0, + (obj->oclass == RING_CLASS))) + return GETOBJ_EXCLUDE_INACCESS; + + /* all good to go */ + return GETOBJ_SUGGEST; +} + +/* getobj callback for P command */ +static int +puton_ok(obj) +struct obj *obj; +{ + return equip_ok(obj, FALSE, TRUE); +} + +/* getobj callback for R command */ +static int +remove_ok(obj) +struct obj *obj; +{ + return equip_ok(obj, TRUE, TRUE); +} + +/* getobj callback for W command */ +static int +wear_ok(obj) +struct obj *obj; +{ + return equip_ok(obj, FALSE, FALSE); +} + +/* getobj callback for T command */ +static int +takeoff_ok(obj) +struct obj *obj; +{ + return equip_ok(obj, TRUE, FALSE); +} + /*do_wear.c*/ diff --git a/src/dothrow.c b/src/dothrow.c index 8318bafb3..ec325bb36 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -9,6 +9,7 @@ static int FDECL(throw_obj, (struct obj *, int)); static boolean FDECL(ok_to_throw, (int *)); +static int FDECL(throw_ok, (struct obj *)); static void NDECL(autoquiver); static int FDECL(gem_accept, (struct monst *, struct obj *)); static void FDECL(tmiss, (struct obj *, struct monst *, BOOLEAN_P)); @@ -20,13 +21,6 @@ static boolean FDECL(toss_up, (struct obj *, BOOLEAN_P)); static void FDECL(sho_obj_return_to_u, (struct obj * obj)); static boolean FDECL(mhurtle_step, (genericptr_t, int, int)); -static NEARDATA const char toss_objs[] = { ALLOW_COUNT, COIN_CLASS, - ALL_CLASSES, WEAPON_CLASS, 0 }; -/* different default choices when wielding a sling (gold must be included) */ -static NEARDATA const char t_bullets[] = { - ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, GEM_CLASS, 0 -}; - /* g.thrownobj (decl.c) tracks an object until it lands */ int @@ -275,6 +269,36 @@ int *shotlimit_p; /* (see dothrow()) */ return TRUE; } +/* getobj callback for object to be thrown */ +static int +throw_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + if (obj->quan == 1 && (obj == uwep || (obj == uswapwep && u.twoweap))) + return GETOBJ_DOWNPLAY; + /* Possible extension: return GETOBJ_SUGGEST for uwep if it will return when + * thrown. */ + + if (obj->oclass == COIN_CLASS) + return GETOBJ_SUGGEST; + + if (!uslinging() && obj->oclass == WEAPON_CLASS) + return GETOBJ_SUGGEST; + /* Possible extension: exclude weapons that make no sense to throw, such as + * whips, bows, slings, rubber hoses. */ + + if (uslinging() && obj->oclass == GEM_CLASS) + return GETOBJ_SUGGEST; + + if (throws_rocks(g.youmonst.data) && obj->otyp == BOULDER) + return GETOBJ_SUGGEST; + + return GETOBJ_DOWNPLAY; +} + /* t command - throw */ int dothrow() @@ -296,7 +320,7 @@ dothrow() if (!ok_to_throw(&shotlimit)) return 0; - obj = getobj(uslinging() ? t_bullets : toss_objs, "throw"); + obj = getobj("throw", throw_ok, GETOBJ_PROMPT | GETOBJ_ALLOWCNT); /* it is also possible to throw food */ /* (or jewels, or iron balls... ) */ @@ -408,7 +432,7 @@ dofire() use direction of previous throw as getobj()'s choice here */ g.in_doagain = 0; /* choose something from inventory, then usually quiver it */ - obj = getobj(uslinging() ? t_bullets : toss_objs, "throw"); + obj = getobj("throw", throw_ok, GETOBJ_PROMPT | GETOBJ_ALLOWCNT); /* Q command doesn't allow gold in quiver */ if (obj && !obj->owornmask && obj->oclass != COIN_CLASS) setuqwep(obj); /* demi-autoquiver */ diff --git a/src/eat.c b/src/eat.c index 13628642b..72254a993 100644 --- a/src/eat.c +++ b/src/eat.c @@ -30,6 +30,7 @@ static void FDECL(fprefx, (struct obj *)); static void FDECL(fpostfx, (struct obj *)); static int NDECL(bite); static int FDECL(edibility_prompts, (struct obj *)); +static int FDECL(tinopen_ok, (struct obj *)); static int FDECL(rottenfood, (struct obj *)); static void NDECL(eatspecial); static int FDECL(bounded_increase, (int, int, int)); @@ -38,6 +39,9 @@ static void FDECL(eataccessory, (struct obj *)); static const char *FDECL(foodword, (struct obj *)); static int FDECL(tin_variety, (struct obj *, BOOLEAN_P)); static boolean FDECL(maybe_cannibal, (int, BOOLEAN_P)); +static int FDECL(eat_ok, (struct obj *)); +static int FDECL(offer_ok, (struct obj *)); +static int FDECL(tin_ok, (struct obj *)); /* also used to see if you're allowed to eat cats and dogs */ #define CANNIBAL_ALLOWED() (Role_if(PM_CAVE_DWELLER) || Race_if(PM_ORC)) @@ -55,18 +59,6 @@ static boolean FDECL(maybe_cannibal, (int, BOOLEAN_P)); #define nonrotting_food(otyp) \ ((otyp) == LEMBAS_WAFER || (otyp) == CRAM_RATION) -static NEARDATA const char comestibles[] = { FOOD_CLASS, 0 }; -static NEARDATA const char offerfodder[] = { FOOD_CLASS, AMULET_CLASS, - 0 }; - -/* Gold must come first for getobj(). */ -static NEARDATA const char allobj[] = { - COIN_CLASS, WEAPON_CLASS, ARMOR_CLASS, POTION_CLASS, - SCROLL_CLASS, WAND_CLASS, RING_CLASS, AMULET_CLASS, - FOOD_CLASS, TOOL_CLASS, GEM_CLASS, ROCK_CLASS, - BALL_CLASS, CHAIN_CLASS, SPBOOK_CLASS, 0 -}; - /* see hunger states in hack.h - texts used on bottom line */ const char *hu_stat[] = { "Satiated", " ", "Hungry ", "Weak ", "Fainting", "Fainted ", "Starved " }; @@ -102,7 +94,6 @@ register struct obj *obj; && !Has_contents(obj)) return TRUE; - /* return (boolean) !!index(comestibles, obj->oclass); */ return (boolean) (obj->oclass == FOOD_CLASS); } @@ -2763,6 +2754,18 @@ doeat() return 1; } +/* getobj callback for object to be opened with a tin opener */ +static int +tinopen_ok(obj) +struct obj *obj; +{ + if (obj && obj->otyp == TIN) + return GETOBJ_SUGGEST; + + return GETOBJ_EXCLUDE; +} + + int use_tin_opener(obj) struct obj *obj; @@ -2788,7 +2791,7 @@ struct obj *obj; res = 1; } - otmp = getobj(comestibles, "open"); + otmp = getobj("open", tinopen_ok, GETOBJ_NOFLAGS); if (!otmp) return res; @@ -3163,6 +3166,60 @@ boolean incr; } } +/* getobj callback for object to eat - effectively just wraps is_edible() */ +static int +eat_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + if (is_edible(obj)) + return GETOBJ_SUGGEST; + + /* make sure to exclude, not downplay, gold (if not is_edible) in order to + * produce the "You cannot eat gold" message in getobj */ + if (obj->oclass == COIN_CLASS) + return GETOBJ_EXCLUDE; + + return GETOBJ_EXCLUDE_SELECTABLE; +} + +/* getobj callback for object to be offered (corpses and things that look like + * the Amulet only */ +static int +offer_ok(obj) +struct obj *obj; +{ + if (!obj || (obj->oclass != FOOD_CLASS && obj->oclass != AMULET_CLASS)) + return GETOBJ_EXCLUDE; + + if (obj->otyp != CORPSE && obj->otyp != AMULET_OF_YENDOR + && obj->otyp != FAKE_AMULET_OF_YENDOR) + return GETOBJ_EXCLUDE_SELECTABLE; + + /* suppress corpses on astral, amulets elsewhere + * (!astral && amulet) || (astral && !amulet) */ + if (Is_astralevel(&u.uz) ^ (obj->oclass == AMULET_CLASS)) + return GETOBJ_DOWNPLAY; + + return GETOBJ_SUGGEST; +} + +/* getobj callback for object to be tinned */ +static int +tin_ok(obj) +struct obj *obj; +{ + if (!obj || obj->oclass != FOOD_CLASS) + return GETOBJ_EXCLUDE; + + if (obj->otyp != CORPSE || !tinnable(obj)) + return GETOBJ_EXCLUDE_SELECTABLE; + + return GETOBJ_SUGGEST; +} + /* Returns an object representing food. * Object may be either on floor or in inventory. */ @@ -3278,11 +3335,18 @@ int corpsecheck; /* 0, no check, 1, corpses, 2, tinnable corpses */ } skipfloor: - /* We cannot use ALL_CLASSES since that causes getobj() to skip its - * "ugly checks" and we need to check for inedible items. - */ - otmp = getobj(feeding ? allobj : offering ? offerfodder : comestibles, - verb); + /* We cannot use GETOBJ_PROMPT since we don't want a prompt in the case + * where nothing edible is being carried. */ + if (feeding) + otmp = getobj("eat", eat_ok, GETOBJ_NOFLAGS); + else if (offering) + otmp = getobj("sacrifice", offer_ok, GETOBJ_NOFLAGS); + else if (corpsecheck == 2) + otmp = getobj(verb, tin_ok, GETOBJ_NOFLAGS); + else { + impossible("floorfood: unknown request (%s)", verb); + return (struct obj *) 0; + } if (corpsecheck && otmp && !(offering && otmp->oclass == AMULET_CLASS)) if (otmp->otyp != CORPSE || (corpsecheck == 2 && !tinnable(otmp))) { You_cant("%s that!", verb); diff --git a/src/engrave.c b/src/engrave.c index c35311d23..fb3a163c6 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -5,6 +5,7 @@ #include "hack.h" +static int FDECL(stylus_ok, (struct obj *)); static const char *NDECL(blengr); char * @@ -435,10 +436,28 @@ freehand() || (!bimanual(uwep) && (!uarms || !uarms->cursed))); } -static NEARDATA const char styluses[] = { ALL_CLASSES, ALLOW_NONE, - TOOL_CLASS, WEAPON_CLASS, - WAND_CLASS, GEM_CLASS, - RING_CLASS, 0 }; +/* getobj callback for an object to engrave with */ +static int +stylus_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_SUGGEST; + + /* Potential extension: exclude weapons that don't make any sense (such as + * bullwhips) and downplay rings and gems that wouldn't be good to write + * with (such as glass and non-gem rings) */ + if (obj->oclass == WEAPON_CLASS || obj->oclass == WAND_CLASS + || obj->oclass == GEM_CLASS || obj->oclass == RING_CLASS) + return GETOBJ_SUGGEST; + + /* Only markers and towels are recommended tools. */ + if (obj->oclass == TOOL_CLASS + && (obj->otyp == TOWEL || obj->otyp == MAGIC_MARKER)) + return GETOBJ_SUGGEST; + + return GETOBJ_EXCLUDE; +} /* Mohs' Hardness Scale: * 1 - Talc 6 - Orthoclase @@ -545,7 +564,7 @@ doengrave() * Edited by GAN 10/20/86 so as not to change weapon wielded. */ - otmp = getobj(styluses, "write with"); + otmp = getobj("write with", stylus_ok, GETOBJ_PROMPT); if (!otmp) /* otmp == cg.zeroobj if fingers */ return 0; diff --git a/src/invent.c b/src/invent.c index e8d8273cc..17e49514d 100644 --- a/src/invent.c +++ b/src/invent.c @@ -26,7 +26,6 @@ static boolean FDECL(worn_wield_only, (struct obj *)); static boolean FDECL(only_here, (struct obj *)); static void FDECL(compactify, (char *)); static boolean FDECL(taking_off, (const char *)); -static boolean FDECL(putting_on, (const char *)); static int FDECL(ckvalidcat, (struct obj *)); static int FDECL(ckunpaid, (struct obj *)); static char *FDECL(safeq_xprname, (struct obj *)); @@ -39,6 +38,7 @@ static void NDECL(dounpaid); static struct obj *FDECL(find_unpaid, (struct obj *, struct obj **)); static void FDECL(menu_identify, (int)); static boolean FDECL(tool_in_use, (struct obj *)); +static int FDECL(adjust_ok, (struct obj *)); static char FDECL(obj_to_let, (struct obj *)); /* wizards can wish for venom, which will become an invisible inventory @@ -1455,142 +1455,6 @@ const char *action; return !strcmp(action, "take off") || !strcmp(action, "remove"); } -/* match the prompt for either 'W' or 'P' command */ -static boolean -putting_on(action) -const char *action; -{ - return !strcmp(action, "wear") || !strcmp(action, "put on"); -} - -/* helper for getobj(), exclude obj if it cannot be used to do word */ -static boolean -getobj_obj_exclude(word, otmp) -const char *word; -struct obj *otmp; -{ - return ((taking_off(word) /* exclude if not worn */ - && !(otmp->owornmask & (W_ARMOR | W_ACCESSORY))) - || (putting_on(word) /* exclude if already worn */ - && (otmp->owornmask & (W_ARMOR | W_ACCESSORY))) -#if 0 /* 3.4.1 -- include currently wielded weapon among 'wield' choices */ - || (!strcmp(word, "wield") - && (otmp->owornmask & W_WEP)) -#endif - || (!strcmp(word, "ready") /* exclude when wielded... */ - && ((otmp == uwep || (otmp == uswapwep && u.twoweap)) - && otmp->quan == 1L)) /* ...unless more than one */ - || ((!strcmp(word, "dip") || !strcmp(word, "grease")) - && inaccessible_equipment(otmp, (const char *) 0, FALSE))); -} - -/* helper for getobj(), exclude obj if it cannot be used to do word */ -static boolean -getobj_obj_exclude_too(word, otmp) -const char *word; -struct obj *otmp; -{ - short otyp = otmp->otyp; - - return ((putting_on(word) - && ((otmp->oclass == FOOD_CLASS && otyp != MEAT_RING) - || (otmp->oclass == TOOL_CLASS && otyp != BLINDFOLD - && otyp != TOWEL && otyp != LENSES))) - || (!strcmp(word, "wield") - && (otmp->oclass == TOOL_CLASS && !is_weptool(otmp))) - || (!strcmp(word, "eat") && !is_edible(otmp)) - || (!strcmp(word, "sacrifice") - && (otyp != CORPSE && otyp != AMULET_OF_YENDOR - && otyp != FAKE_AMULET_OF_YENDOR)) - || (!strcmp(word, "write with") - && (otmp->oclass == TOOL_CLASS - && otyp != MAGIC_MARKER && otyp != TOWEL)) - || (!strcmp(word, "tin") - && (otyp != CORPSE || !tinnable(otmp))) - || (!strcmp(word, "rub") - && ((otmp->oclass == TOOL_CLASS && otyp != OIL_LAMP - && otyp != MAGIC_LAMP && otyp != BRASS_LANTERN) - || (otmp->oclass == GEM_CLASS && !is_graystone(otmp)) - || (otmp->oclass == FOOD_CLASS - && otmp->otyp != LUMP_OF_ROYAL_JELLY))) - || (!strcmp(word, "use or apply") - /* Picks, axes, pole-weapons, bullwhips */ - && ((otmp->oclass == WEAPON_CLASS - && !is_pick(otmp) && !is_axe(otmp) - && !is_pole(otmp) && otyp != BULLWHIP) - || (otmp->oclass == POTION_CLASS - /* only applicable potion is oil, and it will only - be offered as a choice when already discovered */ - && (otyp != POT_OIL || !otmp->dknown - || !objects[POT_OIL].oc_name_known)) - || (otmp->oclass == FOOD_CLASS - && otyp != CREAM_PIE && otyp != EUCALYPTUS_LEAF - && otyp != LUMP_OF_ROYAL_JELLY) - || (otmp->oclass == GEM_CLASS && !is_graystone(otmp)))) - || (!strcmp(word, "rub the royal jelly on") && otmp->otyp != EGG) - || (!strcmp(word, "invoke") - && !otmp->oartifact - && !objects[otyp].oc_unique - && (otyp != FAKE_AMULET_OF_YENDOR || otmp->known) - && otyp != CRYSTAL_BALL /* synonym for apply */ - /* note: presenting the possibility of invoking non-artifact - mirrors and/or lamps is simply a cruel deception... */ - && otyp != MIRROR - && otyp != MAGIC_LAMP - && (otyp != OIL_LAMP /* don't list known oil lamp */ - || (otmp->dknown && objects[OIL_LAMP].oc_name_known))) - || (!strcmp(word, "untrap with") - && ((otmp->oclass == TOOL_CLASS && otyp != CAN_OF_GREASE) - || (otmp->oclass == POTION_CLASS - /* only applicable potion is oil, and it will only - be offered as a choice when already discovered */ - && (otyp != POT_OIL || !otmp->dknown - || !objects[POT_OIL].oc_name_known)))) - || (!strcmp(word, "tip") && !Is_container(otmp) - /* include horn of plenty if sufficiently discovered */ - && (otmp->otyp != HORN_OF_PLENTY || !otmp->dknown - || !objects[HORN_OF_PLENTY].oc_name_known)) - || (!strcmp(word, "charge") && !is_chargeable(otmp)) - || (!strcmp(word, "open") && otyp != TIN) - || (!strcmp(word, "call") && !objtyp_is_callable(otyp))); -} - -/* helper for getobj(), obj is acceptable but not listed */ -static boolean -getobj_obj_acceptable_unlisted(word, otmp, let) -const char *word; -struct obj *otmp; -char let; -{ - long dummymask; - short otyp = otmp->otyp; - - return (/* ugly check for unworn armor that can't be worn */ - (putting_on(word) && let == ARMOR_CLASS - && !canwearobj(otmp, &dummymask, FALSE)) - /* or armor with 'P' or 'R' or accessory with 'W' or 'T' */ - || ((putting_on(word) || taking_off(word)) - && ((let == ARMOR_CLASS) ^ (otmp->oclass == ARMOR_CLASS))) - /* or unsuitable items rubbed on known touchstone */ - || (!strncmp(word, "rub on the stone", 16) - && let == GEM_CLASS && otmp->dknown - && objects[otyp].oc_name_known) - /* suppress corpses on astral, amulets elsewhere */ - || (!strcmp(word, "sacrifice") - /* (!astral && amulet) || (astral && !amulet) */ - && (!Is_astralevel(&u.uz) ^ (otmp->oclass != AMULET_CLASS))) - /* suppress container being stashed into */ - || (!strcmp(word, "stash") && !ck_bag(otmp)) - /* worn armor (shirt, suit) covered by worn armor (suit, cloak) - or accessory (ring) covered by cursed worn armor (gloves) */ - || (taking_off(word) - && inaccessible_equipment(otmp, (const char *) 0, - (boolean) (otmp->oclass == RING_CLASS))) - || (!strcmp(word, "write on") - && (!(otyp == SCR_BLANK_PAPER || otyp == SPE_BLANK_PAPER) - || !otmp->dknown || !objects[otyp].oc_name_known))); -} - void mime_action(word) const char *word; @@ -1614,68 +1478,57 @@ const char *word; suf ? suf : ""); } +/* getobj callback that allows any object - but not hands. */ +int +any_obj_ok(obj) +struct obj *obj; +{ + if (obj) + return GETOBJ_SUGGEST; + return GETOBJ_EXCLUDE; +} + /* * getobj returns: * struct obj *xxx: object to do something with. * (struct obj *) 0 error return: no object. * &cg.zeroobj explicitly no object (as in w-). -!!!! test if gold can be used in unusual ways (eaten etc.) -!!!! may be able to remove "usegold" + * The obj_ok callback should not have side effects (apart from + * abnormal-behavior things like impossible calls); it can be called multiple + * times on the same object during the execution of this function. + * Callbacks' argument is either a valid object pointer or a null pointer, which + * represents the validity of doing that action on HANDS_SYM. getobj won't call + * it with &cg.zeroobj, so its behavior can be undefined in that case. */ struct obj * -getobj(let, word) -register const char *let, *word; +getobj(word, obj_ok, ctrlflags) +register const char *word; +int FDECL((*obj_ok), (OBJ_P)); /* callback */ +unsigned int ctrlflags; { register struct obj *otmp; register char ilet = 0; char buf[BUFSZ], qbuf[QBUFSZ]; - char lets[BUFSZ], altlets[BUFSZ], *ap; - register int foo = 0; - register char *bp = buf; - xchar allowcnt = 0; /* 0, 1 or 2 */ - boolean usegold = FALSE; /* can't use gold because its illegal */ - boolean allowall = FALSE; - boolean allownone = FALSE; - boolean useboulder = FALSE; - xchar foox = 0; + char lets[BUFSZ], altlets[BUFSZ]; + register int suggested = 0; + register char *bp = buf, *ap = altlets; + boolean allowcnt = (ctrlflags & GETOBJ_ALLOWCNT), + forceprompt = (ctrlflags & GETOBJ_PROMPT), + allownone = FALSE; + xchar inaccess = 0; /* counts GETOBJ_EXCLUDE_INACCESS items for a message + tweak */ long cnt; boolean cntgiven = FALSE; boolean msggiven = FALSE; boolean oneloop = FALSE; Loot *sortedinvent, *srtinv; - if (*let == ALLOW_COUNT) - let++, allowcnt = 1; - if (*let == COIN_CLASS) - let++, usegold = TRUE; - - /* Equivalent of an "ugly check" for gold */ - if (usegold && !strcmp(word, "eat") - && (!metallivorous(g.youmonst.data) - || g.youmonst.data == &mons[PM_RUST_MONSTER])) - usegold = FALSE; - - if (*let == ALL_CLASSES) - let++, allowall = TRUE; - if (*let == ALLOW_NONE) - let++, allownone = TRUE; - /* "ugly check" for reading fortune cookies, part 1. - * The normal 'ugly check' keeps the object on the inventory list. - * We don't want to do that for shirts/cookies, so the check for - * them is handled a bit differently (and also requires that we set - * allowall in the caller). - */ - if (allowall && !strcmp(word, "read")) - allowall = FALSE; - - /* another ugly check: show boulders (not statues) */ - if (*let == WEAPON_CLASS && !strcmp(word, "throw") - && throws_rocks(g.youmonst.data)) - useboulder = TRUE; - - if (allownone) - *bp++ = HANDS_SYM, *bp++ = ' '; /* '-' */ - ap = altlets; + /* is "hands"/"self" a valid thing to do this action on? */ + if ((*obj_ok)((struct obj *) 0) == GETOBJ_SUGGEST) { + allownone = TRUE; + *bp++ = HANDS_SYM; + *bp++ = ' '; /* put a space after the '-' in the prompt */ + } if (!flags.invlet_constant) reassign(); @@ -1686,61 +1539,55 @@ register const char *let, *word; (boolean FDECL((*), (OBJ_P))) 0); for (srtinv = sortedinvent; (otmp = srtinv->obj) != 0; ++srtinv) { - if (&bp[foo] == &buf[sizeof buf - 1] + if (&bp[suggested] == &buf[sizeof buf - 1] || ap == &altlets[sizeof altlets - 1]) { - /* we must have a huge number of NOINVSYM items somehow */ + /* we must have a huge number of noinvsym items somehow */ impossible("getobj: inventory overflow"); break; } - if (!*let || index(let, otmp->oclass) - || (usegold && otmp->invlet == GOLD_SYM) - || (useboulder && otmp->otyp == BOULDER)) { - bp[foo++] = otmp->invlet; - - /* remove inappropriate things */ - if (getobj_obj_exclude(word, otmp)) { - foo--; - foox++; - + bp[suggested++] = otmp->invlet; + switch ((*obj_ok)(otmp)) { + case GETOBJ_EXCLUDE_INACCESS: + /* remove inaccessible things */ + suggested--; + inaccess++; + break; + case GETOBJ_EXCLUDE: + case GETOBJ_EXCLUDE_SELECTABLE: /* remove more inappropriate things, but unlike the first it won't trigger an "else" in "you don't have anything else to ___" */ - } else if (getobj_obj_exclude_too(word, otmp) - || (!strcmp(word, "adjust") - && otmp->oclass == COIN_CLASS && !usegold)) { - foo--; - + suggested--; + break; + case GETOBJ_DOWNPLAY: /* acceptable but not listed as likely candidates in the prompt - or in the inventory subset if player responds with '?' */ - } else if (getobj_obj_acceptable_unlisted(word, otmp, *let)) { - foo--; - allowall = TRUE; - *ap++ = otmp->invlet; - } - } else { - /* "ugly check" for reading fortune cookies, part 2 */ - if ((!strcmp(word, "read") && is_readable(otmp))) - allowall = usegold = TRUE; + or in the inventory subset if player responds with '?' - thus, + don't add it to lets with bp, but add it to altlets with ap */ + suggested--; + forceprompt = TRUE; + *ap++ = otmp->invlet; + break; + case GETOBJ_SUGGEST: + break; /* adding otmp->invlet is all that's needed */ + default: + impossible("bad return from getobj callback"); } } unsortloot(&sortedinvent); - bp[foo] = '\0'; - if (foo == 0 && bp > buf && bp[-1] == ' ') + bp[suggested] = '\0'; + /* If no objects were suggested but we added '- ' at the beginning for + * hands, destroy the trailing space */ + if (suggested == 0 && bp > buf && bp[-1] == ' ') *--bp = '\0'; Strcpy(lets, bp); /* necessary since we destroy buf */ - if (foo > 5) /* compactify string */ + if (suggested > 5) /* compactify string */ compactify(bp); *ap = '\0'; - if (!foo && !allowall && !allownone) { - You("don't have anything %sto %s.", foox ? "else " : "", word); + if (suggested == 0 && !forceprompt && !allownone) { + You("don't have anything %sto %s.", inaccess ? "else " : "", word); return (struct obj *) 0; - } else if (!strcmp(word, "write on")) { /* ugly check for magic marker */ - /* we wanted all scrolls and books in altlets[], but that came with - 'allowall' which we don't want since it prevents "silly thing" - result if anything other than scroll or spellbook is chosen */ - allowall = FALSE; } for (;;) { cnt = 0; @@ -1751,7 +1598,7 @@ register const char *let, *word; else if (iflags.force_invmenu) { /* don't overwrite a possible quitchars */ if (!oneloop) - ilet = *let ? '?' : '*'; + ilet = forceprompt ? '*' : '?'; if (!msggiven) putmsghistory(qbuf, FALSE); msggiven = TRUE; @@ -1840,7 +1687,7 @@ register const char *let, *word; /* guard against the [hypothetical] chace of having more than one invent slot of gold and picking the non-'$' one */ || (otmp && otmp->oclass == COIN_CLASS)) { - if (!usegold) { + if (obj_ok(otmp) <= GETOBJ_EXCLUDE) { You("cannot %s gold.", word); return (struct obj *) 0; } @@ -1887,8 +1734,7 @@ register const char *let, *word; } break; } - if (!allowall && let && !index(let, otmp->oclass) - && !(usegold && otmp->oclass == COIN_CLASS)) { + if (obj_ok(otmp) == GETOBJ_EXCLUDE) { silly_thing(word, otmp); return (struct obj *) 0; } @@ -4111,6 +3957,42 @@ reassign() g.lastinvnr = i; } +/* getobj callback for item to #adjust */ +int +adjust_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + /* gold should never end up in a letter slot, nor should two '$' slots + * occur, but if they ever do, allow #adjust to handle them (in the + * past, things like this have happened, usually due to bknown being + * erroneously set on one stack, clear on another; object merger isn't + * fooled by that anymore) */ + if (obj->oclass == COIN_CLASS) { + int goldstacks = 0; + struct obj *otmp; + if (obj->invlet != GOLD_SYM) + return GETOBJ_SUGGEST; + for (otmp = g.invent; otmp; otmp = otmp->nobj) { + if (otmp->oclass == COIN_CLASS) { + goldstacks++; + } + } + + if (goldstacks > 1) { + impossible("getobj: multiple gold stacks in inventory"); + return GETOBJ_SUGGEST; + } + /* assuming this impossible case doesn't happen, gold should be + * outright ignored as far as #adjust is concerned */ + return GETOBJ_EXCLUDE; + } + + return GETOBJ_SUGGEST; +} + /* #adjust command * * User specifies a 'from' slot for inventory stack to move, @@ -4156,14 +4038,13 @@ int doorganize() /* inventory organizer by Del Lamb */ { struct obj *obj, *otmp, *splitting, *bumped; - int ix, cur, trycnt, goldstacks; + int ix, cur, trycnt; char let; #define GOLD_INDX 0 #define GOLD_OFFSET 1 #define OVRFLW_INDX (GOLD_OFFSET + 52) /* past gold and 2*26 letters */ char lets[1 + 52 + 1 + 1]; /* room for '$a-zA-Z#\0' */ char qbuf[QBUFSZ]; - char allowall[4]; /* { ALLOW_COUNT, ALL_CLASSES, 0, 0 } */ char *objname, *otmpname; const char *adj_type; boolean ever_mind = FALSE, collect; @@ -4179,24 +4060,8 @@ doorganize() /* inventory organizer by Del Lamb */ if (!flags.invlet_constant) reassign(); /* get object the user wants to organize (the 'from' slot) */ - allowall[0] = ALLOW_COUNT; - allowall[1] = ALL_CLASSES; - allowall[2] = '\0'; - for (goldstacks = 0, otmp = g.invent; otmp; otmp = otmp->nobj) { - /* gold should never end up in a letter slot, nor should two '$' - slots occur, but if they ever do, allow #adjust to handle them - (in the past, things like this have happened, usually due to - bknown being erroneously set on one stack, clear on another; - object merger isn't fooled by that anymore) */ - if (otmp->oclass == COIN_CLASS - && (otmp->invlet != GOLD_SYM || ++goldstacks > 1)) { - allowall[1] = COIN_CLASS; - allowall[2] = ALL_CLASSES; - allowall[3] = '\0'; - break; - } - } - if (!(obj = getobj(allowall, "adjust"))) + obj = getobj("adjust", adjust_ok, GETOBJ_PROMPT | GETOBJ_ALLOWCNT); + if (!obj) return 0; /* figure out whether user gave a split count to getobj() */ diff --git a/src/pickup.c b/src/pickup.c index 4b5e715cf..bbfec3db7 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -37,9 +37,11 @@ static long FDECL(boh_loss, (struct obj *, int)); static int FDECL(in_container, (struct obj *)); static int FDECL(out_container, (struct obj *)); static long FDECL(mbag_item_gone, (int, struct obj *, BOOLEAN_P)); +static int FDECL(stash_ok, (struct obj *)); static void FDECL(explain_container_prompt, (BOOLEAN_P)); static int FDECL(traditional_loot, (BOOLEAN_P)); static int FDECL(menu_loot, (int, BOOLEAN_P)); +static int FDECL(tip_ok, (struct obj *)); static char FDECL(in_or_out_menu, (const char *, struct obj *, BOOLEAN_P, BOOLEAN_P, BOOLEAN_P, BOOLEAN_P)); static boolean FDECL(able_to_loot, (int, int, BOOLEAN_P)); @@ -2667,7 +2669,22 @@ u_handsy() return TRUE; } -static const char stashable[] = { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, 0 }; +/* getobj callback for object to be stashed into a container */ +int +stash_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + /* downplay the container being stashed into */ + if (!ck_bag(obj)) + return GETOBJ_EXCLUDE_SELECTABLE; + /* Possible extension: downplay things too big to fit into containers (in + * which case extract in_container()'s logic.) */ + + return GETOBJ_SUGGEST; +} int use_container(objp, held, more_containers) @@ -2863,7 +2880,8 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */ add_valid_menu_class(0); } else if (stash_one) { /* put one item into container */ - if ((otmp = getobj(stashable, "stash")) != 0) { + if ((otmp = getobj("stash", stash_ok, + GETOBJ_PROMPT | GETOBJ_ALLOWCNT)) != 0) { if (in_container(otmp)) { used = 1; } else { @@ -3122,7 +3140,26 @@ boolean outokay, inokay, alreadyused, more_containers; return (n == 0 && more_containers) ? 'n' : 'q'; /* next or quit */ } -static const char tippables[] = { ALL_CLASSES, TOOL_CLASS, 0 }; +/* getobj callback for object to tip */ +static int +tip_ok(obj) +struct obj *obj; +{ + if (!obj || obj->oclass == COIN_CLASS) + return GETOBJ_EXCLUDE; + + if (Is_container(obj)) { + return GETOBJ_SUGGEST; + } + + /* include horn of plenty if sufficiently discovered */ + if (obj->otyp == HORN_OF_PLENTY && obj->dknown && + objects[obj->otyp].oc_name_known) + return GETOBJ_SUGGEST; + + /* allow trying anything else in inventory */ + return GETOBJ_DOWNPLAY; +} /* #tip command -- empty container contents onto floor */ int @@ -3222,7 +3259,7 @@ dotip() } /* either no floor container(s) or couldn't tip one or didn't tip any */ - cobj = getobj(tippables, "tip"); + cobj = getobj("tip", tip_ok, GETOBJ_PROMPT); if (!cobj) return 0; diff --git a/src/potion.c b/src/potion.c index 4d3265c0d..d46d4da32 100644 --- a/src/potion.c +++ b/src/potion.c @@ -5,14 +5,14 @@ #include "hack.h" -static NEARDATA const char beverages[] = { POTION_CLASS, 0 }; - static long FDECL(itimeout, (long)); static long FDECL(itimeout_incr, (long, int)); static void NDECL(ghost_from_bottle); +static int FDECL(drink_ok, (struct obj *)); static boolean FDECL(H2Opotion_dip, (struct obj *, struct obj *, BOOLEAN_P, const char *)); static short FDECL(mixtype, (struct obj *, struct obj *)); +static int FDECL(dip_ok, (struct obj *)); /* force `val' to be within valid range for intrinsic timeout value */ static long @@ -481,6 +481,18 @@ ghost_from_bottle() g.nomovemsg = "You regain your composure."; } +/* getobj callback for object to drink from, which also does double duty as the + * callback for dipping into (both just allow potions). */ +static int +drink_ok(obj) +struct obj *obj; +{ + if (obj && obj->oclass == POTION_CLASS) + return GETOBJ_SUGGEST; + + return GETOBJ_EXCLUDE; +} + /* "Quaffing is like drinking, except you spill more." - Terry Pratchett */ int dodrink() @@ -517,7 +529,7 @@ dodrink() } } - otmp = getobj(beverages, "drink"); + otmp = getobj("drink", drink_ok, GETOBJ_NOFLAGS); if (!otmp) return 0; @@ -1888,6 +1900,22 @@ register struct obj *o1, *o2; return STRANGE_OBJECT; } +/* getobj callback for object to be dipped (not the thing being dipped into, + * that uses drink_ok) */ +static int +dip_ok(obj) +struct obj *obj; +{ + /* dipping hands and gold isn't currently implemented */ + if (!obj || obj->oclass == COIN_CLASS) + return GETOBJ_EXCLUDE; + + if (inaccessible_equipment(obj, (const char *) 0, FALSE)) + return GETOBJ_EXCLUDE_INACCESS; + + return GETOBJ_SUGGEST; +} + /* #dip command */ int dodip() @@ -1896,14 +1924,11 @@ dodip() register struct obj *potion, *obj; struct obj *singlepotion; uchar here; - char allowall[2]; short mixture; char qbuf[QBUFSZ], obuf[QBUFSZ]; const char *shortestname; /* last resort obj name for prompt */ - allowall[0] = ALL_CLASSES; - allowall[1] = '\0'; - if (!(obj = getobj(allowall, "dip"))) + if (!(obj = getobj("dip", dip_ok, GETOBJ_PROMPT))) return 0; if (inaccessible_equipment(obj, "dip", FALSE)) return 0; @@ -1957,7 +1982,7 @@ dodip() /* "What do you want to dip into? [xyz or ?*] " */ Sprintf(qbuf, "dip %s into", flags.verbose ? obuf : shortestname); - potion = getobj(beverages, qbuf); + potion = getobj(qbuf, drink_ok, GETOBJ_NOFLAGS); if (!potion) return 0; if (potion == obj && potion->quan == 1L) { diff --git a/src/read.c b/src/read.c index 130259ee3..ba6fa214f 100644 --- a/src/read.c +++ b/src/read.c @@ -12,13 +12,10 @@ ((mndx) == g.urace.malenum \ || (g.urace.femalenum != NON_PM && (mndx) == g.urace.femalenum)) -static NEARDATA const char readable[] = { ALL_CLASSES, SCROLL_CLASS, - SPBOOK_CLASS, 0 }; -static const char all_count[] = { ALLOW_COUNT, ALL_CLASSES, 0 }; - static boolean FDECL(learnscrolltyp, (SHORT_P)); static void FDECL(cap_spe, (struct obj *)); static char *FDECL(erode_obj_text, (struct obj *, char *)); +static int FDECL(read_ok, (struct obj *)); static void FDECL(stripspe, (struct obj *)); static void FDECL(p_glow1, (struct obj *)); static void FDECL(p_glow2, (struct obj *, const char *)); @@ -228,6 +225,20 @@ struct obj *obj; return; } +/* getobj callback for object to read */ +static int +read_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) + return GETOBJ_SUGGEST; + + return GETOBJ_DOWNPLAY; +} + /* the 'r' command; read a scroll or spell book or various other things */ int doread() @@ -258,7 +269,7 @@ doread() if (check_capacity((char *) 0)) return 0; - scroll = getobj(readable, "read"); + scroll = getobj("read", read_ok, GETOBJ_PROMPT); if (!scroll) return 0; otyp = scroll->otyp; @@ -534,33 +545,38 @@ register const char *color; Blind ? "" : " ", Blind ? "" : hcolor(color)); } -/* Is the object chargeable? For purposes of inventory display; it is - possible to be able to charge things for which this returns FALSE. */ -boolean -is_chargeable(obj) +/* getobj callback for object to charge */ +int +charge_ok(obj) struct obj *obj; { + if (!obj) + return GETOBJ_EXCLUDE; + if (obj->oclass == WAND_CLASS) - return TRUE; - /* known && !oc_name_known is possible after amnesia/mind flayer */ - if (obj->oclass == RING_CLASS) - return (boolean) (objects[obj->otyp].oc_charged - && (obj->known - || (obj->dknown - && objects[obj->otyp].oc_name_known))); + return GETOBJ_SUGGEST; + + if (obj->oclass == RING_CLASS && objects[obj->otyp].oc_charged + && obj->dknown && objects[obj->otyp].oc_name_known) + return GETOBJ_SUGGEST; + if (is_weptool(obj)) /* specific check before general tools */ - return FALSE; + return GETOBJ_EXCLUDE; + if (obj->oclass == TOOL_CLASS) { + /* suggest tools that aren't oc_charged but can still be recharged */ if (obj->otyp == BRASS_LANTERN || (obj->otyp == OIL_LAMP) /* only list magic lamps if they are not identified yet */ || (obj->otyp == MAGIC_LAMP && !objects[MAGIC_LAMP].oc_name_known)) { - return TRUE; + return GETOBJ_SUGGEST; } - return (boolean) objects[obj->otyp].oc_charged; + return objects[obj->otyp].oc_charged ? GETOBJ_SUGGEST : GETOBJ_EXCLUDE; } - return FALSE; /* why are weapons/armor considered charged anyway? */ + /* why are weapons/armor considered charged anyway? + * make them selectable even so for "feeling of loss" message */ + return GETOBJ_EXCLUDE_SELECTABLE; } /* recharge an object; curse_bless is -1 if the recharging implement @@ -1541,7 +1557,7 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ was already delivered */ useup(sobj); sobj = 0; /* it's gone */ - otmp = getobj(all_count, "charge"); + otmp = getobj("charge", charge_ok, GETOBJ_PROMPT | GETOBJ_ALLOWCNT); if (otmp) recharge(otmp, scursed ? -1 : sblessed ? 1 : 0); break; diff --git a/src/trap.c b/src/trap.c index d922049db..00d4140ad 100644 --- a/src/trap.c +++ b/src/trap.c @@ -49,6 +49,7 @@ static int FDECL(try_disarm, (struct trap *, BOOLEAN_P)); static void FDECL(reward_untrap, (struct trap *, struct monst *)); static int FDECL(disarm_holdingtrap, (struct trap *)); static int FDECL(disarm_landmine, (struct trap *)); +static int FDECL(unsqueak_ok, (struct obj *)); static int FDECL(disarm_squeaky_board, (struct trap *)); static int FDECL(disarm_shooting_trap, (struct trap *, int)); static void FDECL(clear_conjoined_pits, (struct trap *)); @@ -4574,9 +4575,29 @@ struct trap *ttmp; return 1; } -/* getobj will filter down to cans of grease and known potions of oil */ -static NEARDATA const char oil[] = { ALL_CLASSES, TOOL_CLASS, POTION_CLASS, - 0 }; +/* getobj callback for object to disarm a squeaky board with */ +static int +unsqueak_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_EXCLUDE; + + if (obj->otyp == CAN_OF_GREASE) + return GETOBJ_SUGGEST; + + if (obj->otyp == POT_OIL && obj->dknown && + objects[POT_OIL].oc_name_known) + return GETOBJ_SUGGEST; + + /* downplay all other potions, including unidentified oil + * Potential extension: if oil is known, skip this and exclude all other + * potions. */ + if (obj->oclass == POTION_CLASS) + return GETOBJ_DOWNPLAY; + + return GETOBJ_EXCLUDE; +} /* it may not make much sense to use grease on floor boards, but so what? */ static int @@ -4587,7 +4608,7 @@ struct trap *ttmp; boolean bad_tool; int fails; - obj = getobj(oil, "untrap with"); + obj = getobj("untrap with", unsqueak_ok, GETOBJ_PROMPT); if (!obj) return 0; diff --git a/src/wield.c b/src/wield.c index b5f2dbac8..a296975ba 100644 --- a/src/wield.c +++ b/src/wield.c @@ -54,6 +54,8 @@ static boolean FDECL(cant_wield_corpse, (struct obj *)); static int FDECL(ready_weapon, (struct obj *)); +static int FDECL(ready_ok, (struct obj *)); +static int FDECL(wield_ok, (struct obj *)); /* used by will_weld() */ /* probably should be renamed */ @@ -267,18 +269,51 @@ register struct obj *obj; return; } -/*** Commands to change particular slot(s) ***/ +/* getobj callback for object to ready for throwing/shooting */ +static int +ready_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_SUGGEST; -static NEARDATA const char wield_objs[] = { - ALLOW_COUNT, ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, TOOL_CLASS, 0 -}; -static NEARDATA const char ready_objs[] = { - ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, 0 -}; -static NEARDATA const char w_bullets[] = { /* (different from dothrow.c) */ - ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, ALLOW_NONE, - GEM_CLASS, WEAPON_CLASS, 0 -}; + /* exclude when wielded... */ + if ((obj == uwep || (obj == uswapwep && u.twoweap)) + && obj->quan == 1) /* ...unless more than one */ + return GETOBJ_EXCLUDE_INACCESS; + + if (obj->oclass == WEAPON_CLASS || obj->oclass == COIN_CLASS) + return GETOBJ_SUGGEST; + /* Possible extension: exclude weapons that make no sense to throw, such as + * whips, bows, slings, rubber hoses. */ + + /* Include gems/stones as likely candidates if either primary + or secondary weapon is a sling. */ + if (obj->oclass == GEM_CLASS + && (uslinging() + || (uswapwep && objects[uswapwep->otyp].oc_skill == P_SLING))) + return GETOBJ_SUGGEST; + + return GETOBJ_DOWNPLAY; +} + +/* getobj callback for object to wield */ +static int +wield_ok(obj) +struct obj *obj; +{ + if (!obj) + return GETOBJ_SUGGEST; + + if (obj->oclass == COIN_CLASS) + return GETOBJ_EXCLUDE; + + if (obj->oclass == WEAPON_CLASS + || (obj->oclass == TOOL_CLASS && is_weptool(obj))) + return GETOBJ_SUGGEST; + + return GETOBJ_DOWNPLAY; +} int dowield() @@ -297,7 +332,7 @@ dowield() /* Prompt for a new weapon */ clear_splitobjs(); - if (!(wep = getobj(wield_objs, "wield"))) { + if (!(wep = getobj("wield", wield_ok, GETOBJ_PROMPT | GETOBJ_ALLOWCNT))) { /* Cancelled */ return 0; } else if (wep == uwep) { @@ -438,7 +473,6 @@ dowieldquiver() { char qbuf[QBUFSZ]; struct obj *newquiver; - const char *quivee_types; int res; boolean finish_splitting = FALSE, was_uwep = FALSE, was_twoweap = u.twoweap; @@ -446,18 +480,11 @@ dowieldquiver() /* Since the quiver isn't in your hands, don't check cantwield(), */ /* will_weld(), touch_petrifies(), etc. */ g.multi = 0; - /* forget last splitobj() before calling getobj() with ALLOW_COUNT */ + /* forget last splitobj() before calling getobj() with GETOBJ_ALLOWCNT */ clear_splitobjs(); - /* Prompt for a new quiver: "What do you want to ready?" - (Include gems/stones as likely candidates if either primary - or secondary weapon is a sling.) */ - quivee_types = (uslinging() - || (uswapwep - && objects[uswapwep->otyp].oc_skill == P_SLING)) - ? w_bullets - : ready_objs; - newquiver = getobj(quivee_types, "ready"); + /* Prompt for a new quiver: "What do you want to ready?" */ + newquiver = getobj("ready", ready_ok, GETOBJ_PROMPT | GETOBJ_ALLOWCNT); if (!newquiver) { /* Cancelled */ diff --git a/src/write.c b/src/write.c index 326713886..fb0d28a6a 100644 --- a/src/write.c +++ b/src/write.c @@ -5,6 +5,7 @@ static int FDECL(cost, (struct obj *)); static boolean FDECL(label_known, (int, struct obj *)); +static int FDECL(write_ok, (struct obj *)); static char *FDECL(new_book_description, (int, char *)); /* @@ -87,7 +88,19 @@ struct obj *objlist; return FALSE; } -static NEARDATA const char write_on[] = { SCROLL_CLASS, SPBOOK_CLASS, 0 }; +/* getobj callback for object to write on */ +static int +write_ok(obj) +struct obj *obj; +{ + if (!obj || (obj->oclass != SCROLL_CLASS && obj->oclass != SPBOOK_CLASS)) + return GETOBJ_EXCLUDE; + + if (obj->otyp == SCR_BLANK_PAPER || obj->otyp == SPE_BLANK_PAPER) + return GETOBJ_SUGGEST; + + return GETOBJ_DOWNPLAY; +} /* write -- applying a magic marker */ int @@ -115,7 +128,7 @@ register struct obj *pen; } /* get paper to write on */ - paper = getobj(write_on, "write on"); + paper = getobj("write on", write_ok, GETOBJ_NOFLAGS); if (!paper) return 0; /* can't write on a novel (unless/until it's been converted into a blank diff --git a/src/zap.c b/src/zap.c index 04ebb8181..de51db503 100644 --- a/src/zap.c +++ b/src/zap.c @@ -23,6 +23,7 @@ static void FDECL(skiprange, (int, int *, int *)); static int FDECL(zap_hit, (int, int)); static void FDECL(disintegrate_mon, (struct monst *, int, const char *)); static void FDECL(backfire, (struct obj *)); +static int FDECL(zap_ok, (struct obj *)); static void FDECL(boxlock_invent, (struct obj *)); static int FDECL(spell_hit_bonus, (int)); static void FDECL(destroy_one_item, (struct obj *, int, int)); @@ -2302,7 +2303,15 @@ struct obj *otmp; useupall(otmp); } -static NEARDATA const char zap_syms[] = { WAND_CLASS, 0 }; +/* getobj callback for object to zap */ +static int +zap_ok(obj) +struct obj *obj; +{ + if (obj && obj->oclass == WAND_CLASS) + return GETOBJ_SUGGEST; + return GETOBJ_EXCLUDE; +} /* 'z' command (or 'y' if numbed_pad==-1) */ int @@ -2317,7 +2326,7 @@ dozap() } if (check_capacity((char *) 0)) return 0; - obj = getobj(zap_syms, "zap"); + obj = getobj("zap", zap_ok, GETOBJ_NOFLAGS); if (!obj) return 0;