doengrave was the largest function by far, so split it up into smaller
ones. Use a structure to hold the dozens of variables needed all over it.
Should have no difference in behaviour, but I haven't tested everything.
cg.zeroobj was originally added (under its previous unprefixed name)
for providing a one-line way to zero out the fields of a struct obj.
struct obj tempobj;
tempobj = cg.zeroobj;
initfn(struct obj *otmp)
{
if (otmp)
*otmp = cg.zeroobj;
}
More recently, the address of cg.zeroobj began to be used as a return
flag to indicate some things, but the 'const struct obj zeroobj' wasn't
an ideal fit for the purpose and required a number of casts, including
casting away const.
Provide a better fitting variable (gi.invalid_obj) and eliminate a
number of casts.
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.
Engraving in an empty doorway and then using locking magic to create
a door there resulted in an impossible warning: "engraving sanity:
illegal surface (23)" if the 'sanity_check' option was On (wizard
mode only). Engraving in an open doorway and then simply closing
the existing door produced the same effect.
Accept engravings at closed doors. Presumably hero will be using
Passes_walls to attempt that so treat closed doors same as open ones.
Update the engraving sanity check to deal with that.
Bonus fix: engraving sanity checking stopped after the first problem
instead of checking every engraving. Have it continue instead.
Not fixed: vault wall repair and temporary corridor removal does
not delete engravings and can trigger the illegal surface warning if
player engraves before the repairs. I didn't test shop wall repair
but it doesn't have any engr references so probably has the same bug.
relocate surface(), ceiling(), and avoid_ceiling() to dungeon.c
adjacent to has_ceiling() etc.
astral and fire, like airlevel and waterlevel return FALSE
for has_ceiling()
if a caller does happen to call ceiling() on fire level,
return "flames above"
if a caller does happen to call ceiling() on quest level,
return a more-generic "expanse above", instead of the
word "ceiling"
add "stairs" return to surface()
remove recent update to engrave.c to special-case "stairs"
since surface() will return that now
1. Add "engraved room floor" pchar sym (S_engroom). The symbol that
displays at the engraved part of a room (not a corridor though).
The default symbol is '`' which is currently never shown if people
have defined the boulder symbol to '0' and statues are displayed as
monster symbols. It is bright blue.
Add some stylized variations of the S_engroom symset to some of
the symsets.
2. Add "engraved corridor" pchar sym (S_engrcorr). The symbol that
displays at the engraved part of a corridor. The default symbol is
'#', and it matches the symbol for corridor from for whatever the
current symset uses. It is bright blue to match the color of the
S_engroom symbol. Using the normal corridor symbol for display
preserves the lines of the corridor so is not as visually-disruptive
as a smaller symbol would be. Explicit entries that match the S_corr
symbol have been added to the symset file.
Magic mapping and clairvoyance impacts yet to be determined.
The Guidebook updates will come later.
Pull request from entrez: if bones left dead hero's corpse on top
of a new grave, don't find a corpse or summon a zombie when digging
the grave up. It also removed the chance that a ghoul might be
summoned when engraving on a headstone, switching to zombie or mummy
instead.
Rather than adopting the pull request, this retains summoning a
ghoul via engraving and adds the possibly of doing so when kicking
a headstone. Having a ghoul prowl around the grave is independent
of whether there is a corpse or zombie inside the grave. To achieve
this, another flag in 'struct rm' is needed; the single bit for
'disturbed' isn't sufficient. The bigger 'flags' field wasn't in
use for graves so commandeer that for new 'emptygrave'. 'disturbed'
still uses the 'horizontal' bit in order to have engraving and/or
kicking summon at most one ghoul.
Closes#944
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
^ ^
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() */
Issue reported by vultur-cadens: Elbereth used to be effective in
inhibiting monster movement when an object was present on the same
spot, but since 3.6.0 it isn't. It only functions that way when the
hero--or hero's displaced image--is present these days. So special
levels that have been using engraved Elbereth to try to protect
objects from monsters haven't been providing any useful protection.
This makes Elbereth that's engraved during level creation work like
it used to in 3.4.3 and earlier: when there's at least one object
on the engraving's spot, monsters who are affected by Elbereth will
be affected. [I'm fairly sure that that behavior started out
unintentionally, as a side-effect of an optimization to only check
for scroll of scare monster when there was at least one item present
which is a necessary condition for such a scroll.]
Old-style Elbereth includes Elbereth chosen as a random engraving
during level creation in addition to engravings specified in special
level definitions. Engravings by the player don't have the required
attribute and player-engraved Elbereth behaves in the 3.6 way.
This ought to be replaced by something more general. Perhaps a new
engraving type not usable by the player?
Fixes#900
[sic] should be "engraving in frost mentions dust"
Writing on ice with fingers is described as writing in frost, but if
you overwrite an existing engraving rather than add to it the game
said you wiped out the engraving in the dust (immediately followed
by writing in the frost).
Not mentioned in the report: finishing a multi-turn engraving on
ice had the same problem.
Add a comment to the effect that engraving with Fire Brand doesn't
cause it to become dull.
[I'm not sure that is the behavior we really want. It seems like an
unintended side-effect of changing Fire Brand's engrave type to BURN.]
Change trappers and lurkers above to remove digestion damage. They
fold themselves around rather than swallow the victim. There were
are lot of places that assumed that an engulfer which is an animal
would swallow and digest the victim. In hindsight, it might have
been simpler to take the M1_ANIMAL flag off of trappers and lurkers
above.
This adds a new digests() predicate for creatures with AT_ENGL+AD_DGST
(purple worm) and also enfolds() for AT_ENGL+AD_WRAP (both 't'-class
critters).
There are several minor fixes mixed in with this. I didn't record
them as I went along but the two I remember are
1) if poly'd into a holder and holding on to a monster, the '<' and
'>' commands refursed to work; release the held creature first
and then treat those commands as normal;
2) throwing a non-weapon while engulfed by an ochre jelly reported
"the <item> vanishes into the ochre jelly's /currents/".
This needs a lot more testing. I found and fixed multiple minor
details before my own testing burned out.
This replaces the old pushq/saveq arrays (which were used to save
the keys pressed by the user for repeating a previous command)
with a new command queue. This means there's no hard-coded limit
to the saved keys, and it can repeat extended commands which are
not bound to any key.
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).
sengr_at() is used as a boolean, declared as int, and returns FALSE
if there is no engraving present. Change the declaration to boolean.
Also, using fuzzymatch() without any list of ignorable characters
just to get case-insensitve matching didn't make sense so switch to
strcmpi().
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.
Instead of returning ECMD_OK, the commands now return ECMD_CANCEL
when user declined to pick a direction or an object to act on.
Note that this can be ORed with ECMD_TIME, if the command still
took a turn.
For now this has no gameplay meaning.
Instead of returning 0 or 1, we'll now use ECMD_OK or ECMD_TURN.
These have the same meaning as the hardcoded numbers; ECMD_TURN
means the command uses a turn.
In future, could add eg. a flag denoting "user cancelled command"
or "command failed", and should clear eg. the cmdq.
Mostly this was simply replacing return values with the defines
in the extended commands, so hopefully I didn't break anything.
Solve the uneven distribution situation that has been present for
picking random rumors for a long time and for random engravings,
epitaphs, and hallucinatory monster names since 3.6.0. This relies
on the previous partial solution where short lines have been padded
to a longer length. When that length is N and random seek lands in
a long line of length L, retry if the position is in the first L-N
characters. Put differently, it if takes more than N characters to
reach the next newline, reject that random seek and try again. This
effectively makes long lines behave as if they had the same length
of N as the short lines have been padded to and when all lines are
the same length, all entries have the same chance to be chosen.
Engraving with a weapon which has known enchantment or with a magic
marker which has known charge count that decremented the amount did
so without updating the persistent inventory display. Report was
for curses but applied to every interface that can show perm_invent.
This includes some reformatting (mostly
if {
}
else {
}
to
if {
} else {
}
multiple times; also add some new braces after 'if' or 'else' where
one half already had those and the other didn't). And removal of
some unnecessary 'register' designators.
Plus fix up one sanity check which complained if number of charges
being used was more than a marker had, but then let the engraving
continue and decrement the count by that large amount. If that
impossible situation ever occurred, it would result in a marker
with negative charges.
Reported directly to devteam: when selecting an item to engrave
with, "getobj refactor"'s stylus_ok() classified none-of-the-above
objects as not viable instead of allowing arbitrary things to
engrave in the dust or give specific can't-use-that messages like
for food. Make the suggested fix: change default result when
checking an inventory item for applicability from GETOBJ_EXCLUDE
to GETOBJ_DOWNPLAY.
Engrave feedback rather than getobj selection: splash of venom
isn't necessarily wizard mode-only so don't reject attempting to
engrave with it as using an illegal object. And remove the extra
question mark from its "poison pen" message.
If an engraving was appended to repeatedly, it could eventually exceed
BUFSZ and cause a crash. Add some checks to prevent this from happening
and inform the player when she runs out of space to engrave on a
particular square.
Engraving with a marker can use up remaining charges to write
semi-permanently in ink, or--if the marker has no remaining charges--can
draw in the dust as with a normal blunt item. Handling in engrave after
a recent refactor (9c6a5fc423) didn't fully cover this second case, and
as a result using an empty marker to engrave would deduct a charge per
attempt and cause an impossible. This could be exploited by repeatedly
engraving with the same empty marker to underflow the marker charges.
Due to a logic bug introduced when engraving became an occupation - the
code that tests to see whether the player is writing with a weapon that
will get dulled wasn't correctly checking that they were actually
carving an engraving.
Instead of inexplicably paralyzing the player for the duration of their
engraving. Many a character has died by trying to engrave something and
then sitting there diligently writing it while monsters surround and
attack them. (This was especially prominent back in the 3.4.3 era when
repeated Elbereths were viable, but it still occurs today with e.g.
using a hard stone to engrave Elbereth). There were also some other
oddities - for instance, if something teleported the player away while
they were engraving, they would continue to "engrave" (be paralyzed) on
their new location, but would not produce any text there; the full
engraving would be placed on their initial position.
In this commit, I have converted engraving to use the occupation
framework, which treats it as an interruptible activity. This
necessitated some logical restructuring, mostly involving the engraving
being written out in chunks as the player spends more uninterrupted time
on it.
I've tried to keep this free of regressions except for those inherent to
the occupation system.
What has NOT changed:
- The rate of engraving is still 10 characters per turn, or 1 character
using slow methods.
- The formulas for determining how much a bladed weapon or marker can
engrave before getting exhausted are kept. Though this is a bit
convoluted, and if it's not considered important to preserve the
existing behavior, I would recommend simplifying it by decreasing the
maximum engraving length for weapons by 1 so that each point of
enchantment simply gets you 2 characters' worth of engraving (e.g. a
-2 weapon will only engrave 1 or 2 characters before dulling to -3,
rather than giving it a third "grace character".
- The input buffer is still modified based on confusion/blindness/etc
only at the time when the player inputs it (if they gain a
debilitating status while engraving, it will not affect the text). My
personal preference is to make the text affected in scenarios like
that, but it's not strictly necessary to do here, so I didn't.
- Wand messages such as "The floor is riddled by bullet holes", and
blinding from engraving lightning, still appear before the hero starts
to take any time engraving. As noted above, getting blinded by the
wand still has no effect on accurately engraving the text, unless the
hero was already blind or impaired.
What has changed:
- Moving off the engraving or losing the object being engraved with
causes the player to stop engraving.
- Wands can still engrave an arbitrary amount of text using a single
charge, but if the hero is interrupted and decides to start engraving
again, they will consume a second charge.
As it adds a new field to g.context, this is a save-breaking change.
2015 commit 27d8b631cd incorrectly altered a test
/* Chop engraving down to size if necessary */
if (len > maxelen) {
for (sp = ebuf; (maxelen && *sp); sp++)
-> if (!isspace(*sp)) maxelen--;
if (!maxelen && *sp) {
*sp = (char)0;
if (multi) nomovemsg = "You cannot write any more.";
was changed to:
/* Chop engraving down to size if necessary */
if (len > maxelen) {
for (sp = ebuf; (maxelen && *sp); sp++)
-> if (*sp == ' ') maxelen--;
if (!maxelen && *sp) {
*sp = (char)0;
if (multi) nomovemsg = "You cannot write any more.";
Fixes#457