Source divers! Please help!

33 views
Skip to first unread message

Patric Mueller

unread,
Aug 2, 2008, 2:47:38 PM8/2/08
to

Setting: Levitating or standing on a pool of water or lava while
wearing a ring of conflict and having a non-flying monster
nearby.

What happens: Nothing! The monster circles around you without
attacking. But as soon as the ring of conflict is
removed, the monster starts attacking.

A flying monster like a D or B doesn't act this way. They just attack
regardless of me wearing the ring of conflict or not.

I already reported this as bug to the DevTeam, but one never knows
when or how they respond and I am extremely bothered that I can't
figure out which part of the source code is responsible for this
behavior.

I just wanna know! Please help so I can again sleep at night! ;-)

Bye
Patric

--
NetHack-De: NetHack auf Deutsch - http://nethack-de.sourceforge.net/

NetHack for AROS: http://bhaak.dyndns.org/nethack/nethack-for-aros.html

rogerb

unread,
Aug 2, 2008, 5:43:31 PM8/2/08
to
Patric Mueller wrote:
> Setting: Levitating or standing on a pool of water or lava while
> wearing a ring of conflict and having a non-flying monster
> nearby.
>
> What happens: Nothing! The monster circles around you without
> attacking. But as soon as the ring of conflict is
> removed, the monster starts attacking.
>
> A flying monster like a D or B doesn't act this way. They just attack
> regardless of me wearing the ring of conflict or not.
>
> I already reported this as bug to the DevTeam, but one never knows
> when or how they respond and I am extremely bothered that I can't
> figure out which part of the source code is responsible for this
> behavior.
>
> I just wanna know! Please help so I can again sleep at night! ;-)
>
> Bye
> Patric
>

There are two places in the code where a monster can attack; the "usual"
one starts at monmove.c line 548; this is reached if monster movement is
skipped or the monster elects not to move. In this case, there is no
requirement that the monster must be able to move onto your position.

Monster movement is usually skipped if the monster is hostile and
adjacent to you; however there are exceptions, in the condition at
monmove.c line 475. One of these is monster movement is not skipped if
conflict is in operation (except for the Wizard of Yendor, who is never
affected by conflict).

If monster movement is not skipped, then the monster can attack at two
places; either at monmove.c line 979 if it tries to move onto your
square, or if it elects not to move, at line 548 as above. Because you
are on a water square, the monster can't move onto your position, so it
can't attack you at line 979. Clearly the code elects to move the
monster to a different square adjacent to you, rather than not move at
all then attack you at line 548.

So far I'm pretty confident this analysis is correct. From here I'm not
so certain, but it looks like the critical line is at monmove.c line
941; this is in a loop through all the possible locations a monster
might want to move to (I think). One of the conditions for accepting a
move is !mmoved, which I assume means no moves have been accepted yet.
This is unqualified, so currently a monster must move once it reaches
here, if a square is available to move to. A monster's movement will
always reach here when conflict is in operation, except for the WoY as
noted above, so it can't attack you unless it is able to move to your
square (this is why you don't get the effect for flying creatures, which
may move to your location).

This looks like it could be a bug, but conflict is weird, so who knows
except the DevTeam.

One possible way to change this behaviour would be to reduce the
likelihood a monster will move in this case if it is adjacent to the
hero and the variable appr is 1 (no reason to change current behaviour
for fleeing/peaceful monsters and leprechauns avoiding you). You could
perhaps allow a save vs. conflict instead of or in addition to a random
chance. It should still be possible for monsters to move when adjacent
to you under unresisted conflict, as they need to be able to reach other
monsters, which are actually higher-priority targets as the code is
written now.

Note this code provides "health warnings" such as "Actually, this whole
section of code doesn't work as you'd expect" and "The whole
dochugw/m_move/distfleeck/mfndpos section is serious spaghetti code."
Take these warnings seriously ;)

Cheers


rogerb

Patric Mueller

unread,
Aug 3, 2008, 9:23:42 AM8/3/08
to
rogerb <roge...@ymail.com> wrote:
>
> There are two places in the code where a monster can attack; the
> "usual" one starts at monmove.c line 548; this is reached if monster
> movement is skipped or the monster elects not to move. In this case,
> there is no requirement that the monster must be able to move onto
> your position.
>
> Monster movement is usually skipped if the monster is hostile and
> adjacent to you; however there are exceptions, in the condition at
> monmove.c line 475. One of these is monster movement is not skipped if
> conflict is in operation (except for the Wizard of Yendor, who is
> never affected by conflict).
>
> If monster movement is not skipped, then the monster can attack at two
> places; either at monmove.c line 979 if it tries to move onto your
> square, or if it elects not to move, at line 548 as above. Because you
> are on a water square, the monster can't move onto your position, so
> it can't attack you at line 979. [...]

