Add assistance to fire-command

Allows the fire-command to autowield a launcher; it will now
do either swapweapon or wield an appropriate launcher, if you
have ammo quivered.

This assistance can be turned off with the fireassist boolean option.

Adds a rudimentary command queue, which allows the code to add keys
or extended commands into the queue, and they're executed as if
the user did them.  Time passes normally when doing the queue,
and the queue will get cleared if hero is interrupted.
This commit is contained in:
Pasi Kallinen
2021-06-09 09:02:31 +03:00
parent f6e60c7516
commit 76f77ee0cc
15 changed files with 253 additions and 8 deletions

View File

@@ -568,6 +568,7 @@ stop_occupation(void)
} else if (g.multi >= 0) {
nomul(0);
}
cmdq_clear();
}
void

112
src/cmd.c
View File

@@ -228,6 +228,76 @@ set_occupation(int (*fn)(void), const char *txt, int xtime)
return;
}
/* add extended command function to the command queue */
void
cmdq_add_ec(int (*fn)(void))
{
struct _cmd_queue *tmp = (struct _cmd_queue *)alloc(sizeof(struct _cmd_queue));
struct _cmd_queue *cq = g.command_queue;
tmp->typ = CMDQ_EXTCMD;
tmp->ec_entry = ext_func_tab_from_func(fn);
tmp->next = NULL;
while (cq && cq->next)
cq = cq->next;
if (cq)
cq->next = tmp;
else
g.command_queue = tmp;
}
/* add a key to the command queue */
void
cmdq_add_key(char key)
{
struct _cmd_queue *tmp = (struct _cmd_queue *)alloc(sizeof(struct _cmd_queue));
struct _cmd_queue *cq = g.command_queue;
tmp->typ = CMDQ_KEY;
tmp->key = key;
tmp->next = NULL;
while (cq && cq->next)
cq = cq->next;
if (cq)
cq->next = tmp;
else
g.command_queue = tmp;
}
/* pop off the topmost command from the command queue.
* caller is responsible for freeing the returned _cmd_queue.
*/
struct _cmd_queue *
cmdq_pop(void)
{
struct _cmd_queue *tmp = g.command_queue;
if (tmp) {
g.command_queue = tmp->next;
tmp->next = NULL;
}
return tmp;
}
/* clear all commands from the command queue */
void
cmdq_clear(void)
{
struct _cmd_queue *tmp = g.command_queue;
struct _cmd_queue *tmp2;
while (tmp) {
tmp2 = tmp->next;
free(tmp);
tmp = tmp2;
}
g.command_queue = NULL;
}
static char popch(void);
static char
@@ -2482,6 +2552,18 @@ dokeylist(void)
destroy_nhwindow(datawin);
}
const struct ext_func_tab *
ext_func_tab_from_func(int (*fn)(void))
{
const struct ext_func_tab *extcmd;
for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd)
if (extcmd->ef_funct == fn)
return extcmd;
return NULL;
}
char
cmd_from_func(int (*fn)(void))
{
@@ -3350,13 +3432,30 @@ rhack(char *cmd)
int spkey;
boolean prefix_seen, bad_command,
firsttime = (cmd == 0);
struct _cmd_queue *cmdq = NULL;
const struct ext_func_tab *cmdq_ec = NULL;
iflags.menu_requested = FALSE;
#ifdef SAFERHANGUP
if (g.program_state.done_hup)
end_of_input();
#endif
if (firsttime) {
if ((cmdq = cmdq_pop()) != 0) {
/* doing queued commands */
if (cmdq->typ == CMDQ_KEY) {
static char commandline[2];
if (!cmd)
cmd = commandline;
cmd[0] = cmdq->key;
cmd[1] = '\0';
} else if (cmdq->typ == CMDQ_EXTCMD) {
cmdq_ec = cmdq->ec_entry;
}
free(cmdq);
if (cmdq_ec)
goto do_cmdq_extcmd;
} else if (firsttime) {
g.context.nopick = 0;
cmd = parse();
}
@@ -3539,14 +3638,22 @@ rhack(char *cmd)
register const struct ext_func_tab *tlist;
int res, (*func)(void);
do_cmdq_extcmd:
if (cmdq_ec)
tlist = cmdq_ec;
else
tlist = g.Cmd.commands[*cmd & 0xff];
/* current - use *cmd to directly index cmdlist array */
if ((tlist = g.Cmd.commands[*cmd & 0xff]) != 0) {
if (tlist != 0) {
if (!wizard && (tlist->flags & WIZMODECMD)) {
You_cant("do that!");
res = 0;
cmdq_clear();
} else if (u.uburied && !(tlist->flags & IFBURIED)) {
You_cant("do that while you are buried!");
res = 0;
cmdq_clear();
} else {
/* we discard 'const' because some compilers seem to have
trouble with the pointer passed to set_occupation() */
@@ -3575,6 +3682,7 @@ rhack(char *cmd)
if (!prefix_seen || !help_dir(c1, spkey, "Invalid direction key!"))
Norep("Unknown command '%s'.", expcmd);
cmdq_clear();
}
/* didn't move */
g.context.move = FALSE;

View File

@@ -207,6 +207,9 @@ const struct Race urace_init_data = {
};
const struct instance_globals g_init = {
NULL, /* command_queue */
/* apply.c */
0, /* jumping_is_magic */
-1, /* polearm_range_min */

View File

@@ -2228,11 +2228,13 @@ glibr(void)
otmp = uleft;
Ring_off(uleft);
dropx(otmp);
cmdq_clear();
}
if (rightfall) {
otmp = uright;
Ring_off(uright);
dropx(otmp);
cmdq_clear();
}
}
@@ -2253,6 +2255,7 @@ glibr(void)
xfl++;
wastwoweap = TRUE;
setuswapwep((struct obj *) 0); /* clears u.twoweap */
cmdq_clear();
if (canletgo(otmp, ""))
dropx(otmp);
}
@@ -2288,6 +2291,7 @@ glibr(void)
/* xfl++; */
otmp->quan = savequan;
setuwep((struct obj *) 0);
cmdq_clear();
if (canletgo(otmp, ""))
dropx(otmp);
}

View File

@@ -11,6 +11,7 @@ static int throw_obj(struct obj *, int);
static boolean ok_to_throw(int *);
static int throw_ok(struct obj *);
static void autoquiver(void);
static struct obj *find_launcher(struct obj *);
static int gem_accept(struct monst *, struct obj *);
static void tmiss(struct obj *, struct monst *, boolean);
static int throw_gold(struct obj *);
@@ -399,6 +400,23 @@ autoquiver(void)
return;
}
/* look through hero inventory for launcher matching ammo,
avoiding known cursed items. Returns NULL if no match. */
static struct obj *
find_launcher(struct obj *ammo)
{
struct obj *otmp;
if (!ammo)
return (struct obj *)0;
for (otmp = g.invent; otmp; otmp = otmp->nobj)
if (ammo_and_launcher(ammo, otmp) && !(otmp->cursed && otmp->bknown))
return otmp;
return (struct obj *)0;
}
/* f command -- fire: throw from the quiver */
int
dofire(void)
@@ -454,6 +472,28 @@ dofire(void)
}
}
if (uquiver && iflags.fireassist) {
struct obj *olauncher;
/* Try to find a launcher */
if (ammo_and_launcher(uquiver, uwep)) {
/* Do nothing, already wielding a launcher */
} else if (ammo_and_launcher(uquiver, uswapwep)) {
/* swap weapons and retry fire */
cmdq_add_ec(doswapweapon);
cmdq_add_ec(dofire);
return 0;
} else if ((olauncher = find_launcher(obj)) != 0) {
/* wield launcher, retry fire */
if (uwep && !flags.pushweapon)
cmdq_add_ec(doswapweapon);
cmdq_add_ec(dowield);
cmdq_add_key(olauncher->invlet);
cmdq_add_ec(dofire);
return 0;
}
}
return obj ? throw_obj(obj, shotlimit) : 0;
}

