1396 lines
61 KiB
Plaintext
1396 lines
61 KiB
Plaintext
SPELLS:
|
|
------------------------------------------------------------------------------
|
|
Index:
|
|
|
|
1. Background
|
|
2. Spell Objects
|
|
3. Spell Paths
|
|
4. Spell Subtypes
|
|
5. Spell Casting Objects
|
|
6. Arch Directory Layout
|
|
7. Programming Notes
|
|
8. Abilities
|
|
9. Spell Merging
|
|
|
|
------------------------------------------------------------------------------
|
|
1. BACKGROUND
|
|
|
|
This document describe all things spell and spell related that developers
|
|
need to know. This document outlines the new spell objects and how
|
|
this all works.
|
|
|
|
------------------------------------------------------------------------------
|
|
2. SPELL OBJECTS
|
|
|
|
In the old model, each spell had a specific number associated with it.
|
|
Monster spell abilities, while objects, just said to use spell number
|
|
X. Given that everything else in the game is object, this hardcoding
|
|
of spells was less than ideal.
|
|
|
|
In the new system, spells are objects. The spell object the player knows
|
|
has the characteristics of the spell. As a first pass, the existing
|
|
spells are just converted to object form. As time passes, new spells
|
|
can be easily added in this system. It also simplifies the code - the
|
|
spells a player knows are now objects in his inventory. The spell
|
|
a wand casts is the spell in its inventory. The spell a scroll casts
|
|
is the spell in its inventory, and the spell you learn by reading
|
|
a spellbook is the spell in its inventory.
|
|
|
|
This model makes it very easy to make variations of some spells. Want
|
|
to make an extra large fireball? All this is needed is to take the
|
|
large fireball arch, increase a few parameters, and your all set.
|
|
|
|
The following is the fields in the object structure, and how they
|
|
correspond to a spell. Fields that are not mentioned are not used.
|
|
|
|
char *name: Name of the spell (large fireball, icestorm, etc)
|
|
|
|
char *lore: Description of the spell to be put in books, or perhaps even
|
|
to just let the player see. In a sense, this puts documentation of
|
|
the spells within the object, instead of having to maintain separate
|
|
files.
|
|
|
|
sint16 casting_time : How long it takes to cast the spell (was time)
|
|
This is the number of ticks it takes for the player to cast the spell,
|
|
and is thus not a fixed time.
|
|
|
|
Suggested casting time values:
|
|
5: Combat spells (cones, bolts, balls, etc - ones you cast when in
|
|
danger)
|
|
10-15: Protection spells, other often cast spells. Summoning spells
|
|
would fall into this category, as they are often cast when not
|
|
in immediate danger.
|
|
~25: Detection spells, or other spells done when times are safe.
|
|
Many ability improvement spells might fall into this category.
|
|
|
|
These are only suggestions - they more or less match when the spell will
|
|
be used and usefulness. An attack spell that takes forever to cast
|
|
isn't very useful. There are really two things to keep in mind - when
|
|
the spell is cast, and how long the spell itself last. A long lasting
|
|
spell can have a longer casting time, simply because it isn't cast
|
|
that often and will last for a while (thus, smart players won't cast
|
|
it in combat).
|
|
|
|
face/anim: If the spells is a generic spell that doesn't have an other_arch
|
|
pointer, this is the face (or animation to use when the spell is
|
|
cast.
|
|
|
|
uint8 type: SPELL (101) (to denote this is spell object)
|
|
sint16 resist[] values: For protection spells, resistance they provide.
|
|
uint32 attacktype: Attacktype of the spell.
|
|
|
|
uint32 path_*: spell path information. Note as of this writing, only
|
|
path_attuned is expected to mean anything. However, I can envision
|
|
the user of path_repelled/denied to denote that if the caster is
|
|
attuned to some path, they have penalties in casting this spell.
|
|
|
|
sint32 value: Base value of this spell for scroll, spellbooks, wands, rods.
|
|
sint16 level: Level of the spell
|
|
|
|
other_arch: archetype this spell uses. In the case of bolts, this
|
|
is the bolt that is fired.
|
|
|
|
char *skill (new): Skill needed to cast this spell.
|
|
|
|
sint32 subtype (new): Sub classification for this spell. It sort of
|
|
relates to the existing spell number, but in a more general form.
|
|
At first pass, subtypes would fall into protection spells, bolt
|
|
spells, ball spells, cone spells, stat increase spells. There
|
|
are a number of spells which can not be abstracted and are unique
|
|
in their own right (dimension door, word of recall, polymorph, etc).
|
|
In these cases, there will be one value for that spell type.
|
|
|
|
invisible: Always set to 1, so that the player can't directly see it in their
|
|
inventory.
|
|
|
|
no_drop: Always 1, so when a spell object is destroyed, the spell it
|
|
contains is dropped and not freed.
|
|
|
|
|
|
From living/stats substructure:
|
|
Str, Dex, etc: If spell changes and ability, this is the ability it changes.
|
|
sint16 dam: Base damage the spell does:
|
|
sint16 sp: how many spellpoints the spell costs
|
|
sint16 grace: how much grace the spell costs
|
|
sint16 maxsp/maxgrace: Every 'maxsp/maxgrace' levels beyond its base level,
|
|
the spell cost doubles (this is pro rated like the old code. For example,
|
|
if a first level spell costs 5 sp, and maxsp is 5, then at level 6 it
|
|
would cost 10 sp. However, at level 2, it is 6 sp, level 3 is 7 sp,
|
|
etc.
|
|
New fields added to object structure:
|
|
|
|
uint8 dam_modifier:
|
|
for every maxdam levels above what is needed to cast the spell, it does an
|
|
extra point of damage. Note if you want a spell to double its damage
|
|
every X levels, the maxdam may have to be quite low (1) if the spell does
|
|
high damage. For example, suppose you have a 10'th level spell that does
|
|
50 damage. If dam_modifier is 5, then every 5 levels it does one extra
|
|
point, so at level 25, it would do 53. If dam_modifier is 1, then at
|
|
level 25 it would do 65.
|
|
|
|
sint16 duration: Base duration of the spell (was bdur)
|
|
uint8 duration_modifier: Modifies duration of spell, acts the same way
|
|
as dam_modifier.
|
|
|
|
uint8 range: Range of the spell - this could be how far a lightning
|
|
bolt goes, how big that fireball is, etc. Spells with range 0
|
|
are personal only spells. Spells with range 1 can touch
|
|
a person on a neighboring space. range will not change based
|
|
on level of the caster - if higher range is desired, different
|
|
spell should be made.
|
|
uint8 range_modifier: Modifies range of spell, acts ame way as dam_modifier
|
|
|
|
TBD:
|
|
There are also many fields in the spell structure which are not
|
|
copied - charges, scrolls, scroll_chance, books, defensive,
|
|
cleric, onself.
|
|
|
|
The defensive, and onself can largely be derived from the
|
|
subtype above. if subtype is protection or healing, it'd be
|
|
self.
|
|
|
|
cleric is basically redundant now - the skill says what skill to use
|
|
for this spell. Note that a spell could in theory cost both mana
|
|
and grace - what it costs is not necessarily tied to a skill. However,
|
|
it would be bad practice to make a wizardry spell use mana. However,
|
|
I could potentially see hybrid skills that have some various benefits,
|
|
or if we have say a 'magic' god, perhaps some special spells it provides
|
|
a use mana.
|
|
|
|
So lets next touch on the idea of treasurelist for spell objects.
|
|
|
|
As mentioned above, spell objects, like wands, spellbooks, scrolls,
|
|
etc, contain the spell they cast. The way spells end up in those
|
|
objects is via a mechanism there is already code for - treasurelists.
|
|
|
|
Currently, the treasure generation code notices if a wand, rod, spellbook,
|
|
or scroll is generated. If so, it calls special code that looks
|
|
at the field of the spell structure and randomly chooses a spell.
|
|
|
|
Instead, this is just replaced with treasurelists. The scroll object
|
|
would have a 'randomitems scroll', wands a 'randomitems wand',etc. These
|
|
may be big treasure lists, but that shouldn't post any problem. More
|
|
likely, these lists would be broken up into smaller pieces (eg,
|
|
scroll_low_level, scroll_medium_level, scroll_high_level).
|
|
|
|
This provides finer resolution than is currently allowed in the spell
|
|
structure - you could make some spell available only in rods, but not
|
|
wands. you can better adjust things to better spells show up in
|
|
tougher dungeons, etc,
|
|
|
|
When a spell is cast, often it copies over certain values into the
|
|
SPELL_EFFECT (102). The subtype of the SPELL_EFFECT, which should generally
|
|
match that of SPELL itself, determines what the spell does.
|
|
|
|
------------------------------------------------------------------------------
|
|
3. SPELL PATHS DOCUMENTATION
|
|
|
|
Long ago a number of archmages discovered patterns in the web that spells
|
|
weave in the aether. They found that some spells had structural similarities
|
|
to others and some of the mages took to studying particular groups of spells.
|
|
These mages found that by molding their thought patterns to match the patterns
|
|
of the spells they could better utilise all the spells of the group. Because
|
|
of their disciplined approach, the mages were described as following spell
|
|
Paths. As they attuned themselves to particular spell Paths they found that
|
|
they would become repelled from others, and in some cases found they were
|
|
denied any access to some paths. The legacy of these mages remains in some
|
|
of the magical items to be found around the world.
|
|
|
|
Technical details:
|
|
In the same way that players and objects can be protected, immune, and
|
|
vulnerable to the different types of attacks, they can now be attuned,
|
|
repelled, or denied access to the different spell Paths. An object that
|
|
is attuned to a Path cast spells from that Path at 80% of the spell point
|
|
cost and receives duration/damage bonuses as if the caster were five levels
|
|
higher. An object that is repelled from a Path casts spells from that Path
|
|
at 125% of the spell point cost and receives duration/damage bonuses as if
|
|
the caster were five levels lower (minimum of first level). An object that
|
|
is denied access to a Path cannot cast any spells from it. The casting
|
|
time is also modified by 80% and 125% respectively. These values are
|
|
defined in PATH_SP_MULT (from spells.h), PATH_TIME_MULT (from spells.h), and
|
|
path_level_mod (from spells.c)
|
|
|
|
The Paths themselves are the following:
|
|
"Nothing",
|
|
"Protection",
|
|
"Fire",
|
|
"Frost",
|
|
"Electricity",
|
|
"Missiles",
|
|
"Self",
|
|
"Summoning",
|
|
"Abjuration",
|
|
"Restoration",
|
|
"Detonation",
|
|
"Mind",
|
|
"Creation",
|
|
"Teleportation",
|
|
"Information",
|
|
"Transmutation",
|
|
"Transferrence".
|
|
|
|
See define.h for the number values corresponding to the Path.
|
|
|
|
Some more will be added in the near future. Some spells do not currently
|
|
belong to a Path, this is probably appropriate for some spells.
|
|
Paths are inherited just like protection/immunity/vulnerability, ie if a
|
|
ring contains "path_attuned 1", the wearer becomes attuned to the Path of
|
|
Protection.
|
|
|
|
Paths are quite powerful and shouldn't be given away cheaply. Ideally, most
|
|
objects with path_attuned attributes should have path_repelled and path_denied
|
|
attributes as well, to balance out (eg attuned to Fire, repelled from
|
|
Protection, and denied from Restoration)
|
|
|
|
------------------------------------------------------------------------------
|
|
4. Spell Subtypes
|
|
|
|
This section lists specific spell subtypes and what they really
|
|
do. The idea is to try and generalize the object types (cones,
|
|
bolts, balls, etc). However, some number of spells, most notably
|
|
detection spells, must have their own entries because the code that
|
|
is actually executed needs to re-act accordingly.
|
|
|
|
SP_RAISE_DEAD (1)
|
|
These spells bring back dead players. These spells are only useful
|
|
on servers that use PERMADEATH.
|
|
|
|
Con: The number of con points the target character loses.
|
|
exp: the experience loss ratio (eg, exp -= exp/spell->stats.exp.
|
|
If zero, no exp is lost
|
|
last_heal: If set, corpse isn't required, just character name
|
|
randomitems: If set, then when the spell fails, the treasurelist is used
|
|
to determine what nasty monsters to summon. Note that this
|
|
list is not processed as a normal list - rather, everything
|
|
on the list is created, so chance values are meaningless.
|
|
race: If set, this is used as a treasurelist to determine what
|
|
class to change the character to (reincarnation). Basically,
|
|
it just looks up the treasurelist that is using the race name. This
|
|
should be a treasureone type list.
|
|
|
|
SP_RUNE (2)
|
|
Runes are objects that embed another spell in them, and are activated
|
|
when someone walks on top of the rune. Runes can be disarmed.
|
|
|
|
other_arch: This can be set to one of 3 values:
|
|
Name of rune (type = rune): If this is the case, rune is put on
|
|
the map with few changes.
|
|
name of spell: In this case, the spell is embedded into the the rune.
|
|
generic_rune object will be used, with the face updated.
|
|
NULL: in this case, the player specifies the rune to embed.
|
|
face: Face that the rune object will use.
|
|
|
|
If other_arch is set in the spell object, then it is assumed that
|
|
the sp (or grace) cost in the spell object is the total that should
|
|
be used. If it is an embedded spell, then the cost will be in
|
|
addition to that to cast the spell.
|
|
|
|
Special rules for runes where player can choose what spell to embed:
|
|
skill for the embedded spell must match the rune
|
|
Player must have enough mana/grace to embed the spell as well as cast
|
|
the actual rune.
|
|
|
|
Note that runes created by players/monsters will have the spell put into
|
|
the rune's inventory. However, archetype runes will still use the other
|
|
arch, as inventory in archetypes isn't really supported.
|
|
|
|
This really works fine - This allows custom spells to be embedded into
|
|
runes. For archetypes, using the default spell archetypes work fine.
|
|
|
|
SP_MAKE_MARK (3): This makes a 'mark' that contains a message. In the old
|
|
system, this used to be part of the rune code, but it really doesn't
|
|
have anything in common with runes (except in name).
|
|
|
|
other_arch: Object used to store the message.
|
|
|
|
SP_BOLT (4):
|
|
This fires a bolt (other_arch) of the spell object. All of the
|
|
following values are copied from the spell object and into the
|
|
bolt object (other_arch in the spell object).
|
|
|
|
damage, attacktype, slaying have normal meaning
|
|
|
|
range (exp): How many more spaces this will go before it ends.
|
|
duration (hp): How long it stays on each space.
|
|
Dex: chane (percentage) the bolt has of forking
|
|
Con: Chance that the bolt forks left.
|
|
|
|
Note if duration is less than range, which it should typically be,
|
|
then a bolt will only be in a subsection of the entire range.
|
|
|
|
A bolt object can have the generator flag set, in which case as
|
|
the bolt moves along, it will create an other_arch object. The
|
|
steambolt is an example of this. This can not be set in the spell object,
|
|
and is instead set in the other_arch field that the spell refers to.
|
|
|
|
SP_BULLET (5):
|
|
Bullets are single objects that go in a direction and hit
|
|
something. The simplest case here is magic bullet, that hits
|
|
something and does damage.
|
|
|
|
exploding ball type spells are a more complicated variation. These
|
|
object start out as a bullet, but when they hit something, they
|
|
explode, into fire or poison or the like. The other_arch field
|
|
determines what they explode into.
|
|
|
|
Values for spell object
|
|
range: diameter of the explosion (copied into maxhp).
|
|
Note that for the bullet itself, the range is always set to 50.
|
|
other_arch: bullet object to create.
|
|
dam: how much damage the bullet does.
|
|
attacktype: copied to the bullet.
|
|
slaying: copied to the bullet.
|
|
duration: How long the explosion lasts for. Not used for bullet
|
|
only spells. copied into hp field of the bullet object.
|
|
food (& dam adjust): Damage the explosion does. copied into the bullet
|
|
dam_modifier field.
|
|
|
|
See notes about explosion below.
|
|
|
|
SP_EXPLOSION (6):
|
|
Objects of subtype explosion are really only useful when attached
|
|
to a bullet - you don't really want to explode something on yourself.
|
|
|
|
Note that the set up of the below values is also the same for
|
|
SP_CONE types if a cone is attached to a bullet.
|
|
|
|
attacktype: from other_arch, but add magic if bullet has it.
|
|
damage: this is copied over the the bullet dam_modifier. The
|
|
bullet dam_modifier is based on the 'food' value
|
|
range: Copied over from stats.maxhp. This determines the radius of the
|
|
explosion.
|
|
duration: copied over from stats.hp. This determines how long the
|
|
explosion lingers.
|
|
|
|
Given two objects are created from one spell, tuning this/creating
|
|
new spells of this type is tricky. We can only use the range for
|
|
the bullet or explosion. Likewise, adjusting damage is difficult.
|
|
Ideally, perhaps, some additional fields in the spell object itself
|
|
are used to fully implement this, eg, these two fields determine the
|
|
range/damage of the bullet, and these 3 determine range/blast/damage
|
|
of the ball itself. That is the only real way to make a ball
|
|
spell fully adjustable.
|
|
|
|
SP_CONE (7):
|
|
sp is the overall direction of the cone is moving in.
|
|
other_arch: object to create as it moves along.
|
|
flag_lifesave: if true, object never goes away (eg, lava)
|
|
duration: This is how many ticks the spell stays on space.
|
|
flag_stand_still (was food): Causes the object not to progress
|
|
|
|
Note that SP_CONE can be the other_arch of a SP_BULLET. In this
|
|
case, the set up of the cone values is as described in SP_EXPLOSION
|
|
setting.
|
|
|
|
Note 2: The other_arch that is in the cone can really be any object.
|
|
As such, the code does not modify this object, other than to
|
|
set proper ownership. As such, you can't set what values will be
|
|
set in the arch thus created
|
|
|
|
vitriol is the best example of both of these notes - it starts out
|
|
as a bullet, which then turns into a cone, which then drops acid
|
|
pools.
|
|
|
|
SP_BOMB (8):
|
|
Bombs are as they are described - objects that sit around for
|
|
a little while before exploding.
|
|
|
|
The bomb itself is harmless - its not until it explodes do bad
|
|
things happen.
|
|
|
|
In the bomb object, the following attributes are set up:
|
|
dam: damage of the flying fragments/explosion
|
|
range: how far the fragments/explosion extends.
|
|
duration: How long the explosion lasts.
|
|
attacktype: Type of damage the bomb does.
|
|
|
|
bombs tick at a constant rate, as defined by there speed. They explode
|
|
when the finish their last animation. There is no way to adjust
|
|
this rate of ticking in the spell object, other than to point
|
|
other_arch at a customized bomb.
|
|
|
|
A bomb then explodes. It explodes into 'splints' (this is
|
|
unfortunately hardcode), and the other_arch pointer of the bomb,
|
|
which default is of SP_EXPLOSION.
|
|
|
|
SP_WONDER(9):
|
|
wonder is a spell that will cast another spell. It will also sometimes
|
|
cast a cone of flowers (other_arch).
|
|
|
|
randomitems: treasure list to look for spell to cast.
|
|
|
|
Note old wonder code would only cast spell that would be found in
|
|
books. This new code can be used to cast most any spell, as defined
|
|
as in the treasurelist.
|
|
|
|
Note 2: Ideally, the flower effect should also be in this treasure list,
|
|
so the occurrence could be accurately controlled.
|
|
|
|
SP_SMITE(10):
|
|
smite spells are target spells - the spell looks for an appropriate
|
|
enemy in the direction pointed, and if one is found, hits them with
|
|
whatever is in the other_arch. It is generally assumed that other_arch
|
|
will point to another spell.
|
|
|
|
other_arch: Object to create.
|
|
dam, range, duration (& modifiers) are copied into the spell effect
|
|
that other_arch points to.
|
|
attacktype: Attacktype to give to the spell effect.
|
|
|
|
Special notes if attacktype includes a death attacktype (finger
|
|
of death):
|
|
Won't work on undead - makes them strong.
|
|
the level of the created object is set to the dam (& modifier)
|
|
of the spell.
|
|
|
|
|
|
SP_MAGIC_MISSILE(11):
|
|
magic missiles are really just a bullet type object that have
|
|
some more intelligence in their guiding (but not much). Basically,
|
|
a magic missile will more or less go in the direction it is fired,
|
|
but will adjust course to hit a monster. Thus, if something isn't
|
|
perfectly lined up, magic missile will still hit it.
|
|
range, attacktype, etc, all have normal meanings.
|
|
|
|
|
|
SP_SUMMON_GOLEM(12):
|
|
This spell summons a friendly creature/object that helps out the
|
|
player.
|
|
|
|
other_arch: Pet/object this spell creates.
|
|
race: If set to 'holy servant' or 'avatar', this is now a
|
|
summon avatar spell, whose monster is determined based on the
|
|
god being worshipped. Note that these names are just passed
|
|
to determine_holy_arch()
|
|
dam: How much damage the golem will do. dam_modifier alters this in the
|
|
expected way. If zero, damage from the golem is used, but damage
|
|
modifier will still be added.
|
|
duration: increases the golems hp, which basically amounts to duration.
|
|
attacktype: If set, golem gets this attacktype instead of the
|
|
normal arch attacktype.
|
|
range_modifier: This adjust the wc and speed of the creature.
|
|
|
|
golems lose 1 hp each time they do something. This effectively
|
|
limits their duration, but does mean it can be extended by healing
|
|
the golem. Not sure if this is the way to go, or if duration
|
|
should really just be used.
|
|
|
|
Note - the old code had ldam and ldur have multiple meanings, and
|
|
the code multipled them by 10 internally. This is no longer done -
|
|
instead, the archetype itself should have the real value (10 times
|
|
the old compiled in values)
|
|
|
|
SP_DIMENSION_DOOR(13):
|
|
dimension door lets the player move some large number of spaces.
|
|
One still can not teleport through no magic areas.
|
|
range: How far we will move the player
|
|
maxsp, level_modifier: Normal spell meaning.
|
|
|
|
SP_MAGIC_MAPPING(14):
|
|
This draws an overview of the map around the player. There are currently
|
|
no parameters the spell takes - the results are always the same. One could
|
|
forsee parameters to change how far one sees, what one sees, etc.
|
|
|
|
SP_MAGIC_WALL(15):
|
|
This spell creates a magic wall. Walls can have a wide set of
|
|
properties. Many are determined by the other_arch or race (the wall
|
|
the spell creates).
|
|
|
|
other_arch: wall to create.
|
|
|
|
race: Name of the object to summon. This should include the %d.
|
|
eg, 'director_%d'. Note that if the object is non directional,
|
|
just don't include the %d in the race. Note that if both other_arch
|
|
and race is set, then other_arch takes precedence
|
|
|
|
move_block: If set, then the object can not be created on the space the
|
|
player is standing on. In all cases, the object can not be
|
|
created on a space that is blocked by an object.
|
|
|
|
is_used_up: If set, then this flag is set in the created object
|
|
|
|
tear_down: If set, this flag is then set in created object, as well
|
|
as flag_alive
|
|
|
|
range (and range_modifier): How many spaces in each direction the wall
|
|
should go. If this is 1, the wall will be 3 spaces total (1 + 1 in
|
|
each direction). If this is 0, the wall is a single space.
|
|
|
|
attacktype: Attacktype to set on the created wall.
|
|
duration (and duration_modifier): The number of hit points/duration
|
|
of the wall.
|
|
dam and dam_modifier: Damage the wall does.
|
|
|
|
Exactly how the a attacktype, duration, and damage values play out
|
|
depend on what is being created and the spell.
|
|
|
|
Created object is of type SPELL_EFFECT: attacktype, dam, and duration
|
|
are copied into fields of same name.
|
|
|
|
Created object is alive: duration becomes the objects hit points.
|
|
|
|
is_used_up (either in spell, or created object): duration put
|
|
into objects food value.
|
|
|
|
tear_down (in spell only): Spells damage value is copied into
|
|
objects hp/maxhp
|
|
|
|
The level of the created object is always set to half that of
|
|
the caster level.
|
|
|
|
SP_DESTRUCTION(16):
|
|
Destruction hits all the spaces within some distance of the player
|
|
with damage.
|
|
|
|
range (& modifier): Number of spaces in each direction to do damage to.
|
|
dam (& modifier): Amount of damage to do to each creature.
|
|
attacktype: Attacktype to hit the creatures with.
|
|
other_arch: This can in practice be anything, but is typically just used
|
|
as an effect to show what spaces where hit.
|
|
race
|
|
|
|
Note: As converted to a spell object, this is now usable by monsters.
|
|
The rule for what is damaged is basically this:
|
|
If cast by a player/friendly creature, damages non players/non friendlies.
|
|
If cast by a monster (non friendly), damages players/friendlies.
|
|
|
|
SP_PERCEIVE_SELF(17):
|
|
This is really just a hook to know to call the right routine - at
|
|
current time, none of the dam/range/duration fields of the spell
|
|
have any meaning - only think that is used is the grace and skill
|
|
pointers.
|
|
perceive self basically presents information to the player about themselves,
|
|
eg, race, any depletions, etc. Generally, isn't that useful since most
|
|
all the same information is available with built in commands.
|
|
|
|
SP_WORD_OF_RECALL(18)
|
|
Word of recall teleports the player back 'home' after some amount
|
|
of time. Parameters:
|
|
duration: How many ticks until the player is returned hom.
|
|
duration_modifier: This _reduces_ the duration above so that the player
|
|
return home quicker.
|
|
|
|
|
|
SP_INVISIBLE(19):
|
|
Makes the character invisible to monsters.
|
|
duration (& duration_modifier): How long to be invisible for.
|
|
race: What to make the player invisible against. Note this
|
|
only applies for when players cast invisible - monsters
|
|
will always be invisible to players, and not care about
|
|
the race field. race 'undead' has a bit special meaning,
|
|
as it will chack against the flag_undead field. race can
|
|
be NULL, in which case it will make the player invisible
|
|
to everything _but_ undead. Note also that check is done that
|
|
'race' a subset of the race of the monster (eg, don't use
|
|
comma separated values).
|
|
It is not possible to be invisible to both undead and non
|
|
undead at the same time.
|
|
make_invisible: if set, this is 'improved' invisible vs temporary
|
|
invisibility that disappears when attacking.
|
|
|
|
SP_PROBE (20): This spell looks in some direction (as specified by the player)
|
|
and returns information about a monster.
|
|
range (& modifier): How far off in a direction to look.
|
|
|
|
SP_HEALING(21): This spell heals a character/monster. Creature must
|
|
be where the player is.
|
|
attacktype: Attacktypes to heal:
|
|
AT_DISEASE: cure diseases.
|
|
AT_POISON: remove poisoning.
|
|
AT_CONFUSION: remove confusion.
|
|
AT_BLIND: remove blindness
|
|
stats.food: Food amount to add to character.
|
|
stats.dam: how much to heal.
|
|
stats.hp: Number of d6 to roll for healing.
|
|
last_sp: Regenerate this number of spell points.
|
|
last_grace: Regenerate this number of grace points.
|
|
(since the sp and grace fields are used for how much it costs
|
|
to use the spell, we can't re-use those fields unfortunately)
|
|
other_arch: seems unused - kept could be used as a spell effect
|
|
indicator.
|
|
Note that any number of these fields can be combined.
|
|
|
|
SP_CREATE_FOOD(22): This creates food for the player. The food is
|
|
not worth any money, but otherwise normal food.
|
|
food: Base amount of food to create.
|
|
duration_modifier: Creates an addition 50 food/increase of this value.
|
|
|
|
The formula for how much food value is created is:
|
|
food_value=spell_ob->stats.food +
|
|
+ 50 * SP_level_duration_adjust(caster,spell_ob);
|
|
|
|
SP_EARTH_TO_DUST(23): This destroys earthwalls near the player.
|
|
range (& modifier): Radius to destroy earthwalls in. Note that
|
|
this is a 'square' radius.
|
|
|
|
SP_CHANGE_ABILITY(24): this adjust some ability the player has (str,
|
|
dex, attacktypes, etc). This is only for beneficial effects - this
|
|
is mostly because of the targetting logic (negative effect would
|
|
still target friendly creature).
|
|
|
|
By default, only one benefit spell of each time can be active at a
|
|
time (eg, you could have a strength and a dex, but not two strength
|
|
spells). This is normally tracked by changing the force name to
|
|
be the same name as the spell, but see the race attribute below.
|
|
|
|
If a spell is already in effect, the duration of the existing
|
|
spell will be increased, but otherwise none of the spell effects
|
|
will be changed.
|
|
|
|
race: If this is set, this is used instead of the spell name when
|
|
renaming the force. This is useful if you only want one of a group
|
|
of spells active at once (this is currently used for bless &
|
|
holy possession - you're allowed only one.) name_pl of the force
|
|
will always contain the other spell name.
|
|
|
|
duration (& modifier): How long the spell lasts for. Note that duration
|
|
is in ticks. Note old system used damage for duration, and each
|
|
point of damage was basically 50 ticks. Note also that duration_modifier
|
|
is a bit difference - this increases durations in 50 ticks - this
|
|
is because most of these spells are long duration, and increasing
|
|
a 200 duration spell by 15 ticks if the caster is really high
|
|
level is pretty meaningless.
|
|
|
|
resist_*: The protection the spell grants. Note that the code does
|
|
no sanity checking on these values, other than to make sure they
|
|
do not exceed 100. Before spell objects, there was code to put
|
|
caps on these values. There is no practical way to say what the
|
|
caps should be if so put on - instead, just make sure that
|
|
that dam_modifier is a sane value.
|
|
|
|
dam_modifier: Increases the resistance granted by 1 point.
|
|
|
|
ac: Benefit to armor class. This is copied over unmodified.
|
|
wc: Benefit to weapon class. This is copied over unmodified
|
|
hp: Copied over, but damage adjustment added as a bonus. hp
|
|
in the form of a force increases characters hp regen rate.
|
|
|
|
Str, Dex, Con, Wis, Int, Pow, Cha: Attempted adjustment to the
|
|
stat. These values are number of d3's rolled to adjust. In
|
|
addition, these values also determine the max benefit based on the
|
|
recipient - the table is 15 + 5 * attr_value. If 1, then the
|
|
highest the recipients stat after this would be 20. If 2, highest
|
|
would be 25, if 3, highest would be 30, etc.
|
|
|
|
flying, can_see_in_dark, xrays: Copied over to force -
|
|
gives caster this ability.
|
|
|
|
attacktype: Added to force object, giving the caster the attacktype.
|
|
|
|
exp: Copied over - this acts as bonus speed. This value is adjusted
|
|
so really fast players get less a benefit than slower players.
|
|
|
|
SP_BLESS(25):
|
|
This blesses the character - the character gets various benefits
|
|
based on the god he worships. Note that BLESS spells use the same
|
|
type of force object as ability change above, include the race
|
|
to prevent multiple castings. Note that most of all of the values
|
|
are copied _from the god_. The value in the spell_object is only
|
|
really used to know if the value from the god should be copied.
|
|
|
|
|
|
resist_godpower: The character gets the resistance benefits that his
|
|
god gives (which can be anything). resist_godpower is the upper limit
|
|
of any benefit the character will get.
|
|
|
|
last_grace: If set, players get same attunement as their god.
|
|
|
|
attacktype: If non zero, the player gets the attacktype of the god.
|
|
The character also gets the slaying of the god if attacktype is set.
|
|
|
|
ac, wc: Copied over from spell_ob to force.
|
|
|
|
SP_CURSE(26):
|
|
This is a lot like BLESS. Notable difference is that this is a bad
|
|
effect for the recipient.
|
|
|
|
ac,wc: Copied over from spell_ob - should be negative, since these
|
|
should be penalties.
|
|
|
|
last_grace: If set, recipient is denied/repelled to spellpath that the
|
|
god is denied/repelled with.
|
|
|
|
race: Same as for SP_BLESS, in that it can be used to limit number
|
|
of course type effects.
|
|
|
|
range: Range of the spell.
|
|
|
|
SP_SUMMON_MONSTER (27):
|
|
This covers a large subtype of monster/object summoning. At its
|
|
very basic, this spell will create an object that is unchanged
|
|
from the archetype, and thus can be used to basically create any object.
|
|
|
|
other_arch: Object to create.
|
|
|
|
randomitems: If set, a treasurelist to use to select the monster from.
|
|
The randomitems list for these spells is a bit special - the magic
|
|
entry is what the monsters effective level is. One can not summon
|
|
monsters of higher level than they are. Note that magic values
|
|
should be unique, as the list will use the highest value available.
|
|
So if two identical magic values are used, for different objects,
|
|
only the second entry would actually have monsters created.
|
|
In addition, the magic values should be in order.
|
|
|
|
race: If set to "GODCULTMON", this is a special spell that will
|
|
create monsters associated with the players god. We can't use
|
|
either of the two fields to denote this, because they point to
|
|
other structures, and what monsters we should create can not be
|
|
defined easily in the archs, as it will depend on what god the player
|
|
worships.
|
|
|
|
If multiple fields above are set, the order used is other_arch,
|
|
random_items, race. Only one of the values will be used - you can't
|
|
get multiple things created by setting up multiple values.
|
|
|
|
attack_movement (move_type): If set, this is set in the monster. Thus, if
|
|
the object should be a pet, move_type should be set to PET_MOVE.
|
|
|
|
dam (& modifier): Number of creatures to create. In the case of
|
|
spells that use randomitems (which can specify their own nrof), this
|
|
is acted as additional monsters.
|
|
|
|
flag_monster: If set, creature will not be friendly. If cleared,
|
|
flag_friendly will be set on the monster (using the flag_friendly
|
|
in the spell_ob itself is problematic in that there is a special
|
|
list for friendly objects, and we really don't want the spell on that
|
|
list.
|
|
The reason not all summoned monsters are friendly is because there are
|
|
some spells that create monsters specifically meant to be aggressive
|
|
to the player.
|
|
If flag_monster is set, the monster/created object is not changed.
|
|
Only if it is not set, do we set a value (flag_friendly).
|
|
|
|
SP_RECHARGE (28):
|
|
This spell is used to recharge a wand.
|
|
|
|
dam (& modifier): Number of 'spell levels' to recharge. Basically,
|
|
the number of charges increase in the wand by dam / wand spell level.
|
|
Thus, very low level wands are easier to recharge than high
|
|
level wands.
|
|
|
|
SP_POLYMORPH (29):
|
|
This spell by default is disabled because of various abuses within
|
|
the spell.
|
|
|
|
The spell normally turns one object into another similar type of
|
|
object (eg, orc to goblin, sword to dagger, etc).
|
|
|
|
range (& modifier) How far the polymorph spell extends.
|
|
other_arch: Visual effect to draw on the map as it moves along.
|
|
|
|
SP_ALCHEMY (30)
|
|
This turns nearby objects into gold nuggets.
|
|
|
|
duration (& modifier): This is multiplied by 1000 to determine the
|
|
amount of material that will be converted (think of these values
|
|
as the amount of kilograms to be converted)
|
|
|
|
SP_REMOVE_CURSE (31)
|
|
This removes the cursed/damned status of objects. It only
|
|
effects objects equipped by the player.
|
|
|
|
cursed: uncurse cursed objects.
|
|
damned: remove damnation.
|
|
|
|
SP_IDENTIFY(32)
|
|
This identifies objects in the players inventory and on the ground.
|
|
dam (& modifier): The number of objects to identify
|
|
|
|
SP_DETECTION(33)
|
|
This detects nearby objects (including ones in the players inventory).
|
|
Detection in this context typically means revealing properties about
|
|
the objects.
|
|
|
|
range (& modifier): How big a radius to look for objects. Radius
|
|
in this context is really a square - the size of the square would
|
|
be 2*range + 1, with the player being in the center.
|
|
|
|
other_arch: The spell effect to draw on the map for spaces that match
|
|
the object. Note that for some detection types, the face is replaced
|
|
by the face of the matching object.
|
|
|
|
This following attributes determine what objects to show. Note that
|
|
more than one of these can be set (for a 'detect all spell' for example).
|
|
|
|
make_invisible: If true, show invisible objects
|
|
known_magical: If set, detect magical objects.
|
|
flag_monster: If set, detect monsters
|
|
known_cursed: If set, detects cursed objects.
|
|
race: If set, only detects creatures of this race. if race is
|
|
'GOD', then it uses the god's slaying race for the match.
|
|
|
|
SP_MOOD_CHANGE (34):
|
|
This changes the 'mood' of a monster. See below for how moods are
|
|
changed.
|
|
|
|
range (& modifier): How for this spell effects, just like SP_DETECT above.
|
|
race: If set, only monsters of matching race are effected. Can be set
|
|
to GOD_SLAYING, in which enemy races of the god are effected.
|
|
if set to GOD_FRIEND, then races aligned with the god are effected.
|
|
attacktype: Attacktype the spell hits with. The spell doesn't do any
|
|
damage - this instead is used for saving throw information. Note
|
|
that the monster uses its best resistance for the saving throw.
|
|
EG, the more attacktype the spell has, the better the odds.
|
|
|
|
berserk (flag): Set the monster to be berserk, which basically means
|
|
it attacks anything (conflict spell)
|
|
unaggressive (flag): Sets the monster to be unaggressive. (pacify)
|
|
undead (flag): By default, undead are immune to mood changes. however, if
|
|
this flag is set, undead will be effected (limited by other criteria
|
|
above, like race (if set) and saving throw)
|
|
no_attack (flag): Make the creature friendly (charm & charm monster))
|
|
monster (flag): Wakes the monster up, makes it an enemy of the player
|
|
(aggravation)
|
|
|
|
Note that a spell could set multiple of these fields, but it wouldn't
|
|
really do much - a monster can really only have one mood.
|
|
|
|
SP_MOVING_BALL (35): This is really only used as a spell effect. Values
|
|
of note:
|
|
|
|
path_repelled: If set, the player must be attuned to these paths
|
|
to cast the spell. This can basically be used to control
|
|
who can cast the spell.
|
|
|
|
duration (& modifier): Copied into food of the object - this is
|
|
basically how long the spell lasts for.
|
|
dam & attacktype have expected meaning.
|
|
other_arch: Object that is used for the manifestation of the spell.
|
|
Must be set for spell to work.
|
|
|
|
SP_SWARM (36)
|
|
swarms are spells that fire other spells. It fires the same spell,
|
|
but multiple times (eg, 5 lightning bolts).
|
|
|
|
Set during casting:
|
|
duration: Number of d3 to roll to determine number of sub bolts.
|
|
duration_modifier: Added to duration as a bonus.
|
|
other_arch: Name of the other spell it fires.
|
|
|
|
SP_CHANGE_MANA (37)
|
|
This gives/takes spellpoints to the target of the spell. If this spell
|
|
gives sp, the the cost should be more than the sp it gives. target must
|
|
be in the same or adjacent space.
|
|
|
|
dam (& modifier): number of sp to transfer. If dam is negative, then this
|
|
spell takes away, and dam and modifier represent what percentage of sp
|
|
to drain (dam_modifier should still be positive). These sp are then
|
|
given to the caster of the spell.
|
|
|
|
This subtypes corresponds to the old transferrence and drain magic
|
|
spells.
|
|
|
|
SP_DISPEL_RUNE (38)
|
|
This removes runes from the ground. There are currently no
|
|
tunables for this spell.
|
|
|
|
SP_CREATE_MISSILE (39)
|
|
Creates bolts or arrows.
|
|
|
|
dam (& modifier): max plus of arrow that can be created.
|
|
duration (& modifier): Number of arrows to create.
|
|
|
|
Note that making good (highly magical) arrows reduces
|
|
number of arrows to be made.
|
|
|
|
SP_CONSECRATE (40)
|
|
This converts an altar to that of players god. There are currently
|
|
no tunables to this spell.
|
|
|
|
SP_ANIMATE_WEAPON (41)
|
|
This spell is similar to the GOLEM spells - it basically creates
|
|
a golem, but uses a donor weapon for the face, basic attributes,
|
|
etc. To implement this, it puts the donor weapon in the inventory
|
|
of the golem, so when the golem dies, the weapon is returned
|
|
to the ground. Note that in the conversion, I modified this
|
|
spell to use the weapon 'marked' by the player, instead of the
|
|
equipped weapon.
|
|
|
|
other_arch: The object used to encapsulate the weapon.
|
|
race: If set, the donor weapon must match this name. If this is not
|
|
set, then the face of the golem will be set to the weapon.
|
|
The reason for this is that if race is set, then since it
|
|
is matching a specific weapon, it is presumed that the
|
|
other_arch field can be appropriate set.
|
|
range_adjust: Bonus to wc and speed for the spell
|
|
duration & modifier: Bonus to creatures hp.
|
|
dam & modifier: Adjust to damage the golem does.
|
|
|
|
SP_LIGHT (42)
|
|
Arguably, such a basic spell wouldn't seem to need its own subtype.
|
|
However, looking at the other spells, it really didn't fit in
|
|
very well elsewhere - the magic_wall code passes most of its
|
|
parameters to the object it creates. Something like summon_monster
|
|
doesn't work, because it wants a free space to put the object.
|
|
And while it would be nice to somehow merge create food, create
|
|
missile, and this, they are sufficiently different that they don't
|
|
work very well. So instead, I try to abstract this as much
|
|
as I can.
|
|
|
|
attacktype: If set, and there is a monster on the target
|
|
space, the monster gets hit with the attacktype and the
|
|
damage setting. If this happens, the spell progresses no
|
|
further.
|
|
dam: As per note above, how much damage this does.
|
|
|
|
other_arch: Object to place on the map.
|
|
duration: set the the 'food' value in the created object -
|
|
if is_used_up is set in the arch, this is how long the
|
|
spell lasts for.
|
|
range: If the created object has a glow radius, range
|
|
is used to replace it. In this way, we don't make non
|
|
glowing objects glow, but do make ones that do glow
|
|
glow brighter.
|
|
|
|
SP_CHANGE_MAP_LIGHT (43)
|
|
This changes the light level on the map. There is only one
|
|
tunable:
|
|
|
|
dam: amount to change the lightlevel. This is passed to
|
|
change_map_light, as such, negative values are lighter,
|
|
positive is darker
|
|
|
|
SP_FAERY_FIRE (44)
|
|
Faery fire makes creatures within the area of effect glow.
|
|
This code uses the cast_destruction routine, but just handles
|
|
what happens when it finds a valid target.
|
|
|
|
range (& modifier): Number of spaces in each direction to do damage to.
|
|
duration & modifier: Potency of the spell - determines how long the
|
|
creature will glow for (in 10's of ticks)
|
|
other_arch: This can in practice be anything, but is typically just used
|
|
as an effect to show what spaces where hit.
|
|
|
|
Note: As converted to a spell object, this is now usable by monsters.
|
|
The rule for what is damaged is basically this:
|
|
If cast by a player/friendly creature, damages non players/non friendlies.
|
|
If cast by a monster (non friendly), damages players/friendlies.
|
|
|
|
SP_DISEASE (45)
|
|
disease spells infect players with a harmful disease.
|
|
|
|
range (& modifier): How far to look in the line for matching
|
|
targets.
|
|
other_arch: Disease to infect the target with.
|
|
duration_modifier: This is used to adjust the disease stats
|
|
in several ways:
|
|
wc += mod/2
|
|
magic += mod / 4
|
|
maxhp, maxgrace += mod
|
|
|
|
dam_modifier: Modifiers other aspects of the disease:
|
|
dam, maxsp, ac, last_eat, hp, sp: modified by full mod
|
|
last_sp: modifier by mod * 2
|
|
|
|
The disease code treats these modified values in its own way.
|
|
|
|
|
|
SP_AURA (46):
|
|
Auras create fields around the players that effect anyone that steps
|
|
into them.
|
|
|
|
other_arch: aura type to insert
|
|
duration: How long the aura lasts for. For this spell, each
|
|
duration_modifier is 10 ticks.
|
|
dam & modifier: How much damage the aura does.
|
|
attacktype: Attacktype of the aura.
|
|
|
|
SP_TOWN_PORTAL (47):
|
|
This creates two linked portals which the player can use to get
|
|
back and forth quickly.
|
|
|
|
other_arch: Name of the force object to use to mark the first portal
|
|
target location (town_portal).
|
|
race: Name of the object used to mark where the portals go
|
|
(town_portal_active)
|
|
slaying: Name of the object used for the actual exit objects
|
|
(town_portal_magic)
|
|
|
|
Note the spell otherwise doesn't have any tunables.
|
|
It should be noted that if different archetypes were made it,
|
|
it should be possible to have multiple town portal type spells that
|
|
lead to different destinations.
|
|
|
|
|
|
Old values:
|
|
hp: duration
|
|
exp: range
|
|
maxhp: set to a ->count variable, so spells can be uniquely identified.
|
|
|
|
SP_PARTY_SPELL (48):
|
|
document, or clean totally broken spell :)
|
|
|
|
SP_ITEM_CURSE_BLESS (49):
|
|
This will curse or bless player's marked item. Curse will not change
|
|
weight, just set the cursed flag. Bless will make the item god-given.
|
|
|
|
cursed: if set, item gets cursed.
|
|
blessed: if set, item gets blessed.
|
|
|
|
Note that if both cursed and blessed are set, cursed only applies.
|
|
Also, no check is made to not have an item both cursed and blessed.
|
|
|
|
------------------------------------------------------------------------------
|
|
5. Spell Casting Objects
|
|
|
|
As described in section 2 above, objects that cast spells (wands, rods,
|
|
scrolls, etc) contain the spell they cast in their inventory. When the player
|
|
uses the object, the code looks for an object in the inventory, and uses that
|
|
as the type of spell to cast.
|
|
|
|
Treasure lists are used to determine what spell goes in the object. Thus,
|
|
what spells show up in the objects is determined purely by the treasure lists,
|
|
and unique lists can be made for potions, scrolls, wands, horns, and even
|
|
different lists for heavy and light rods.
|
|
|
|
The value of the finished object is the value field in the spell object
|
|
multiplied by the value object in the original object (rod, wand, scroll).
|
|
|
|
For items that come in different levels, the value is also adjusted based on
|
|
the level of the object based on the difference of level. The
|
|
code for this is in common/treasure.c
|
|
|
|
The nrof field for the treasurelists for these objects have special meanings -
|
|
since the spell objects are invisible objects within the spell casting object,
|
|
the nrof field has no actual meaning. However, we borrow that meaning for use
|
|
in the parent object.
|
|
|
|
For wands, nrof is used for the number of charges the item has.
|
|
For scrolls, nrof is the number of scrolls to make. This overrides the
|
|
nrof value for the scroll itself - this allows for fine tuning number
|
|
of scrolls that show up for different spells.
|
|
|
|
RODS/HORNS:
|
|
----------
|
|
hp is the amount of 'energy' the rod currently has. when a spell is
|
|
cast, hp is reduced by the amount of sp/grace the spell takes up.
|
|
speed: how often the rod recharges. There used to be a much more complicated
|
|
way of of regenerating charges. Instead, each time a rod activates,
|
|
it regenerates 10% of its total power. Thus, a rod of speed 1.0 would
|
|
fully recharge in 10 ticks, a rod with speed 0.1 would fully recharge in 100
|
|
ticks.
|
|
|
|
This change in the way rods recharge now mean the speed of a rod can
|
|
be set in a map (or elsewhere), and that change would stick.
|
|
|
|
Within the archetype itself, the maxhp value determines the number of
|
|
spells the rod can hold before it needs to recharge again.
|
|
|
|
POTIONS/DUSTS:
|
|
--------------
|
|
potions and dusts (which were really just potions with a is_dust flag set)
|
|
have been redone in several ways.
|
|
|
|
First, potions had varying meanings for the same archetype. You could
|
|
have potions that improve stats permanently, ones that cast spells,
|
|
and dust.
|
|
|
|
|
|
There is now a SPELL_POTION (116) type. This is used for potions that
|
|
ast spells. These type of potions really never should have been the
|
|
same type in the first place - other than name, they really had
|
|
none of the same code.
|
|
|
|
|
|
FIREWALL (62)
|
|
----------------------------
|
|
These objects are very basic - they cast a spell whenever they activate.
|
|
If they have a spell object in their inventory
|
|
(must be first item), that is the spell that is cast. Otherwise, they
|
|
cast what other_arch points to.
|
|
|
|
Firewalls can be activated by buttons, and can also cast spells in specific
|
|
directions. The direction the firewalls fire in is stored in the 'sp' field
|
|
of the firewall.
|
|
|
|
Note that FIRECHEST (61) got folded into FIREWALLS, because functionally,
|
|
they were identical - just set 'sp 0' in the firechest, and it fires
|
|
in a random direction.
|
|
|
|
------------------------------------------------------------------------------
|
|
6. Arch Directory Layout
|
|
|
|
This section describes the basic layout of the archetypes in the arch
|
|
directory. This explanation is here to try and prevent confusion (where
|
|
should this arch go), where would I find an arch, etc.
|
|
|
|
I thought about this a bit - would it be better to organize spells by
|
|
attacktype (eg, all fire spells together, all cold spells, etc), or by type of
|
|
spell (bolt spells, bullet spells, cone spells, etc).
|
|
|
|
I think both methods have valid reasons for and against them. I decided to do
|
|
it by spell type because I think it will make it easier to compare spells.
|
|
|
|
For example, if one bolt spell does 20 damage, and another does 30 damage,
|
|
even if by another attacktype, pretty easy to see that the later is more
|
|
potent.
|
|
|
|
This also organizes the spells more by their subtype, which is the more
|
|
standard way the arch's have been done in the past. It makes for designing
|
|
new spells much easier (you'd just copy a starter arch from the same
|
|
directory, and not need to hunt for another one - imagine something like
|
|
acid bolt).
|
|
|
|
That said, the organization (all relative to the arch top level directory)
|
|
|
|
magic: This directory goes away. There are many things that are magical
|
|
that are not spells, so having a directory named magic is IMO not the
|
|
best of name:
|
|
|
|
spell: top level directory. This directory is for the spell archetypes,
|
|
and is not meant to contain non related spell code. thus, the
|
|
pentagram (actually a teleporter), and the weapon improver scrolls
|
|
(not really in any way related to spells) would get relocated.
|
|
|
|
Bolt: Contains the bolt spells
|
|
Bullet: Bullet spells.
|
|
Common: Contains objects that are related to spells in many areas,
|
|
eg, the burnout is used by many types of spells, and things like
|
|
a flaming square or icy square are used for cone, bolt, and
|
|
exploding ball spells - those would be located here.
|
|
Cone: Contains cone spells
|
|
Healing: healing spells
|
|
Potions: Contain the various potions.
|
|
SeekingBall: spells like ball lightning
|
|
Spellbook: spellbook/prayerbook objects.
|
|
Wand: Contain the wand, staff, rod archetypes.
|
|
|
|
There are almost certainly sub types I'm missing that I'll have to fill in.
|
|
The idea here is to try to sketch something out that gives me a working
|
|
layout to fill things in.
|
|
|
|
Within each of the spell subtype directories, the entries for the spell
|
|
information would be needed. And example below:
|
|
|
|
spell_lightning_bolt.arc: This is the spell object that goes in the player/
|
|
monster inventory that says they know the lightning bolt spell. As
|
|
per other documentation, this also contains things like the skill needed
|
|
to use this spell, spell point cost, level cost, etc.
|
|
|
|
lightning_bolt.arc: this is the other_arch of the spell_lightning_bolt.arc.
|
|
Few values in this are actually used - most of the values come from
|
|
the parent arc. However, this (lightning_bolt.arc) contains the
|
|
information like what the animation for the spell look like.
|
|
|
|
lightning_bolt.base.111.png (and so on) are the images used by the
|
|
lightning_bolt.arc arch.
|
|
|
|
In the case of subtype directories with lots of entries, it is likely that the
|
|
directories may then get broken up by things like attacktype of the spells.
|
|
|
|
------------------------------------------------------------------------------
|
|
7. PROGRAMMING NOTES
|
|
|
|
The number of top level object types is reduced - instead, many are now
|
|
SPELL_EFFECT, with the subtype being the same as the spell that created
|
|
them.
|
|
|
|
The server/time.c still has a dispatch for the SPELL_EFFECT, but it
|
|
is in server/spell_util.c which determines how each subtype should
|
|
be handled.
|
|
|
|
I try to keep all the same spell related code together, eg, put the
|
|
code that casts the cone as well as moves the cone in the same file
|
|
next to each other. This should reduce bugs - if someone changes
|
|
one piece, they are more likely to notice the other piece and also update
|
|
that. This is better than having the function in a completely different
|
|
file.
|
|
|
|
spell_util.c really only contains very general code - dispatch routines,
|
|
funcitons to adjust spells, etc. The actual work is done in either
|
|
spell_attack.c, spell_effect.c, rune.c, or pets.c.
|
|
|
|
basically all the defines are in include/spells.h. This is a much simpler
|
|
file than once was here. Remember, all the data now comes from the
|
|
archetypes.
|
|
|
|
------------------------------------------------------------------------------
|
|
8. ABILITIES
|
|
|
|
In the old code, abilities had some special meaning in terms of what
|
|
spells the monster would cast.
|
|
|
|
In the new system, abilities are no different than spells, and use the
|
|
same type/subtype.
|
|
|
|
Abilities may still be separate for a few reasons: 1) monsters are not
|
|
likely to have all the needed skills. 2) sp/grace costs may be
|
|
different. 3) Many abilities shouldn't add attacktype magic. 4) Abilities
|
|
generally have a very fast casting time, since monsters move slower.
|
|
|
|
It should be noted that many creatures just use the spell, and not
|
|
the ability. Only some spells have ability counterparts.
|
|
|
|
Note: Before giving spell abilities to monsters, be sure that the monster
|
|
will actually use them - the code in monster.c results in monsters only
|
|
casting spells of certain subtypes.
|
|
|
|
maxsp: This increased the likelihood of monster choosing the spell.
|
|
(eg, maxsp 3 vs maxsp 1 would mean 3 times as likely). This should
|
|
instead by done in the treasurelist, so that more of that ability
|
|
is created.
|
|
|
|
hp/sp: Index for the spell that is used. One was 'short' range vs
|
|
long range.
|
|
|
|
------------------------------------------------------------------------------
|
|
9. SPELL MERGING
|
|
|
|
In May 2007, I (Mark Wedel) added code added causes spells to merge. This
|
|
section briefly defines what was done, performance impact, and other random
|
|
thoughts.
|
|
|
|
Before this merge logic, each spell, no matter how similar, was its own object
|
|
on a space. This meant that for some spells, there would be hundreds of
|
|
objects on a space. This doesn't seem that large until one looks closely at
|
|
the code - for each object inserted, all the other objects on the space had to
|
|
be examined, making this on O(n^2) operation.
|
|
|
|
Even reducing the number of objects on the space makes a significant
|
|
difference - the difference between 20 and 10 objects isn't a factor of 2, it
|
|
is a factor of 4.
|
|
|
|
The first step in the process was to write a test program
|
|
(test/unit/server/comet_perf.c) to provide consistent test runs - this not
|
|
only let me get performance numbers with a known environment, it also let me
|
|
generate a test case to make sure that the amount of damage comes out the
|
|
same (or close enough - in the revised code, the results were +/- 3%)
|
|
|
|
In the end, through the various changes, the code ran 70% faster than before.
|
|
|
|
Breakdown of gains:
|
|
Setting op->range in explosion() after propagating spell: 32%
|
|
Making was_destroyed() a macro: 9%
|
|
was_destroyed() + op->range changes 48%
|
|
Allowing spells to merge: 21%
|
|
|
|
As is noted, many of these changes have interactions - the range +
|
|
was_destroyed() makes a bigger change together than separately. And
|
|
the spell merging requires op->range=0 change, as described below.
|
|
|
|
Notes on the changes:
|
|
|
|
op->range=0 in explosion:
|
|
-------------------------
|
|
Each time the explosion object got an action, if op->range>0, it would try
|
|
to propagate to all neighboring squares. In almost all cases this is a
|
|
futile exercise, because if that neighboring square has an instance of this
|
|
spell, it won't put another one there (one thing that ok_to_put_more()
|
|
checks on). By setting the range to 0, we prevent this attempt. It also
|
|
has the side benefit of allowing more spells to merge (we can not merge
|
|
spells with different ranges). The one visible change this has is that if a
|
|
door/gate is closed but in the explosion area, in the old code, the
|
|
explosion would go through the space if that door is open - in the revised
|
|
code it won't do that, since it has already attempted to do so and was
|
|
unsuccessful.
|
|
|
|
The op->range check in explosion is actually a very important change -
|
|
important enough that we will only merge spells if the range is zero.
|
|
|
|
This can be illustrated with a small example. Use this diagram of spaces:
|
|
|
|
12
|
|
34
|
|
|
|
If a spell (A) is cast from the top, it will hit spaces 1&2 at the same time.
|
|
If there is another spell (B) cast from the right, it will hit spaces 2 and 4.
|
|
|
|
When the spells merge, there is now a single spell object on space 2 that
|
|
includes the tags of spells A & B. Spell AB@2 will not move to space 1 -
|
|
space 1 already has spell A, so ok_to_put_more() says no. Now spell B@4 could
|
|
move to 1, but if A@1 moves first, it merges with B@4, so now there is AB@4,
|
|
and like AB@2, it will not move to space 1 (spell is already there).
|
|
|
|
But enforcing the range==0 check, this works fine - if the spells are still
|
|
propagating to different spaces, we don't merge. So in the above example,
|
|
spells A & B @ 2 do not merge, but instead remain individual spell object - B
|
|
can move to 1, A can move to 4, and after doing so, the range to each spell is
|
|
set to zero, and they can then merge at that point
|
|
|
|
Making was_destroyed() a macro:
|
|
-------------------------------
|
|
This is pretty clear cut - by making it a macro, it reduces the function
|
|
call overhead. This was also a trivial 1 line function, so making it
|
|
a macro was not particularly messy.
|
|
|
|
Allowing spells to merge:
|
|
-------------------------
|
|
The simplest aspect of this is to have like spells be merged in
|
|
insert_ob_in_map(). However, as noted above, we won't propagate a spell to a
|
|
space if there is already an instance of that spell from the same casting. So
|
|
to do this, we needed to record the tags of the parent spell that we merge in,
|
|
so the spell_tags array (dynamically allocated) is created. For speed, a
|
|
simple hash is used to access the data in that array.
|
|
|
|
When merging spells, we make sure the spells are similar in many respects -
|
|
must be same type of spell, same owner, same level, etc. We then adjust the
|
|
damage and duration of the merged spell. We can not merge spells of different
|
|
ranges - one can not really associate what effect that should have on damage
|
|
|
|
There are some player noticeable effects to this - players will not see as many
|
|
spell objects on a space they are on, as some could have been merged together.
|
|
|
|
Also, the test case makes it so that damage for objects not moving within the
|
|
spell are the same. With different duration spells, if a character ducks out
|
|
quickly from the effect, they will take slightly less damage than before the
|
|
changes. Example: two spells with dam 50 each and duration 10 & 20. In the
|
|
first case, for the first 10 ticks, you would take 100 damage/tick, and for
|
|
the last 10, you would take 50. Under the merging, you would take 75 damage
|
|
for all 20 ticks. In most cases, the differences in duration will not be as
|
|
extreme, so the actual difference in damage will no be so much.
|
|
|
|
There is a single tunable to this right now - the SPELL_TAG_SIZE in object.h -
|
|
this determines the size of the hash table. In my testing, a size of 16
|
|
worked out pretty good. Note that the hashing method is very basic - if we
|
|
get a conflict in the hash table between 2 objects, we just say they can not
|
|
be merged - we do not use backoff methods, etc, which would complicate other
|
|
aspects of the code, notable merging the tables together. So a too small hash
|
|
table would result in not enough objects getting merged.
|
|
|
|
In the test case above, following results on my system with the 16 slot table:
|
|
Got 201640 suppressions, 8421 spell merges, 289 full tables
|
|
3.623u 0.060s 0:03.90 94.3% 0+0k 0+0io 0pf+0w
|
|
|
|
With a 32 slot table:
|
|
3.462u 0.031s 0:03.98 87.6% 0+0k 0+0io 0pf+0w
|
|
Got 201641 suppressions, 8748 spell merges, 249 full tables
|
|
|
|
Doubling the size of the table makes a minor improvement in full tables as
|
|
well as performance (.16 seconds is about 4%). And there is definitely a
|
|
trade off - if the tables are very large, it will take longer to merge the
|
|
tables when the objects are merged.
|
|
|
|
One note with the merge code - while the other changes give some definite
|
|
benefits, merging should show better results with the new objects - my tests
|
|
were done with castings of 30 comets - a reason for this was that with larger
|
|
number of comets, the old code would just never finish (it had run for several
|
|
minutes before I gave). Running the new code with it casting 100 comets now
|
|
actually still had it finish relatively quickly - under 10 seconds. The more
|
|
spells around, the more that could be merged to give a benefit - in
|
|
comparison, the op->range=0, while reducing the load quite a bit, still would
|
|
not reduce the number of objects on the different spaces.
|
|
|
|
Other Thoughts Related on Merging:
|
|
----------------------------------
|
|
After the changes above, this is the top CPU users when run in the test
|
|
program:
|
|
|
|
63.42 1.82 1.82 687168 0.00 0.00 hit_map
|
|
17.07 2.31 0.49 1500 0.33 1.86 process_events
|
|
2.79 2.39 0.08 37118 0.00 0.00 remove_ob
|
|
2.44 2.46 0.07 784262 0.00 0.00 update_object
|
|
2.44 2.53 0.07 713910 0.00 0.00 animate_object
|
|
|
|
ok_to_put_more() could be optimized - store the necessary information (spell
|
|
tags, attacktypes) in the MapCell itself, but it is far enough down the
|
|
performance chart there isn't a big win there. hit_map() has the same problem
|
|
as other routines - it needs to examine every object on the space. And in
|
|
most cases, the only things on the space are other spell objects, so it is
|
|
wasting time. It could be useful to store more useful information in the
|
|
MapCell about what is on the map space so that hit_map() could quickly decide
|
|
that there is nothing interesting and just not do anything. In particular,
|
|
hit map really only cares about 3 object types right now - living objects,
|
|
transports (which may contain living objects) and items that can be destroyed
|
|
by spells.
|
|
|