Commit Graph

326 Commits

Author SHA1 Message Date
Pasi Kallinen
f4cd5ed065 Fix vision with pushing a boulder and temp clouds
remove_object cleared the vision when the last boulder was removed
from a location, without considering temporary [poison] clouds.
This particular case happened when pushing a boulder.
2025-01-06 20:22:56 +02:00
Pasi Kallinen
9313fb7747 Clear tin-eating struct when object goes away
The tin-eating context was pointing to a non-existent object,
causing an error when the fuzzer somehow managed to continue eating
the freed tin object.
Clear the pointer when the tin leaves inventory or the object
is deleted.
2025-01-03 22:21:14 +02:00
Pasi Kallinen
87694e1a95 Hero remembers trapped boxes
After finding a trap on a chest or a large box, remember it
as trapped: "You see here a trapped large box."
Randomly generated chests and boxes can be obviously trapped.
Allow defining obviously trapped containers via lua.

Invalidates saves and bones.
2024-12-19 13:11:25 +02:00
Alex Smith
3491535548 More bugfixes for the recent artifact gifting changes 2024-12-12 07:35:50 +00:00
Alex Smith
d87cadaf73 Artifact and #offer rebalance, part 1: sacrifice gifts
In 3.6, artifact gifts are often either a) entirely useless or
b) gamebreaking, neither of which is really ideal from a balance
perspective.

This commit aims to make artifact gifts more useful in the early
game by greatly increasing the chance for situational artifacts to
generate positively enchanted.  However, the most powerful
artifacts will now only be gifted if you offer a high-value corpse,
meaning that they are only likely to be accessible later in the
game.  The selection of which artifact to gift has become more
complicated in order to a) increase the chance that it fits the
character and b) reduce cheese strategies (e.g. it is no longer
possible for elves to force the gifting of Stormbringer as the
first sacrifice gift).
2024-12-12 03:14:47 +00:00
nhmall
02259dd1d0 light source troubleshooting
I don't think this solves the recent light source reports,
but it changes a couple of things in an attempt to get more
information.

1. Having gy.youmonst.m_id field always be zero makes it tough
to distinguish it from uninitialized memory, or a random memory
value. This changes the m_id for the hero's gy.youmonst.m_id
to always hold the identifier 1, instead of 0.

2. write_ls was taking the stashed pointer in the light source,
and using it to immediately extract the m_id field and search
for that m_id. This changes the approach slightly, to actually
try and locate the stashed pointer itself in one of the monster
chains. Only if the monster pointer is located, do we dereference
it to obtain the m_id field.

3. For the interim, mark the saved ls with another set bit when
there has been a failure to locate the monst. At this time,
no code is acting on that bit, but it can be seen in a debug
session.

Hopefully, the next report will provide enough information to
understand the scenario a little better.
2024-12-09 15:52:29 -05:00
nhmall
0792e5fe9e expand implicit fallthrough detection to non-gcc compilers
gcc has recognized various "magic comments" for white-listing
occurrences of implicit fallthrough in switch statements for
a long time:

    The range and shape of "falls through" comments accepted are
    contingent upon the level of the warning. (The default level is =3.)

    -Wimplicit-fallthrough=0 disables the warning altogether.
    -Wimplicit-fallthrough=1 treats any kind of comment as a "falls through" comment.
    -Wimplicit-fallthrough=2 essentially accepts any comment that contains something
     that matches (case insensitively) "falls?[ \t-]*thr(ough|u)" regular expression.
    -Wimplicit-fallthrough=3 case sensitively matches a wide range of regular
     expressions, listed in the GCC manual. E.g., all of these are accepted:
        /* Falls through. */
        /* fall-thru */
        /* Else falls through. */
        /* FALLTHRU */
        /* ... falls through ... */
       etc.
    -Wimplicit-fallthrough=4 also, case sensitively matches a range of regular
     expressions but is much more strict than level =3.
    -Wimplicit-fallthrough=5 doesn't recognize any comments.

Plenty of other compilers did not recognize the gcc comment convention,
and up until now the compiler warning for detecting unintended
fallthrough had to be suppressed on other compilers. That's because the code
in NetHack has been relying on the gcc approach, and only the gcc approach.

The C23 standard introduces an attribute [[fallthrough]] for the
functionality, when implicit fallthrough warnings have been enabled.

