item-action 'I' - use #adjust to split a stack

More context-sensitive inventory support.  While examining inventory,
if you pick an item other than gold and it has a quantity of more
than 1, "I - Adjust inventory by splitting this stack" will be one
of the menu choices.

Breaking doorganize() into two parts was much easier than expected,
but the new internal command added to be an alternate for the first
part had more niggling details than anticipated.

Message history only shows the first digit with "Split off how many?"
if the player enters more than that.
This commit is contained in:
PatR
2022-04-20 13:38:09 -07:00
parent 627fa5efad
commit a9a9d19038
5 changed files with 120 additions and 36 deletions

View File

@@ -286,7 +286,7 @@ extern const char *directionname(int);
extern int isok(int, int);
extern int get_adjacent_loc(const char *, const char *, xchar, xchar, coord *);
extern const char *click_to_cmd(int, int, int);
extern char get_count(char *, char, long, cmdcount_nht *, boolean);
extern char get_count(const char *, char, long, cmdcount_nht *, unsigned);
#ifdef HANGUPHANDLING
extern void hangup(int);
extern void end_of_input(void);
@@ -1148,6 +1148,7 @@ extern void free_invbuf(void);
extern void reassign(void);
extern boolean check_invent_gold(const char *);
extern int doorganize(void);
extern int adjust_split(void);
extern void free_pickinv_cache(void);
extern int count_unpaid(struct obj *);
extern int count_buc(struct obj *, int, boolean(*)(struct obj *));

View File

@@ -476,6 +476,11 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */
#define SUPPRESS_HISTORY 4
#define URGENT_MESSAGE 8
/* get_count flags */
#define GC_NOFLAGS 0
#define GC_SAVEHIST 1 /* save "Count: 123" in message history */
#define GC_ECHOFIRST 2 /* echo "Count: 1" even when there's only one digit */
/* rloc() flags */
#define RLOC_NONE 0x00
#define RLOC_ERR 0x01 /* allow impossible() if no rloc */

View File

