diff --git a/include/extern.h b/include/extern.h index d6a4ca252..c5cd0e839 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/include/hack.h b/include/hack.h index 95edc50ee..2f54b327c 100644 --- a/include/hack.h +++ b/include/hack.h @@ -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 */ diff --git a/src/cmd.c b/src/cmd.c index 957b874f0..3050db6a0 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -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; } diff --git a/src/invent.c b/src/invent.c index 5d616f0ff..8289f19ee 100644 --- a/src/invent.c +++ b/src/invent.c @@ -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 */ + 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; diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c index fca63ed05..ea4b9e9a8 100644 --- a/win/curses/cursmisc.c +++ b/win/curses/cursmisc.c @@ -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, ¤t_count, FALSE); + to negative */ + 0L, ¤t_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 */