Commit Graph

175 Commits

Author SHA1 Message Date
nhmall
0792e5fe9e expand implicit fallthrough detection to non-gcc compilers
gcc has recognized various "magic comments" for white-listing
occurrences of implicit fallthrough in switch statements for
a long time:

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

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

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

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

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

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

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

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

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

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

The gcc comment approach has also been left in place at this time.
2024-11-30 14:16:27 -05:00
PatR
fe91590d17 a couple of comments 2024-09-10 23:16:55 -07:00
PatR
993c3b303f some reformatting (4 of 4) 2024-09-05 16:49:42 -07:00
PatR
96787441e9 pauper tweaks
For 'pauper', most roles start without any skills.  However, strong
fighter types were starting with basic bare-handed combat/marital arts,
giving them a big advantage.  Knights were starting with basic riding,
which is probably useless now that they have to acquire a saddle to
use it (see below).  Take those initial skills away, producing an even
playing field.

Non-paupers get their initial skills without needing to spend any skill
credits (aka slots) on those.  Give paupers 2 starting credits that
they'll be able to use once they acquire suitable weapons or spells
and train them up, so that once they're reasonably developed they won't
lag in skills compared to non-paupers.

Pauper knights were still starting with a saddled pony.  Suppress the
saddle when the knight is a pauper.

Wizards start knowing a lot of spellbooks.  Pauper wizards shouldn't.

Give most roles advance knowledge of one low level spell or other item.
It won't benefit them unless/until they find the corresponding item.
2024-09-01 15:11:28 -07:00
nhmall
6c0ae092c6 distinguish global variables that get written to savefile
The g? structs had a mix of variables that were written to
the savefile, and those that were not.

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

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

Details:

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

Related note:
There are some pointer variables that are heads of chains that were not
moved from 'g?' to 'sv?', because they are not actually written to the
savefile directly, but the objects/monst/trap/lightsource/timer in the
chains they point to are. That can be changed, if desired.
Examples: gi.invent, gm.migrating_objs, gb.billobjs, gm.migrating_mons,
          gf.ftrap, gl.light_base, gt.timer_base
2024-07-13 14:57:50 -04:00
PatR
24e06171db fix issue #1256 - no_of_wizards incorrect \
if Wizard escapes the dungeon

Reported by vultur-cadens:  a fix to prevent quest feedback when quest
nemesis is removed from the game during bones creation introduced a
regression for an earlier fix that kept context.no_of_wizards up to
date if the Wizard of Yendor escapes the dungeon without dying.

Change 'wizdead()' to 'wizdeadorgone()' and call it from m_detach()
for mongone() as well as for mondead().

Fixes #1256
2024-06-13 12:18:17 -07:00
PatR
c623ddf2e2 fix github issue #1246 - fading spellbook
Issue reported by chadministratorwastaken:  persistent inventory
window didn't update if reading a spellbook caused that book to fade
to blank paper.

Issue was reported for 'curses' but it was general.  The core made
the possibly-revised book type become discovered but that wouldn't
update perm_invent unless the type of book hadn't been discovered.
So if plain spellbook wasn't known yet, the persistent inventory
window did update as intended, but if spellbook of blank paper was
already discovered, the window continued to show the old book type
instead of changing to reflect the revised type (until subsequent
activity triggered another perm_invent update).

Fixes #1246
2024-05-22 22:22:59 -07:00
PatR
1f86001e27 chain lightning vs sleeping monster
Wake up a sleeping target that gets hit by chain lightning.  Also,
bring mimics and hiders out of hiding.