But being able to move to the players position is not a prerequisite
for attacking.

In fact, a purple worm will happily engulf you and move to your
current position, even if you stand on lava or water and it will burn
or drown the next turn (which BTW is a variant of bug C343-306).

> So far I'm pretty confident this analysis is correct. From here I'm
> not so certain, but it looks like the critical line is at monmove.c
> line 941; this is in a loop through all the possible locations a
> monster might want to move to (I think).

Yes, that's correct. It loops through all location that are in
"coord poss[9]". This array gets filled by mfndpos() at mon.c:1000.
This function only returns safe locations.

> One of the conditions for
> accepting a move is !mmoved, which I assume means no moves have been
> accepted yet. This is unqualified, so currently a monster must move
> once it reaches here, if a square is available to move to. A monster's
> movement will always reach here when conflict is in operation, except
> for the WoY as noted above, so it can't attack you unless it is able
> to move to your square (this is why you don't get the effect for
> flying creatures, which may move to your location).

Using a debugger[1] I observed the critical difference between
attacking and non-attacking monsters being the function m_move()
returning 0 resp. 1.

With a return value of 1 "monster moved" the code returns without
attacking the player.

With a return value of 0 "no movement" the code proceeds to line 548
and the player gets attacked.


But this part of the code is only executed if the condition on line
478 to 492 [2] is true. As you already noted Conflict is checked
there.

So if the player doesn't cause conflict, the whole part from line 478
until line 546 is skipped, going straight to the part of attacking the
player.

Whew!

> This looks like it could be a bug, but conflict is weird, so who knows
> except the DevTeam.

Maybe weird from the code's POV but to a player conflict is simple. :)

If the player gets attacked without conflict, he should
be attacked with conflict.

> One possible way to change this behaviour would be to reduce the
> likelihood a monster will move in this case if it is adjacent to the
> hero and the variable appr is 1 (no reason to change current behaviour
> for fleeing/peaceful monsters and leprechauns avoiding you). You could
> perhaps allow a save vs. conflict instead of or in addition to a
> random chance. It should still be possible for monsters to move when
> adjacent to you under unresisted conflict, as they need to be able to
> reach other monsters, which are actually higher-priority targets as
> the code is written now.

I'm not sure such a complicated solution is necessary.

If you look closer at the conditions at monmove.c:487, to me they look
all like conditions when *not* to melee-attack.

So why is "(Conflict && !mtmp->iswiz)" in this list?

> Note this code provides "health warnings" such as "Actually, this
> whole section of code doesn't work as you'd expect" and "The whole
> dochugw/m_move/distfleeck/mfndpos section is serious spaghetti code."
> Take these warnings seriously ;)

Yes, there is some scary code in there. Even for people used to
NetHacks code.

> Cheers

Thanks. You put me on the right track.

Bye
Patric

[1] I hate having to use debuggers. If code is only understandable by
using a debugger, it is clearly not fit for human consumption.

[2] Argh! Who spreads an if condition over 15 lines?

Kent Paul Dolan

unread,
Aug 3, 2008, 2:56:34 PM8/3/08
to
Patric Mueller wrote:

> [2] Argh! Who spreads an if condition over 15 lines?

Mostly people who've decided to write maintainable code,
and thus want the nesting to be obvious from the
indentation, and also wherever possible, want to reduce
perceived complexity by writing a single meme per line.

Whitespace is pretty much free, so good programmers use
lots of it for lots of reasons.

The right answer, of course, is to make the insides
of the if(...) into a subroutine, and call it at
that point. Since almost everything in NetHack is of
global scope, this is pretty trivial to do. Now, of
course, you lose code locality. Spagetti code
doesn't unsnarl well. This stuff is _so_ much easier
in an OOPL.

xanthian.

How that "maintainable" could possibly apply to
NetHack source code, unmaintainable and proud of it,
is up to the reader to invent.

rogerb

unread,
Aug 3, 2008, 7:02:14 PM8/3/08
to
Patric Mueller wrote:
> Whew!

You got there in the end ;)

