Commit Graph

249 Commits

Author SHA1 Message Date
PatR
95dece1a2b object splitting bit
Something else noticed while hunting for the revive() panic.  Like
the zap.c change (56b791f7cc), doesn't fix that or impact its
eventual fix.
2022-06-10 10:14:06 -07:00
PatR
62bc0c846c glob rot - replace misspelled dissipates with \
dissolves

"The glob of <type> dissippates completely" was misspelled.  Instead
of just fixing the spelling, switch to a different term since
dissipate is also being used for gas clouds and globs aren't gaseous.
2022-04-01 10:47:25 -07:00
PatR
77dc522a62 artifact tracking again
Redo the recent artifact creation stuff by replacing several nearly
identical routines with one more general one.  Also adds a tracking
bit for one or two more creation methods.  That changed artiexist[]
from an array of structs holding 8 or less bits to one holding 9, so
bump EDITLEVEL in case the total size changed.
2022-03-12 17:25:54 -08:00
Pasi Kallinen
ea52bacfce Unhide monster hiding under shrinking glob 2022-03-11 17:34:26 +02:00
PatR
d37fa4138a found_artifact() groundwork
Lay groundwork for generating a log event when finding an artifact
on the floor or carried by a monster.  This part should not produce
any change in behavior.

Move g.artidisco[] and g.artiexist[] out of the instance_globals
struct back to local within artifact.c.  They are both initialized
at the start of a game (and only used in that file) so don't need
to be part of any bulk reinitialization if restart-instead-of-exit
ever gets implemented.

Convert artiexist[] from an array of booleans to an array of structs
containing a pair of bitfields.  artiexist[].exists is a direct
replacement for the boolean; artiexist[].found is new but not put to
any significant use yet.  If will be used to suppress the future
found-an-artifact event for cases where a more specific event (like
crowning or divine gift as #offer reward) is already produced.

Remove g.via_naming altogether and add an extra argument to oname()
calls to replace it.

Add an extra argument to artifact_exists() calls.
2022-03-07 02:06:55 -08:00
SHIRAKATA Kentaro
757139476f move validations before their first use 2022-02-25 22:41:21 -08:00
PatR
1abf723f03 place_object() bit: panic/impossible decl 2022-02-24 18:15:48 -08:00
Michael Meyer
f5e3bc3d96 Remove gendered mons indices from roles, races
There are no longer distinct gendered versions of monsters, so femalenum
is unused (i.e. set to NON_PM) for all roles and races. Take a pass at
removing all uses of/references to femalenum, and rename 'malenum' to
'mnum' since it no longer has any particular association with
gender or sex.
2022-01-14 13:51:26 -08:00
nhmall
e4e65c4b8b whitelist several non-literal format strings
djgpp cross-compiler was griping about several.

This also removes these lines from sys/unix/hints/include/compiler.370.
    CFLAGS+=-Wno-format-nonliteral
    CCXXFLAGS+=-Wno-format-nonliteral

-Wformat-nonliteral should not be incompatible with the printf
argument-checking capabilities on literal format strings and there
shouldn't be any new warnings created.

-- &< --

artifact.c: In function 'artifact_hit':
artifact.c:1309:23: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1309 |                       mon_nam(mdef));
      |                       ^~~~~~~
artifact.c:1328:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1328 |                 pline(behead_msg[rn2(SIZE(behead_msg))], wepdesc, "you");
      |                 ^~~~~

ball.c: In function 'drop_ball':
ball.c:896:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  896 |                 pline(pullmsg, "pit");
      |                 ^~~~~
ball.c:899:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  899 |                 pline(pullmsg, "web");
      |                 ^~~~~
ball.c:904:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  904 |                 pline(pullmsg, hliquid("lava"));
      |                 ^~~~~
ball.c:908:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  908 |                 pline(pullmsg, "bear trap");
      |                 ^~~~~

dig.c: In function 'liquid_flow':
dig.c:747:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  747 |         pline(fillmsg, hliquid(typ == LAVAPOOL ? "lava" : "water"));
      |         ^~~~~

fountain.c: In function 'floating_above':
fountain.c:28:5: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
   28 |     You(umsg, what);
      |     ^~~