Don't let chain lightning pass into the water on Plane of Water (or
water-wall elsewhere); not sure whether that makes sense physics-wise
but it would have feedback problems due to visibility issues.  Do let
it pass over lava.
2024-05-17 16:10:22 -07:00
Pasi Kallinen
f131942dd2 Another tamedog message
Give a different message when a peaceful creature was tamed.
Allow suppressing this and the previous message, when the caller
handles messaging.
2024-03-24 10:48:29 +02:00
nhkeni
9c0ed8ae63 NOSTATICFN for src/* 2024-03-14 17:41:51 -04:00
nhkeni
acf60063d5 Add missing prototypes for static functions to avoid warnings. 2024-02-29 10:49:53 -05:00
nhmall
0a985459f0 make style consistent for function ptr arguments 2024-02-19 17:21:04 -05:00
nhmall
688ac6ffbe remove register from variable declarations 2024-02-19 16:30:07 -05:00
PatR
d253e947da more getpos() highlighting plus jumping in place
Simplify the valid-position highlighting via '$' by combining the
tmp_at(start) and tmp_at(populate) steps.

Add highlighting for a couple of targetting operations that would
give valid/invalid feedback via autodescribe but weren't displaying
highlight markers for $.

I made several jumping changes, most of them dealing with picking
hero's own spot.
2024-02-16 15:36:17 -08:00
nhmall
4e19221e55 variable 'display' causes shadow variable warnings in X11 build
display.botl      -> disp.botl
display.botlx     -> disp.botlx
display.time_botl -> disp.time_botl
2024-01-05 05:58:51 -05:00
nhmall
49a5d043c0 consistent use of TRUE vs 1 with botl and botlx 2024-01-04 23:48:38 -05:00
nhmall
22e52ee905 bundle the display-related hints, that tell bot() and others
that an update is required, into a struct. Remove it from
context since there is no reason to save those.
2024-01-04 23:16:27 -05:00
Pasi Kallinen
780d30eca9 Wielded quarterstaff gives a small spellcasting bonus
From NetHack Fourk.
2023-12-31 17:53:03 +02:00
Pasi Kallinen
42a6a5df66 Cursed welded quarterstaff doesn't prevent spellcasting 2023-12-31 17:41:04 +02:00
PatR
c41cb1a7fa source reformatting
Fixup some of the inconsistently formatted code that has been
introduced recently or been building up for a while.  Done manually.
I wasn't systematic except for looking for lines ending in '&' or '|'
(which wouldn't find such things if they're followed by a comment)
so there might be lots more.  I changed a bunch of C++-style //...
comments to old style C /*...*/ so that they'll match the rest of
the core's code rather than because they shouldn't be used.
2023-12-08 00:30:10 -08:00
Alex Smith
0d508cc936 Implement the spellbook of chain lightning
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).
2023-12-06 20:02:35 +00:00
Alex Smith
495a1a9b07 Tweak to Wizard spellbook knowledge
Wizards now start out recognising all level 1 spellbooks, making
it possible to write a low-level spellbook in order to start
training a spell skill of choice.
2023-12-03 16:16:19 +00:00
Alex Smith
319dfbdaa3 Wizards learn about spellbooks as they enhance their spell skills
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.
2023-12-02 03:46:55 +00:00
Pasi Kallinen
dd5ca5b058 Change menu_headings to accept color and attribute
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
2023-11-13 07:33:56 +02:00
Pasi Kallinen
8dc7a4882a Make spell menu work with repeat command 2023-11-05 13:49:04 +02:00
Pasi Kallinen
56f4657041 Simplify add_menu, part 10 2023-11-03 22:37:50 +02:00
Pasi Kallinen
a6051dae81 Simplify adding menu headings 2023-11-03 19:07:15 +02:00
Pasi Kallinen
638c487c59 Split deadbook undead pacify effect into separate function
Also make the monster chain iterator functions more robust,
in case the called function changes the next monster pointer.
2023-10-28 19:01:46 +03:00
PatR
f9a35132d6 crystal helmet
Discussed a long time ago, change helm of brilliance from iron to
crystal so that it doesn't need to be a special case for metallic
armor's affect on spell casting.  It now has a fixed description of
"crystal helmet" but is not pre-discovered.

Add new helm of caution to retain the "etched helmet" description
among the shuffled helms.  Wearing it confers the Warning attribute.
That's fairly lame but not necessarily useless.  It's iron and gets
half the former probability for a random piece of armor being helm
of brilliance so is not likely to be popular.

Helm of caution keeps the old helm of brilliance tile and new helm
of brilliance is basically the same image with different color and
pointed on the top.

Not changed:  the etched helmet was marked as green and is drawn
that way for text but the tile doesn't actually use green for it.
Crystal helmet started as hi_glass (rendered as 'bright cyan') but
has been changed to clr_white to match crystal plate mail.

