Most of these documents are EXTREMELY outdated and are kept
more for historical reasons than informative reasons. Most outdated is
the building information since Carnage no longer runs on a DIKU data
system but rather uses a custom XML structure. Despite all this the
information can be very useful when designing your area.

Creating Spells (The BlobbieScript Way)
---------------------------------------
As you may have read in the BlobbieScript documentation, the BlobbieScript
language is extremely flexible and power and opens up the possibility to do
almost any thing in script that can be done internally. the advantage to
implementing spells in BlobbieScript is that the logic and data can be
reloaded into the MUD server without the need for a reboot. This is an
extremely powerful feature during the tweaking stage of any new spell. For the
most part you will use the Web interface for setting spell data like skill %
gained per practice, how much mana is consumed, is the spell aggressive, what
classes can use the spell, and from what base stats (str, int, wis, etc) is
the spell derived. This document serves only to cover the scripting aspect.
An Example Spell
----------------
To greatly help with the learning process I will first provide an example
spell written in BlobbieScript and then i will continue by describing what
data it receives, and what specialized functions are available to minimise the
repetitive work necessary for creating a spell.
Armour Spell Example:
protect
{
%spellRoutineMobileTargets( group, 1 )
@duration = 24;
@duration = 1 MAX ((@duration * @quality) / 100)
foreach( @targets as @target )
{
%spellRoutineMobilesOnly( @target )
%spellRoutineCheckAllowedTarget( @actor, @target )
if( !%skillAffectExists( @target, @spellId ) )
{
%echoTo(
\ "You feel someone protecting you.\n", @target )
%act( "$n's armour shimmers for a moment.",
\ 0, @target, %null(), %null(), skipActor )
%skillAffectAdd( @target, @spellId, ac, -20, @duration )
}
endif
}
endforeach
}
endprotect
For the most part the script is fairly simple adhering to the constructs of
BlobbieScript and using various documented functions. There are however THREE
special functions also used:
- %spellRoutineMobileTargets( group, 1 )
- %spellRoutineMobilesOnly( @target )
- %spellRoutineCheckAllowedTarget( @actor, @target )
These are special because the rely on the script being structure a certain
way. More specifically the require the script be populated with data intended
for a spell script.
The first function, %spellRoutineMobileTargets(), uses the parameters given
and the variables predefined for a spell script to find the targets for the
spell script and populate them into the @targets array without any extra work
by you. This is extremely useful since otherwise the spell script must check
the type of caster, the type of targets, whther there are predefined targets,
etc. This function encapsulates a lot of low level repetitive checks and data
manipulation (which generaly contribute about 100 lines of code).
The second function, %spellRoutineMobilesOnly(), checks to see if a given
target is a mobile (PC or NPC) and if not then it automatically performs a
CONTINUE to jump script execution back to the top of the FOREACH loop. For
this reason it is important that you use the FOREACH loop when traversing the
@targets list. In this particular case the code saved is only a couple of
lines, but in most cases a lot of code is saved, and more importantly for the
implementors, the script EXACTLY follows the same format as internal C code
which uses macros to the same effect.
The last function, %spellRoutineCheckAllowedTarget(), checks that the @actor
(mobile, object, room, etc) is allowed to cast the spell on the @target. When
sepaking of allowed here, we mean in the sense that a guilded cannot attack a
non-guilded and vice versa. This principle isn't used in the game anymore, but
we retain it's functionality in case we should choose to revert to some system
of protection in the future.
These functions and several others that are specific to spells (denoted by the
%spellRoutineXxx() naming scheme) are documented fully in the BlobbieScript
%documentation.
Traversing Targets
------------------
In our example there is a FOREACH loop which we use to traverse the spell's
targets. You might be tempted to say:
"the armor spell only targets the caster or their victim so I
don't need a FOREACH loop"
This attitude is flawed (unless you have a really good reason for doing so).
The reason being that this same script is used by wands, potions, staves,
scrolls, etc. It may even be invoked by another script with a preset list of
targets. For this reason it is imperative that you use the FOREACH loop to
traverse ALL of the targets found.
As the loop moves through each target it checks to see if @target is already
affected by the armour spell:
if( !%skillAffectExists( @target, @spellId ) )
And if not then is applies the spell:
%skillAffectAdd( @target, @spellId, ac, -20, @duration )
With a little bit of output for the caster (if applicable) and the victim.
The @spellId variable is automatically populated for you as are many others.
It is useful enough to mention that where we have used @spellId we could also
have used "armour" for the same effect. It just happens though, that using the
ID is more efficient than using the name. It also ensures that if the name is
changed, that the spell continues to work properly.
Prepopulated Variables
----------------------
Many variables are prepulated into the script so that the script writer can
implement powerful spells with a minimum of effort. below are listed all of
the variables currently populated for the spell creator:
variable @ch:
If the spell has been invoked by a mobile AND @spellType is set to
"cast" then this will be set to the actor. This is a convenient
shorthand for sending output that should only be sent when the spell
has been cast by a mobile.
variable @actor:
This is a text pointer to the invoker of the spell. Note that it can
be any valid text pointer (room, mobile, object, etc). It may also be
%null() for an anonymous spell source. in the case of a wand, staff,
etc., this will be set to the user of the given artifact.
variable @master:
This is a text pointer to the master of the spell invocation. For
instance if you are forced, commanded, or suggested to cast the spell,
then you would be the @actor, and the person controlling you would be
the @master. In the case of a wand, staff, etc., this will be set to
the artifact. This can be %null().
variable @command:
This is the command input to facilitate the spell. For instance
"cast", or in the case of a staff, "use". It may also be other strange
commands like "throw" if the spell was the result of throwing a
potion. This can be %null().
variable @arguments:
This will contain all the arguments passed to the spell when it was
invoked. Normally this is used to determine the target of the spell,
but sometimes the @targets will have been preset and this will
generally be ignored. Note that when the spell is cast, this variable
will not contain the spell portion itself, it will contain everything
after the spell name. For instance "cast armour self" would result in
"self" being the value of @arguments.
variable @spell:
The name of the spell that has been cast. Generally this isn't needed
but it can be used in output where the spell's name is needed and will
prevent any problems if the spell's name is changed.
variable @spellId:
This is set to the ID of the spell. Spell creators have no control
over the ID assigned to their spells; however, they can see it in the
URL of the spell editor after it has been saved once. As shown in our
example this can be used to apply affects to a given @target.
variable @manaCost:
The amount of mana normally charged when this spell is cast. This will
have been subtracted before the script is run (if the spell was cast)
and is provided in case the spell creator has other needs for it
(perhaps to refund some mana based on some state of the caster).
variable @castingTime:
The amount of time the caster should be delayed (generally only
applicable to mobiles) when the spell has been cast. If your spell has
set the "shareScript" flag then the waiting period will have already
been handled by the default spell script wrapper.
variable @level:
This will be set to the level at which the spell was invoked. This can
and generally SHOULD be used to aid in the determination of spell
duration and power. It is most often used in combination with the
@quality variable.
variable @quality:
This will be set to the quality of the spell. This is a percentage and
should be used to modify the power or duration of a spell. In the case
of spells with boolean non-duration based spells, then this should be
used against a 1d100 dice roll to determine if the spell's affect
should occur at all:
if( 1d100 < @quality )
When a player casts a spell the @quality parameter calculated as a
measure of their effective skill level and of the source stats for the
spell. For instance 70% skill and a relevant stat of 15 (out of 19)
would result in a quality of about 55%.
variable @targets:
If the spell has been passed a preset list of tragets, then this
variable will be set; otherwise it will not be set at all. When set
your spell should ignore any targetting arguments and use this preset
list only.
variable @spellType:
This is set to the type of spell. Generally this provides information
about how the spell was invoked. The following are possible values for
this variable:
cast staff
potion scroll
wand other
For instance if the spell is the result of using a wand then this will
be set to "wand".
variable @commandUserFlags:
Generally you will never need this variable but it is provided in case
the event does arise. This variable contains any user flag settings
for the invoked command:
other pc
room npc
object nonPc
For instance if the "room" and "object" flags were set then only rooms
and objects could have issued the command that invoked the spell.
variable @commandSourceFlags:
This is set to any of the command source flags that were set when the
command was issued. The following are possible flags:
gameLoop lordOrdered
trigger commanded
forced suggested
ordered
Some of these can be very useful to your script. For instance you
might check to see if the command was suggested, commanded, or ordered
and provide alternate functionality as a result.
variable @commandAttributeFlags:
This variable will contain any attribute information for the command
that was issued. The following are recognized:
isRepeatable isGhostAction
doSpecials noForce
noOverride noOrder
fullTrigger noLordOrder
isAction skipCommOut
allowInHell
Again these can be useful, but for the most part you will never have
any need for them.
variable @skillSources:
This will contain a set of flags which denote from which base stat sources
the spell is derived. The following values are recognized:
strength dexterity
intelligence constitution
wisdom charisma
As with most flags you probably won't need to use these since.
variable @skillClasses:
This will consist of a set of flags which denote to which class
(discipline) the skill belongs.
mage immortal
cleric hero
thief forsaken
warrior necromancer
ranger blackKnight
paladin enchanter
druid shaman
As with most flags you probably won't need to use these since.
variable @skillFlags:
Various flag information that aplies to this spell will be set in this
variable. The following values are recognized:
isSpell isAggressive
isOffensive isBuff
isDefensive pcHide
These flags are used by the %spellRoutineCheckAllowedTarget() function
(as seen in our example). If you need to provide functionality of your
based on whether the skill is aggressive, offensive, etc. then you
will want to use this variable.
variable @spellFlags:
This is set to any flags that pertain to the spell itself. Currently
the only recognized member is "shareScript" which denotes that the
spell uses the common spell wrapper.
Closing Notes
-------------
Now you've been exposed to everything available to your spell script. Some
variables and functions you may never use, and others you will be sure to use
almost every time. Creativity is one of the best ingredients to any endeavour,
and it is no exception when creating spells; however, it is important to also
keep in mind balance whenever you create a new spell. To help further in your
task I have provided a second spell example:
Acid Splash Example:
protect
{
//
// Set up various damage flags for the spell.
//
@damageFlags->1 = magic
@damageFlags->2 = acid
@damageFlags->3 = skipMinimum
%spellRoutineMobileTargets( notGroup, 0 )
foreach( @targets as @target )
{
%spellRoutineMobilesOnly( @target )
%spellRoutineNoGhosts( @target )
%spellRoutineCheckAllowedTarget( @actor, @target )
@damage = ((((@level / 10) + 1) D 5) * @quality) / 100
if( %saveVersusSpell( @target, @actor, @level ) )
{
@damage /= 2
}
endif
%damage(
\ @actor, @target, @damage,
\ @damageFlags, "spell acid splash" )
if( !%isImmortal( @target ) )
{
if( !%isGladiating( @actor, @target ) )
{
%damage(
\ @actor, %getEquipment( @target ), 15 + 1d15,
\ @damageFlags, "spell acid splash" )
}
endif
}
endif
}
endforeach
}
endprotect
|
|
 |