Prior to this commit, there was no good way to deal with swarms of
weak, good-AC enemies using magic; trying to play a wizard as a
pure spellcaster would make bees and ants very difficult to deal
with (because they would usually dodge force bolts).
This commit adds a new spell designed to be very good against
swarms of weak enemies: "chain lightning", which does 2d6 lightning
damage to every monster around you. It has an initially short range,
but can chain from monster to monster to cover potentially large
distances (as long as none of the monsters en route are shock
resistant or tame/peaceful; the spell can't chain past shock
resistant monsters and avoids peacefuls). Monsters within one
space of the visible lightning bolts are affected. Unlike other
lightning effects, this one does only 2d6 damage, not enough to
blind affected monsters.
This commit breaks existing save and bones files (thus the
EDITLEVEL increase).
The same checks were being repeated for every damage type; this
sends them through two centralised functions (one for checking
whether an extrinsic blocks a specific instance of item destruction
and one for the enlightenment message), so that new mechanisms of
item destruction prevention will need to change only one point in
the code.
The tracks left by hero were cleared when player saved and
restored the game, or changed levels. Now the tracks are
saved in the dungeon level, so changing levels keeps the tracks
left by hero in that level.
Also increased the length of tracks from 50 to 100, and
simplify the tracking function.
Thing not done: fade out old tracks when returning to a level.
Breaks saves and bones.
Report was that converting a novel into a blank spellbook via water
damage resulted in a spellbook of blank paper that increased weight
when put into a bag of holding.
Spellbooks weigh 50 units but novels were defined with a weight of 0;
when one was created, a non-zero weight of 1 got assigned. Blanking
it didn't update the weight; that stayed at 1. Putting it into a
container reset the weight to match the new type: spellbook of blank
paper, so its weight increased.
Do that when blanking rather than wait until a container might fix it
up. If it is already in a [possibly nested] container, update that
container's weight too along with any outer ones.
This also changes the base weight of novel from 0 to 10, so it still
gets magically heavier when turned into a spellbook of blank paper.
(The alternative seems to be to destroy it instead.)
The Book of the Dead weighed only 20 units which seemed odd to be so
much less than a spellbook. This changes that to 50 to match those.
Currently, options.c is the only file that #includes "optlist.h".
In theory, if a source file did want to include optlist.h (perhaps
for the struct allopt_t declaration so they could deal with a
pointer to such a struct), they wouldn't be able to include it
because of a static function prototype that it contains.
Add some protection to only include that static function
prototype when optlist.h is included from options.c.
At present, potions of healing generate primarily in Gehennom,
which causes significant balance issues (e.g. you don't have them
to heal in the early game, or even if you do, you can't identify
them in time to use them). In this series of commits, I'm aiming to
make potions of healing a more viable early-game healing source,
which means making them both (much) more likely to generate, and
easier to identify so that they are actually usable in the early
game.
This commit radically increases the generation chance of potions
of healing (reducing the chance of most other potions slightly to
compensate), and gives them a unique base price. This should make
them fairly easy to identify either by price-ID or quantity-ID
(and the unique base price is chosen to be fairly easy to figure
out even for unspoiled players).
Previously, Wizards got a boost to the chance of writing unknown
spellbooks based purely on being a Wizard (with the chance still
luck-based), leading to a very large power spike when the Wizard
gained access to a luckstone and the ability to max out luck.
This had two main issues: this power spike came *after* the major
early-game difficulty spike, often leaving Wizards forced to deal
with it without having appropriate spells; and it promotes
grinding (for Luck and for Magicbane) at an early point in the
game, meaning that the Wizard early game effectively followed a
sequence of extreme difficulty -> grinding -> minimal difficulty,
which isn't very good balance-wise.
With this commit, Wizards lose their advantage to writing unknown
spellbooks by guessing, and instead learn spellbook IDs based on
their spell skills (advancing a skill gives knowledge of higher-
level spellbooks). This means that writing unknown spellbooks
becomes guaranteed with sufficient skill, but has no advantage
over non-Wizards in schools where the Wixard does not have
sufficient skill.
Due to Wizards' skill caps, there are two spells which they can't
ever write guaranteed: create familiar and charm monster. Create
familiar is a fairly niche spell (that doesn't match the Wizard
playstyle that well) and being unable to write it is not a major
problem. The inability to easily write charm monster is
intentional.
This is for completely destroying an altar with extra-powerful magical
digging -- the normal altar_wrath() punishment didn't seem sufficient
for such an outrage to me, so skip straight to slinging the lightning
bolts. Destroying an altar is unlikely to happen by accident (though
it's possible with poorly timed usage of a drum of earthquake).
character_race() was going out of bounds when scanning the races[]
array, relying on a field value that the fencepost entry didn't set.
This incorporates the previous fix for UNDEFINED_RACE but also changes
character_race() to not care about that anymore.
If any items are in use and hero isn't wielding anything, include
| - bare hands
in the primary weapon slot of the display of used items as an alert.
More useful for perm_invent than for #seeall.
If no items at all are in use, continue to show "not using any items"
without any specific weaponless alert.
When sortloot() is called for inuse_only, pass a filter that screens
out items which aren't in use so they won't be needlessly sorted.
For '*' and for persistent inventory with perminv_mode==inuse, show
the items in a specific order and within four labelled groups rather
than within their object classes:
|Accessories
| amulet
| right ring
| left ring
| blindfold
|Wielded/Readied Weapons
| primary weapon
| alternate or secondary weapon
| quiver/ammo pouch
|Armor
| suit
| cloak
| shield
| helmet
| gloves
| boots
| shirt
|Miscellaneous
| lit candles and/or lamps
| attached leashes
The accessories come first due to the default 'packorder' position
for amulets; weapons before armor likewise. If you wield a potion or
quiver some gold, those non-')' items will appear in the weapons
section since the ordering is based on slot rather than object class.
choose_classes_menu was declared extern. The only caller presently
was calling it was optfn_pickup_types() in options.c.
It could have had the extern declaration from include/extern.h and
declare it as static within options.c, if that was the only use
anticipated. Also, if the one existing caller were all there would
ever be, the argument passed to it that was the subject of pr #1146
could have just been removed along with the switch.
Checking the comments above the function, however, it was clearly
designed as a general-purpose function that could be called from
anywhere for the functionality desired, even though there's presently
just the one caller, passing just the one variation of the category
argument.
Relocate the general-purpose function over to src/windows.c, where
several interface-related / menu-related general-purpose functions
already reside.
options.c has gotten *huge* and this is a fitting opportunity to
reduce its size a little.
A very small number of relatively recent macro definitions got
placed in mondata.h, even though they had a 'struct monst *'
argument, rather than a 'struct permonst *' argument.
Relocate the macros with the 'struct monst *' argument to
include/monst.h, leaving the macros with the 'struct permonst *'
argument in include/mondata.h.
This is useful for debugging and it allows the index
to be used directly instead of calculated in a
monsndx() function, which has been removed.
I left monsndx() in as a simple short-hand macro for the value
and didn't change the use cases, the reasoning being that this:
monsndx(mon->data)
is arguably a little easier on the eyes than:
mon->data->pmidx
LOW_PM, NON_PM, SPECIAL_PM have been included in the 'enum monnums'
now, instead of as individual macro definitions.
I chose to add the pmidx field as an instance of the enum declaration,
because that has very advantageous results in some debuggers, where it is
then shown as:
pmidx PM_GRAND_MASTER (349) monnums
instead of the less-informative:
pmidx 349 int
Adding the element count to the extern declaration for mons from:
'extern struct permonst *mons[];'
to the more specific declaration to that in src/monst.c:
'extern struct permonst *mons[NUMMONS + 1];'
then allows navigation through the mons array in one of the debuggers.
Macros to conditionally include color fields in defsyms[] and objects[]
are no longer needed. The one for mons[] was already gone but traces
were present in a comment.
When picking up multiple items (or looting multiple items) in one
operation, only show encumbrance for the first item that causes that
to change instead of complaining about non-zero encumbrance on every
item. The very first item is treated as if it caused a change, so
you get "you have a little trouble lifting <obj>" even if you were
already burdened, same as before.
Only lightly tested.
Sync with decl.c. dbridge.c never had anything to do with these
variables. They were part of decl.c before they got collected into
struct g, then split among struct ga to gz. (This only fixes up
the comment in gl; there may be more, possibly quite a few more.)
These might be better classified as /*dungeon.c*/ although they
weren't defined there.
"engraved part of a room" and "engraved part of a corridor" sound
silly. Change to "engraving in a room" and "engraving in a corridor".
Still displayed to player as just "engraving".
An orhpaned wintype.h tweak got dragged in. Renumbers to_core_flags.
Sometimes I annotate a level with a note like "watch out, chameleon
below", which is useful to remind myself of some danger or thing to
remember when returning to the level -- but if saving and restoring on
the level itself there's no reminder of that annotation. If you restore
on a level with an annotation, print it as part of the "welcome back"
message.
<color>
off: map, menu items, menu headings, menu prompt/title all, everything should have color suppressed.
<curses guicolor>
on: map, menu items, menu headings, menu prompt/title can all feature color, as can
menu borders, menu-selector letters.
off: map, menu headings, menu prompt and menu items (menucolors on) can still feature color,
but all other non-map features such as menu borders, menu-selector
letters will not have color.
<menucolors>
on: menu items can have colors if they match one of the regex in config
file; menu headings, menu prompt can also be in color (based on menu_headings option).
off: menu items won't have colors, but menu headings, menu prompt still
will feature colors (based on menu_headings option); those are not impacted by turning
off menucolors.
This implements the mechanics to use the ctrl_nhwindow() interface
capability to pass down a setting change from the core to the active
window port, without resorting to accessing a core global variable
from within the window port, and without altering the interface..
The passed setting is honored in the tty and curses window ports.
X11 and mswin receive and store the values, but no implementation
to change the menu prompt style is there yet.
Qt does not store the values or have an implementation.
The setting change is done in allmain.c immediately after
creating the WIN_INVEN window.
If the value is "no color&none" report it as "no-color&none" in 'm O'
and for #saveoptions.
Allow "OPTIONS=menu_headings" without any color or attribute value to
mean "no-color&inverse" as it once did before the player could choose
which attribute or color was supported, and matching the default used
when 'menu_headings' hasn't been specified at all.
Accept "OPTIONS=!menu_headings", meaning "no-color&none".
Explicitly reject "OPTIONS=!menu_headings:anything". It was rejecting
that due to blanket rejection of negated option, but reporting "can't
both have a value and be negated" whether there was any value present
or not.
For preselected menu entries when interactively choosing a new value
via the submenu of 'm O', use the current color and attribute rather
than NO_COLOR and ATR_NONE.
Remove menu_color support from the window port side of the interface.
The window port just has to honor the color parameter that was added
to the add_menu() interface definition in June 2022 commit
2770223d10, and let the core-side of
the interface handle things.
To that end, this does the following:
Removes the #define of add_menu() from include/winprocs.h and add a
real core-side add_menu() function to windows.c which acts as a
trampoline to the window port win_add_menu() function, while providing
a single location to adjust the parameters passed to the window port
function. get_menu_coloring() is now called in there.
Moves get_menu_coloring() from options.c into windows.c and makes it
static.
Removes all the calls to get_menu_coloring() from the tty, Qt, X11,
curses, and win32 interfaces and adjusts their code to simply honor
the color parameter in add_menu, similar to what the menu_headings
change from earlier today did.
Instead of just accepting an attribute, it's now possible to
use a color, or both color and attribute, for example:
OPTIONS=menu_headings:inverse
OPTIONS=menu_headings:red
OPTIONS=menu_headings:red&underline
Default is still just inverse.
This lets the player change the menu heading color without
needing to use menu colors for them.
Also makes it so the core uses NO_COLOR instead of 0, for all
the menu lines which don't have any prefedefined color.
Tested for tty, curses, x11, qt, and win32
There is a comment above the function indicating that it should be
aligned with hero ammo breakage, but this wasn't the case. One big
difference is that any monster-thrown or -shot object would be deleted
unconditionally if it hit another monster trapped in a pit. I don't
know why that was in there, but it's not present in hero ammo breakage
chances, and it meant that a monster could sling the Mines luckstone at
the hero, hit a monster in the pit, and permanently lock the hero out of
getting the luckstone (as just happened to a player during the current
tournament). This pulls the hero breakage rules out into their own
function and uses that for monster breakage as well, to make sure they
are aligned. I also refactored drop_throw a bit to reduce the number of
separate variables tracking whether the object was deleted (was create,
objgone, and retvalu), and changed its (and ohitmon's) type to boolean.
I used ^G to create a monster and specified "invisible owlbear". I
then got "An owlbear appears next to you." Except it didn't; it was
invisible and I lacked see-invisible. I imagine that newsym() was
called for the new-yet-invisible monster, but that remained buffered
and was gone overridden by the time pending map update got flushed
at some point after the monster was made invisible.
Add a new makemon() flag to turn a newly created monster invisible
during its creation, before "monster appears" message is delivered.
Since that message will now be suppressed in this situation, use the
cursor-flash hack that indicates where the new, unseen monster got
placed. Creating "1000 invisible <mon>" is something you probably
won't do twice.
These are meant to be 'cwt' values and directly comparable to a
particular pm->cwt, which is an unsigned short. My concern here was
signed/unsigned comparison warnings like the one fixed in 1538b40, but
I'm not sure if this is actually necessary: there is already a
comparison between cwt and WT_HUMAN in max_mon_load() which apparently
doesn't produce the warning...
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.