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),
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,
#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
*/
/* 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) */
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));
*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()
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;
*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)
dropx(obj);
return;
}
- otmp = getobj(lubricables, "grease");
+ otmp = getobj("grease", grease_ok, GETOBJ_PROMPT);
if (!otmp)
return;
if (inaccessible_equipment(otmp, "grease", FALSE))
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) {
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;
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 */
return 1;
}
-static void
-add_class(cl, class)
-char *cl;
-char class;
-{
- char tmp[2];
-
- tmp[0] = class;
- tmp[1] = '\0';
- Strcat(cl, tmp);
-}
-
-static const char tools[] = { TOOL_CLASS, WEAPON_CLASS, WAND_CLASS, 0 };
-
-/* augment tools[] if various items are carried */
-static void
-setapplyclasses(class_list)
-char class_list[];
+/* 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;
{
- register struct obj *otmp;
- int otyp;
- boolean knowoil, knowtouchstone;
- boolean addpotions, addstones, addfood, addspellbooks;
-
- 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;
- }
-
- 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);
+ if (!obj)
+ return GETOBJ_EXCLUDE;
+
+ /* 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;
+
+ /* certain weapons */
+ if (obj->oclass == WEAPON_CLASS
+ && (is_pick(obj) || is_axe(obj) || is_pole(obj)
+ || obj->otyp == BULLWHIP))
+ return GETOBJ_SUGGEST;
+
+ /* 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;
+
+ /* 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;
+ }
+
+ return GETOBJ_EXCLUDE;
}
/* the 'a' command */
{
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.");
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;
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 *));
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
{
struct obj *obj;
- obj = getobj(invoke_types, "invoke");
+ obj = getobj("invoke", invoke_ok, GETOBJ_PROMPT);
if (!obj)
return 0;
if (!retouch_object(&obj, FALSE))
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) {
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
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)
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);
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 */
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;
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
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 {
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) {
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 *
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 */
return 0;
}
if (Narmorpieces != 1 || ParanoidRemove)
- otmp = getobj(clothes, "take off");
+ otmp = getobj("take off", takeoff_ok, GETOBJ_NOFLAGS);
if (!otmp)
return 0;
return 0;
}
if (Naccessories != 1 || ParanoidRemove)
- otmp = getobj(accessories, "remove");
+ otmp = getobj("remove", remove_ok, GETOBJ_NOFLAGS);
if (!otmp)
return 0;
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;
}
(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;
}
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*/
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));
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
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()
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... ) */
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 */
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));
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))
#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 " };
&& !Has_contents(obj))
return TRUE;
- /* return (boolean) !!index(comestibles, obj->oclass); */
return (boolean) (obj->oclass == FOOD_CLASS);
}
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;
res = 1;
}
- otmp = getobj(comestibles, "open");
+ otmp = getobj("open", tinopen_ok, GETOBJ_NOFLAGS);
if (!otmp)
return res;
}
}
+/* 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.
*/
}
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);
#include "hack.h"
+static int FDECL(stylus_ok, (struct obj *));
static const char *NDECL(blengr);
char *
|| (!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
* 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;
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 *));
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
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;
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();
(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;
else if (iflags.force_invmenu) {
/* don't overwrite a possible quitchars */
if (!oneloop)
- ilet = *let ? '?' : '*';
+ ilet = forceprompt ? '*' : '?';
if (!msggiven)
putmsghistory(qbuf, FALSE);
msggiven = TRUE;
/* 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;
}
}
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;
}
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,
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;
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() */
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));
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)
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 {
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
}
/* 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;
#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
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()
}
}
- otmp = getobj(beverages, "drink");
+ otmp = getobj("drink", drink_ok, GETOBJ_NOFLAGS);
if (!otmp)
return 0;
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()
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;
/* "What do you want to dip <the object> 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) {
((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 *));
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()
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;
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
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;
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 *));
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
boolean bad_tool;
int fails;
- obj = getobj(oil, "untrap with");
+ obj = getobj("untrap with", unsqueak_ok, GETOBJ_PROMPT);
if (!obj)
return 0;
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 */
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;
+
+ /* 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;
+}
-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
-};
+/* 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()
/* 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) {
{
char qbuf[QBUFSZ];
struct obj *newquiver;
- const char *quivee_types;
int res;
boolean finish_splitting = FALSE,
was_uwep = FALSE, was_twoweap = u.twoweap;
/* 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 */
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 *));
/*
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
}
/* 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
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));
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
}
if (check_capacity((char *) 0))
return 0;
- obj = getobj(zap_syms, "zap");
+ obj = getobj("zap", zap_ok, GETOBJ_NOFLAGS);
if (!obj)
return 0;