rogerb <roge...@ymail.com> wrote:
>> This looks like it could be a bug, but conflict is weird, so who knows
>> except the DevTeam.
>
> Maybe weird from the code's POV but to a player conflict is simple. :)
>
> If the player gets attacked without conflict, he should
> be attacked with conflict.

Can you be so certain of the developers' intent? I know I'm not. Under
conflict, monsters will never attack you unless they resist or there is
no other target for them to hit, even if otherwise they would have
attacked you, excepting as usual the WoY (mon.c line 618). Conflict is
an enormous modifier to monster behaviour; in the absence of
documentation, only the developers know what they intended.

>> One possible way to change this behaviour would be to reduce the
>> likelihood a monster will move in this case if it is adjacent to the
>> hero and the variable appr is 1 (no reason to change current behaviour
>> for fleeing/peaceful monsters and leprechauns avoiding you). You could
>> perhaps allow a save vs. conflict instead of or in addition to a
>> random chance. It should still be possible for monsters to move when
>> adjacent to you under unresisted conflict, as they need to be able to
>> reach other monsters, which are actually higher-priority targets as
>> the code is written now.
>
> I'm not sure such a complicated solution is necessary.
>
> If you look closer at the conditions at monmove.c:487, to me they look
> all like conditions when *not* to melee-attack.

Well no, otherwise the attack code at line 979 would be pointless and
there would be an "else" before the attack code further down. Rather, it
is conditions when a monster *might* not attack even though it's
adjacent to you. Stunned monsters will pretty much always attack you
regardless for example (although this may be a bug).

> So why is "(Conflict && !mtmp->iswiz)" in this list?

Either it is a bug as you assume, or it is intended that under conflict,
monsters might move instead of attack as I assume. In the absence of
documentation, only the developer knows which is the correct explanation.

> Thanks. You put me on the right track.

Glad you found the analysis helpful.

> [1] I hate having to use debuggers. If code is only understandable by
> using a debugger, it is clearly not fit for human consumption.

It is understandable without a debugger, just - I never used one.

> [2] Argh! Who spreads an if condition over 15 lines?

Hehe the use of an ifndef partially overlapping an if statement is
particularly egregious. I *hope* this was an automated merge...


rogerb

rogerb

unread,
Aug 3, 2008, 7:47:43 PM8/3/08
to
Kent Paul Dolan wrote:
> The right answer, of course, is to make the insides
> of the if(...) into a subroutine, and call it at
> that point.

Wouldn't really help much in this case as it's pretty much "if (var1 ||
var2 || var3 ...)". Changing this to "if ( func (var1, var2, var3,...))"
would further obfuscate matters.

> Since almost everything in NetHack is of
> global scope, this is pretty trivial to do.

Except in this case it's not. That is an exaggeration anyway.

> Now, of course, you lose code locality.

Which is actually pretty darn important to maintainability.

> Spagetti code doesn't unsnarl well.

No arguments there.

> This stuff is _so_ much easier in an OOPL.

You obviously haven't seen the "OO" code I have. Proper discipline
during design and development is the key to maintainable code, no matter
what the programming languages used in a project. Language features can
encourage or discourage this discipline and also affect the probability
and type of bugs of course, but you can produce both maintainable and
unmaintainable code in any language (I know I have in the languages I
know ;) ).

> How that "maintainable" could possibly apply to
> NetHack source code, unmaintainable and proud of it,
> is up to the reader to invent.

"ultra-legacy" would probably be a better description of NetHack code ;)

rogerb

sjde...@yahoo.com

unread,
Aug 4, 2008, 3:05:01 AM8/4/08
to
On Aug 3, 7:02 pm, rogerb <roger...@ymail.com> wrote:
> Patric Mueller wrote:
> > Whew!
>
> You got there in the end ;)
>
> rogerb <roger...@ymail.com> wrote:
> >> This looks like it could be a bug, but conflict is weird, so who knows
> >> except the DevTeam.
>
> > Maybe weird from the code's POV but to a player conflict is simple. :)
>
> > If the player gets attacked without conflict, he should
> > be attacked with conflict.
>
> Can you be so certain of the developers' intent?

As a 3rd party: if conflict makes monsters less likely to attack you
(without obvious extenuating circumstances) that seems pretty
obviously broken. I'm definitely willing to say that although I'm not
_certain_, I don't believe the developers intended this behavior--and
if they did, their intent isn't the best game behavior. I could be
persuaded otherwise, but it's certainly odd enough behavior (and the
code implementing it is not clearly intentional) that a comment in
support would be expected if it were intended. And I'd be pretty
surprised, frankly, if any of the devs posted saying they meant it to
happen.

