It's possible to get a strange command result of ECMD_CANCEL+ECMD_TIME.
If/when that happens don't just treat it as cancel, make sure that time
elapses.
If an invalid command is provided, clear the do-again queue so that ^A
won't attempt to try the same invalid command again.
This is enough to prevent abuse by denying access to functions and
denial of service (RAM and instruction step limits), but not enough
to allow restricted use of things that require finer control (e.g.
filesystem access).
If something goes wrong, the whole thing can be turned off, for
now, in config.h (see NHL_SANDBOX).
None of the current functionality requires changes to build systems;
some of the possible future functionality may require some #defines
- TBD. There is lots of dead code (#ifdef notyet) for bits of that
additional functionality; we can rip it out if we don't want those
additions or we can complete (parts of) it depending on our needs.
All current uses of Lua are connected to sandboxes and guarded with
nhl_pcall (sandbox and lua_pcall wrapper); options and limits can
be set at the callsites in the passed nhl_sandbox_info. Some of
the error handling may be wrong - panic() vs. impossible() vs
silence.
Memory and instruction step limits should be tuned prior to release;
there's no point tuning them now.
There's no 'w-' or 'Q-' for alternate weapon, but context-sensitive
inventory is starting from the object rather than the command so can
finesse that. 'A' does allow alternate weapon to be directly unset
(aka reset to bare/gloved hands) but is not friendly to being passed
queued input.
This adds an extra internal command which only handles unset uswapwep,
even though that is something which is awfully specialized to get it's
own command. Users don't see this command so that shouldn't matter.
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.
Mostly attempting to clean up potential error handling but I don't
have any error cases to test with. Doesn't seem to break anything
when there aren't any errors....
This attempts to make item-actions, #herecmd, and #therecmd be more
robust. When rhack() or yn_function() take queued input off cmdq
and get something unexpected, discard the rest of the queue.
It also fixes the two crash cases that entrez reported. There are
bound to be others though.
I think a lot of actions that can be executed by queued input are
going to need nomul(0) calls to handle repeat counts that should be
ended early if something unexpected happens or something expected
fails to happen. But that clears cmdq so may be tricky to decide
where to use.
Using '&#' or '?f#' showed "# perform an extended command (##)".
The "(##)" part looks rather silly and is not helpful. Expand the
text a little and omit command name for that particular command:
"# enter and perform an extended command".
The comment preceding new 'savech_extcmd()' said that the core didn't
care whether it was given the full command name or just enough leading
substring to be unambiguous. Then it tested the string against
"repeat" which contradicts that comment. Didn't seem to be an actual
problem because "#repeat" is not flagged for auto-completion, but fix
the code to match the intent of the comment and reword the comment to
match the code.
Fix '#repeat' for tty; both it and ^A can repeat an extended command.
Fix both for curses; they can repeat an extended command instead of
just repeating the initial '#' to start getting an extended command.
X11 (tested), Qt (tested), and probably Windows GUI (not tested)
behave the same as before: ^A (or #repeat) after an extended command
just repeats the # to run the dialog to get an extended command.
I hope this introduces fewer bugs than it fixes but I don't think I'd
bet on that....
If you used ^A to repeat a command which had taken no time, the
repeat execution would take time. This fixes that. Also, give some
feedback when trying to repeat an invalid command.
Internals bit: don't use 'X == cmd_from_func(do_repeat)' to decide
whether key X is the key for #repeat. Both X and Y might be bound to
that action and cmd_from_func() could return Y rather than X.
There is another ^A bug that I haven't figured out how to fix:
t ESC start to throw but don't finish
^A nothing seems to happen
^A "You don't have that object."
The first ^A repeats 't', doesn't display a prompt for what to throw,
but does request input for it. The second ^A fulfills that input and
doesn't match any inventory item. Either 't' shouldn't have been put
into the do-again buffer or do-again handling should have ceased when
it was taken out and there was no further remembered input, so that
normal prompting would resume. My tentative attempts for both those
approaches didn't work.
Issue #734 reported as "parse function" by Meklon2007: the change
yesterday intended to make ^A work for commands that were preceded
by a prefix was triggering a crash if used after a keystroke that's
not assigned to any command.
'M^A' or '~^A' would segfault by derefencing a null pointer when
checking whether 'M' or '~' was a prefix. This prevents the check
attempt from doing that, but a better fix would be to not put the
invalid command keystroke into the do-again buffer in the first
place.
Fixes#734
Reported by luxidream via the web contact form and also as github
issue #732: using the repeat command after F+direction would take a
step in direction if there was no target to fight.
The direction was being repeated without the F prefix. It wasn't
specific to F; m+dir misbehaved too. This fix seems to work but it
should be replaced with something more robust.
Fixes#732
Allow the player to precede q/#quaff or M-d/#dip with the 'm' prefix
to skip asking about fountains, sinks, or pools if one of those
happens to be present, similar to how using it for e/#eat skips food
on the floor and goes straight to inventory.
If you use it and don't have any potions, you'll get "you don't have
anything to drink" or "you don't have anything to dip into", same as
when there is no suitable dungeon feature present combined with no
potions. However, if an applicable dungeon feature is present and
you don't use the prefix but answer 'no' to drink from fountain,&c
and you don't have any potions, "else" will be inserted into the
message: "you don't have anything else to drink".
A big part of the diff is just a change in indentation level for
code that is now inside 'if (!iflags.menu_requested) {' ... '}'.
rhack() normally calls parse(), parse() sets context.move to True
assuming that the player's next action will take game time, then
when it returns, rhack() sets context.move back to False if the
assumption turned out to be incorrect. But when performing actions
after picking something in inventory, rhack() doesn't call parse()
so context.move is left at False.
This was hidden by making the inventory command take game time if
the player picked an item and set up an action to be done with it
even though the action hadn't taken place yet. So time was being
accounted for but if the hero didn't get consecutive moves then
monsters got their turn between the shouldn't-take-time inventory
command and the ought-to-behave-like-normal-command queued action.
My initial attempt to fix this (before figuring out how context.move
works) by stopping inventory from taking time didn't work because
queued item-actions stopped taking time too, or rather the fact that
they took no time became exposed. This second attempt doesn't have
that problem and I think it is correct.
Normally dipping gets the thing to dip first and what to dip it
into second and the item-action handling knows that. I'm not sure
why that wasn't working as intended and I couldn't figure out how
to make it do that, so went another way: this adds an internal
extended command that executes an alternate dip routine which gets
the potion to dip into first and the thing to dip into it second.
The #dip command should allow an 'm' prefix to skip fountains and
pools, similar to how eating accepts it to skip food on the floor.
But this doesn't implement that.
Picking a corpse while looking at inventory issued a menu that had
entry for eating that and if on an altar another one for offering
that. Picking the eat or offer choice worked as long as there
weren't any other corpses on the ground or altar. If there were
others, they'd be skipped but you'd get prompted for which item in
inventory to eat or offer instead of operating on the one that was
used to initiate the action.
Allow selecting an item from inventory and show a menu of actions
applicable for that particular item. Some of the entries might
be slightly spoilerish (eg. it'll reveal that you can read T-shirts),
but the improved usability for new players is more than worth it.
Generally known as "item actions", this was first implemented
in AceHack by Alex Smith.
Remove redundant 'if (K)' from there_cmd_menu(). The !K case doesn't
get there and that test's presence fools the compiler (oldish clang)
into warning that 'npick' might be used uninitialized.
My fixes to the travel stuck oscillation did not fix all of them,
and I've even seen a 3-step loop - which my fixes cannot detect.
I guess there could be arbitrary-sized loops too.
To definitely fix this, keep track of all the map locations travel
has moved the hero through, and if it tries to go on a location already
used, stop travel and give the unsure -message.
Offer the chance to explicitly hide via #monster when poly'd into a
hides-under creature. hides_under() doesn't pass the is_hider() test
so wasn't being allowed before.
If poly'd hero's monster form is both a webmaker and can hide-under,
have #monster prompt the player for which is intended. When poly'd
hero successfully spins a web, say so.
If poly'd hero deliberately tries to hide under a cockatrice corpse,
turn to stone.
Add a type to force g.{command_count,last_command_count,multi} to have the
same type (because cmd.c: g.multi = g.command_count;) and some resulting
cleanup.
Reported by k21971, the dumplog section labeled "major events" showed
all logged events rather than just the ones classified as major.
Filter out the non-major ones when writing dumplog.
At the moment only a couple of ones other than achievements are major.
Probably various other types should be too.
The #chronicle command still lists all logged events unless they're
flagged as 'spoiler'. So far the mines' end luckstone is the only
one flagged that way. Unfortunately a player with access to live
logging could still learn whether or not the gray stone that has just
been picked up on the last mines level is the target luckstone by
viewing the log from outside of the game.
The #chronicle command would be more useful if it gathered all the
categories of events present and put up a menu allowing the player to
choose which ones to view. I haven't attempted to implement that.
Closes#687
Add a type to force g.{command_count,last_command_count,multi} to have the
same type (because cmd.c: g.multi = g.command_count;) and some resulting
cleanup.
A monster hurtling over liquid would drown immediately the instant it
touched the first square of water, even if normally it would have kept
moving (e.g. hurtling over a short moat). Additionally, its placement
on liquid would not take into consideration other monsters, so it could
overwrite an existing monster on that spot and lead to an impossible,
and/or two monsters occupying a single position.
Fix these issues, so that liquid effects like drowning only happen if
the monster ends up in liquid at the end of the hurtle, and so that
other monsters in the way will stop it early even if they're floating
over or swimming on a pool/water/lava square.
Also use canspotmon instead of canseemon for the wiztelekinesis debug
command.
For testing mhurtle, which is used for jousting or
bare-handed combat.
Improve mhurtle_step to handle bumping into another monster,
and when the monster gets killed or stuck in a trap.
When you have a polearm as secondary weapon, have a fireassist on,
and press 'f' to fire, the code tries to swapweapon to the polearm.
This failed badly and got stuck in a loop if you were also wearing
a shield - as polearms are two-handed and shield prevents wielding
those.
Add a new "command failed" result, and clear the command queue
in that case. Also make swapweapon and wield actually return
the ECMD flags back to the rhack loop.
Log all level gains and loses. For the existing logging of changes
in rank, mention the level number with the new title. Classifying
level loss as "minor achievement" seems weird but I didn't see any
choice more appropriate.
Make '#chronicle' autocomplete. That makes "#ch" ambiguous, but
better to have to type #cha to chat than to have to completely spell
out #chronicle. (Changing it to #journal would make #j ambigious
but might still be an improvement.)
Log game events, such as entering a new dungeon level, breaking
a conduct, or killing a unique monster, in a new "Major events"
chronicle. The entries record the turn when the event happened.
The log can be viewed with #chronicle -command, and the entries
also show up in the end-of-game dump, if that is available.
This feature is on by default, but can be disabled by
defining NO_CHRONICLE compile-time option.
This also contains "live logging", writing the events as they
happen into a single livelog-file. This is mostly useful for
public servers. The livelog is off by default, and must be
compiled in with LIVELOG, and then turned on in sysconf.
Mostly this a version of livelogging from the Hardfought server,
with some changes.
Trying to use #reqmenu/#rush/#run/#fight prefixes by their extended
command names didn't work because rhack()'s post-processing was stuck
dealing with the entry for the '#' key after using doextcmd() to run
any command. Use a static variable (actually a global one since I put
it into struct g) to notify rhack() of the command that ultimately got
executed.
Reject an attempt to specify a conflicting or redundant prefix with
a Ctrl+<move> or Shift+<move> (not numpad) or Alt+<move> (numpad).
'm' prefix is accepted.