PatR [Tue, 1 Nov 2022 06:13:05 +0000 (23:13 -0700)]
Longbow of Diana
Give an extra +1 to potential multi-shot to rangers wielding the
Longbow of Diana and shooting any type of arrow. When an elf ranger
has been wielding an elven bow to shoot elvish arrows or an orc
ranger has been wielding an orcish bow to shoot orcish arrows, they
lose their racial bonus but won't lose any multi-shot capability by
switching to their quest artifact. Human and gnome rangers gain the
+1 bonus. (I have no idea how a gnome could wield a longbow. One
would need a step ladder to hold it vertically, and could only draw
the string back a stubby arm's length if they held if horizonally.)
That bonus gets applied before feeding the multi-shot counter to
rnd() so doesn't mean an extra arrow every time. And you have to
be wielding your own quest artifact--in addition to it being the
appropriate launcher for the ammo you're shooting--so doesn't provide
any multi-shot benefit to other types of characters who wish for it.
PatR [Mon, 31 Oct 2022 22:43:14 +0000 (15:43 -0700)]
build fix for NODUMPENUMS and more issue #916
I made more things in dump_enums() static and/or const. In the
process I discovered both compile problems for NODUMPENUMS and when
fixed, link problems for NODUMPENUMS+ENHANCED_SYMBOLS.
The uft8map.c portion has no changes, just reformatting.
nhmall [Mon, 31 Oct 2022 17:29:08 +0000 (13:29 -0400)]
omdump declaration
..\src\allmain.c(1061): warning C4221: nonstandard extension used: 'ed': cannot be initialized using address of automatic variable 'omdump'
..\src\allmain.c(1056): note: see declaration of 'omdump'
PatR [Mon, 31 Oct 2022 07:53:10 +0000 (00:53 -0700)]
status condition options
Option parsing rejected
|OPTIONS=!cond_X
for all valid X.
Using the menu to unselect all condition options treated that as not
having made any choice and didn't make any changes. That would be
reasonable if nothing was preselected, but things are so unselecting
all of them is a choice. (A bizarre one, but still should be viable.)
Mostly this deals with including cond_X options when #saveoptions is
used to write a new RC file. It now produces something like
|OPTIONS=!cond_barehanded,cond_blind,!cond_busy,cond_conf,!cond_deaf,\
| cond_iron,cond_fly,cond_foodPois,!cond_glowhands,cond_grab,\
| cond_hallucinat,!cond_held,!cond_ice,cond_lava,cond_levitate,\
| !cond_paralyzed,cond_ride,!cond_sleep,cond_slime,!cond_slip,\
| cond_stone,cond_strngl,cond_stun,!cond_submerged,cond_termIll,\
| !cond_tethered,!cond_trap,!cond_unconscious,!cond_woundedlegs,\
| !cond_holding
after the last alphabetical option and before the bound keys, menu
colors, and others which aren't simple OPTIONS=X settings. This only
happens if there is already one or more OPTIONS=cond_X entries in the
old file when it was read or if 'mO' gets used to make any changes.
Not fixed: after my RC had something similar to the above and before
I changed status conditions to accept negation, I was getting several
"the cond_ option may not both have a value and be negated" messages
written to stdout instead of the config file error handler. So they
vanished when the screen was initialized without providing a --More--
prompt to acknowledge that they have been seen.
PatR [Sun, 30 Oct 2022 06:48:46 +0000 (23:48 -0700)]
tweak for #914 - attacking nothing
In the code that checks for attacking the edge of the map, the m_at()
that was just introduced isn't at risk of using <0,0> because of the
way 'glyph' is initialized. But guard against future changes.
And I omitted this when checking the PR #914 commit in:
Closes #914
Michael Meyer [Sat, 29 Oct 2022 01:06:11 +0000 (21:06 -0400)]
Some displacer beast swapping tweaks
When fighting an unseen displacer beast mapped as an 'I' glyph on the
map, its typical ability to swap places with you was disabled and
replaced by it being treated like "thin air". This was because
execution reaches domove_fight_empty when the target swaps places with
you. Other than the displacement passive, this function is typically
only reached if there's no monster on the target square, so it prints
the "thin air" message and wastes a turn if you'd "expect" to attack
something (either because the player used an 'F' prefix, or because
there is an 'I' mapped on the destination square being moved into).
Hitting "thin air" seems like OK behavior for force-fighting a displacer
beast, since you are explicitly not trying to move into its spot (though
you could probably make an argument that the displacement should happen
even then, since it's the displacer beast initiating it), but the mon
being mapped as an 'I' doesn't seem like a good reason to disallow the
actual displacement from happening.
Don't treat an 'I' as "thin air" in domove_fight_empty if there's
actually a monster there, since it means there's a displacer beast
trying to swap places with you.
A couple other related changes: put an 'I' down on the map when an
unseen displacer beast swaps places with you, and use 'something' in the
'it swaps places with you' message when you didn't even realize there
was a monster there to begin with, so there's no context for the 'it' (I
used Some_Monnam at first, but the 'something' felt a little weird when
you were intentionally attacking an 'I' or warning glyph; this limits
the usage of 'something' further than Some_Monnam does).
nhmall [Sat, 29 Oct 2022 14:54:25 +0000 (10:54 -0400)]
some C99 changes
Instead of using index() macro defined to strchr, use C99 strchr.
Instead of using rindex() macro defined to strrchr, use C99 strrchr.
If you want to try building on a platform that doesn't offer those
two functions, these are available:
define NOT_C99 /* to make some non-C99 code available */
define NEED_INDEX /* to define a macro for index() */
define NEED_RINDX /* to define a macro for rindex() */
PatR [Sat, 29 Oct 2022 06:26:39 +0000 (23:26 -0700)]
fix RC file CHOOSE '[section] #comment'
Two years ago I modified the parsing for [section] labels for the
config file's CHOOSE directive to allow end-of-line comments, but
the code used had a logic error (don't think I can blame it on
copy+paste). It looked for '#' after ']' but allowed anything--
rather than just spaces--in between.
"[section-name]abc#comment" would become "section-name" as if the
trailing junk hadn't been present. Parsing that should produce
"section-name]abc" and get rejected as invalid.
PatR [Fri, 28 Oct 2022 00:25:14 +0000 (17:25 -0700)]
end-of-game disclosure for gold in invent on tty
Reported by entrez: disclosing inventory at end of game did not show
gold. Not mentioned: only for tty.
It was using the same window as gets used for perm_invent (although
not shown _as_ perm_invent because end of game turns that off) and the
default for whether to show gold is different for tty than for other
interfaces due use of experimental TTYINV from player's environment.
Force the end of game inventory disclosure to work the same as the
dumplog inventory listing and use a different window, by falsely
telling display_inventory() that a response is requested. Works but
the whole inventory mechanism has become quite convoluted.
PatR [Thu, 27 Oct 2022 22:55:57 +0000 (15:55 -0700)]
more knockback induced dismount
When looking at the previous commit, I realized that my comment about
radius 1 was wrong. The original code prefers dismounting to spots
that are orthogonal to the steed's position over diagonal ones. It
doesn't say why.
PatR [Thu, 27 Oct 2022 22:33:49 +0000 (15:33 -0700)]
knockback knocking riding hero out of saddle
I've implemented targetted dismount such that being knocked out of
the saddle will place the hero opposite the attacker in preference
to a random spot adjacent to the steed. If that opposite spot
isn't appropriate, the two spots next to it get tried.
In these map fragments, H is knocking mounted hero off of u. The
digits indicate priority of potential destinations.
If spot 1 isn't acceptable, both of spots 2 (in random order) will
be tried next. If those aren't acceptable either, it will try the
other 5 spots adjacent to the steed (the one of those with the
attacker will always be unacceptable). And as before, it none of
those work, it uses enexto() to pick a random spot as close to the
steed as feasible.
Not knockback: when dismounting due to polymorph, avoid diagonal
adjacent spots if hero's new form can't move diagonally. (The hero
can't already be in no-diagonal form because riding requires that
the rider be humanoid. I keep thinking the restriction is "can't
be polymorphed" but that isn't correct.)
Michael Meyer [Thu, 27 Oct 2022 19:58:51 +0000 (15:58 -0400)]
Update Guidebook #showgold info
The #showgold command now does mention known contained gold in your
inventory, so the various lines in the Guidebook which explicitly state
that it doesn't needed to be updated. Wish I had noticed this in time
to put it into the previous Guidebook patch I submitted, but what can
you do.
This is a problem because g.youmonst.data is not unique to the hero:
the '(ptr) == g.youmonst.data' test will also be true of all player
monsters of the same role. For this reason, any of those player
monsters will be treated as sharing the hero's race, producing strange
results. For example, if the player is an elven ranger, any ranger
player monster generated will be considered 'elven' too (so will get a
to-hit bonus when attacking orcs, etc) -- but only while the hero is
unpolymorphed.
There are already other ways of checking the hero's race in addition to
her current polyform, most notably the maybe_polyd() macro. maybe_polyd
or something similar is already used in nearly all the cases where the
hero's race is being evaluated, meaning Race_if gets used instead when
the hero is in her natural form. So I think the check of the hero's
race in is_foo had very little effect except for the unintended
side-effects on player monsters.
In reviewing all the uses of is_{elf,dwarf,gnome,orc,human}, I noticed
only one case that relied on the hero-race-checking behavior. That has
been changed in this commit to use maybe_polyd (there's another 'raw'
is_human(g.youmonst.data) a few lines down, but it doesn't need
maybe_polyd since it already distinguishes between 'hero in nonhuman
polyform' vs 'nonpolyd or human polyform'). same_race(mondata.c) is
another case where &g.youmonst.data can be passed to is_foo, but
everywhere that calls it for the hero also calls your_race() or
same_race(&mons[Race_switch]) to handle the racial case.
PatR [Wed, 26 Oct 2022 08:13:01 +0000 (01:13 -0700)]
more steadfast
Make changes similar to the suggested patch from entrez: support
for 'youmonst' as the monster passed to m_carrying(). This doesn't
change carrying(otyp) to call m_carrying(&g.youmonst,otyp) though.
Also, treat being on the Plane of Air or in an air bubble on the
Plane of Water similar to flying or levitating: wielded Giantslayer
(or carried loadstone) doesn't prevent knockback there.
PatR [Tue, 25 Oct 2022 22:33:08 +0000 (15:33 -0700)]
github pull request #909 - Qt yn prompt
Pull request from chasonr: remove NUL characters from constructed
prompt for Qt_yn_function(). I couldn't see any with Qt 5.11 on
OSX 10.11.6, but with some font(s) or Qt newer versions they were
visible as reported in issue #566 about 15 months ago.
PatR [Tue, 25 Oct 2022 20:58:26 +0000 (13:58 -0700)]
\#wizborn fix
The #wizborn command shows 'E' for an extinct species and 'G' for a
genocided one, but if a species first becomes extinct and then later
gets genocided, instead of showing both flags it stopped showing
either. I was going to add a second flag column and show 'E' and 'G'
separately but decided to stick with one column and display 'X' for
the unlikely 'both extinct and genocided' case.
PatR [Tue, 25 Oct 2022 09:55:49 +0000 (02:55 -0700)]
X11 getlin()'s prompt
Reissuing getlin() with a different prompt wasn't reliably resizing the
X11 prompt widget. After a lot of hacking away at win/X11/dialogs.c I
eventually tried setting the response portion of the widget first and
got much better results, enough to throw away the tentative changes to
dialogs.c.
There's still a lot of room from improvement but I think it would need
to replace the ghostview prompting instead of trying to massage that.
PatR [Mon, 24 Oct 2022 17:32:17 +0000 (10:32 -0700)]
more PR #906 - steadfastness
If someone gets hit for a knockback effect but resists it due to
wielding Giantslayer or carrying a loadstone, give feedback saying
so, otherwise the lack of knockback is indistinguishable from an
ordinary hit.
The message is not likely to appear for non-hero target since that
target needs to have special equipment. A hero wielding Giantslayer
might see it enough for the player to become annoyed; if so,
MSGTYPE=hide could be used to suppress it.
nhmall [Mon, 24 Oct 2022 13:14:35 +0000 (09:14 -0400)]
warning fix
mhitm.c: In function 'hitmm':
mhitm.c:583:30: warning: '%s' directive writing between 8 and 9 bytes into a region of size between 0 and 255 [-Wformat-overflow=]
583 | Sprintf(buf, "%s %s", magr_name,
| ^~
In file included from ../include/config.h:671,
from ../include/hack.h:10,
from mhitm.c:6:
../include/global.h:279:24: note: 'sprintf' output between 10 and 266 bytes into a destination of size 256
279 | #define Sprintf (void) sprintf
mhitm.c:583:13: note: in expansion of macro 'Sprintf'
583 | Sprintf(buf, "%s %s", magr_name,
| ^~~~~~~
PatR [Sun, 23 Oct 2022 22:31:12 +0000 (15:31 -0700)]
map documentation refinement
Add minor detail to recently added map description in doc/window.txt:
origin is in upper left and positive y goes downward, so not typical
Cartesian x,y coordinate plane.
PatR [Sun, 23 Oct 2022 08:11:14 +0000 (01:11 -0700)]
fix github issue #907 - bad shade logic
Issue reported by vultur-cadens: one of the checks for whether a
shade would be harmed by an attack was erroneously inside a block
of code that only executed when you could see the attack. Basic
physical damage wasn't affected but some monster (or poly'd hero)
damage types that shouldn't affect shades didn't when seen but did
when unseen.
Could also get "attack passes harmlessly through the shade" when
an unseen attack for physical damage hit and failed to deal damage.
PatR [Sun, 23 Oct 2022 07:22:18 +0000 (00:22 -0700)]
pull request #908 - rename update_mon_intrinsics
Pull request from entrez: rename update_mon_intrinsics() to
update_mon_extrinsics() since it deals with properties conferred
by equipment in use rather than internal capabilities.
Michael Meyer [Sun, 23 Oct 2022 01:58:28 +0000 (21:58 -0400)]
Rename update_mon_intrinsics to ...extrinsics
There was a TODO about this; not exactly a great challenge but it feels
like a worthwhile change since the name was misleading. I also updated
the name of the do_intrinsics parameter of extract_from_minvent(worn.c),
since it was in a similar situation (and directly related, since it
controls whether to call update_mon_{in/ex}trinsics).
PatR [Sun, 23 Oct 2022 07:18:42 +0000 (00:18 -0700)]
fixes entry for PR #904 - duplicate invlet
Pull request from entrez: explicitly throwing 1 out of stack of more
than 1 and then having the throw be rejected by tageting yourself did
not recombine the split, resulting in stacks of 1 and N-1 that both
had the same inventory letter. Undo the split if throwing fails.
Michael Meyer [Wed, 19 Oct 2022 02:55:43 +0000 (22:55 -0400)]
Fix: duplicate invlet from throwing obj with count
Specifying a count of 1 when throwing an object could leave you with two
stacks sharing one inventory letter. The second stack gets split off
when the player specifies a count (e.g. 't1o'), but keeps its original
invlet. Some early returns, like trying to throw at yourself with '.',
could fail to unsplit the stack. Theoretically, specifying multiple
items to multishot and then failing to throw them all could also leave a
partial stack; I don't think this is actually possible right now with
't' but I tried to make sure it won't become a problem if greater counts
than 1 are ever allowed.
The fix doesn't affect 'f', which can be a combined "create a quiver
stack and throw" action and doesn't have the issue with duping invlets.
Specifying a count to split off a new quiver stack with 'f' shouldn't be
reverted if the throwing fails or only part of the stack is thrown,
because the newly created stack may be intended for continued use as the
quiver in future turns. This slightly changes the behavior of the
existing unsplit when cancelling the throw (which previously unsplit the
newly created quiver and quivered the entire parent stack), but I think
this actually makes more sense -- the player only declined to throw the
new stack, not to create it (as if they canceled earlier in the action).
I routed a couple early returns through the stack unsplitting that
shouldn't actually need it (like Mjollnir and welded items) for
consistency's sake; I don't think it hurts anything.
PatR [Sun, 23 Oct 2022 06:57:05 +0000 (23:57 -0700)]
PR #906 - loadstone confers 'steadfastness'
Pull request by Theyflower: carrying a loadstone prevents big
monsters from hitting their target for knockback effect, same as
wielding Giantslayer.
The PR code needed fixing (unintended switch from 'otmp' to 'obj')
so I didn't use the commeit. The PR code also required that the
loadstone be blessed which sounds nethackish but would mean that
nobody would ever notice. Allow carrying any loadstone to prevent
being knocked back. It will still be a rare accident or uncommon
tactical decision. (It doesn't happen if the target is flying or
levitating because those checks deliberately come first.)
PatR [Sun, 23 Oct 2022 00:14:22 +0000 (17:14 -0700)]
allow big humanoids to wear mummy wrappings
A giant mummy starts out with a mummy wrapping but couldn't wear it.
Allow humanoids who are bigger than human size (including poly'd hero
when applicable) to wear such cloaks. They won't do so if they are
invisible and the cloak would let hero start seeing them.
PatR [Sat, 22 Oct 2022 22:47:48 +0000 (15:47 -0700)]
cansee() and couldsee()
cansee(), couldsee(), and templit() are macros which are described
as boolean and used as if boolean, but they've been using bit
masking to return integer values greater than 1. That works since
C treats any non-zero as True but doesn't match boolean intent.
PatR [Fri, 21 Oct 2022 22:16:25 +0000 (15:16 -0700)]
document map column 0
Something that FIQ once pointed out: the fact that map column 0 is
not shown wasn't explicitly described anywhere. Add a paragraph for
NHW_MAP to doc/window.txt and describe it there.
PatR [Fri, 21 Oct 2022 21:31:33 +0000 (14:31 -0700)]
menu_drop() vs ECMD_TIME
Some routines return ECMD_TIME|ECMD_CANCEL (for instance when 'a'pply
wields an item and player cancels the attempt to use it) so change
drop_menu() to test that properly. I don't think drop() ever returns
that combined mask value but be prepared to handle time passage if it
ever does.
PatR [Thu, 20 Oct 2022 23:49:25 +0000 (16:49 -0700)]
PR #896 tweak - ^X shows known container gold
When not carrying any contained gold, or the only contained gold is
inside container(s) whose contents aren't known, ^X writes one line
about the hero's "wallet". When known contained gold is present, it
writes two lines for gold, first one about wallet with the second
one about contained gold being a continuation of the first. Move
the conjunction that combines them from the start of the second line
to the end of the first.
So change
|Your wallet contains M zorkmids,
|and you have N more contained in your pack.
to
|Your wallet contains M zorkmids, and
|you have N more contained in your pack.
and
|Your wallet is empty,
|but you have N zorkmids contained in your pack.
to
|Your wallet is empty, but
|you have N zorkmids contained in your pack.
It evens out the line lengths a little bit and starting the second
line with uncapitalized "you" seems slightly less jarring than with
"and" or "but".
PatR [Thu, 20 Oct 2022 17:27:21 +0000 (10:27 -0700)]
fix github issue #905 - ^A vs getpos()
Reported by entrez: using ^A instead of #retravel after interrupted
travel can pick wrong location if cursor was previously positioned
with movement commands rather than feature targeting because it
won't be starting from the original spot. Also, ^A after ';' will
just redescribe whatever was examined previously instead of having
the player pick a new spot.
This suppresses cursor positioning from the do-again queue so that
repeating travel or quick-look or other command that needs player
to choose a position will repeat the command but then need to have
a position chosen. For interrupted #travel, the cursor will already
be placed on the previous destination so that's relatively painless,
but also allows a different destination to be chosen.
It adds iflags.remember_getpos that callers of getpos() could set to
be able to restore the old behavior but none do so far.
PatR [Sat, 15 Oct 2022 09:13:39 +0000 (02:13 -0700)]
fix github issue #900 - "Elbereth" engravings
Issue reported by vultur-cadens: Elbereth used to be effective in
inhibiting monster movement when an object was present on the same
spot, but since 3.6.0 it isn't. It only functions that way when the
hero--or hero's displaced image--is present these days. So special
levels that have been using engraved Elbereth to try to protect
objects from monsters haven't been providing any useful protection.
This makes Elbereth that's engraved during level creation work like
it used to in 3.4.3 and earlier: when there's at least one object
on the engraving's spot, monsters who are affected by Elbereth will
be affected. [I'm fairly sure that that behavior started out
unintentionally, as a side-effect of an optimization to only check
for scroll of scare monster when there was at least one item present
which is a necessary condition for such a scroll.]
Old-style Elbereth includes Elbereth chosen as a random engraving
during level creation in addition to engravings specified in special
level definitions. Engravings by the player don't have the required
attribute and player-engraved Elbereth behaves in the 3.6 way.
This ought to be replaced by something more general. Perhaps a new
engraving type not usable by the player?
PatR [Fri, 14 Oct 2022 21:42:54 +0000 (14:42 -0700)]
fix several monster difficulty ratings
Fix most of the things pointed out by #wizmondiff.
Weakening of placeholder 'elf' is due to recent removal of M2_STRONG
for it as part of the "orc strongmonst" changes.
I assume that the discrepancies for multiple quest leaders came about
as part of the change that allows killing the leader as an alternate
way to gain access to the lower levels of the quest, but didn't check.
I don't know what's up with 'piranha' but just changed it to match
generated value.
'{freezing,flaming,shocking} sphere' still show up as discrepancies
with hardcoded (mons[].difficulty) value higher than generated value.
They got harder when their explosion was beefed up, so the formula to
calculate difficulty ought to be updated to account for that.
PatR [Fri, 14 Oct 2022 19:42:12 +0000 (12:42 -0700)]
rename #wizcheckmdifficulty to #wizmondiff
Shorten the name of the recently added debug command that validates
monster difficulty values. 'wizcheckmdifficulty' was 19 characters
long, the next longest is 14 ('wiztelekinesis'). The extra width
messed up the Qt interface's extended command selection dialog when
wizard mode commands are included. It sizes the button for every
command to fit the longest name; the increase in size from 14 to 19
made the button grid become too big for the screen.
Add monsters' base difficulty level to the #wizmondiff output.
PatR [Thu, 13 Oct 2022 21:03:52 +0000 (14:03 -0700)]
more #saveoptions
Force windowtype to be the first option written to new RC file since
its value can affect how other options are processed. (Only saved if
comes from existing RC file, not command line.) doset() lists a few
compound options before the rest too. Combine the two sets of want-
to-be-first and move the handling for that to optlist.h where the only
cost is that the options are no longer in alphabetical order.
PatR [Thu, 13 Oct 2022 20:19:58 +0000 (13:19 -0700)]
\#saveoptions fix
I hadn't ever used #saveoptions before and when I checked to see
whether the autounlock:none changes were being handled properly, I
discovered that options set via 'm O' weren't being handled at all.
This includes some miscellaneous reformatting of things noticed
while tracking down the problem.
Michael Meyer [Fri, 7 Oct 2022 17:18:14 +0000 (13:18 -0400)]
Fix: error handling for invalid autounlock value
Because the existing error was the default case in a switch/case
statement only reachable if the option matched one of the expected ones
in the list, it wasn't actually reachable: something totally out of
left-field wouldn't match one of the expected options so never hit the
switch, and something that did match one of the expected options would
by definition have a first character handled by one of the cases in the
switch/case.
Do it a slightly different way that should successfully raise an
unexpected value error for 'OPTIONS=autounlock:foobar'. I didn't remove
the default case entirely, because it could still catch an error if
some new value is added to unlocktypes[] without a corresponding case
being added to the switch statement.
Michael Meyer [Fri, 7 Oct 2022 16:39:18 +0000 (12:39 -0400)]
Remove explicit 'none' opt from autounlock handler
The autounlock handler included an explicit 'none' option, a choice that
gave it a different UX from similar existing compound option handlers
(e.g. paranoid_confirm or pickup_types), which set 'none' simply by
deselecting all options. It didn't make the menu any easier to use (at
least in my experience), since in order to go from some combination of
options to 'none', you'd have to deselect everything anyway (which on
its own was enough to set 'none', so there was no reason to explicitly
select it after doing so).
Make the autounlock handler work like other compound option handlers,
such that deselecting all options is the way to set 'none', and there is
no explicit 'none' option included in the list.
Michael Meyer [Thu, 6 Oct 2022 03:01:07 +0000 (23:01 -0400)]
Move stashed gold in #attributes to its own line
The line got a lot longer than most other #attributes lines when the
hero had gold both in open inventory and in stashed containers, so break
it up into two lines (using the same approach as the pantheon info in
the first section). Maybe this isn't necessary but it does make it
stand out less.