invent.c: In function 'hold_another_object':
invent.c:1018:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1018 |                 pline(drop_fmt, drop_arg);
      |                 ^~~~~
invent.c:1073:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1073 |         pline(drop_fmt, drop_arg);
      |         ^~~~~
invent.c: In function 'silly_thing':
invent.c:1811:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1811 |         pline(silly_thing_to, word);
      |         ^~~~~

lock.c: In function 'pick_lock':
lock.c:375:19: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  375 |             pline(no_longer, "hold the", what);
      |                   ^~~~~~~~~
lock.c:379:19: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  379 |             pline(no_longer, "reach the", "lock");
      |                   ^~~~~~~~~
lock.c: In function 'pick_lock':
lock.c:375:19: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  375 |             pline(no_longer, "hold the", what);
      |                   ^~~~~~~~~
lock.c:379:19: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  379 |             pline(no_longer, "reach the", "lock");
      |                   ^~~~~~~~~
mcastu.c: In function 'cast_cleric_spell':
mcastu.c:670:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  670 |             pline(fmt, Monnam(mtmp), what);
      |             ^~~~~

mhitu.c: In function 'hitmsg':
mhitu.c:68:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
   68 |             pline(pfmt, Monst_name);
      |             ^~~~~

mkobj.c: In function 'insane_object':
mkobj.c:2848:20: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2848 |         impossible(altfmt, mesg, fmt_ptr((genericptr_t) obj), where_name(obj),
      |                    ^~~~~~
mkobj.c:2852:20: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2852 |                    objnm);
      |                    ^~~~~

mon.c: In function 'mon_givit':
mon.c:1469:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1469 |         pline(msg, Monnam(mtmp));
      |         ^~~~~
mon.c: In function 'mondead':
mon.c:2485:33: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2485 |                                 | SUPPRESS_INVISIBLE), FALSE));
      |                                 ^

muse.c: In function 'mon_reflects':
muse.c:2438:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2438 |             pline(str, s_suffix(mon_nam(mon)), "shield");
      |             ^~~~~
muse.c:2445:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2445 |             pline(str, s_suffix(mon_nam(mon)), "weapon");
      |             ^~~~~
muse.c:2450:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2450 |             pline(str, s_suffix(mon_nam(mon)), "amulet");
      |             ^~~~~
muse.c:2458:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2458 |             pline(str, s_suffix(mon_nam(mon)), "armor");
      |             ^~~~~
muse.c:2464:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2464 |             pline(str, s_suffix(mon_nam(mon)), "scales");
      |             ^~~~~
muse.c: In function 'ureflects':
muse.c:2476:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2476 |             pline(fmt, str, "shield");
      |             ^~~~~
muse.c:2483:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2483 |             pline(fmt, str, "weapon");
      |             ^~~~~
muse.c:2487:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2487 |             pline(fmt, str, "medallion");
      |             ^~~~~
muse.c:2493:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2493 |             pline(fmt, str, uskin ? "luster" : "armor");
      |             ^~~~~
muse.c:2497:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2497 |             pline(fmt, str, "scales");
      |             ^~~~~

polyself.c: In function 'polyman':
polyself.c:201:5: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  201 |     urgent_pline(fmt, arg);
      |     ^~~~~~~~~~~~

potion.c: In function 'make_hallucinated':
potion.c:423:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  423 |             pline(message, verb);
      |             ^~~~~
potion.c: In function 'peffect_gain_level':
potion.c:1033:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1033 |                 You(riseup, ceiling(u.ux, u.uy));
      |                 ^~~
potion.c:1044:21: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1044 |                     You(riseup, ceiling(u.ux, u.uy));
      |                     ^~~

priest.c: In function 'intemple':
priest.c:487:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  487 |                 You(msg1, msg2);
      |                 ^~~

read.c: In function 'doread':
read.c:522:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  522 |         pline(silly_thing_to, "read");
      |         ^~~~~

