OBJECT
/obj/monster/spellmonster
SYNOPSIS
inherit "/obj/monster/spellmonster";
#include <spellmonster.h>
LAST UPDATE
Thragor, 07-Apr-97, 21:30 MET
DESCRIPTION
You need a monster, which can cast fireballs, scare players or
just give a message to its enemies, like 'Hartward laughs
scornfully at Thragor.'?
Then /obj/monster/spellmonster is the right choice :-)
If you find (protected) below a function this means, that the
function is only available to the monster, which inherits the
spellmonster. A call_other() to this function won't work.
The functions to configure the spellmonster:
mapping P_SPELLS "Spells"
A mapping of all spells looking like this:
([<id>:([<spell-components>])]).
The <spell-components> are described in AddSpell() (see
below).
int P_CAST_CHANCE "CastChance"
The chance that a spell gets cast during an
attack-phase. Default value is 20.
The main function to configure the spellmonster is:
mapping AddSpell(string id,int mindam,int maxdam)
mapping AddSpell(void|int|string id,mapping props)
The first version is the most basic one. You may use this,
if the monster shall just cast a simple fireball:
AddSpell("fireball",5,15);
The player who got hit by the fireball, will get the
message 'Hartward casts a fireball at you.' and receive
about 10 points of damage. The default damagetype is
DT_FIRE, so if you add a spell like this, the damage is
always done as DT_FIRE.
Much more work, and much more effect has the second version.
You don't need to specify the <id>, if you don't want to
access this spell later to modify it. In this case an
integer value will be taken as id.
<props> is a mapping of properties taken from
<spellmonster.h>. Here's the description of the properties:
closure S_CHECKFUN "CheckFunction"
This is a function called to evaluate if the spell might
be possibly cast. As this is only a test, you don't
have to perform any actions, as the spell which will be
cast might be another one.
Example:
Hartward might have too few spellpoints during a
fight to cast any other spell. In this case Hartward
should cast a spell, which sucks spellpoints from all
livings in the room. The S_CHECKFUN returns true,
when the spellpoints of Hartward are below 30.
May later be modified with the function
void SetSpellCheck(int|string id,closure f).
closure S_FUN "Function"
This function will be called before the spell gets
actually executed. As parameter the functions gets the
mapping which contains all data about the spell and a
reference to the victims-array. The victims-array is 0
on call. If it is 0 after the call the victims will be
evaluated with GetVictims(). If the function returns 0,
no further actions will be performed.
If the function returns a (the?) mapping which describes
the spell, this spell will be executed. This is
e. g. useful, if you want to modify something in the
mapping before it gets executed.
Example: Spell "sucksp" in Hartward.
May later be modified with the function
void SetSpellFun(int|string id,closure f).
int S_CHANCE "Chance"
The chance, that if a spell gets casted, THIS spell gets
casted. To ease reading the code, it's useful to try to
give the chance in percentage, but you don't need
to. You may also give all lets say 4 spells a chance of
one. As a result all spells will be taken with a chance
of 25%.
May later be modified with the function
void SetSpellChance(int|string id,int c).
int S_COMBAT_DELAY "CombatDelay"
If the spell got cast the monster will stop all combat
actions for S_COMBAT_DELAY heartbeats (i. e. rounds).
May later be modified with the function
void SetSpellCombatDelay(int|string id,int d).
int S_DELAY "Delay"
Different from S_COMBAT_DELAY this is the time in
heartbeats the monster has to wait before it can cast
THIS spell again.
May later be modified with the function
void SetSpellDelay(int|string id,int d).
status S_SIMULT "Simultaneously"
This flag decides if the spell might be cast together
with a physical attack of a player, or, if the spell got
cast, that no other combat actions should be done.
May later be modified with the function
void SetSpellSimult(int|string id,status s).
int S_VICTIMS "Victims"
The maximum number of victims this spell might aim at. A
value of 30 should hit all current enemies. If the value
is negative, all livings in the room will be affected by
the spell if they are no ghosts and there HP are equal
or greater than 0.
May later be modified with the function
void SetSpellVictims(int|string id,int v).
int S_DAMTYPE "DamType"
The type of damage (taken from <combat.h>) of which the
spell is. This might be any of the conventional damage
types or of the magic ones.
May later be modified with the function
void SetSpellDamType(int|string id,int d).
int S_MINDAM "MinDamage"
The minimum damage done by the spell.
May later be modified with the function
void SetSpellMinDam(int|string id,int d).
int S_MAXDAM "MaxDamage"
The maximum damage of the spell. If S_MAXDAM is 0, no
damage will be done at all, i. e. Defend() won't be
called in the opponent, so casting this spell, doesn't
automatically make the victim an enemy of the monster.
This is e. g. useful if you just want to give a message
to the room.
Example: Spell "comment" in Hartward.
May later be modified with the function
void SetSpellMaxDam(int|string id,int d).
int S_SP "Spellpoints"
Of course casting a spell should cost spellpoints for
the monster, too. If it can't afford the spellpoints,
the spell won't be cast.
May later be modified with the function
void SetSpellSP(int|string id,int s).
int S_HP "Hitpoints"
Ever heard of blood-magic? Now the spellmonster can do
this, too. The S_HP set here will be taken from the
actual hitpoints of the monster. If this would reduce
the hitpoints below the whimpy-value set for the
monster, the spell won't be cast.
May later be modified with the function
void SetSpellHP(int|string id,int s).
Now something more complicate about the properties: The
message-system. It should guarantee a very good handling for
the player, so that (s)he may see what the monster does.
All messages are parsed through parse_string():
string parse_string(string str)
(protected)
If the string is empty (=="") the string will be
returned unmodified. This is needed for 'empty'
messages.
The string will be parsed through process_string and
then automatically wrapped. So you don't need to enter
newlines, if you don't want to force them to appear at a
certain position. If the newline at the end of the
string is missing, a newline will be appended.
string|string* S_GLOBAL_ENVMSG "GlobalEnvMsg"
All players in the room will get the message, before the
spell gets actually cast. If the spell targets more than
one enemy, functions like VicName() (see below) return
appropriate values, in this special case a list of all
names: "Thragor, Tnt and Guest1".
If S_GLOBAL_ENVMSG is a string, every living in the room
will receive the same message. If S_GLOBAL_ENVMSG is an
array of two strings, the first entry will be printed to
all livings which can see, the second entry to all which
can't.
mapping S_ENVMSG "EnvironmentMessage"
The mapping may contain two entries:
string|string* MSG_NORMAL "Normal"
This message will be printed to the environment of
the current victim (in this case only one victim at
a time). If the entry is an array the first entry
will be printed to all who can see, the second one
to all those who can't.
string|string* MSG_DEAD "Dead"
If the victim is dead, after damage got dealt, this
message will be printed. The entry follows the same
rules as MSG_NORMAL.
mapping S_VICMSG "VictimMessage"
The mapping may contain two entries:
string|string* MSG_NORMAL "Normal"
This message will be printed to the current victim
(in this case only one victim at a time). If the
entry is an array the first entry will be printed to
all who can see, the second one to all those who
can't.
string|string* MSG_DEAD "Dead"
If the victim is dead, after damage got dealt, this
message will be printed. The entry follows the same
rules as MSG_NORMAL.
For the messages, several functions are defined to be parsed
through process_string. As all messages are 'bare',
i. e. nothing is appended to them automatically, you have to
add the name of the monster, too, if you want it to be
mentioned in the message. These are the functions:
string build_list(string* strs)
(protected)
This is internally used, to get a list of names. If
there's only one entry in strs, this entry will be
returned, otherwise a list like:
"Thragor, Tnt and Guest1"
will be returned.
string MyName(void|string cap)
Returns the name of the spellmonster. If called with
<cap> set, the name will be returned capitalized.
Example:
"@@MyName@@" will be replaced by "a dragon"
"@@MyName|1@@" will be replaced by "A dragon"
string MyPronoun(void|string cap)
Returns the pronoun of the spellmonster. If called with
<cap> set, the pronoun will be returned capitalized.
Example:
"@@MyPronoun@@" will be replaced by "he"
"@@MyPronoun|1@@ will be replaced by "He"
string MyPossessive(void|string cap)
Returns the possessive pronoun of the spellmonster. If
called with <cap> set, the possessive pronoun will be
returned capitalized.
Example:
"@@MyPossessive@@" will be replaced by "his"
"@@MyPossessive|1@@" will be replaced by "His"
string MyObjective(void|string cap)
Returns the objective pronoun of the spellmonster. If
called with <cap> set, the objective pronoun will be
returned capitalized.
Example:
"@@MyObjective@@" will be replaced by "him"
"@@MyObjective|1@@" will be replaced by "Him"
string MyGenitive(void|string cap)
Returns the name with the correct genitive ending. If
called with <cap> set, the name will be returned
capitalized.
Example:
"@@MyGenitive@@" will be replaced by "a dragon's"
"@@MyGenitive|1@@" will be replaced by "A dragon's"
string VicName(void|string cap)
If called with S_GLOBAL_ENVMSG (see above) this returns a
list of all victims (like "Thragor, Tnt and Guest1")
otherwise only the name of the current victim. If called
with <cap> set, the first name will be returned capitalized.
Example:
"@@VicName@@" will be replaced by
"a cat, Thragor and a familiar"
"@@VicName|1@@" will be replaced by
"A cat, Thragor and a familiar"
string VicPronoun(void|string cap)
Returns the pronoun of the victim. If called in
S_GLOBAL_ENVMSG and several victims are effected "they"
will be returned. If called with <cap> set, the pronoun
will be returned capitalized.
Example:
"@@VicPronoun@@" will be replaced by "he"
"@@VicPronoun|1@@" will be replaced by "He"
string VicPossessive(void|string cap)
Returns the possessive pronoun of the victim. If called in
S_GLOBAL_ENVMSG and several victims are effected "their"
will be returned. If called with <cap> set, the possessive
pronoun will be returned capitalized.
Example:
"@@VicPossessive@@" will be replaced by "his"
"@@VicPossessive|1@@" will be replaced by "His"
string VicObjective(void|string cap)
Returns the objective pronoun of the victim. If called in
S_GLOBAL_ENVMSG and several victims are effected "their"
will be returned. If called with <cap> set, the objective
pronoun will be returned capitalized.
Example:
"@@VicObjective@@" will be replaced by "him"
"@@VicObjective|1@@" will be replaced by "Him"
string VicGenitive(void|string cap)
Returns the name(s) of the victim(s) appended with the
correct genitive ending. If called with <cap> set, the
first name will be returned capitalized.
Example:
"@@VicGenitive@@" will be replaced by
"a cat's, Thragor's and a species'"
"@@VicGenitive|1@@" will be replaced by
"A cat's, Thragor's and a species'"
string VicS(string verb,void|string cap)
This returns either the plural form of the verb or the
normal form of the verb depending on the amount of
enemies. This is only useful to call in S_GLOBAL_ENVMSG,
as otherwise there's always only one victim.
As verb you should give the plural form.
If <cap> is set, the verb is returned capitalized.
Example:
"@@VicS|are@@"
1 victim -> "is"
2 victims -> "are"
"@@VicS|feed@@"
1 victim -> "feeds"
2 victims -> "feed"
"@@VicS|are|1@@"
1 victim -> "Is"
2 victims -> "Are"
"@@VicS|feed|1@@"
1 victim -> "Feeds"
2 victims -> "Feed"
That's all about the messages. For the message-system two
functions are used, which might be useful for you, too:
object *SetVictims(object *v)
object *QueryVictims()
This returns all actual victims of the spell. During
S_FUN, this entry is still 0. Afterwards it will contain
all victims until the victims are handled one by one,
i. e. S_GLOBAL_ENVMSG and get_damage() (see below) might
query all victims. Afterwards QueryVictims() will always
return only one victim, or 0 if the spell got cast.
Last part of the description are the internal functions (which
I haven't mentioned yet):
int get_free_id()
(protected)
Returns the first integer id, which isn't used yet (0 if no
integer values have been used yet.
status check_cast(int|string id)
(protected)
Checks whether a spell might be cast or not, i. e.: The
spell mustn't be delayed (counter will be decreased), the
monster has to have enough spell- and hitpoints to cast and
if all of this got passed, the S_CHECKFUN (see above) is
called, whether to execute the spell or not.
This is just used to get the spells, the monster might
possibly cast this turn! Not to get the spell, which the
monster will cast.
int|string get_spell()
(protected)
Returns the id of a spell, which shall be cast. The chance
check is done in here, as well as check_cast() (see above)
is called to evaluate the spells, which might be cast.
int get_damage(object victim,int mindam,int maxdam)
(protected)
Returns the damage, the victim will receive. In the default
version <victim> is not used, but if you e. g. want to
reduce the damage done to demons, you might check this
inside here.
status valid_victim(object victim)
Returns true, if the spell might take effect on this
victim. Default is to check if the victim is a ghost or
if it has less than 0 hitpoints. In this case false (==0) is
returned.
object *GetVictims(int nr_victims,
void|status check_repulse,
void|int damtype,
void|mixed spell_id)
This function is used to evaluate the victims which will be
effected by the spell. By default it is called from
CastSpell() where the arguments contain the following:
nr_victims: value from S_VICTIMS
check_repulse: true (1)
damtype: value from S_DAMTYPE
spell_id: the key-id of the spell
status CastSpell(int|string id)
Now cast the spell! If 0 is returned, a physical attack
might take place, too. This is the order of the things which
will be done:
1. Check S_FUN (see above).
2. If S_FUN returned 0, abort.
3. Set the combat delay of the monster.
4. If the spell has S_DELAY set, add the spell to the
delayed-list.
5. Reduce the spellpoints.
6. Reduce the hitpoints.
7. Evaluate all victims.
8. Set the victims (SetVictims()).
9. Get the damage for each of the victims calling
get_damage() (see above).
10. Show S_GLOBAL_ENVMSG to the environment.
11. Now go through all victims and damage them...
a) Set the victims to the actual victim.
b) Print MSG_NORMAL to the victim.
c) Print MSG_NORMAL to the environment.
d) Do the damage to the player.
e) If player died:
I) Print MSG_DEAD to the victim.
II) Print MSG_DEAD to the environment.
12. Set the victims to 0.
13. Return the entry of S_SIMULT.
void Attack()
Do the actual casting/attack. If CombatDelay is set, no
spell will be cast. Then the CastChance (see SetCastChance
above) is checked to random(100)+1.
If no spell got cast, or CastSpell() (see above) returned 0,
perform the physical attack.
int create()
As /std/npc, create returns 1, if we're configuring the
blueprint. The CastChance will be set to default 20 and the
spellmonster will be registered (to ease update of the
spellmonster).
EXAMPLE
/d/archwiz/common/monster/hartward.c
INHERITANCE TREE
obj/monster/spellmonster
`-std/npc
SEE ALSO
npc(S)