@@ -2605,6 +2605,7 @@ struct ext_func_tab extcmdlist[] = {
/* internal commands: only used by game core, not available for user */
{ '\0', "clicklook", NULL, doclicklook, INTERNALCMD, NULL },
{ '\0', "altdip", NULL, dip_into, INTERNALCMD, NULL },
{ '\0', "altadjust", NULL, adjust_split, INTERNALCMD, NULL },
{ '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */
};
@@ -5124,20 +5125,25 @@ click_to_cmd(int x, int y, int mod)
/* gather typed digits into a number in *count; return the next non-digit */
char
get_count(
char *allowchars,
char inkey,
long maxcount,
cmdcount_nht *count,
boolean historicmsg) /* whether to include in ^P history: True => yes */
const char *allowchars, /* what comes after digits; if Null, anything */
char inkey, /* if caller already got first digit, this is it */
long maxcount, /* if user tries to enter a bigger count, use this */
cmdcount_nht *count, /* primary output */
unsigned gc_flags) /* control flags: GC_SAVEHIST, GC_ECHOFIRST */
{
char qbuf[QBUFSZ];
int key;
long cnt = 0L;
boolean backspaced = FALSE;
boolean backspaced = FALSE, showzero = TRUE,
/* should "Count: 123" go into message history? */
historicmsg = (gc_flags & GC_SAVEHIST) != 0,
/* normally "Count: 12" isn't echoed until the second digit */
echoalways = (gc_flags & GC_ECHOFIRST) != 0;
/* this should be done in port code so that we have erase_char
and kill_char available; we can at least fake erase_char */
#define STANDBY_erase_char '\177'
*count = 0;
for (;;) {
if (inkey) {
key = inkey;
@@ -5151,19 +5157,26 @@ get_count(
cnt = 0L;
else if (maxcount > 0L && cnt > maxcount)
cnt = maxcount;
} else if (cnt && (key == '\b' || key == STANDBY_erase_char)) {
/* if we've backed up to nothing, then typed 0, show that 0 */
showzero = (key == '0');
} else if (key == '\b' || key == STANDBY_erase_char) {
if (!cnt && !echoalways)
break;
showzero = FALSE;
cnt = cnt / 10L;
backspaced = TRUE;
} else if (key == g.Cmd.spkeys[NHKF_ESC]) {
break;
} else if (!allowchars || index(allowchars, key)) {
*count = cnt;
*count = (cmdcount_nht) cnt;
if ((long) *count != cnt)
impossible("get_count: cmdcount_nht");
break;
}
if (cnt > 9 || backspaced) {
if (cnt > 9 || backspaced || echoalways) {
clear_nhwindow(WIN_MESSAGE);
if (backspaced && !cnt) {
if (backspaced && !cnt && !showzero) {
Sprintf(qbuf, "Count: ");
} else {
Sprintf(qbuf, "Count: %ld", cnt);
@@ -5199,7 +5212,7 @@ parse(void)
* reset to 0 by readchar() */
if (!g.Cmd.num_pad || (foo = readchar()) == g.Cmd.spkeys[NHKF_COUNT]) {
foo = get_count((char *) 0, '\0', LARGEST_INT,
&g.command_count, FALSE);
&g.command_count, GC_NOFLAGS);
g.last_command_count = g.command_count;
}

View File

@@ -34,6 +34,7 @@ static void menu_identify(int);
static boolean tool_being_used(struct obj *);
static int adjust_ok(struct obj *);
static int adjust_gold_ok(struct obj *);
static int doorganize_core(struct obj *);
static char obj_to_let(struct obj *);
static boolean item_naming_classification(struct obj *, char *, char *);
static int item_reading_classification(struct obj *, char *);
@@ -1659,7 +1660,7 @@ getobj(
pline("No count allowed with this command.");
continue;
}
ilet = get_count(NULL, ilet, LARGEST_INT, &tmpcnt, TRUE);
ilet = get_count(NULL, ilet, LARGEST_INT, &tmpcnt, GC_SAVEHIST);
if (tmpcnt) {
cnt = tmpcnt;
cntgiven = TRUE;
@@ -2777,16 +2778,15 @@ itemactions(struct obj *otmp)
ia_addmenu(win, IA_ENGRAVE_OBJ, 'E',
"Write on the floor with this object");
/* i: #adjust inventory letter */
if (otmp->oclass != COIN_CLASS) /* gold is always "letter" '$' */
/* i: #adjust inventory letter; gold can't be adjusted unless there
is some in a slot other than '$' (which shouldn't be possible) */
if (otmp->oclass != COIN_CLASS || check_invent_gold("item-action"))
ia_addmenu(win, IA_ADJUST_OBJ, 'i',
"Adjust inventory by assigning new letter");
#if 0
/* I: #adjust inventory item by splitting its stack */
if (otmp->quan > 1L && otmp->oclass != COIN_CLASS)
ia_addmenu(win, IA_ADJUST_STACK, 'I',
"Adjust inventory by splitting this stack");
#endif
/* O: offer sacrifice */
if (IS_ALTAR(levl[u.ux][u.uy].typ) && !u.uswallow) {
@@ -2971,11 +2971,8 @@ itemactions(struct obj *otmp)
cmdq_add_key(otmp->invlet);
break;
case IA_ADJUST_STACK:
#if 0 /* will need an alternate command routine (like #altdip) in
* order to prompt for a count */
cmdq_add_ec(doorganize);
cmdq_add_ec(adjust_split); /* #altadjust */
cmdq_add_key(otmp->invlet);
#endif
break;
case IA_SACRIFICE:
cmdq_add_ec(dosacrifice);
@@ -4760,18 +4757,8 @@ adjust_gold_ok(struct obj *obj)
int
doorganize(void) /* inventory organizer by Del Lamb */
{
struct obj *obj, *otmp, *splitting, *bumped;
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 *objname, *otmpname;
const char *adj_type;
int (*adjust_filter)(struct obj *);
boolean ever_mind = FALSE, collect, isgold;
struct obj *obj;
/* when no invent, or just gold in '$' slot, there's nothing to adjust */
if (!g.invent || (g.invent->oclass == COIN_CLASS
@@ -4789,8 +4776,80 @@ doorganize(void) /* inventory organizer by Del Lamb */
/* get object the user wants to organize (the 'from' slot) */
obj = getobj("adjust", adjust_filter, GETOBJ_PROMPT | GETOBJ_ALLOWCNT);
return doorganize_core(obj);
}
/* alternate version of #adjust used by itemactions() for splitting */
int
adjust_split(void)
{
struct obj *obj;
cmdcount_nht splitamount = 0L;
char let, dig = '\0';
/* invlet should be queued so no getobj prompting is expected */
obj = getobj("split", adjust_ok, GETOBJ_NOFLAGS);
if (!obj || obj->quan < 2L || obj->otyp == GOLD_PIECE)
return ECMD_FAIL; /* caller has set things up to avoid this */
if (obj->quan == 2L) {
splitamount = 1L;
} else {
/* get first digit; doesn't wait for <return> */
dig = yn_function("Split off how many?", (char *) 0, '\0');
if (!digit(dig)) {
pline1(Never_mind);
return ECMD_CANCEL;
}
/* got first digit, get more until next non-digit (except for
backspace/delete which will take away most recent digit and
keep going; we expect one of ' ', '\n', or '\r') */
let = get_count(NULL, dig, LARGEST_INT, &splitamount, GC_ECHOFIRST);
/* \033 is in quitchars[] so we need to check for it separately
in order to treat it as cancel rather than as accept */
if (!let || let == '\033' || !index(quitchars, let)) {
pline1(Never_mind);
return ECMD_CANCEL;
}
}
if (splitamount < 1L || splitamount >= obj->quan) {
static const char
Amount[] = "Amount to split from current stack must be";
if (splitamount < 1L)
pline("%s at least 1.", Amount);
else
pline("%s less than %ld.", Amount, obj->quan);
return ECMD_CANCEL;
}
/* normally a split would take place in getobj() if player supplies
a count there, so doorganize_core() figures out 'splitamount'
from the object; it will undo the split if player cancels while
selecting the destination slot */
obj = splitobj(obj, (long) splitamount);
return doorganize_core(obj);
}
static int
doorganize_core(struct obj *obj)
{
struct obj *otmp, *splitting, *bumped;
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 *objname, *otmpname;
const char *adj_type;
boolean ever_mind = FALSE, collect, isgold;
if (!obj)
return ECMD_CANCEL;
/* can only be gold if check_invent_gold() found a problem: multiple '$'
stacks and/or gold in some other slot, otherwise (*adjust_filter)()
won't allow gold to be picked; if player has picked any stack of gold
@@ -4843,7 +4902,11 @@ doorganize(void) /* inventory organizer by Del Lamb */
compactify(lets);
/* get 'to' slot to use as destination */
Sprintf(qbuf, "Adjust letter to what [%s]%s?", lets,
if (!splitting)
Strcpy(qbuf, "Adjust letter");
else /* note: splitting->quan is the amount being left in original slot */
Sprintf(qbuf, "Split %ld", obj->quan);
Sprintf(eos(qbuf), " to what [%s]%s?", lets,
g.invent ? " (? see used letters)" : "");
for (trycnt = 1; ; ++trycnt) {
let = !isgold ? yn_function(qbuf, (char *) 0, '\0') : GOLD_SYM;

View File

@@ -702,9 +702,11 @@ curses_get_count(int first_digit)
curses's message window will display that in count window instead */
current_char = get_count(NULL, (char) first_digit,
/* 0L => no limit on value unless it wraps
* to negative;
* FALSE => suppress from message history */
0L, &current_count, FALSE);
to negative */
0L, &current_count,
/* default: don't put into message history,
don't echo until second digit entered */
GC_NOFLAGS);
ungetch(current_char);
if (current_char == '\033') { /* Cancelled with escape */