shk.c: In function 'shk_names_obj':
shk.c:2576:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2576 |         pline(fmtbuf, obj_name, (obj->quan > 1L) ? "them" : "it", amt,
      |               ^~~~~~
shk.c:2579:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2579 |         You(fmt, obj_name, amt, plur(amt), arg);
      |         ^~~
shk.c: In function 'shk_chat':
shk.c:4506:13: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 4506 |             pline(Izchak_speaks[rn2(SIZE(Izchak_speaks))], shkname(shkp));
      |             ^~~~~
shk.c: In function 'check_unpaid_usage':
shk.c:4633:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 4633 |         verbalize(fmt, arg1, arg2, tmp, currency(tmp));
      |         ^~~~~~~~~

sounds.c: In function 'dosounds':
sounds.c:66:21: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
   66 |                     pline(throne_msg[2], uhis());
      |                     ^~~~~
sounds.c:259:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  259 |                 You_hear(msg, halu_gname(EPRI(mtmp)->shralign));
      |                 ^~~~~~~~

timeout.c: In function 'choke_dialogue':
timeout.c:269:26: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  269 |                          body_part(NECK));
      |                          ^~~~~~~~~
timeout.c:274:17: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  274 |                 urgent_pline(str, hcolor(NH_BLUE));
      |                 ^~~~~~~~~~~~
timeout.c: In function 'levitation_dialogue':
timeout.c:339:26: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  339 |                          danger ? surface(u.ux, u.uy) : "air");
      |                          ^~~~~~
timeout.c: In function 'slime_dialogue':
timeout.c:379:34: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  379 |                     urgent_pline(buf, hcolor(NH_GREEN));
      |                                  ^~~