Several popular compilers already support that, or a very similar attribute
style approach, today, even ahead of their C23 support:

       C compiler                       whitelist approach
       ---------------------------   -------------------------------------
       C23 conforming compilers         [[fallthrough]]

       clang versions supporting
       standards prior to
       C23                              __attribute__((__fallthrough__))

       Microsoft Visual Studio
       since VS 2022 17.4.
       The warning C5262 controls
       whether the implict
       fallthrough is detected and
       warned about with
       /std:clatest.                    [[fallthrough]]

This adds support to NetHack for the attribute approach by inserting a
macro FALLTHROUGH to the existing cases that require white-listing, so
other compilers can analyze things too.

The definition of the FALLTHROUGH macro is controlled in include/tradstdc.h.

The gcc comment approach has also been left in place at this time.
2024-11-30 14:16:27 -05:00
PatR
93ff5e6409 comment thinko/editing typo 2024-09-15 16:55:45 -07:00
PatR
c2d22bfbe6 glob vs hider fix
Caught by 'sanity_check':  hides-under monster hiding under nothing
after the glob it was under coalesced with an adjacent one.  Tricky
to test because dropped, thrown, or kicked globs will always merge
with the one being hidden under.  Needed to kill the type of monster
that drops the type of glob since it gets created on the floor so
is eligible to draw in the existing one.
2024-09-15 16:49:59 -07:00
PatR
cc1066b1ff some reformatting (3 of 4) 2024-09-05 15:55:24 -07:00
PatR
0ce2439e82 github issue #1275: curses init vs pauper
Reported by ars3niy, the curses interface could behave strangely on
the first turn if the 'pauper' option/conduct was specified.

There isn't any definitive flag indicating whether or not the game
has started.  Since 'moves' has traditionally been initialized to 1
rather than to 0, there were several instances of
|  if (moves <= 1 && invent != NULL)
being used to determine the starting state on the assumption that
once hero has inventory, the game has begun.  Introduction of the
'pauper' option made the test for non-Null invent become unreliable.
For paupers, the program would behave as if the game hadn't started
yet until the player finally made a time-consuming move.

This changes compile-time initialization of 'moves' from 1 to 0,
then sets it to 1 when initial inventory would be bestowed (even
when 'pauper' inhibits that).  That's probably not the best place
for it, but testing for 'moves==0' now should produce an identical
effect as 'moves<=1 && invent!=NULL' used to accomplish.

It would have been much simpler just to give paupers 1 gold piece,
or perhaps one rock, in place of usual starting gear so that their
initial inventory wouldn't be empty, but the moves+invent way of
checking for start-of-play has always bothered me.

Should 'pauper' be preventing 'nethack -X' from giving its starting
wand of wishing?  Conducts and explore mode don't really overlap so
maybe it doesn't matter.

Fixes #1275
2024-08-31 14:08:04 -07:00
nhmall
6c0ae092c6 distinguish global variables that get written to savefile
The g? structs had a mix of variables that were written to
the savefile, and those that were not.

For better clarity and to distinguish those that end up in
the savefile, relocate some g? variables that get written
directly to the savefile into different structs.

This updates EDITLEVEL, although technically it probably
didn't need to, since savefile contents are not changing.

Details:

    gb.bases            -> svb.bases
    gb.bbubbles         -> svb.bbubbles
    gb.branches         -> svb.branches
    gc.context          -> svc.context
    gd.disco            -> svd.disco
    gd.dndest           -> svd.dndest
    gd.doors            -> svd.doors
    gd.doors_alloc      -> svd.doors_alloc
    gd.dungeon_topology -> svd.dungeon_topology
    gd.dungeons         -> svd.dungeons
    ge.exclusion_zones  -> sve.exclusion_zones
    gh.hackpid          -> svh.hackpid
    gi.inv_pos          -> svi.inv_pos
    gk.killer           -> svk.killer
    gl.lastseentyp      -> svl.lastseentyp
    gl.level            -> svl.level
    gl.level_info       -> svl.level_info
    gm.mapseenchn       -> svm.mapseenchn
    gm.moves            -> svm.moves
    gm.mvitals          -> svm.mvitals
    gn.n_dgns           -> svn.n_dgns
    gn.n_regions        -> svn.n_regions
    gn.nroom            -> svn.nroom
    go.oracle_cnt       -> svo.oracle_cnt
    gp.pl_character     -> svp.pl_character
    gp.pl_fruit         -> svp.pl_fruit
    gp.plname           -> svp.plname
    gp.program_state    -> svp.program_state
    gq.quest_status     -> svq.quest_status
    gr.rooms            -> svr.rooms
    gs.sp_levchn        -> svs.sp_levchn
    gs.spl_book         -> svs.spl_book
    gt.timer_id         -> svt.timer_id
    gt.tune             -> svt.tune
    gu.updest           -> svu.updest
    gx.xmax             -> svx.xmax
    gx.xmin             -> svx.xmin
    gy.ymax             -> svy.ymax
    gy.ymin             -> svy.ymin