Old save and bones files are invalidated.
2023-04-29 02:18:29 -07:00
PatR
1c94bdac89 blindness overhaul
I was working on this at the time 3.6.0 was released and set it aside
until later.  Later has finally arrived.  Redo the Blind, Blinded,
Blindfolded,&c macros to make more complete use of intrinsic property
handling.  Blinded was being treated as a number which could be added
to or subtracted from; now that has to be done via TIMEOUT mask
because it has FROMOUTSIDE (OPTIONS:blind) and FROMFORM (poly'd into
!haseyes() form) bits included.  Object definitions for blindfold and
towel now specify the BLINDED property; overriding blindness via the
Eyes of the Overworld is accomplished via props[BLINDED].blocked.

Code generated for the scores of Blind and !Blind tests throughout
the program should be smaller.

One bug that has been fixed is that putting on the Eyes of the
Overworld cured permanent blindness (from OPTIONS:blind).  The
u.uroleplay.blind flag was cleared and stayed so after taking them
off.  Putting the Eyes on still breaks blind-from-birth conduct but
now blindness will resume when they are removed.

This was untested at the time it was set aside and is only lightly
tested now.  A large number of the changes here are just to switch
from Blinded to BlindedTimeout for current timed value and to call
set_itimeout() for setting a new value.
2023-04-27 14:53:28 -07:00
nhmall
8bbe9282aa add soundeffects hooks to core
Insert the calls to trigger a number of potential soundeffects
into the core.

If no additional soundlib support is integrated into the
build, then the Soundeffect macro (sndprocs.h) expands to nothing:

[#define Soundeffect(seid, vol)
]

If, however, at least one additional soundlib support is integrated
into the build, then the Soundeffect macro gets defined as this
in sndprocs.h:

[#define Soundeffect(seid, vol) \
    do {                                                              \
        if (!Deaf && soundprocs.sound_soundeffect                     \
            && ((soundprocs.sndcap & SNDCAP_SOUNDEFFECTS) != 0))      \
            (*soundprocs.sound_soundeffect)(emptystr, (seid), (vol)); \
    } while(0)
]

That macro definition checks for the hero not being Deaf; it checks
to ensure that the active soundlib interface has a non-null
sound_soundeffect() function pointer; and it checks to ensure
that the active soundlib interface has declared that it supports
soundeffects by setting the SNDCAP_SOUNDEFFECTS bit in its sndcap
entry. That just means that the interface routines are prepared to
accept and deal with the calls from the core, whether or not it
actually produces the desired soundeffect.
2023-01-20 14:20:08 -05:00
PatR
bdaa51a689 another static analyzer bit - cast_protection()
The code tested u.uswallow and then accessed u.ustuck.  Under normal
circumstances that works fine but it could be a problem if the two
fields got out of synch.  This ought to fix the analyzer complaint
and avoid any trouble with mondata access.
2023-01-13 23:55:37 -08:00
nhmall
ba5356603a yn()
A number of C compiler suites have a math.h library that includes a yn()
function name that conflicts with NetHack's yn() macro:
"The y0(), y1(), and yn() functions are Bessel functions of the second kind,
for orders 0, 1, and n, respectively. The argument x must be positive. The
argument n should be greater than or equal to zero. If n is less than zero,
there will be a negative exponent in the result."

At one point, isaac64.h included math.h, although that has since been removed.

Some libraries used in NetHack (Qt for one) do include math.h and that required
build work-arounds to avoid the conflict.

Rename the NetHack macro from yn() to y_n() and avoid the math.h conflict
altogether, eliminating the need for that particular work-around.
2023-01-12 16:04:40 -05:00
nhmall
02a48aa8cf split g into multiple structures
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
     ^ ^