timeout.c:381:30: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
  381 |                 urgent_pline(buf, an(Hallucination ? rndmonnam(NULL)
      |                              ^~~

uhitm.c: In function 'hmon_hitmon':
uhitm.c:1398:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1398 |         pline(fmt, whom);
      |         ^~~~~
uhitm.c:1421:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1421 |         pline(fmt, whom);
      |         ^~~~~
uhitm.c: In function 'stumble_onto_mimic':
uhitm.c:5301:9: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 5301 |         pline(fmt, what);
      |         ^~~~~

../win/tty/wintty.c: In function 'tty_clear_nhwindow':
../win/tty/wintty.c:1649:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 1649 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
../win/tty/wintty.c: In function 'tty_display_nhwindow':
../win/tty/wintty.c:2339:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2339 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
../win/tty/wintty.c: In function 'tty_dismiss_nhwindow':
../win/tty/wintty.c:2432:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2432 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
../win/tty/wintty.c: In function 'tty_destroy_nhwindow':
../win/tty/wintty.c:2477:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2477 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
../win/tty/wintty.c: In function 'tty_curs':
../win/tty/wintty.c:2503:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2503 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
../win/tty/wintty.c: In function 'tty_putsym':
../win/tty/wintty.c:2599:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2599 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
../win/tty/wintty.c: In function 'tty_add_menu':
../win/tty/wintty.c:2967:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 2967 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
../win/tty/wintty.c: In function 'tty_end_menu':
../win/tty/wintty.c:3032:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 3032 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
../win/tty/wintty.c: In function 'tty_select_menu':
../win/tty/wintty.c:3140:15: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
 3140 |         panic(winpanicstr, window);
      |               ^~~~~~~~~~~
2022-01-09 14:18:10 -05:00
PatR
d0197bd3e7 document obj->owornmask
ship_object() and obj_delivery() use obj->owornmask for something
other than tracking what slot(s) an object is worn in.
2021-12-14 05:49:31 -08:00
PatR
1fd379a980 glob update
Initialize glob weight sooner in mksobj() and have weight() skip the
fix up it had been doing when it was passed a glob with weight 0.
This allows shrink_glob() to be simplified slightly in the situation
where a glob inside a container shrinks away to nothing.

weight(glob) with glob->owt==0 now yields 0 instead of 20.  That was
only needed for 'obj->owt=weight(obj)' during object creation and
the new earlier weight init makes the 0 to 20 fixup obsolete.  In
turn, that allows a glob which has shrunk to 0 while in a container
to not have to be removed before updating the container's weight.
2021-12-13 07:04:30 -08:00
PatR
e25f8ec6e3 endless shrink_glob...
A shrink_glob timer keeps running for a glob on the migrating objects
list so shink_glob() needs to be aware of that.
2021-12-11 18:39:36 -08:00
PatR
ba255875cd shrink_glob bug when when glob is totally gone
When a wielded glob shrank away to nothing, an impossible warning:
"obfree: deleting worn obj" would be issued.

If a glob is quivered or wielded or set up as swap weapon when it
shrinks away to nothing, clear the relevant weapon slot before
destroying the glob.

Won't happen for monsters since they never wield globs.  Also won't
happen for migrating objects (which overload obj->owornmask) because
they have to have arrived somewhere in order to have their shrink
timer execute.
2021-12-10 20:16:30 -08:00
PatR
59aedc3b37 yet another shrink_glob fix...
When catching up for lost time spent on another level, globs inside
containers that shrank away to nothing didn't need to have those
containers' weight explicitly adjusted because obj_extract_self()
does that, so yesterday I removed the unneeded container_weight()
call.  However, ones that shrank only partially did need to have
their containers' weights adjusted and that wasn't being done.
The weight would be brought up to date within 25 or so turns when
the contained glob's next shrink_glob event took place.  Until then
attempting to pick up the container by hero or monster, or to pick
up something else by a monster already carrying it, could have been
impacted by the weight discrepancy.
2021-12-01 17:05:27 -08:00
PatR
35ac9a09e2 miscellaneous tweaks
Simplify a glob handling bit in a recent shrink_glob change used when
catching for lost time upon returning to a level.

Revise a clumsily worded fixes entry.

Fix a comment typo in makedefs that's been there for a bunch of years
now.  It's been within the diff context for several recent patches
and I still hadn't noticed it until just now.
2021-11-30 14:04:00 -08:00
PatR
e88126cf6f format issues
Fix a couple of things that prototyping pline() with FORMAT_F(1,2)
pointed out.  The mkobj.c one looks familiar; I thought it had
already been fixed.  Maybe it matches a pull request that hasn't
been incorporated yet.
2021-11-26 21:52:38 -08:00
SHIRAKATA Kentaro
546377581a use %d for short
obj->otyp is short, so corresponding format string is %d.

(from Coverity Scan)
2021-11-24 06:30:00 +09:00
PatR
a94650159e shrink_glob nitpicking
For a glob in a container carried by the hero, shrinking away to
nothing would have indirectly updated the container's weight when
obj_extract_self() was called, then the 'old_top_owt' value would
never be different from current topcontnr->owt.  That only matters
for the shrink-but-not-gone case and could only happen for the gone
case so didn't result in anything noticeably wrong.  But fix it to
match the comment about weight not being adjusted yet.
2021-11-13 23:45:47 -08:00
PatR
05a843826d shrink_glob while away from level
If the hero left a level that had globs on the floor or in floor
containers or being carried by monsters and stayed away for a
while, returning to the level only shrunk them by one unit of
weight.  Account for all the time away.  The complexity of this
has steadily grown; I hope its peak has been reached.
2021-11-12 23:05:55 -08:00
PatR
41ede41b8b shrink_glob feedback
When carrying a glob, possibly inside a container, give shrink
feedback more often (twice in the ~500 turn cycle to shrink from
20aum to 0aum rather than just once).
2021-11-11 16:16:13 -08:00
PatR
90aded3648 weight of gold
I had wizweight On when testing glob changes and noticed
|Slasher drops a gold piece (0 aum).

Coins are supposed to weigh 1/100 of a unit, and the calculation
rounds rather than simply truncates any fraction, but that still
yielded 0 for quantities of 1..49.  Force any non-zero stack of
gold to weigh at least 1 unit.

Also, add a check for attempting to weigh a quantity 0 or less
(of anything, not only for gold) just in case.
2021-11-07 13:27:44 -08:00
PatR
e37ab5f0f9 decaying glob tweaks
Don't hardcode the weight (20) of an unaugmented glob, use
objects[].oc_weight (also 20) instead.

When a glob inside a container has decayed all the way to nothing
(weight 0), take it out before updating the container's weight.
Otherwise weight() would use objects[].oc_weight instead of 0 for
that glob.
2021-11-07 02:00:46 -08:00
PatR
e2ca016484 decaying globs of {ooze,pudding,slime}
Globs never rotted away but did become tainted after a relatively
short while, which seemed like a contradiction.  Change them to never
be tainted but shrink by 1 unit of weight approximately every 25
turns.  An ordinary glob (one that hasn't combined with any others)
starts out weighing 20 units, so it takes about 500 turns to vanish.
That's roughly twice as long as a corpse takes to rot away.

Shrinking globs give feedback when in hero's invent or in a container
in hero's inventory, but rarely (when going from an exact multiple
of 20 weight units; that is, from integral number of N globs to
N-1 + 19/20, or if weight reduction triggers an encumbrance change).
When a glob goes away completely, there is feedback for those two
circumstances and also for seeing the glob vanish from the floor.

I haven't touched how much nutrition eating a glob confers.  I have
changed formatting of glob names to use "small", "medium", "large",
"very large" instead of "small", [no adjective], "large", &c.  You
still need to have at least five globs coalesced together for the
adjective to become "medium", same amount as before.

I don't think EDITLEVEL needs to be modified but have incremented it
anyway to play things safe.
2021-11-06 18:24:36 -07:00
Michael Meyer
2abe08d0bf Formally block artifacts as random box contents
Another (latent) case of an artifact possibly being generated and
immediately deleted: part of the process of a mimic disguising itself as
an item involves generating a random object, then deleting it.  If this
item is a box or sack, it will generate with random contents, which will
be deleted along with the container. If artifacts are allowed as random
box contents, this can silently remove an artifact from being available
in the game.

This is effectively blocked already, since none of the artifacts
eligible for random generation are items from classes marked as valid
box contents (see boxiprobs[] in mkobj.c).  Nonetheless, formally
preventing artifacts from generating as box contents will guarantee this
issue won't crop up if a randomly generated artifact tool, ring, etc, is
added in the future.
2021-10-26 11:55:00 -07:00
nhmall
39713783d1 some trailing whitespace in src, include 2021-10-16 12:12:21 -04:00
PatR
b29168aa43 fix the new boulder->next_boulder sanity check 2021-10-10 13:14:01 -07:00
PatR
708c2a4346 boulder sanity checking
Three new checks:
 1) boulders are expected to be at the top of their piles, or when
    not on top, only other boulders are above them;
 2) boulders shouldn't be located at water or lava spots;
 3) verify that boulders don't have their 'next_boulder' flag still
    set at times when sanity checks take place; that's only valid
    during moverock() [plus its calls to boulder_hits_pool()].