Related note:
There are some pointer variables that are heads of chains that were not
moved from 'g?' to 'sv?', because they are not actually written to the
savefile directly, but the objects/monst/trap/lightsource/timer in the
chains they point to are. That can be changed, if desired.
Examples: gi.invent, gm.migrating_objs, gb.billobjs, gm.migrating_mons,
          gf.ftrap, gl.light_base, gt.timer_base
2024-07-13 14:57:50 -04:00
PatR
0e46439814 fix add_one_tobill() 'fixme'
Not exhaustively tested.
2024-07-04 14:19:40 -07:00
PatR
bf1e3d3e56 more obj->where == OBJ_DELETED 2024-06-21 14:26:48 -07:00
PatR
d5d646a9e9 ancient comment typo/thinko
Change "it gets size gets reduced" to "its size gets reduced".
This was present in 3.4.3, doubtless even ealier.
2024-06-21 13:32:21 -07:00
PatR
07de17d747 obj->where == OBJ_DELETED
The list of possible object locations used when formatting obj->where
wasn't updated when the objs_deleted list was introduced.  If object
sanity checking ever tried to report it for something, it would have
been described as "unknown[9]" rather than as the intended "deleted".
2024-06-21 12:57:31 -07:00
PatR
ec016f9956 deferred obj deletion bit
I've no idea whether there are any cases where this matters.
2024-05-09 14:17:43 -07:00
PatR
0f2897c704 deleted object sanity checking revisited
Give a different message from "obj not free" if attempting to delete
an already deleted object.  Also, skip sanity checking of in_use/
bypass/nomerge bits for deleted objects instead of clearing them when
going onto the objs_deleted list.
2024-05-08 07:34:22 -07:00
PatR
8d9b26e26d sanity check of objs_deleted
Teach obj_sanity_check() and clear_bypasses() about the new obj list.
It should always be empty when sanity checks are performed.  That
might not be the case when obj bypasses are cleared, although failing
to clear bypasses for deleted objects wouldn't make any difference,
so this is mainly cosmetic.
2024-05-07 12:19:43 -07:00
Pasi Kallinen
1a58ed8de9 Rework object deletion
Make object deletion work similarly to monster deletion:
it's marked for deletion (by setting the where-field to OBJ_DELETED
and moved to specific deleted-objects chain), but they're actually
freed at the beginning of turn.

This may need some more tweaking, especially in places that iterate
over object chains, but fuzzing did not find any obvious problems.

Fix a case of accessing freed memory: a monster breathed at hero,
destroying some items.  The code stored the next item in the chain
(a cloak), but a ring of levitation was destroyed, causing hero to
plop down into lava, destroying the cloak.  The item destruction
code then tried to access the destroyed cloak object.
Make the code check the object where-field - which will be different
if the object was marked for deletion.  Also removed an extra loop
going through the whole object chain looking for the items to
destroy.
2024-05-06 17:57:47 +03:00
PatR
8a1c745515 leash sanity checking refinement 2024-05-01 06:10:39 -07:00
PatR
6ebc3a7291 leash sanity checking
I still haven't found any explanation for the report by a hardfought
player recently that going down some stairs with a pair of leashed
pets got one into a confused state where it was flagged as leashed
but the corresponding leash was no longer in use.

This adds some new object and monster sanity checks regarding leashes,
and it changes o_unleash(obj) to clear obj->leashmon even if/when the
monster can't be found.

It also changes behavior for dipping an attached leash into a potion
of polymorph when that happens to yield another leash--now the new
one will end up being pre-attached.
2024-04-30 13:27:10 -07:00
Michael Meyer
b662134eba Fix: bill_dummy_obj billed excessively for stacks
Add a way to request that unpaid_cost() produce the cost for a single
item, which is necessary for the price adjustment made in
bill_dummy_object.  Another option would be to simply divide by quan in
bill_dummy_object, but this might be more future-proof in case
unpaid_cost ever involves more than simple multiplication by quan
(e.g. the use of alternate units vs the base price, as are used for
globs).

