Delete engravings made in a breach of a shop's wall or of a vault's
wall or in the guard's temporary corridor when the wall is repaired
or the corridor removed. If 'sanity_check' was On, those would
trigger impossible warning "engraving sanity: illegal surface (x)"
where x was the terrain type code for solid rock or relevant walls.
Adding del_engr_at() calls to the shop code was straightforward.
The vault code is very complicated and I'm not sure that all the
calls I added were actually necessary.
Neither my fix for #969 nor the followup by entrez dealt with this:
if you were in one shop and dug the wall it shared with another shop,
sometimes the shopkeeper for the room your were in would teleport to
you and demand payment--or attack if you lacked funds--other times the
one from the far room would do so. For the latter, if you maneuvered
to the gap in the wall (possibly declining to die if angry shopkeeper
managed to kill you) you would get "this shop is deserted" (which is
accurate) but if you subsequently died there, you could get "Welcome
to so-and-so's shop" when the shopkeeper who abandoned her shop was
returned to occupancy after one of them took possession of invent.
And the welcome message might come from the shop that hadn't been
deserted and that you had never left. (Perhaps always from that one;
I'm not sure.)
Possibly the shopkeeper for the room you're in should get priority
when demanding payment for repairs so that the other one won't
abandon the far shop, but I didn't attempt to tackle that. This
just suppresses room entry messages when returning the shopkeeper to
her shop if the game is ending.
Not fixed, but amusing: in one of the tests, the 'far' shopkeeper
who had teleported into the near shop to demand payment for the dug
wall picked up an item from the near shop (in the case I noticed, a
hardware store shk picked up a food ration; just an ordinary item
owned by the stop) while pursuing me to the wall gap. One shk was
stealing from the other. :-)
inherits() only examined the first item in u.ushops, so some shopkeepers
that should have had first dibs were ignoring the hero, one of the
causes of #969. Examine the entire u.ushops array instead of just the
first item so that the hero's position within the shop will be correctly
identified (and do the same in set_repo_loc, though it's probably not
really necessary there).
sound_verbal(char *text, int32_t gender, int32_t tone, int32_t vol,
int32_t moreinfo);
-- NetHack will call this function when it wants to pass text of
spoken language by a character or creature within the game.
-- text is a transcript of what has been spoken.
-- gender indicates MALE or FEMALE sounding voice.
-- tone indicates the tone of the voice.
-- vol is the volume (1% - 100%) for the sound.
-- moreinfo is used to provide additional information to the soundlib.
-- there may be some accessibility uses for this function.
It may be useful for accessibility purposes too.
A preliminary implementation has been attempted for macsound to test
the interface on macOS. No tinkering of the voices has been done.
Use of the test implementation requires the following at build time with make.
WANT_SPEECH=1
That needs to be included on the make command line to enable the test code,
otherwise just the interface update is compiled in.
I don't know for certain when AVSpeechSynthesizer went into macOS, but older versions
likely don't support it, and would just leave off the WANT_SPEECH=1.
If built with WANT_SPEECH=1, the 'voices' NetHack option needs to be enabled.
It was a bit strange, when I first started up the test, to hear Asidonhopo,
the shopkeeper, talking to me as I entered his shop and interacted with him.
Issue reported for a hardfought player by k2: dying in a shop wall
produced "place_object: <item> [0] off map <0,0>" when hero's invent
was dropped. It happened in Mine Town where multiple shopkeepers are
present and it is possible to have two shops share a wall.
I could not reprouce the problem, even after setting up--and dying
various times at a gap in--a wall shared by two shops.
paybill() -> inherits() -> set_repo_loc() sets up the destination
prior to disclosure and finish_paybill() -> drop_upon_death() later
places invent at the spot iff bones are going to he saved. inherits()
is convoluted and evidently took at least one path that failed to
call set_repo_loc(). Change it to always call set_repo_loc() when
returning 'True' so that the destination should always be set if
really_done() calls finish_paybill().
Some followups by entrez are probably still useful.
Closes#965
This fixes the reported sanity check warning about a buried object
within shop boundary staying flagged no_charge after the shopkeeper
leaves the shop. Leaving the shop to pursue the hero moves unpaid
items off the bill to owed-as-robbery and changes no_charge items
to shop-owned but it wasn't doing the latter for buried objects.
I haven't attempted to test on a level with multiple shopkeepers.
If that was working correctly for unpaid items than I think it
ought to work correctly for no_charge items now. I'm not sure how
thoroughly the handling for unpaid items was tested though.
Insert the calls to trigger a number of potential soundeffects
into the core.
If no additional soundlib support is integrated into the
build, then the Soundeffect macro (sndprocs.h) expands to nothing:
[#define Soundeffect(seid, vol)
]
If, however, at least one additional soundlib support is integrated
into the build, then the Soundeffect macro gets defined as this
in sndprocs.h:
[#define Soundeffect(seid, vol) \
do { \
if (!Deaf && soundprocs.sound_soundeffect \
&& ((soundprocs.sndcap & SNDCAP_SOUNDEFFECTS) != 0)) \
(*soundprocs.sound_soundeffect)(emptystr, (seid), (vol)); \
} while(0)
]
That macro definition checks for the hero not being Deaf; it checks
to ensure that the active soundlib interface has a non-null
sound_soundeffect() function pointer; and it checks to ensure
that the active soundlib interface has declared that it supports
soundeffects by setting the SNDCAP_SOUNDEFFECTS bit in its sndcap
entry. That just means that the interface routines are prepared to
accept and deal with the calls from the core, whether or not it
actually produces the desired soundeffect.
Add a couple of redundant tests for 'shkp = shop_keeper()' yielding
Null to pacify the static analyzer.
Make the paired calls to shkp = shop_keeper() and inhishop(shkp) look
more consistent. Barring typos, the behavior hasn't been changed.
A number of C compiler suites have a math.h library that includes a yn()
function name that conflicts with NetHack's yn() macro:
"The y0(), y1(), and yn() functions are Bessel functions of the second kind,
for orders 0, 1, and n, respectively. The argument x must be positive. The
argument n should be greater than or equal to zero. If n is less than zero,
there will be a negative exponent in the result."
At one point, isaac64.h included math.h, although that has since been removed.
Some libraries used in NetHack (Qt for one) do include math.h and that required
build work-arounds to avoid the conflict.
Rename the NetHack macro from yn() to y_n() and avoid the math.h conflict
altogether, eliminating the need for that particular work-around.
The shopkeeper is speaking out loud, so use verbalize for consistency
with other types of speech.
I couldn't figure out a way to wrap the multiline version in quotes in a
way that actually worked and looked good, so I restricted this to the
pline responses.
A mute shopkeeper shouldn't be able to verbally tell you the prices of
objects. For normal chatting, on the other hand, shk_chat can handle a
mute shopkeeper (by changing from speech to "indications" -- hand signs,
body language, etc), so allow execution to reach that even if the
shopkeeper is mute (in a silent polyform).
Also more generally allow a shopkeeper to continue chatting with normal
shopkeeper responses if polymorphed into another creature, since they
apparently retain their minds (are able to tell you prices, can
transact, etc).
This is mostly inspired by the fact shk_chat has extensive handling for
mute shopkeepers, but it was unreachable as far as I can tell. It is
also funny to think of a newt or something wriggling around to indicate
it's been making a lot of money lately.
Replace FIRST_GEM and LAST_GEM with FIRST_REAL_GEM, LAST_REAL_GEM,
FIRST_GLASS_GEM, and LAST_GLASS_GEM and define those along with
objects[] rather than separately. Do the latter for FIRST_AMULET
and LAST_AMULET too. Also new FIRST_SPELL and LAST_SPELL used to
compute MAXSPELLS. (That value looks wrong to me, but this defines
it with the same value as before. If it gets fixed, EDITLEVEL will
need to be incremented.)
This started as just proof of concept that extra information could
be collected as objects[] gets initialized at compile time.
I realized that the "move the boulder billing to 'used-up items'" part
of 20392a6 didn't properly handle boulder movement from one shop to
another via a gap in a shared wall, becuase it wasn't looking at the
complete in_rooms() array (plus, it only triggered if the new spot
wasn't in any shops at all). When I tried to fix that, I realized that
stolen_value() was similarly not working reliably when passed a location
shared between two shops, and for the same reason: it was only using the
first character of the in_rooms() array.
I think this patch fixes both those things, but it would be worth
examining the change to stolen_value() carefully, to ensure getting the
roomno via the shkp won't change anything about its normal functioning.
I'm not totally sure about that -- I didn't notice any problems in some
brief tests of typical stolen_value() uses, but it seems like it
probably has some tricky edge cases.
At the very least, passing a boulder fully through a shared wall between
two shops one way, then back the other way, no longer triggers an unpaid
obj sanity check in my testing.
When a shopkeeper becomes angry, clear the no_charge flag for all
floor objects on the level, even if they happen to be in another
shopkeeper's shop. Should prevent sanity_check warnings if/when the
angry shk leaves the shop, and once the shk is pacified, items in
the shop that used to be available for free will become for-sale.
Take unpaid shop items off the bill if they're on the floor and
wall repair moves them from the shop boundary to all the way inside
the shop.
I don't think it's possible for items to be moved out of the shop
except for the very special case of moving into an adjacent shop
which shares the wall, so clearing no_charge for an item that is no
longer inside a shop is academic.
More unpaid/no_charge sanity checking. If a shop contained any
no_charge objects and was robbed, they would be left no_charge and
trigger sanity check warnings (no_charge in "untended shop") once
the shopkeeper got past any Kops in the way and exited the shop.
Earlier testing didn't wait around long enough for that exit to
happen.
Clear no_charge as soon as the robbery is detected.
An object in a shop that was marked no_charge and got removed from
the shop by means other than the hero picking it up (test case
teleported it out while hero was inside shop) was left with the
no_charge bit set. It's supposed to only be set for objects inside
shops so was triggering the recently added no_charge sanity checks.
Changing stolen_value() to have it pass the reset_nocharge arg to
billable() solves this but could have unanticipated results with
other stealing from shops.
Used up items moved to the billobjs list still have obj->unpaid set.
That should probably be cleared since it has no meaning there, but
this hasn't done that.
For those keeping score: unpaid checking has triggered three false
positives (so far) and found one bug.
The consolidation of global variables from scattered source
files into decl.c and declared in decl.h was begun in 3.7.0.
Their placement in common files was done for centralized
initialization and potential re-initialization during a
"play again" scenario.
It wasn't really necessary for all of them to be housed in a
single huge structure to meet the "play again" requirement,
and the single huge structure has been a little unwieldy when
it comes to maintenance.
Following this commit, instead of one single extremely large structure
named 'g' to house all of the relocated global variables, they
are distributed into several ga through gz.
To make things easy for the developer, each variable is placed
into the struct corresponding to the starting letter of the variable.
That way, no lookup is required in order to know which struct houses
a particular variable, it is a simple match to the starting letter
for all the centralized global variables.
A global variable named 'amulets', would be found in ga.
ga.amulets
^ ^
A global varable named 'move', would be found in gm.
gm.moves
^ ^
A global variable named 'val_for_n_or_more' would be found in gv.
gv.val_for_n_or_more
^ ^
A global variable named 'youmonst' would be found in gy.
gy.youmonst
^ ^
Handle items in gaps of a wall shared between adjacent shops.
Make handling of shop boundaries more explicit: walls, the door,
and the "free spot" by the door aren't classified as 'costly' but
obj->unpaid and obj->no_charge are valid there.
Move unpaid/no_charge checking into its own routine to unclutter
objlist_sanity().
Pushing a shop-owned boulder to the free spot or doorway or gap in
wall triggers the sanity check for the time being.
Pushing a shop-owned boulder out of the shop wouldn't charge the hero
anything. Remedy this (and remove the boulder from the bill if the hero
then pushes it back in). Also tried to handle a couple other uncharged
boulder "theft" scenarios: pushing a boulder into lava or water, into a
trapdoor or hole, or into a level teleporter (various other traps
already charged for the boulder -- it was pretty inconsistent).
I externified onbill() for this, since relying on otmp->unpaid by itself
impossibles if you push a boulder through a gap in a wall between two
adjoining shops.
Short for distu(mtmp->mx, mtmp->my) (i.e. the distance between the hero
and the specified monster), which is a very common use of distu(). The
idea is that this would be a convenient shorthand for it; I actually
thought it (or something very similar) existed already, but couldn't
find it when I tried to use it earlier. Based on the number of uses of
fully-spelled-out 'distu(mtmp->mx, mtmp->my)' replaced in this commit
I'm guessing I just imagined it.
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() */
Reverts 690e072, which changed the various is_foo macros from this:
| #define is_elf(ptr) ((((ptr)->mflags2 & M2_ELF) != 0L)
to this:
| #define is_elf(ptr) ((((ptr)->mflags2 & M2_ELF) != 0L) \
| || ((ptr) == g.youmonst.data && \
| !Upolyd && Race_if(PM_ELF)))
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.
Make the token selling prices for unidentified gems not depend on how
many items were defined before FIRST_GEM. Now the unidentified gem
selling prices will depend only on the number and defined order of the
types of gems, and won't inexplicably change when objects are added,
or depend on compile-time options such as MAIL.
Also don't do the regular item price reduction for unidentified gems,
since they are already not based on the actual value. This restores
the pre-3.6 behavior, allowing players to gain a bit more information
from the nominal selling prices of unidentified gems.
Whoever first introduced this special handling for gems probably
intended for players to be able to gain information from gem prices
this way, but probably nobody has been doing it since 3.6.
By temporarily changing the type definition for each of xint16 and
coordxy to int32_t, the compiler was able to find several places where
the type definitions were wrong.
When you steal from a shop, its shopkeeper will remember you as a thief
and charge you higher prices in the future (as well as be more curt and
less polite in interactions with you, though not outright hostile) even
if you pacify them, or die on the level and revisit it later as a bones
file. This was an idea aosdict had, and I think it makes sense that a
shopkeeper doesn't forgive and forget, immediately returning to treating
you exactly like anyone else, just because you were terrorized into
paying her back. Paying a shopkeeper off may cause her to stop actively
attacking you, but it feels like she'd have her eye on you as a known
thief going forward (and maybe would hang up a sign with your picture,
saying something like "DO NOT ACCEPT CHECKS FROM THIS HERO").
This surchage already existed, but since it was tied to active anger
(which typically causes a shopkeeper to quickly abandon their shop to
follow you) it was somewhat rare to see it in action.
I did not implement it here, but one possible further tweak might be to
clear the surcharge if the shopkeeper is pacified via taming magic
(which more-or-less magically brainwashes the target to feel positively
towards the hero) but not if you simply pay your debts.
One of the drivers of this change was that screen coordinates require a
type that can hold values greater than 127. Parameters to the window
port routines require a large type in order to be able to have values
a fair bit larger than COLNO and ROWNO passed to them, particularly for
their use to the right of the map window.
This splits the uses of xchar into 3 different situations, and adjusts
their type and size:
xchar
|
-----------------------
| | |
coordxy xint16 xint8
coordxy: Actual x or y coordinates for various things (moved to 16-bits).
xint16: Same data size as coordxy, but for non-coordinate use (16-bits).
xint8: There are only a few use cases initially, where it was very
plain to see that the variable could remain as 8-bits, rather
than be bumped to 16-bits. There are probably more such cases
that could be changed after additional review.
Note: This first changed all xchar variables to coordxy. Some were
reviewed and got changed to xint16 or xint8 when it became apparent that
their usage was not for coordinates.
This increments EDITLEVEL in patchlevel.h
Switch to using a macro invocation Verbos(n, s) in place of the
flags.verbose checks.
Provide the mechanics for individual suppression of any of the
existing messages that were considered verbose.
Mechanics only - this code update does not provide any means of
setting the suppression bits.
iflags.verbose = 0
is still a master suppression of all the verbose messages.
iflags.verbose = 1
turns on the verbose messages only for those whose suppression
bit is 0 (not set).
Reported directly to devteam by entrez, the rloc() monst vanishes/
appears nearby/&c message was being given before "satisified, <shk>
suddenly disappears" making the latter redundant. As discussed, the
fix isn't as simple as suppressing one message or the other because
both are given conditionally.
This seems to solve it but has only been lightly tested.
Commit 593c3532fc took the 'catchup'
argument away from repair_damage() and calculated it in the routine.
Commit 699a25c00b put the argument
back but neglected to remove calculating and assigning it, making
the passed value be ignored. Take that away, finishing 699a25c00b.
That affected shop repair messages but wasn't enough to prevent a
shop wall repair display glitch. This seems to make things work
properly but is little iffy.
Stop attempting to catch up for lost time for shop damage repair
when getlev() loads a previousl visited level. Normal shopkeeper
behavior will take care of that.
Also, fixes the display related aspects of shop damage repair
interacting with ball and chain. They don't happen when its done
while the map is being shown.
Issue #722 posted by copperwater and commented on by Entrez: both
shk_your() and the() inserted "the" in front of a unique monster's
corpse, yielding "the Lord Surtur's corpse glows iridescently" and
"the Lord Surtur's corpse drops to the floor."
Teach both of those routines to skip "the" when used for monsters
with personal names. It now omits "the" for "Medusa's corpse" but
still gives "the Oracle's corpse".
shk_your() operates on an object and can deal explicitly with corpses
of named monsters. the() operates on text and has to guess whether
it is being used in a similar situation. Right now the guess is just
"is there an apostrophe present?" and might need further refinement.
Fixes#722
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.
If you want to declare a pointer which the address pointed to is constant,
you should declare it as like `static const char *const var = "...";`.
This commit supplies missing `const` and prevents some programming
error in the future.
unpaid_cost(): 'shkp' might be used without being initialized. That
sort of warning is sometimes wrong but was correct in this instance.
qt_Splash(): the Qt 6.2 warning suggests switching the argument for
setMask() from QBitmap() to fromPixmap() but that doesn't work with
5.11. However, switching from setMask() to setPixmap() does work,
and results in a crisp splash image instead of the fuzzy one I
recently complained about. I've no idea what the right QT_VERSION
check ought to be but another QLabel propery is documented as "added
in 4.2" and the pixmap one has no such mention so I picked Qt < 4
for the code that's been in use and Qt >= 4 for the new code.
Recent commit 5c14f3e fixed an issue with unpaid_cost when the hero is
in two shops at once (e.g. phasing through a shared wall), but
introduced new problems with finding the cost of a container with unpaid
contents -- an unpaid item in a player-owned container would generate
the same "unpaid_cost: object wasn't on any bill" impossible as the
issue the commit fixed.
The main issue was that finding the bill entry used to depend on finding
the shopkeeper, but after the change, finding the shopkeeper depended on
finding the bill entry; for a container with unpaid items which has a
shopkeeper, but no bill entry, this caused issues.
Revert part of 5c14f3e and tackle the problem another way, which should
correctly handle unpaid items in inventory or a container, even if they
belong to a mixture of different shops and/or the hero is on a space
shared between multiple shops.
Fix the reported bug of using Passes_walls to carry an unpaid item
from inside a shop into the wall it shares with another shop
producing a situation where examining inventory issued an impossible
"unpaid_cost: object wasn't on any bill" when trying to append the
cost to the formatted inventory line. u.ushops[], u.ushops0[], and
others are lists that can have up to four entries and the relevant
code was only checking the first one.
Not mentioned in the report: continuing another step into the other
shop didn't get recognized as robbing the first shop, for a similar
reason.
For either of these bugs, digging a breach in the wall, paying off
the miffed shopkeeper, then stepping into the breach before it gets
repaired will probably trigger the same results without the need for
polymorphing into a xorn or earth elemental. But not on the tourist
goal level test case since walls there are undiggable.
I didn't go looking for other instances of not checking all relevant
shopkeepers. It wouldn't surprise me if there are some more.