2021-10-10 12:59:17 -07:00
PatR
b700e3abf9 "new boulder" fix
The default value for obj->corpsenm is NON_PM which is -1, so the
default value of boulder->next_boulder was non-zero instead of 0
as expected.  Because of that, boulder object formatting by xname()
was yielding "next boulder" when plain "boulder" was intended.
Until the boulder or one in a pile above it got pushed, then it
was explicitly reset.
2021-10-10 12:39:27 -07:00
PatR
b1d76a772f fix github issue #596 - wishing exploit
for helm of opposite alignment.

Discovered and described by vultur-cadens.

The #adjust command can be used to split an object stack and if the
shop price of the two halves are different, the new stack will have
its obj->o_id modified to make the prices the same.  That could be
used to tip off the player as to what the low bits of the next o_id
will be.  Since no time passes, no intervening activity such as
random creation of a new monster can take place, so the player could
wish for something that depends on o_id with some degree of control.
Matters mainly for helms of opposite alignment intended to be used
by neutral characters since the player isn't supposed to be able to
control that.  (Other items like T-shirt slogan text and candy bar
wrapper text had a similar issue but controlling those wouldn't have
had any tangible difference on play.)

The issue writeup suggested allowing the player to specify a helm's
alignment during a wish.  That would defeat the purpose of having
o_id affect the helm's behavior in an arbitrary but repeatable way
so is rejected.

I implemented this fix before seeing a followup comment that suggests
using a more sophisticated decision than 'obj->o_id % N' for the
arbitrary effect.  This just increments context.ident for the next
obj->o_id or mon->m_id by 1 or 2 instead of always by 1 and should
be adequate.  It also has the side-effect that two consecutive wishes
for helm of opposite alignment won't necessary give one for each of
the two possible 'polarities', even with no intervening activity by
monsters, reinforcing the lack of player control.