View File

@@ -3028,6 +3028,7 @@ nomul(int nval)
if (nval == 0)
g.multi_reason = NULL, g.multireasonbuf[0] = '\0';
end_running(TRUE);
cmdq_clear();
}
/* called when a non-movement, multi-turn action has completed */

View File

@@ -1488,6 +1488,31 @@ getobj(const char *word,
boolean oneloop = FALSE;
Loot *sortedinvent, *srtinv;
struct _cmd_queue *cmdq = cmdq_pop();
if (cmdq) {
/* it's not a key, abort */
if (cmdq->typ != CMDQ_KEY) {
free(cmdq);
return (struct obj *)0;
}
for (otmp = g.invent; otmp; otmp = otmp->nobj)
if (otmp->invlet == cmdq->key) {
int v = (*obj_ok)(otmp);
if (v == GETOBJ_SUGGEST || v == GETOBJ_DOWNPLAY) {
free(cmdq);
return otmp;
}
}
/* did not find the object, abort */
free(cmdq);
cmdq_clear();
return (struct obj *)0;
}
/* is "hands"/"self" a valid thing to do this action on? */
switch ((*obj_ok)((struct obj *) 0)) {
case GETOBJ_SUGGEST: /* treat as likely candidate */

View File

@@ -320,6 +320,7 @@ dowield(void)
g.multi = 0;
if (cantwield(g.youmonst.data)) {
pline("Don't be ridiculous!");
cmdq_clear();
return 0;
}
@@ -327,12 +328,14 @@ dowield(void)
clear_splitobjs();
if (!(wep = getobj("wield", wield_ok, GETOBJ_PROMPT | GETOBJ_ALLOWCNT))) {
/* Cancelled */
cmdq_clear();
return 0;
} else if (wep == uwep) {
already_wielded:
You("are already wielding that!");
if (is_weptool(wep) || is_wet_towel(wep))
g.unweapon = FALSE; /* [see setuwep()] */
cmdq_clear();
return 0;
} else if (welded(uwep)) {
weldmsg(uwep);
@@ -341,6 +344,7 @@ dowield(void)
/* if player chose a partial stack but can't wield it, undo split */
if (wep->o_id && wep->o_id == g.context.objsplit.child_oid)
unsplitobj(wep);
cmdq_clear();
return 0;
} else if (wep->o_id && wep->o_id == g.context.objsplit.child_oid) {
/* if wep is the result of supplying a count to getobj()
@@ -396,6 +400,7 @@ dowield(void)
setuqwep((struct obj *) 0);
} else if (wep->owornmask & (W_ARMOR | W_ACCESSORY | W_SADDLE)) {
You("cannot wield that!");
cmdq_clear();
return 0;
}
@@ -428,10 +433,12 @@ doswapweapon(void)
g.multi = 0;
if (cantwield(g.youmonst.data)) {
pline("Don't be ridiculous!");
cmdq_clear();
return 0;
}
if (welded(uwep)) {
weldmsg(uwep);
cmdq_clear();
return 0;
}