Especially since there are known (devteam-admitted) bugs related to
lava/etc.

Patric Mueller

unread,
Aug 4, 2008, 1:36:30 PM8/4/08
to
Kent Paul Dolan <xant...@well.com> wrote:
> Patric Mueller wrote:
>
>> [2] Argh! Who spreads an if condition over 15 lines?
>
> Mostly people who've decided to write maintainable code,
> and thus want the nesting to be obvious from the
> indentation, and also wherever possible, want to reduce
^^^^^^^^^^^
I don't ...

> perceived complexity by writing a single meme per line.

^^^^^^^^^^^^^^^^^^^^^^
... think you are ...


> Whitespace is pretty much free, so good programmers use
> lots of it for lots of reasons.

^^^^^^^^^^
talking about the code in question:
http://nethack.wikia.com/wiki/Monmove.c#line477

I agree with you but exactly these points are violated in this section
of monmove.c



> How that "maintainable" could possibly apply to
> NetHack source code, unmaintainable and proud of it,
> is up to the reader to invent.

I wouldn't call it "unmaintainable and proud of it". It's a program
with an enormous size that has grown over 20 years.

My pet peeve with NetHacks code is that there are numerous places
where the programmer has foregone code readability in favor of a few
saved bytes.

It also lacks modularity. OOTB there are almost no source code files
that could be linked without the rest (e.g. for creating unit
tests [1]).


Bye
Patric

[1] Well, *I* didn't manage to do it.

For NetHack-De I wanted a way to reliably and automatically test the
wishing code.

I couldn't find a way to create a stripped down executable that would
only run the wishing code and verify that the correct objects would be
created.

I ended up just calling a function out of the regular game executable.

Patric Mueller

unread,
Aug 4, 2008, 1:55:53 PM8/4/08
to
rogerb <roge...@ymail.com> wrote:
> Patric Mueller wrote:
>> Whew!
>
> You got there in the end ;)
>
> rogerb <roge...@ymail.com> wrote:
>>> This looks like it could be a bug, but conflict is weird, so who knows
>>> except the DevTeam.
>>
>> Maybe weird from the code's POV but to a player conflict is simple. :)
>>
>> If the player gets attacked without conflict, he should
>> be attacked with conflict.
>
> Can you be so certain of the developers' intent? I know I'm not. Under
> conflict, monsters will never attack you unless they resist or there
> is no other target for them to hit, even if otherwise they would have
> attacked you, excepting as usual the WoY (mon.c line 618). Conflict is
> an enormous modifier to monster behaviour; in the absence of
> documentation, only the developers know what they intended.

I don't know if the original programmer anticipated such a situation.

But judging from the wording of C343-306, the current DevTeam
programmer should consider this a bug:

+----[C343-306]----
| Engulfer under influence of *conflict* or confusion can swallow a
| monster and not be affected by water/lava/trap under monster until
| next turn.
+----

An engulfer can engulf another monster that is on a non-safe location
if it is conflicted or confused and the wording indicates that not the
engulfing is the bug but instead that the engulfer doesn't immediately
suffer damage from the location.

Bye
Patric

Duncan Booth

unread,
Aug 7, 2008, 3:47:51 AM8/7/08
to
"sjde...@yahoo.com" <sjde...@yahoo.com> wrote:

>> > If the player gets attacked without conflict, he should
>> > be attacked with conflict.
>>
>> Can you be so certain of the developers' intent?
>
> As a 3rd party: if conflict makes monsters less likely to attack you
> (without obvious extenuating circumstances) that seems pretty
> obviously broken.

IMHO making monsters less likely to attack you is the main point of
conflict.

--
Duncan Booth http://kupuguy.blogspot.com

Doug Freyburger

unread,
Aug 7, 2008, 12:24:06 PM8/7/08
to
Duncan Booth <duncan.bo...@invalid.invalid> wrote:

> "sjdevn...@yahoo.com" <sjdevn...@yahoo.com> wrote:
>
> > As a 3rd party: if conflict makes monsters less likely to attack you
> > (without obvious extenuating circumstances) that seems pretty
> > obviously broken.
>
> IMHO making monsters less likely to attack you is the main point of
> conflict.

Here's how I see it -

Without conflict all hostile monsters make a beeline for you
and fight you until they die or you die. There is no team
work among attacking monsters to the point they will shoot
or zap through each other trying to get to you. Without
conflict peaceful monsters wander around.