Minor bonus fix:  it moves the incrementing check for wrap-to-0 into
a single place instead of replicating that half a dozen times.  Ones
that should have been there for shop billing and for objects loaded
from bones files were missing.

Fixes #596
2021-09-30 13:08:27 -07:00
Pasi Kallinen
b30061b5ad Allow dropping just picked up items
When using a menu to drop or put in items into a container,
allow putting in the item (or items) you picked up previously,
by selecting the 'P' entry from the item class menu

Inspired by the itemcat patch by Stanislav Traykov.

Invalidates saves and bones.
2021-09-17 21:00:06 +03:00
copperwater
f855fb5e45 Remove g.monstermoves
It's redundant with g.moves, so there is no more need for it.

Way, way back, it looks like g.moves and g.monstermoves can and did
desync, where g.moves would track the amount of moves the player had
gotten (and would therefore increase faster if the player were hasted)
and g.monstermoves would track the amount of monster move cycles, aka
turns. But this has not been the case for a long time, and they both
increment together in the same location in allmain.c. There are no
longer any cases where they will not be the same value.

This is a save-breaking change because it changes struct
instance_globals, but I have not updated the editlevel in this commit.
2021-08-28 16:22:38 -07:00
copperwater
0e05c94400 Remove requirement of object probs adding to 1000
When discussing the recent commit that removed makedefs -o from the
build process, nhmall pointed out that a sanity check ensuring all
objects within one class add up to 1000 probability had been removed as
well. This requirement was a perennial thorn in the side for anyone
doing anything that touches object probabilities, because allocating
probability to something meant deciding what to take it away from,
without a good way to evenly distribute that across all the other
members of the object class.

I had gotten around this in xNetHack by removing the sanity check and
making mkobj() total up the probability within an object class and then
using that instead of 1000. This commit takes a similar approach, but
instead of inefficiently recalculating the sum every time mkobj() is
called, it instead computes it at the start of the game or when
restoring the save file and stores it in a global variable.

This fixes a slight bias problem with rings - they are all supposed to
be of equal probability, but there are 28 of them and 1000 is not evenly
divisible by that, so the old formula made the later rings slightly more
likely. Now instead of a 35/1000 or 36/1000 chance, they are all
uniformly 1/28. (Internally they have a oc_prob of 1 now, not 0).

Gems are also weird, because their oc_prob values change every level.
This ought to have still worked without a change, because the arcane
formula for assigning the probabilities would still end up with them
adding to 1000. But I added in code to reset the total gem probability
anyway; this may help make the formula less arcane in the future.

There is still a sanity check against object classes having a nonzero
number of objects but zero total probability, in which case an
impossible will be thrown and every member of the class will be given
equal probability. I also downgraded the "probtype error" panic in
mkobj() to an impossible because it has a reasonable failure case -
return the first item in that class.
2021-08-28 17:03:39 +03:00
PatR
6d291e7718 sanity check false positive for obj->nomerge
The luckstone in the Mines and the amulet of reflection or bag of
holding in Sokoban have their 'nomerge' bit set until they make
it into the hero's inventory.  So don't complain about them when
sanity_check is enabled.
2021-08-19 13:08:22 -07:00
PatR
f74121d1ce extended object sanity checking
When sanity checking is enabled, check objects for bits used as
temporary flags that should always be cleared by the time that a
sanity check pass gets made:  o.in_use, o.bypass, and o.nomerge.

Also, fix glob checking.  It was unintentionally placed within
the braces of ``if (obj->owornmask) { ... }'' so didn't actually
check globs except for the unlikely case when wielded in one of
the uwep/uswapwep/uquiver slots.
2021-08-18 17:17:14 -07:00
PatR
5a09a01a13 gold dragon and scales
Add two new monsters and two new objects:
 gold dragon
 baby gold dragon
 gold dragon scale mail
 set of gold dragon scales

A couple of variants seem to have added these already, but this came
off my ancient list of monsters to add and was done from scratch.
It's a clone of silver dragon, but instead of having reflection and
breathing cold, a gold dragon emits light and breathes fire; because
of the latter it can be seen with infravision like a red dragon.
Adult gold dragons are lawful as in the AD&D Monster Manual rather
than chaotic as the wiki pages show for the variant versions.