2022-11-29 21:53:21 -05:00
Michael Meyer
619781dbb8 Add 'mdistu' macro
Short for distu(mtmp->mx, mtmp->my) (i.e. the distance between the hero
and the specified monster), which is a very common use of distu().  The
idea is that this would be a convenient shorthand for it; I actually
thought it (or something very similar) existed already, but couldn't
find it when I tried to use it earlier.  Based on the number of uses of
fully-spelled-out 'distu(mtmp->mx, mtmp->my)' replaced in this commit
I'm guessing I just imagined it.
2022-11-18 23:42:47 -08:00
nhmall
99a93fe50b some C99 changes
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() */
2022-10-29 10:54:25 -04:00
Michael Meyer
02367077bd Use function for combined str/hp loss from poison
Since losestr and losehp calls go together most of the time, this feels
like it probably makes more sense than repeating the killer name/format
twice in a row all over the place.
2022-10-08 16:29:56 -07:00
Michael Meyer
70fe2ce5cd Don't make callers responsible for losestr death
Remove callers' responsibility to deal with possible hero death when
calling losestr.  This is less fragile and error-prone than leaving it
in the caller's hands, but it means that death from the monster spell
'weaken target' no longer goes through done_in_by, and the death reason
is no longer "killed by <monster name>".
2022-10-08 16:29:55 -07:00
Pasi Kallinen
e952f67f2c Add #wizcast command to cast any spell
Wizard-mode command to cast any spell without checks that would
prevent casting, and with no energy use.

Mainly to allow the fuzzer to exercise the spell code paths.
2022-09-06 22:58:28 +03:00
PatR
b07fe59b3c attack/damage by trapper and lurker above
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.
2022-08-15 04:14:36 -07:00
Pasi Kallinen
fd9745f9c6 Command repeating by using cmd queues
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.
2022-08-09 11:54:45 +03:00
Pasi Kallinen
0517974b6d Spell location feedback
Casting some spells at high enough skill allows picking for
the spell location. Say which locations are out of range
when picking the location.
2022-07-28 13:15:41 +03:00
nhmall
3004cf2d34 be more consistent with coordinates 2022-07-02 09:10:03 -04:00
nhmall
2770223d10 interface groundwork for core-side color decisions
(user-side decisions really, but as it stands right now
user-side decisions/options are made and processed by the core)

add a parameter to add_menu so color can be passed
2022-06-25 13:21:51 -04:00
copperwater
a61a97856b Externify trycall() and replace many docall() calls with it
trycall() is a short docall() wrapper that is a no-op if the item is
already identified or the player has called the object type already. For
some reason, many calls to docall() did those same exact checks
beforehand.

This commit eliminates that redundancy by converting those calls into
trycall(), which is now made extern rather than local to do.c. No
behavior should be changed by this commit; I've checked that none of the
affected places could take a different code path now that the
oc_name_known and oc_uname checks are removed.
2022-03-14 09:48:19 -04:00
PatR
e3490743e0 divine gift of spell knowledge
Remove the conduct-specific aspect of receiving spells as prayer boon.
Anyone now has a 25% chance of having the spell directly implanted
into their head, not just characters who have maintained illiterate
conduct.  It can now also restore a forgotten spell or refresh one
that is nearly forgotten.  It still tries to choose a spell which
isn't already known (new: or was known but has been forgotten) but if
it picks one that is known and doesn't need refreshing, a redundant
book will be given, same as the behavior in earlier versions.

The chance for receiving a blank spellbook is higher when that item
is undiscovered.  When given as a prayer reward, make it become
discovered even if hero doesn't read it so that it will be less likely
to be given again.  There's a 1% chance for that auto-discovery to
happen with other bestowed books.  Unlike blank boots, having the book
be discovered doesn't lessen their chance of being repeat gifts.

Minor bug fix:  for a spell implanted from scratch, the book remains
unknown.  That's ok; it's actually more interesting than discovering
a book you haven't seen yet.  But after acquiring and reading the book
you could get "you know <spell> quite well already" and the book would
stay undiscovered even though you were just told what spell it's for.
2022-03-09 07:06:37 -08:00
Pasi Kallinen
b172957831 Use macro for spell pw cost per spell level 2022-02-28 22:21:37 +02:00
Pasi Kallinen
e0a83630e1 Some spell code reorg
Keep the internal spell array index inside spell.c,
and refer to spells outside of it with the otyp id.
2022-02-23 10:52:10 +02:00
Pasi Kallinen
33556788c3 Start with a level 1 spell and enough power
Ensure the first spell - if any - given to the hero in initial
inventory is level 1 - otherwise you can end up with a situation
where the hero knows level 3 spells, but won't have enough power
to cast them.

If the hero starts out with a spell, ensure enough power (5)
to cast that level 1 spell.
2022-02-22 12:26:06 +02:00
Pasi Kallinen
78f7df9a51 Casting a forgotten spell uses some random amount of power
... in addition to the confusion/stunning it does.
2022-02-22 11:32:08 +02:00