With conflict all monsters, peaceful or hostile, will attack
anything adjacent to them. Hostile monsters without any
adjacent monster will continue to make a beeline towards
you. Peaceful monsters without any adjacent monsters
will continue to wander aimlessly.

Since some peaceful monsters are strong out of depth, the
earlier you are in the game the more dangerous conflict is
to you. But by the time you're high level and on crowded
levels, conflict's largest effect starts to be having monsters
combat each other instead of you. This seems like a well
calculated deliberate feature to me. It also seems to be a
standard of Nethack that early characters are in far more
danger than late characters.

In the mines conflict can cause the watch captain to kill
other watchmen, but it can also cause the watch captain
to kill you unless you are extremely carefull.

Conflict also has a dangerous effect on pets - Normally a
pet in combat retreats when it has lost half of its hit points.
What I've seen of conflict is pets continue combat without
retreating. I've lost several pets to watch captains because
of this apparent feature of combat. I'm not sure if this is
because peacefulls don't normally attack pets so normally
all a pet needs to do to heal is stop fighting or if it is
because the mine town tends to be close quarters to
retreat is not easy.

Kent Paul Dolan

unread,
Aug 8, 2008, 11:23:27 AM8/8/08
to
Duncan Booth wrote:
> "sjde...@yahoo.com" <sjde...@yahoo.com> wrote:
[attributions not included, grrr]

>>>> If the player gets attacked without conflict,
>>>> he should be attacked with conflict.

>>> Can you be so certain of the developers' intent?

>> As a 3rd party: if conflict makes monsters less
>> likely to attack you (without obvious extenuating
>> circumstances) that seems pretty obviously
>> broken.

> IMHO making monsters less likely to attack you is
> the main point of conflict.

I think the focus needs to be brought back to the
original situation, which is that the player
character is occupying by levitation (or by
appropriate boots?) a grid square _onto which the
monster could not normally move without dying_.

In a "game common sense" mode, it may well be the
developers' intention that monsters not be
deliberately suicidal [even though them attacking
the PC almost always results in them dying
_anyway_].

So, the code for monster action looks for a square
to which motion is possible in the sense of not
impeded by structures the monster cannot penetrate
or cannot survive entering, before deciding whether
to attack some monster discovered on one of those
safe to occupy squares, when conflict is in
operation.

In that regard, the existing code makes the monster
behave sanely from the perspective of the monster's
own desire to survive. Since there is other code
that causes monsters to flee when their hit points
become low, that is consistent with the design goal
"monsters try to survive".

The issue then would seem to be whether the monster
should be made smart enough to realize that it won't
move onto the grid square lethal to it while the PC
is occupying that square, and if it kills the PC,
the game will be won anyway, so that attacking the
PC while the PC is in that square is entirely safe.

This, I think, would be giving the monster a
"meta-knowledge" of the game, and so is probably
inconsistent with the current game design.

Thus my vote is that the current conflict-side code
is operating correctly and its operation should
stand unchanged.

The question to me is whether the non-conflict code
should be changed so that monsters behave as they
do with respect to monster-lethal squares the PC
occupies in the conflict code, making hovering over
water or lava a refuge for the PC against melee by
all but swimming or fireproof monsters,
respectively.

Comments?

xanthian.


Janis Papanagnou

unread,
Aug 9, 2008, 8:23:50 AM8/9/08
to

Assumed I understand you correct, I disagree that this
is meta-knowledge. It would be meta-knowledge if there
is a "hit-and-move-if-the-square-occupier-dies" game
mechanics; but there isn't, the hit-deadly and the move
onto the square are strictly disjunct separate turns
(for the player - and I don't assume that this has to
be considered different - in theory - for the monster).

Currently I'd assume that monsters stab, hit, or bite
in the direction of the player while standing on a
safe spot, independent of whether the attack target is
located on any "unsafe" spot.

Conflicting is no special case, IMO; a monster should
(randomly or not) attack any adjacent monster whether
they're standing on a non-accessible spot or not.

NB: Consider also that it is possible for the player
to hit a xorn that is embedded in a wall.

> Thus my vote is that the current conflict-side code
> is operating correctly and its operation should
> stand unchanged.
>
> The question to me is whether the non-conflict code
> should be changed so that monsters behave as they
> do with respect to monster-lethal squares the PC
> occupies in the conflict code, making hovering over
> water or lava a refuge for the PC against melee by
> all but swimming or fireproof monsters,
> respectively.
>
> Comments?

(See above.)

Janis

Reply all
Reply to author
Forward
0 new messages