Worn gold dragon scales operate similar to wielded Sunsword:  when
blessed, radius is 3 (same as a lamp), if uncursed, radius is 2, and
if cursed, radius is 1 (but functions as 2 when worn by the hero,
otherwise there would be no tangible effect).  Gold dragon scale mail
gets an extra +1, making blessed gold DSM have a bigger radius than
lamps.  Embedded scales have radius 1 regardless of BUC state; light
for that case comes from the gold dragon monster form the hero is in.
When not worn, gold scales and scale-mail don't emit any light.

The tiles use a mix of yellow (for gold) and red.  The two object
tiles seem reasonable variations of the corresponding silver dragon
ones.  The two monster tiles definitely need work since the silver
ones were mostly cyan and changing that to red did not produce very
good result; subsequent attempt at a mixture was haphazard at best.
2021-07-23 10:41:57 -07:00
PatR
0b5a112b0c gender for figurines
Use (obj->spe & CORPSTAT_GENDER) for figurines as well as for
statues and corpses.

Support wishing for
 "{female,male,neuter} {corpse,statue,figurine} [of <monster>]".
and
 "{female,male,neuter} <monster> {corpse,statue,figurine}".
Also
 "{corpse,statue,figurine} of {female,male,neuter} <monster>"
where the qualifier might be in the middle instead of a prefix.
2021-06-17 16:03:45 -07:00
PatR
04a8ddcce1 fix github issue #531 - genderless corpses
Dead monsters that had traits saved with the corpse would revive as
the same gender, but ordinary corpses revived with random gender so
could be different from before they got killed.

Since corpses of monsters lacked gender, those for monsters with
gender-specific names were described by the neuter name.

This is a fairly big change for a fairly minor problem and needs a
lot more testing.

Fixes #531
2021-06-08 03:43:46 -07:00
PatR
dae25564bc more object->dknown handling
Fix a couple of places that set obj->dknown to 0 to deal with some
special cases where it should be left at 1.  (Other places do that
but deal with potions where hardcoded 0 remains appropriate.)
2021-05-13 09:38:53 -07:00
PatR
b630d5e038 fix issue #509 - shop 'glob' pricing segfault
A recent change made it possible for a glob to have its dknown flag
cleared and that exposed globby_bill_fixup() passing Null shopkeeper
to get_cost(), triggering a crash.

Make the routine that clears dknown/known/bknown/&c also be the
routine used to initialize those flags for a new object so that it
is the place that handles various special cases.  That hides the
shop bug again.

But also fix the shop bug even though it won't be triggered.

Fixes #509
2021-05-12 16:13:40 -07:00
PatR
c0c7190f74 fix #K3317 - warning when eating corpse
The report was misleading because the warning about partly eaten
food being more nutritious than untouched food was actually given
when the partly eaten corpse was used to calculate hit points of
the new monster as the corpse was reviving as a zombie, rather
than when a bite was taken from it.  Pull request #497 had correct
analysis and a fix, although I've put the fix in a different place.

Closes #497
2021-05-01 18:36:46 -07:00
PatR
328dc5bdfa github issue #475 revisited - Trollsbane
Change Trollsbane versus troll corpse revival:  instead of revival
failing if Trollsbane is wielded at time of revival attempt, mark
the corpse no-revive if killed by Trollsbane (whether by the hero
or a monster).

If a no-revive corpse is within view when time to revive occurs,
give "the troll corpse twitches feebly" even when the hero isn't
responsible.  That used to only apply if the hero zapped the
corpse with undead turning, which would have become inoperative
because now being zapped by undead turning clears the no-revive
flag and revives as normal.  In other words, undead turning magic
overrides killed-by-Trollsbane or non-ice troll having been in an
ice box.
2021-04-02 10:38:57 -07:00
PatR
6c22520b1a fix pull request #479 - statues of stoning-immunes
Statues on Medusa's level are supposed to be from petrified creatures
rather than somebody's artwork, so creatures that can't be turned to
stone aren't eligible.  However, creatures who change form when hit
with stoning damage (foo golems to stone golem) were being allowed.
Also, statues in cockatrice nest rooms are supposed to be from former
characters and take their names from the high scores file.  But when
'record' is empty, the statue would be of a random creature instead
of being changed into a player character, so both not the latter and
possibly something that can't be petrified.