Fixes #1236
2024-04-27 18:42:50 -07:00
nhkeni
9c0ed8ae63 NOSTATICFN for src/* 2024-03-14 17:41:51 -04:00
nhkeni
acf60063d5 Add missing prototypes for static functions to avoid warnings. 2024-02-29 10:49:53 -05:00
RainRat
a3658f85ac fix typos 2024-02-28 20:15:56 -08:00
nhmall
0a985459f0 make style consistent for function ptr arguments 2024-02-19 17:21:04 -05:00
nhmall
688ac6ffbe remove register from variable declarations 2024-02-19 16:30:07 -05:00
PatR
9cd1a571ae more weight tweaks
Update some potential weight issues.  Eggs won't hatch when in
containers so they weren't affected but add some bulletproofing.
Corpse revival from inside containers was already ok too, so
effectively there's no change except for making container_weight() be
global instead of local to mkobj.c.
2024-01-25 23:09:12 -08:00
PatR
de5864b7b4 comment typo 2024-01-24 14:56:21 -08:00
PatR
267daeaa0f supply chest fixes for "odd encumbrance behavior"
Changing the quantity to 2 (50:50 chance) when creating a potion of
healing (also 50:50 chance for each attempt) to place inside a supply
chest wasn't updating the potion stack's weight, resulting in the odd
encumbrance behavior that was reported last December.

Taking the stack out of the container doesn't fix the weight but
drinking one of the potions splits the stack of 2 into two stacks
of 1 and does update the weight for both.  That gives the hero higher
encumbrance when the formerly weightless one has its proper weight.
Finishing drinking the potion uses it up, removing second potion's
weight again.  When below an encumbrance threshold by the weight of
one potion or less, player will see encumbrance increase and then
decrease, with healing message given before both due to sequencing.

Supply chests weren't having their own weight updated when they were
populated, so would behave as if empty if hero carried them around.
Removing something, breaking something by kicking the chest, or adding
something would update its weight to match its contents.

I also noticed a refutation (or should that be rebuttable?) to my own
remarks in this:

| commit cd91d0630b
| Author: PatR <rankin@nethack.org>
| Date:   Sat Dec 30 17:10:39 2023 -0800
|
| github issue #1180 - humans and murder
|
| Issue reported by Umbire:  reviving a human corpse into a human
| monster and then killing it entails murder penalty even when it is
| hostile.
|
| This is probably a non-issue.  Human monsters tend to not leave
| human corpses, they leave shopkeeper corpses or sergeant corpses
[...]

Dead fake hero corpses placed at trap locations on early levels are
leaving plain human|dwarf|elf|gnome|orc corpses rather than fake
player monster ones (which are always human but resurrect as player
monsters rather than as plain humans), so there are more plain human
corpses now than there were in 3.6.x or early to-be-3.7.  I've added
a comment about the situation.
2024-01-23 15:42:19 -08:00
nhmall
25a8c258e6 replace x >= LOW_PM with ismnum(x) shorthand macro 2024-01-11 14:01:10 -05:00
PatR
c6897bf331 fix github issue #1191 - obj->age of oil potions
Issue reported by AmyBSOD:  several actions change the object type of
a potion rather than force creation of a replacement one, and if/when
the type was changed to oil, the age wasn't converted from absolute
to relative.  Relative age is the amount available and/or the number
of turns it will burn if applied.  The later in a game a potion got
converted into oil, the longer it would burn.  Not mentioned:  reverse
situation was also the case, although that didn't have any noticeable
effect since incorrect absolute age of former oil doesn't matter.

Not thoroughly tested.  I got a potion of oil from a horn of plenty
and it burned for 400 turns, but it might have been created directly
rather than be a rejected magic potion that was converted into oil.

Closes #1191
2024-01-03 13:29:15 -08:00
nhmall
1c867ce528 another static analyzer bit for mkobj.c
src/mkobj.c(419): warning: '((obj2))->oextra->omonst' could be '0'
                : this does not adhere to the specification for the
                  function 'memcpy'.
src/mkobj.c(421): warning: Dereferencing NULL pointer
                  '((obj2))->oextra->omonst'.
                  See line 419 for an earlier location where this can occur

The analyzer was not aware that newoextra() sets up an oextra block:
    if (!obj2->oextra)
        obj2->oextra = newoextra();

The analyzer was also not aware that newomonst() was setting up a valid
OMONST pointer.
    if (!OMONST(obj2))
        newomonst(obj2);

Add an assert(has_omonst(obj2)) before copying the content from
OMONST(obj1) into OMONST(obj2).
2023-12-22 22:30:38 -05:00
nhmall
18efc74db2 static analyzer bit in mkobj.c
src/mkobj.c(2211): warning: Reading invalid data from 'gl.level.objects[x]'.
2023-12-22 19:32:07 -05:00
nhmall
c5a5b55c15 nonnull for some static functions during recent analysis 2023-12-16 10:51:59 -05:00
Michael Meyer
1736f3caaa Add 'pickup_stolen' option
Add pickup_stolen option to autopick items stolen from you by a nymph or
monkey, even if they don't match your normal autopickup settings.
Replace was_dropped, was_thrown with a 2-bit bitfield that can contain
values LOST_DROPPED, LOST_THROWN, and LOST_STOLEN (or 0), since they
should all be mutually exclusive anyway as they track the most recent
way the item left the hero's inventory.

[Rebase/merge conflict fixed up.  PR]
2023-12-08 15:19:54 -08:00
Michael Meyer
c07d114644 Add object sanity check for was_dropped/was_thrown 2023-12-08 15:19:05 -08:00
Pasi Kallinen
5dc94f3d83 Macro for picking random entry from array 2023-12-05 10:06:27 +02:00
nhmall
a6772da65d pull request 1156 follow-up bit 2023-11-30 12:19:16 -05:00
SHIRAKATA Kentaro
3ab0d92ac9 remove unnecessary condition on save_mtraits()
`mtmp->data` here is always non-null, otherwise it leads segv at earlier code.
2023-12-01 02:05:38 +09:00
nhmall
76d328d86a gi.invalid_obj -> hands_obj 2023-11-11 19:49:38 -05:00
PatR
dbd39f2cd5 prevent weightless statues
From a reddit thread:  statue weight is one and half times corpse
weight and some monsters that don't leave a corpse are defined as
having mons[].cwt==0 so statues of those weighed nothing.

Rather than assigning a non-zero corpse weight to every type of
creature, statues that weigh less than an arbitrary value based on
monster size have their weight increased to that value.  The weight
of a statue of a killer bee jumps from 1 to 100, that of a leprechaun
increases from 90 to 100, that of a yellow light is changed from 0
to 300, wraith from 0 to 500, and air elemental from 0 to 900.  That
last one is actually too low but making the formula more complex
doesn't seem worth it.
2023-07-12 09:48:24 -07:00
PatR
e9c58c2fe4 breaking crystal armor
Instead of a 5% chance for crystal plate mail or crystal helmet to
break each time it's subjected to breakage, switch to a 10% chance
but the damage is treated as erosion rather than break/don't-break.
'crystal foo' will need to go through four stages of damage before
breaking:  cracked crystal foo, very cracked crystal foo, thoroughly
cracked crystal foo, then gone.  Crackproof handling is included,
described as tempered crystal foo.

It mostly still applies to throwing and kicking the item.  Having
some hits trigger damage might be worthwhile but isn't implemented.

Object creation within lua code probably needs to be updated, and
when the Mitre of Holiness is created in the priest/priestess quest
it should start out as tempered (erodeproof).  Perhaps it ought to
be erodeproof regardless of where/how it's created.
2023-06-14 15:54:04 -07:00
Pasi Kallinen
a54b6ba75c Split object init out of mksobj
Move the random erosions in there, as those should only be done if
the object is getting a random initialization.
2023-05-13 12:12:36 +03:00
PatR
cc2410e349 freeing objects and monsters
Making the zeroing out of memory used by an object that is about to
be freed unconditional, and do the same for monsters.  Should never
matter aside from an undetectable amount of extra overhead.
2023-03-29 15:18:25 -07:00
PatR
3ce02acbc5 fix #K3888 - object lost panic cased by lava
Report was for spell-casting monster using the destroy armor spell on
hero's levitation boots while hero was floating over lava.  The boots
became unworn but still in inventory, hero dropped into lava, the
boots happened to be an inventory item which got burned up, then the
call stack unwound back to the destroy armor routine which tried to
finish by deleting them but they were already gone by then.  Could
also happen for black dragon breath, hero reading scroll of destroy
armor, or overenchanting the boots with scroll of enchant armor, so
not so unlikely that nobody would be expected to notice.

Initially I couldn't reproduce the object lost panic.  It only happens
if the memory used by the boots is cleared or clobbered during first
time it's freed, otherwise second free doesn't notice any problem.

The 'wornarm_destroyed()' portion of this commit is sufficient to fix
this.  The other bits are things I tried before figuring out how to
reproduce it, plus zeroing out any object passed to dealloc_obj().
2023-03-20 17:05:02 -07:00
nhmall
de79240dea some comment spelling fixes 2023-03-16 22:27:01 -04:00
nhmall
57fceb0112 replace a couple of tabs 2023-03-06 15:42:53 -05:00
nhmall
3ada7caf61 missed one, there were two 2023-02-10 14:28:27 -05:00