I've taken the Medusa part as-is but did the cockatrice nest part
differently.  It rejected statues of non-stonable creatures in case
the named character attempt failed.  I've changed things so that when
a named player character can't be created, it will use an unnamed one
instead of random creature.  The issue of maybe ending up with a non-
stonable form goes away because all player characters are vulnerable.

Fixes #479
2021-04-01 12:12:05 -07:00
copperwater
ee7664684a Unify code for extracting an object from a monster's inventory
The code for doing this (basically an obj_extract_self() call plus
handling if the object was worn or wielded) was duplicated all over, and
inconsistent - for instance, though all of them updated the monster's
misc_worn_check to indicate it was no longer wearing something in
whatever slot, only one call also set the bit that flags the monster to
consider putting on other gear afterwards.

Under a new function, extract_from_minvent, all this extra handling is
checked in one function, which can simply replace the obj_extract_self
call.

A few callers (such as stealing) have some common code *after* the
object is extracted and some other things happen such as message
printing, such as calling mselftouch if the object was worn
gloves. extract_from_minvent does not handle these cases.
2021-01-29 19:19:03 -05:00
nhmall
f963c5aca7 switch source tree from k&r to c99 2021-01-26 21:06:16 -05:00
Pasi Kallinen
4729062846 Make Death revive faster
Death will revive faster than the other riders.
Make all the riders revive after 67 turns, instead of 500.
There was practically a zero chance a rider would revive at 500,
so keep it somewhat sensible.
2020-12-13 12:28:45 +02:00
Pasi Kallinen
d79b5388ce Remove dealloc_obj b&c sanity checking 2020-12-01 22:18:48 +02:00
PatR
2db51cf8bd fix #K3016 - kicking a bag of gold in a shop
Kicking a container that had gold in it took the gold amount
away from hero's credit or added to hero's debt, then didn't
give a refund if the container and its gold landed within the
shop.  Throwing behaved likewise, just less verbosely.

The problem is caused by addtobill() treating gold specially
and then subfrombill() not being able to perform a reverse
operation.  Actually, it may be possible for subfrombill() to
do that, but verifying all its uses is too much work.  This
moves the gold handling for drop+selling into its own routine
and adds calls to that for the throwing and kicking refunds.
The other calls to subfrombill() outside of shk.c appear to be
ok as-is.  (The calls inside that file are the ones that still
need evaluation if the gold handling is to move to there.)

bill_dummy_object() now uses the same o_id assignment for its
dummy object as split_object() does for its new partial stack.
I don't know whether the old code led to any price glitches.
2020-11-25 14:33:14 -08:00
Pasi Kallinen
229930e505 Fixes and sanity checks for monster undetected and trapped states
Adds sanity checks for mtrapped and mundetected states.

Fixes cases where those were left in wrong state.

1. Trapped monster (eg. a nymph) teleported out of a trap
2. Monster was hiding under ball or chain, which then got removed
3. While restoring a level, a zombie corpse revived while monster
   was hiding under it
4. A general case where the only object was deleted off floor and
   a monster was hiding under it

Monsters hiding under ball or chain will now get revealed when
the b or c are moved.
2020-11-24 19:37:43 +02:00
Pasi Kallinen
6ec55a3624 Rework stairs structure
Use a linked list to store stair and ladder information, instead
of having fixed up/down stairs/ladders and a single "special" (branch)
stair.

Breaks saves and bones.

Adds information to migrating objects and monsters for the dungeon
and level where they are migrating from.
2020-11-13 20:27:17 +02:00
Pasi Kallinen
aeb0ea65e3 Mild Zombie Apocalypse
When a zombie (or lich) kills a monster in melee without a weapon,
the monster can rise few turns later as a zombie.

The only creatures that can be zombified are ones that actually have
a zombie counterpart monster. A zombie cannot turn a jackal into
a zombie, for instance. But it could turn a shopkeeper into a human
zombie, or a dwarf king into a dwarf zombie.

Zombies will fight with monsters that can be turned into zombies.

Originally this was a SliceHack feature, but this is based on xNetHack
version of it, with some modifications.
2020-10-23 19:47:10 +03:00