PRELIMINARY
OBJECT
/std/living
LAST UPDATE
Mateese, 12-July-1998
SYNOPSIS
#include <attributes.h>
#include <living.h>
#include <combat.h>
#include <search.h>
inherit "/std/living";
OR
clone_object("/std/living");
DESCRIPTION
This is the generic simple living.
It contains all the basic mechanics needed by monsters, npcs
and players, for combat, health, talk and movement.
It's purpose in life is 'cloned to die', so it does not
feature the fancy skill and stat system of npcs or players,
nor does it need to eat or drink.
/std/living itself is in fact mostly a wrapper for a collection
of modules which provide the real functionalities. /std/living
just merges them together and provides functions to setup
reasonable default values.
Therefore you can use the modules where you need them, but be
aware that they are quite interpedendant (they just assume
functions from other modules to be existing).
You can also equip a normal /std/living with modules from
/std/npc or else, of course.
The several modules are:
/std/living : The wrapper.
/std/living/actions : Automatic actions.
/std/living/attributes : Attribute implementation.
/std/living/body : Health and death.
/std/living/chat : Chatting and listening.
/std/living/combat : Physical combat.
/std/living/commands : Command- and notify_fail-handling.
/std/living/description : Visual appearance.
/std/living/heart : Heartbeat control.
/std/living/restrictions : Weight and contents.
/std/living/hands : Hand (and tail) management.
/std/living/moving : (Automatic) movement.
/std/living/stats : Stats and Skills.
Also inherited are:
/std/base : for the property handling.
/std/room/items : for easier equipping.
DESCRIPTION - /std/living
As mentioned above, this top module is a wrapper for all the
modules constituting a living.
It also features some functions to setup the living to
reasonable values:
void SetInitLevel (int level, void|int randHealth)
Sets the living up to the stats a <level> (0..20) living
of this race has, including an appropriate amount of XPs.
Also, the living's skills will be set to 5*level.
If <randHealth> is non-zero, its HP and SP will be at some
random level, else at maximum.
void SetDefault (int level, string race)
Set the living up to be a <race> living of <level>.
The stats will be set using InitLevel(), this function
also sets description, weight, hands and intrinsical
defenses.
Both functions also accept their arguments collected as array
as passed as only argument.
The following functions are redefined:
int create ()
Initialises the submodules, sets P_NOGET to 1, and if the
object is a clone, enables commands and sets it up to be a
level-1 human.
It returns non-zero for the blueprint, and 0 for the clone.
void reset ()
This calls ForgetEnemies(), to enable memory cleanup in
times of idleness, and refreshes the items by a call to
/std/room/items.
void init ()
Calls the init() of /std/living/actions and
/std/living/chats.
DESCRIPTION - /std/living/attributes
Attributes are not much different from normal properties,
except that they are saveable using save_object(), and thus
also restoreable.
To achieve this, an extra set of functions is needed.
Its implementation makes attributes a subset of properties,
however.
This means: the attribute A_SHORT and the property P_SHORT
both mean the same thing: the short description of an object.
But whereas a call to Set(P_SHORT) succeeds for every
object, a call to SetAttr(A_SHORT) succeeds just for livings.
A call to SetShort(), however, will automatically reach the
right recipient (A_SHORT and P_SHORT are in fact the same
name).
As with properties, there are hardcoded attributes "Attrs",
and 'soft' ones "Attributes", which are internally stored as a
mapping. The latter are those which can be freely set and
modified by e.g. guild objects.
mixed SetAttr (string attribute_name, mixed arg, void|int sc)
Sets or changes the attribute <attribute_name> to
<value> and returns the value actually set.
If <sc> is true, always the 'soft property' of the given
name is set.
If the attribute doesn't exist, it is created.
mixed QueryAttr (string attribute_name, void|int sc)
Retrieves the value of attribute <attribute_name>.
If <sc> is non-zero, always the 'soft attribute' of the
given name is queried.
void RemoveAttr (string name)
The entry for the 'soft' attribute <name> is deleted.
mapping QueryAttrs (void|int sc)
Returns a mapping containing all attributes of the object,
"built in" as well as soft ones.
If <sc> is non-zero, only the soft attributes are returned
(and the function is less costly).
This function is very costly.
mapping SetAttrs (mapping attributes, void|int sc)
The function sets the attributes of the object to the
values in the given <attributes> mapping. Builtin and soft
attributes are properly handled, resp. if <sc> is
non-zero, only the soft attributes are set.
Result is a mapping with the values actually set.
mapping QueryStats()
Returns a mapping containing just the STAT attributes of
the object.
mapping SetStats (mapping stats)
Set just the STAT attributes to the values in the given
<stats> mapping. Stats not listed there aren't changed.
Result is the updated QueryStats() result.
int CheckStat(string attribute, int chance, int percent)
This handles Stat-Checking. With this function you can
use stats like P_DEX or P_INT as a skill.
This can become useful e.g. for rooms with slippery
ground. Simply use a CheckStat(P_DEX, 500, percent) in
a closure and check the result...
Example:
int percent;
if(!TP->CheckStat(P_DEX,500, percent))
{
/* Check failed. Lets assume a serious failure is below
** 30%
*/
if(percent<30)
{
write(
"You slip on the wet ground and fall to the ground, "
"breaking your nose on the floor.\n");
TP->DoDamage(4);
return 1;
}
}
/* check was successful here. everything is fine. */
TP->move("/d/silvere/rooms/harbour/street1", M_GO);
return 1;
For permanent and temporary changes to the values of integer
attributes, these functions exist:
int ChangeAttr(string aname, int delta)
int ChangeAttr(string aname, int delta, int min, int max)
The value of attribute <aname> is changed by amount
<delta>. If <min> and <max> are given and valid, the new
value of the attribute is confirmed to this range.
Result is the new attribute value.
int ModifyAttr(string aname, int delta)
int ModifyAttr(string aname, int delta, int min, int max)
As ChangeAttr(), but this change is recorded in the
P_BONI property as well, so a call to RestoreAttr() will
undo any of these modifications.
A similiar system exists for non-integer attributes. Though the
same functions are used, the meaning is slightly different, as the
changes do not accumulate. Instead, the changes are used as temporary
replacements for the real values.
mixed ChangeAttr(string aname, mixed new)
The value of attribute <aname> is changed to <new>.
Result is the new attribute value.
mixed ModifyAttr(string aname, mixed new)
As ChangeAttr(), but this change is recorded in the
P_BONI property as well, so a call to RestoreAttr() will
undo any of these modifications.
All the changes can be undone using a single call:
void RestoreAttr()
Undo any ModifyAttr() as remembered in P_BONI.
This function should not be called manually, as it is
called at every login.
The changes done by ModifyAttr() are recorded in
mapping P_BONI "Boni"
Key are the attribute names, value the summed up change
("bonus") so far.
This property must not be set manually!
In rare(!) special cases, direct access to the boni are
available via
int SetBonus (string aname, mixed val)
Sets the bonus for attribute <aname> to <val>.
mixed QueryBonus (string aname)
Returns the current bonus for attribute <aname>.
int AddBonus(string aname, int delta)
The bonus for <aname> is changed by <delta>, the new value
is returned.
mixed AddBonus(string aname, mixed new)
The temporary value for <aname> is set to <new>, this new
value is also returned.
The special functions to handle just the soft attributes are:
mixed SetAttribute(string name, mixed arg)
== SetAttr(name, arg, 1).
mixed QueryAttribute(string name)
== SetAttr(name, 1).
void RemoveAttribute(string name)
== RemoveAttr(name).
int ChangeAttribute(...)
int ModifyAttribute(...)
mapping SetAttributes(mapping arg)
== SetAttrs(arg, 1).
mapping QueryAttributes()
== QueryAttrs(1).
DESCRIPTION - /std/living/body
This module implements the basic health functions and
attributes of a living.
The healing is bound to the HEART_BODY heartbeat.
Following attributes and properties are builtin:
int A_MAX_HP "MaxHP"
int A_MAX_SP "MaxSP"
int A_MAX_PEP "MaxPEP"
int A_MAX_MEP "MaxMEP"
If not set or set to 0, the default value according to the
livings stats is returned.
int A_HP "HP"
int A_SP "SP"
int A_PEP "PEP"
int A_MEP "MEP"
The amount of health/spell/pysical endurance/mental endurance
points the living currently has.
It is not possible to set this value for netdead players
or ghosts.
Setting it starts the HEART_BODY heartbeat.
int AddHP (int points)
int AddSP (int points)
int AddPEP (int points)
int AddMEP (int points)
Add <points> to the current amount of A_HP/A_SP/A_PEP/A_MEP
and returns the amount.
varargs int ReduceHP (int points, int minimum, void|int check_ko)
varargs int ChangeSP (int points, int minimum)
varargs public int Drain(int points, int min)
varargs public int Tire(int points, int min)
Subtract <points> from the current amount of A_HP/A_SP/
A_MEP/A_PEP and return the new amount. However, the new amount
can't be less than <minimum> (default is 0). If <points> is given
as reference (&variable), it contains after the call the
actual number of points subtracted.
For Drain and Tire not giving of poitns will reduce a reasonalbe
default amount.
The HEART_BODY heartbeat will be started, but the
Con/Int-Stat is _not_ 'used'.
ReduceHP() also informs the living about its woundlevel if
the commanded change is grave enought. It is used for both
damaging and healing the living. If <check_ko> is
non-zero, the damage done may knock the living out.
int ReduceSP (int points, int minimum)
Subtract <points> from the current amount of A_SP if this
won't lower the value below <minimum>.
Return the amount subtracted (always equal to <points>) on
success, and 0 if A_SP was too low.
On success, the HEART_BODY heartbeat will be started, but
the Int-Stat is _not_ 'used'.
Note: For compatibility reasons this function is named
analogue to ReduceHP(), but differs in function.
int A_RATE_HP "RateHP"
int A_RATE_SP "RateSP"
int A_RATE_PEP "RatePEP"
int A_RATE_MEP "RateMEP"
The basic A_HP/A_SP/A_PEP/A_MEP regeneration rate in
1/10-points per heartbeat. It can't be a value less than 1.
Setting it also sets the P_REG_HP/P_REG_SP/P_REG_PEP/P_REG_MEP.
Do NOT temper with these values. Use P_REG_HP/P_REG_SP for
things like healing potions, illnesses and else.
These attributes are initialised to HP_RATE resp. to
SP_RATE from /sys/health.h .
int P_REG_HP "RegHP"
int P_REG_SP "RegSP"
int P_REG_PEP "RegPEP"
int P_REG_MEP "RegMEP"
The current effective A_HP/A_SP/A_PEP/A_MEP regeneration
rate in 1/10-points per heartbeat.
int AddRegHP (int diff)
int AddRegSP (int diff)
int AddRegPEP (int diff)
int AddRegMEP (int diff)
Add <diff> to the current P_REG_HP/P_REG_HP/P_REG_PEP/P_REG_MEP
and return the result.
int A_MAX_POISON "MaxPoison"
The threshold value of how much poison points a living can
stand w/o direct damage.
It is hardcoded to a value depending on the livings A_CON.
int A_POISON "Poison"
The amount of poison points the living has currently
soaked.
The value can't be set for netdead players, and must be at
least 0.
If its set, it starts the HEART_BODY heartbeat.
Also, if the set value is greater than A_MAX_POISON, A_HP
will be reduced by the difference.
int AddPoison (int points)
Add <points> to the current amount of A_POISON and returns
the result.
int P_RATE_POISON "RatePoison"
The basic A_POISON degeneration rate in 1/10-points per heartbeat.
It can't be a value less than 1.
Setting it also sets the P_DEG_POISON.
Do not temper with this value. Use P_DEG_POISON for
things like poison potions.
This attribute is initialised to POISON_RATE from
/sys/health.h .
int P_DEG_HP "DegPoison"
The current effective A_POISON degeneration rate in 1/10-points
per heartbeat.
int AddDegPoison (int diff)
int AddRegSP (int diff)
Add <diff> to the current P_DEG_POISON and return the
result.
It is advisable only to use the Addxxx() functions to change
the settings.
Due to its special effects, an overload of the living is not
reflected in A_RATE_HP/A_REG_HP, but instead computed on the
fly in the HEAL heartbeat.
If the P_LOAD gets greater than LOAD_THRESH%, the effective
RATE_HP is proportionally reduced until it reaches 0 for a
100% P_LOAD. If the load is even larger (up to 200% is
possible), the effective RATE_HP goes negative down to a
minimum of the set RATE_HP negative. The damage caused by this
negative rate is immediately computed, and if the A_HP fall
below LOAD_MINHP(A_MAX_HP) due to this, the livings drops
enough things to reach a load of 50%.
However, the overload can reduce the effective RATE_HP just down
to 0 as far as the other computations in the HEAL heartbeat are
concerned.
int P_HEALTH "Health"
The health of the living as denoted by its P_HP/P_MAX_HP
relation, returned as an integer in the range (0..100).
This property can't be set.
string P_HEALTH_MSG "HealthMsg"
Returns a short string describing the health of the living.
It is in fact produced by HealthMsg() (see below).
This property can't be set.
string HealthMsg (int health, void|int stunned)
Returns a short string describing the given <health> and
the <stunned>.
mixed A_WHIMPY "Whimpy"
The threshold of A_HP below which the living would run
away.
The attribute can be an integer, giving the absolute
number of A_HP, or a string containing the percentual
amount of A_HP.
int A_XP "XP"
The amount of experience points the living has.
int AddXP (int diff)
Adds <diff> points to A_XP and returns the result.
int P_KILL_XP "KillXP"
This is the amount of experience points an attacker gets
for killing this living. If not set, or set to 0, a
default value computed from A_XP is returned upon query.
The function
int QueryKillXP (void|int count)
optionally gets passed the number of attackers as
argument, and should take this into account.
A <count> of 0 is equal to 'one attacker'.
To apply damage or healing to a living, use these functions:
int HealHP (int amount)
int HealSP (int amount)
int HealPEP (int amount)
int HealMEP (int amount)
Apply <amount> points of healing to the livings HPs/SPs,
result is the amount of points actually healed.
The HP/SP can not be healed to a value greater than
A_MAX_HP/A_MAX_SP though.
The CON/INT stat is used once.
void Heal (int amount)
Does HealHP(amount) and HealSP(amount) in one call.
int DoDamage (int damage, void|int gentle)
Apply <damage> points of damage to the living.
It is not possible to damage netdead players or ghosts.
If <gentle> is specified, it is the number of HPs the
victim should have after the attack. Positive, it is the
number itself, negative, it is the number given in percent
of A_MAX_HP.
The amount will be subtracted from the current amount of
A_HP, and the HEART_BODY heartbeat is started.
Also, a MSG_DAMAGE will be issued, and the living will be
notified about grave changes in its health.
The amount of damage done is returned.
If there are no HP left, the living will die immediately.
If the living survives, and the A_HP are below the A_WHIMPY
threshhold, it will try to go away.
Note that this function does _not_ counterattack by
itself.
The MSG_DAMAGE contains this elements
object DAMAGE_CAUSE : the object causing the damage.
int DAMAGE_AMOUNT: the amount of damage done.
Whenever DoDamage() is called, the stat 'Con' is used.
The healing is done in the heartbeat:
int body_heartbeat()
If the living is knocked-out, a check against its constitution
and health is done, which on success causes an immediate
awakening of the living.
The A_RATE_HP/damage handling possibly caused by
overload is handled.
Apply the A_DEG_POISON, A_REG_SP and A_REG_HP to the
living to heal it, refresh it and to decrease the amount
of poison soaked. HP/SP healing does not take place during
combat.
It is possible these will have the contrary effect and the
living will die, e.g. if the poison can't be degraded fast
enough.
If A_POISON is greater than A_MAX_POISON, it will cause
the difference in points to damage to A_HP. Also, the
difference will be reduced by the half.
If A_POISON is not zero, but below A_MAX_POISON, the
effective HP regeneration will be reduced proportionally.
If during one call neither of the values changed, the
HEART_BODY heartbeat will be stopped.
Note that the handling with 1/10-points for the regeneration
rates will automatically cause a delay of a few heart beats
between actual changes of HP/SP/POISON.
The death of a living is done with this functions:
void Die (void|int silent)
The living died.
It is set to be a ghost, all combat (and the heart) is
stopped, the A_XP is reduced.
If the living was killed, the alignment of the killer is
updated, and all current enemies will get XP_GAIN(livings A_XP
divided by the number of enemies) experience points, but not
more than MAX_XP_ADD. The killer will get twice the amount
of the other combattants.
Then, a corpse is created for the living (default is
/std/corpse), all carried objects are transferred into the
corpse and the corpse is triggered to decay.
The living finally issues a MSG_DIE (with the only element
'object DIE_CORPSE' denoting the corpse object left
behind) and calls Death().
If silent is zero, the killer and attackers will get a
message about their victory.
object LeaveCorpse()
This function is called from Die() and has to create the
corpse (according to A_CORPSE) and move all possessions
into it. Result is the corpse object, or 0 on error.
void Death (void|object * killers)
This function is called at the end of the death process of
a living. It may do now something fancy with the ghost of
the living.
Argument is the (possibly empty) list of active enemies at
the time of death. If this_player() exists, it is _the_
killer.
Default is just a selfdestruct by remove(), but player
objects for example will have a second life here.
The corpse to use is determined by a non-builtin attribute:
string A_CORPSE "Corpse"
Filename of the corpse to clone if the living dies,
default is "/std/corpse".
If set to a non-string, no corpse at all is created.
To ease the handling of resistance, the module also implements
a soft(!) property and two access functions:
mapping P_RESISTANCE "Resistance"
When set, this is a mapping of resistances. Each
resistance type (an integer or string) is used as key for
the resistance value (always integer).
A living with no resistance may have 0 or ([]) here.
int Resistance (mixed type)
Return the resistance for <type>, as stored in
P_RESISTANCE.
int AddResistance (mixed type, int delta)
Change the resistance <type> by <delta> and store it in
P_RESISTANCE.
Note that the interpretation of the resistance entries is
solely up to the caller.
The module is initialised in
void create ()
These functions are assumed as being available in other
modules:
std/base : Set(), Query()
std/living/actions : QueryStunned(), SetStunned()
std/living/attributes : QueryAttr(), SetAttr()
std/living/description : QueryAlign(), QueryLState(), SetLState()
: QueryName(), QueryRace()
std/living/enemies : QueryEnemies(), StopAllCombat()
std/living/moving : remove(), Whimpy()
std/living/heart : AddHeart(), RemoveHeart(), CheckHeart()
std/living/stats : QueryCon(), QueryInt(), UseStat()
std/living/restrictions: QueryWeight(), QueryWeightContent(),
QueryLoad()
DESCRIPTION - /std/living/chat
One important thing about livings is the ability to chat and
to answer question.
int P_CHAT_CHANCE "ChatChance"
A value in 0..100 giving the chance per heartbeat that
the living will say something.
Setting the value to something non-zero will (re)start the
call_outs to OneChat().
int P_ACHAT_CHANCE "AChatChance"
A value in 0..100 giving the chance per combat round that
the living will say something.
mixed * P_CHATS "Chats"
The collections of chats to be said over the time.
Each chat can be a string, which is just said, or a
closure, which is called to return the string to say (or 0
to be silent).
mixed * P_ACHATS "AChats"
The collections of chats to be said during combat.
Each chat can be a string, which is just said, or a
closure, which is called with the current enemy as
argument to return the string to say (or 0 to be silent).
The chatting is done using self-initiated call_outs to this
function:
void DoChat ()
This function does one chat by selecting randomingly one
from P_CHATS and saying it.
void OneChat (int justSched)
If <justSched> is zero, one call to DoChat() is made and a
news call to OneChat() is scheduled.
If <justSched> is negative, a new call to OneChat() is
scheduled only if there is none pending yet.
Else, just a new call is scheduled.
The call_out is scheduled with an delay approbiate to
P_CHAT_CHANCE to OneChat() to continue chatting IF there
is a living around to hear it.
If P_CHAT_CHANCE is zero, nothing is scheduled.
void DoAttackChat (object enemy)
This function issues an randomingly selected attack chat
from P_ACHATS to the room, if the P_ACHAT_CHANCE permits.
It is called directly by the combat heartbeat.
These functions ease the initialization of the chats:
void InitChats (int chance, mixed *strs)
Sets P_CHAT_CHANCE to <chance> and P_CHATS to <strs>.
void InitAChats (int chance, mixed *strs)
Sets P_ACHAT_CHANCE to <chance> and P_ACHATS to <strs>.
The living is also able to react on things it hears using 'talks'.
Everything it hears is parsed to be one of this forms:
'<who> <type><match> <what>'
'<who> <type><match><what>'
'<who> <type><match>'
'<who> <type> <match> <what>'
'<who> <type> <match><what>'
'<who> <type> <match>'
<type> and <match> are predefined (default is the empty string ""),
<who> and <what> are parsed from the message heard.
So every talk is described by <type> and <match>, and if a
parse was successful, the <who> (and possible <what>) are
given to an 'talkfun'ction in a 'talkobject' to process.
string P_TALK_MATCH "TalkMatch"
An array of <match> strings.
string P_TALK_TYPE "TalkType"
An array of <type> strings.
mixed P_TALK_FUN "TalkFun"
An array of function designations.
Each can be the string with a function name, or a closure.
int P_TALK_DELAY "TalkDelay"
An array of integers defining the delay between the match
of a talk and the call to its function. 0 means no delay.
mixed P_TALK_OBJ "TalkObject"
The talk object to call the talk function in.
It can be given as string with the object name, or as
actual object.
Talks for which a closure is set as function, ignore the
talkobject.
void InitMatch ( mixed *fun , string *type
, string *match, int *delay
, object ob )
Sets P_TALK_FUN from <fun>, P_TALK_TYPE from <type>,
P_TALK_MATCH from <match>, P_TALK_DELAY from <delay> and
P_TALK_OBJ from <obj>.
void AddMatch ( mixed fun , string type
, string match, int delay )
Adds <fun> to P_TALK_FUN, <type> to P_TALK_TYPE, <match>
to P_TALK_MATCH and <delay> to P_TALK_DELAY.
Multiple calls to this function can emulate the all-in-one
behaviour of InitMatch().
mixed TestMatch (string str)
Match the given <str> against all set talks and return the
result of the talkfunction call (or 0).
Moreover a living is able to perform menu driven talks.
Using this the player is presented a menu where he/she can
choose a predefine answer. This leads to a talk tree like this
talk with orc
|
A-------------------B-----------C
| | !
AA------------AB------AC BA----BB kill
| | ! ! !
AAA--AAB------AAC *2) kill *1) kill
| | |
AAAA *3) AACA-------AACB--AACC
! | | !
AACAA--AACAB *4)
! !
varargs int AddTalk(mixed key,mixed talk,mixed answer,string result)
Add a talk menu entry to the NPC
string|string *key: The talk sequence idendifier (e.g. "acab")
string|closure talk: The string or the function returning a
string which will be shown in the menu corresponding
to "acab". The closure gets the arguments object who,
string talk_sequence, i.e. the player which is talking
and the talk sequence "acab" to reuse one function
string|closure answer: The text which the NPC will answer to
the player when "acab" is said. In case of a closure
nothing is said but the closure executed with the
same arguments as described above in 'talk'.
string result: 'goto' modifier - let the talk proceed as if
the sequence given with 'result' (e.g. "abbb") had
been entered. In this way you can jump to another
part of the talk tree avoiding double writing
return: 1: ok, 0:fault
Finally, the living can react on questions.
This is implemented as a command 'ask' bound to the function
fask().
The answers (and the default 'shrug message') can be set as
strings or as closures. In the latter case, the closure is
evaluated when needed with the questioned keyword as argument
and has to return the actual answer.
A third possibility is to set the answer/shrugmessage to an
array of two strings - the first is sent to the player asking,
the second the the environment. As 0 or "" entries are
ignored, 'silent' asking is possible (only the asking player
hears the answer).
mapping P_QUESTIONS "Questions"
A mapping containing the answers, indexed by the key
strings in a question.
To ease initialisation, the property may be _set_ using a
flat array of: ({ <question>, <answer>, <question>,
<answer>... }), whereas each <question> may be a single
string, or an array of strings which then all get the same
answer.
void AddQuestion (string|string* q, mixed a, void|int silent)
Add an answer <a> for the question <q>.
Setting <silent> to non-zero is an easy way to specify
'silent' answers:
AddQuestion("foo", "bar", 1)
is equivalent to
AddQuestion("foo", ({ "bar", 0 }))
<q> may be an array of questions.
mixed P_SHRUG_MSG "ShrugMsg"
The default answer given if a living does not know the
answer to a question. As the answers, this may be a string
or a closure. If not set, "<name> shrugs helplessly." is
used as default.
Each question must be unique!
The function called for a question 'asked', is
int fask (string str, void|int inh)
Accept a question of type 'ask <living name> about <q>'
('for' may be replaced for 'about' as well) and write the
answer if <q> is one of the set questions in P_QUESTIONS.
<living name> must be of course the asked living.
If the command fails, a decent failure message is set
using notify_fail().
If <inh> is non-zero, the function returns a special
code on failures instead of the normal 0:
ASK_OK : question understood and answered
ASK_NO_ARG : <str> is missing
ASK_CANT_PARSE: <str> is not of the required format,
or <living name> does not denote this
living
ASK_SHRUG : question understood, but no answer was
provided.
By overloading fask() and using these codes more complicated
questions can be added.
The following standard functions are used to implement talks
and questions:
void catch_tell (string str)
This will get all the messages from outside.
If no talk object is set, or the living hears itself,
nothing is done.
Else the <str> heard is passed to TestMatch().
Recursion is prevented using a semaphore scheme and a
buffer.
void ResetSema ()
Reset semaphore and buffer of catch_tell().
Use this in emergencies only!
void SetByCmsg(int status)
(Re)Sets an internal flag allowing the next (and only the
next) use of catch_tell() coming from the living itself.
This is necessary to 'see' the messages received by catch_msg().
void init ()
If questions are set, the command 'ask' is bound to
fask().
These functions are assumed as being available in other
modules:
/std/living/description: QueryName()
DESCRIPTION - /std/living/combat
This module implements the combat-related functions of the
living.
The functions handling the attacking:
void Kill (object living)
Starts an attack on living by putting it into the list of
current enemies and turning on the combat heartbeat.
void Attack ()
Called by the heartbeat, this handles the attack chatting
and makes the living actually attack by calling do_hit for
every wielded weapon and free hand.
status CalcIsHit(object attacker)
Decides whether the attack on this living by living object
attacker succeeds or misses. This is done by some calculation
using both living's dexterity and a random factor.
mixed do_hit (mixed x)
Execute one hit to an enemy with weapon x.
The enemy will be selected at random from the list of
current enemies.
Weapon may either be an object variable, the actual weapon
with which this attack takes place, or an integer number,
in which case it represents an element in the hand-array of
this living.
Apropriate messages will be displayed to all living involved
and the damage will be calculated and applied to the enemy
by a call to the enemy's Defend.
Return value is the enemy attacked.
int Defend (int dam, int dam_type, mixed weapon)
Calculate the defence of this living against an attack doing
dam points of damage of damage-type dam_type. weapon can be
either an object (the weapon with which the attack was done),
0 (if the attacker attacked barehanded) or a logical
combination of different flags.
If both this living and the attacker are interactive players
(and not both of them are wizards), the attack is logged.
If weapon is an integer containing the flag DEFEND_F_NOLIV,
the attack will succeed 100%, else the success will depend
on a chance calculated from the dexterity of both this
living and the attacking living.
The protection this living has against dam_type is calculated
from its intrinsic defences and the defences of all armours
worn. It is subtracted from the damage dam done by the
attack. If the resulting damage is < 1, it is set to 1.
Finally, DoDamage is called in this living with this resulting
damage as parameter, and the appropriate combat messages are
displayed.
The return value is the damage actually done to the living.
To check if this living will automatically attack another
living, the following functions and properties are checked:
int CheckAutoAttack (object victim)
Called by init(), this function returns 1 if victim will
be attacked automatically, 0 else.
Checks if this living is aggressive at all, if victim is
a ghost or a friend of this living, and if this living
can actually see victim.
If victim is a wizard with wizmode turned on, 0 is returned
in any case.
int P_AGGRESSIVE "Aggressive"
Aggressive livings may automatically attack other livings
they encounter.
This property is used by CheckAutoAttack() to decide if
this living will automatically attack another one.
int P_ATTACK_CHANCE "AttackChance"
The chance that an aggressive living will actually attack.
It ranges from 0 to 1000 (default).
This is checked by CheckAutoAttack().
object P_FRIEND_OBJ "FriendObj"
The object in which CheckAutoAttack() calls IsFriend() to
find out if a living is a friend of this living (and
therefore won't be attacked automatically).
If P_FRIEND_OBJ is not set, IsFriend is called in this
object.
int IsFriend (object victim)
Returns 1 if victim is a friend of this living, 0 else.
The result is used in CheckAutoAttack() to choose
whether to attack victim or not (friends are never
attacked automatically).
Per default, IsFriend returns 1 if victim and this living
both are NPC or both are Player.
int P_SEE_INVIS "SeeInvis"
int P_SEE_INVIS_CHANCE "SeeInvisChance"
These non-builtin properties show whether or not the living
can see invisible livings. Invisible livings can only be
attacked if this living can see them.
P_SEE_INVIS_CHANCE ranges from 1 to 1000, giving the chance
that this living will actually see the invis living (default
is 1000).
If P_SEE_INVIS is 0, P_SEE_INVIS_CHANCE will have no effect.
These properties are checked by CanSeeLiving() in
/std/living/description which in turn is checked by
CheckAutoAttack().
A living may be delayed in its actions for a number of
heartbeats by a combat delay, which is handled by the
following functions:
mixed SetCombatDelay (int time, string s)
Set a combat delay of time heartbeats for this living with
a combat delay message s.
While the living has a combat delay set, it cannot fight,
move (on its own), cast a spell etc.
If no delay message has been set, it is automatically set
to 'You are unconscious.'.
The combat delay is decreased by one every heartbeat, the
delay message written to the living every second heartbeat.
The delay set will be returned.
mixed QueryCombatDelay ()
Returns the current combat delay, that is the number of
heartbeats that this living is still delayed.
string SetCombatDelayMsg (string s)
Set the delay message that will be displayed to this living
as long as it stayes delayed. Default is 'You are unconscious.'
string QueryCombatDelayMsg ()
Return the delay message currently set.
The properties showing the list of current enemies and livings
this living is currently hunting together with the functions for
their handling:
object *P_ENEMIES "Enemies"
The array of current enemies.
This Property is read-only, use AddEnemy() and RemoveEnemy()
to add/remove enemies from the array.
object *P_HUNTERS "Hunters"
The array of livings this living is currently hunting.
This Property is read-only, use AddHunter() and RemoveHunter()
to add/remove livings from the array.
status AddEnemy (object e)
Add enemy e to the array of current enemies.
If the function succeeds, the function NotifyAddEnemy()
is called in all objects following this living with
enemy e as parameter.
Return value is 1 on success, 0 on failure.
status RemoveEnemy (object e)
Remove enemy e from the array of current enemies.
If the remove is successful, the function
NotifyRemoveEnemy() is called in all objects
following this living with enemy e as parameter.
Return value is 1 on success, 0 on failure.
status AddHunter (object h)
Add living h to the array of livings currently hunted by
this living.
If the function succeeds, the function NotifyAddHunter()
is called in all objects following this living with
living h as parameter.
Return value is 1 on success, 0 on failure.
status RemoveHunter (object h)
Remove living h from the array of livings currently
hunted by this living.
If the remove is successful, the function
NotifyRemoveHunter() is called in all objects
following this living with living h as parameter.
Return value is 1 on success, 0 on failure.
void StartHunting (object e)
Remove enemy e from the list of current enemies and put it
into hunters. e will receive a message that it is now
hunted by this living.
void StopHunting (object e)
Remove enemy e from both the list of current enemies and
hunters.
void StopAllHunting ()
Clean the lists of current enemies and hunters and make all
enemies/hunters found stop hunting this living.
int StopAttack ()
Set the enemies- and hunters-arrays to the empty arrays
without notifying any enemies/hunters.
Return value is 1.
int stop_hunting_mode ()
Call StopAttack() and write an acknowledgement to the
current player.
Return value is 1.
The functions dealing with the weapons this living is wielding
are:
int wieldme (object ob)
Wield weapon ob if possible.
Returns 1 on success, 0 if ob couldn't be wielded.
Call this function if you want this living to wield weapon
ob. All appropriate checks and notifications are made.
void unwieldme (object ob)
Unwield weapon ob.
Call this function if you want this living to unwield weapon
ob. All appropriate checks and notifications are made.
object *P_WEAPONS "Weapons"
The weapons currently wielded by this living.
This property is read-only.
int Grip (object ob)
Grip equipment object ob.
The appropriate number of free hands given by
ob->QueryNumberHands() is covered with ob if it isn't
already wielded.
Return value is 1 if the action succeeded (that is enough
free hands could be found or ob is already held in hand),
else 0.
This function is called by the weapon itself after all
appropriate checks have been done and shouldn't be called
manually.
void Ungrip (object ob)
The hands covered by ob are freed.
This function is called by the weapon itself after all
appropriate checks have been done and shouldn't be called
manually.
int AddWeapon (object ob)
Add the weapon ob to P_WEAPONS and set its P_WIELDED to
this object.
Return value is 1 on success, else 0.
This function is called by the weapon itself after all
appropriate checks have been done and shouldn't be called
manually.
void RemoveWeapon (object ob, int flags)
Remove the weapon ob from P_WEAPONS and set its P_WIELDED
to 0.
This function is called by the weapon itself after all
appropriate checks have been done and shouldn't be called
manually.
The functions dealing with the armours this living is wearing
are:
int wearme (object ob)
Wear armour ob if possible.
Returns 1 on success, 0 if ob couldn't be worn.
Call this function if you want this living to wear armour
ob. All appropriate checks and notifications are made.
void removeme (object ob)
Remove armour ob.
Call this function if you want this living to unwear
armour ob. All appropriate checks and notifications are made.
object *P_ARMOURS "Armours"
The armours currently worn by this living.
This property is read-only.
int AddArmour (object ob)
Add the armour ob to P_ARMOURS and set its P_WORN to this
object.
Return value is 1 on success, else 0.
This function is called by the armour itself after all
appropriate checks have been done and shouldn't be called
manually.
void RemoveArmour (object ob, int flags)
Remove the armour ob from P_ARMOURS and set its P_WORN
to 0.
This function is called by the armour itself after all
appropriate checks have been done and shouldn't be called
manually.
Some more properties describing the living are built into
this module:
int P_AC "AC"
The 'built-in' AC of this living
int *P_DEFENCES "Defences"
The defences-array of this living, holding the AC in
element 0. Offsets to the AC for individual damage-types
are held in this array indexed by the damage-type.
void AddDefence (int type, int val)
This function sets the AC-offset for the damage-type 'type'
to 'val'.
int GetDefence (int type)
Returns the protection offset for damage-type 'type'.
mixed *P_HANDS "Hands"
The hands of the living.
object P_CAST_OBJ "CastObj"
The magicobject controlling the spell currently cast by this
living.
object *P_EQUIP_OBJ "EquipObj"
The array of objects checked on wielding/wearing/
unwielding/unwearing any pieces of equipment.
This living itself as well as its race object are
automatically included in P_EQUIP_OBJ.
If you call QueryEquipObj(int flag) with a flag other
than 0, the array will be returned without the living
and its race object.
object *AddEquipObject (mixed obj)
object *RemoveEquipObject (mixed obj)
These functions allow to add to or remove from P_EQUIP_OBJ
the object or array of objects obj.
Return value is the resulting P_EQUIP_OBJ.
This living itself as well as its race object can't
be removed.
The following functions are redefined:
void create ()
Some basic properties are initialized.
void init ()
Via a call to CheckAutoAttack(this_player()), the living
checks if it is aggressive against the current user and
schedules an attack if necessary.
int combat_heartbeat ()
If this living has enemies and no combat delay is set,
an attack is scheduled by a call to Attack().
In case of combat delay, the delay is reduced by 1 and
an appropriate message displayed to this living.
The enemy-array is cleaned, those enemies that disappeared
will be put into the hunters array.
Finally, if there are no more enemies and no combat delay,
the combat heartbeat is dropped.
These functions are assumed as being available in other
modules:
/std/living/description: QueryRaceObj(), CanSeeLiving(),
QueryIsNPC(), QueryIsPlayer()
/std/living/heart : AddHeart(), RemoveHeart()
/std/living/stats : QueryCon(), QueryDex(), QueryInt(),
QueryStr(), UseStat()
/std/living/body : DoDamage(), AddXP()
DESCRIPTION - /std/living/commands
This module implements a stack which grows and shrinks
according to the command level nesting.
Its primary use is to keep track of the notify_fail() data.
but can be used to store other data which needs to live just
for this very command.
Another important use is to delay the execution of functions
until that moment this very command has been finished (thus
replacing call_out("fun", 0)s).
For external users, only the functions command_me(),
SetCmdData(), QueryCmdData() and RegisterCmdFun() are of
interest, all other functions are managed by the mudlib alone.
int command_me (string cmd)
Command the living to perform <cmd>.
Result is 0 for failure, or the evaluation costs of the
command.
mixed * QueryLastNotifyFail()
Returns an array ({ <data>, <priority> }) with the
notify_fail()-data of the last executed command.
This information is valid until the next command given by
the player resp. the next call to command_me().
mixed SetCmdData (string name, mixed data)
Stores the <data> under the <name> on the current frame of
the stack.
mixed QueryCmdData (string name)
Retrieves the <data> stored under the <name> from the
current frame of the stack. Data from other commands
frames is inaccessible.
The actual notify_fail-data is accessible under the name
"NotifyFail" (the data might be 0).
void RegisterCmdFun (string fun, void|mixed arg)
void RegisterCmdFun (closure fun, void|mixed arg)
void RegisterCmdFun (mixed *fun, void|mixed arg)
Register function <fun> for execution with argument <arg>
at the end of this very command. If <fun> has been
registered before, overwrite that registration with this
one.
<fun> will be called with <arg> as first argument and an
integer as second. If the latter is 0, it has been a normal
command termination, if nonzero, the command ran into a
runtime error and the mudlib just tries to clean it up.
<fun> may take following forms:
- a closure defining the function to call.
- a string of the form "name" with <name> denoting the
lfun to call in the registering object.
- a string of the form "obj->name", denoting lfun <name>
to call in object <obj>. <obj> is resolved according to
resolve_file().
- an array ({ obj, name }).
The following CmdData names are reserved for internal use:
object "PreviousPlayer" : The previous this_player()
object "ThisPlayer" : don't use
mixed *"RegisteredFuns" : don't use
mixed *"NotifyFail" : don't use
The stack is handled with the following functions - don't call
them manually!
int QueryCmdDataSize ()
Returns the current size of the stack.
void ResetCmdData ()
Empties the stack. It should be called every
now and then from outside of commands (heart_beat(),
reset()) to prevent memory leakages.
The functions in this module set up a (single) heartbeat
call to this function whenever data is put into the stack.
void EmptyCmdData (int unwind)
Empties the stack, executing the functions registered in
each entry. <unwind> is passed as second parameter to
those functions.
This function is called during error clean ups.
void PushCmdData ()
Pushes a new 'default' entry onto the stack.
mapping PopCmdData(int unwind)
Pops the topmost entry from the stack, generating a
default entry if the stack is empty.
Functions registered in the popped entry are executed,
passing <unwind> as second parameter.
Result is the popped entry, an mapping.
void ExecuteRegistered (mapping entry, int unwind)
Execute the functions registered in the given stack
<entry>. <unwind> is passed as second parameter to the
functions.
varargs void NotifyFail (string|closure msg, void|int pri)
Sets a notify_fail message <msg> on the top of the stack if
its <pri>ority is at least as high as the priority of the
fail message already on the stack.
The <msg> may be a string or a closure yielding the
string.
These functions are assumed as being available in other
modules:
/std/living/heart : AddHeart()
DESCRIPTION - /std/living/description
This module implements the outer appearance and senses of the
living. For this it overloads some functions of
/std/thing/description and /std/room/description.
All livings are in one of three states: normal, ghosted or
frogged. The non-normal states lead to certain restrictions,
and also alter the appearance.
Additionally, ghosts have several advantages: they can see at
any lighting, don't suffer from hunger or thirst, can see
invisible beings and hidden exits and walk through closed
doors.
The gender of livings can be male, female or other. Ghosted
livings are always 'other'.
These inherited properties are redefined as attributes:
A_ADS for P_ADS
A_IDS for P_IDS
A_INT_LONG for P_INT_LONG
A_INT_NOISE for P_INT_NOISE
A_INT_SMELL for P_INT_SMELL
A_INT_SHORT for P_INT_SHORT
A_LONG for P_LONG
A_NOISE for P_NOISE
A_SHORT for P_SHORT
A_SMELL for P_SMELL
A_SIZE for P_SIZE
The short descriptions (A_SHORT, A_INT_SHORT, A_LONG,
A_INT_LONG) are redefined as this:
string|string* A_SHORT "Short"
string|string* A_INT_SHORT "IntShort"
string|string* A_LONG "Long"
string|string* A_INT_LONG "IntLong"
The (internal) short/long description of the living.
The value can be a single string, or an array of up to two
strings for the possibles states of the living:
[LSTATE_NORMAL] : normal
[LSTATE_GHOST ] : ghosted
If a string is not given, a default is used.
Following attributes and properties are builtin:
int A_ACTIONS "Actions"
The number of actions the living has per combat round.
int A_INVIS "Invis"
Non-zero if the living is invisible.
int A_IVISION "IVision"
The lower light level the living needs to see.
int A_UVISION "UVision"
The upper light level the living can use to see.
int A_MAGIC_DEFENCE "MagicDefence"
The ability of the living (0..100) to resist a spell
casted on it.
int A_LEVEL "Level"
The current 'level' of the living, whatever it means.
int A_LSTATE "LState"
The state of the living:
0 = LSTATE_NORMAL
1 = LSTATE_GHOST
3 = LSTATE_FROG
Setting a living to non-NORMAL may force it to drop some
of its belongings.
It is not possible to unfrog frog-npcs. Frogged livings
have an enforced P_SIZE of PSIZE_SMALL.
int P_FROG "Frog"
int P_GHOST "Ghost"
These are just frontends to A_LSTATE.
int A_GENDER "Gender"
The gender of the living:
0 = GENDER_UNSET.
1 = GENDER_MALE,
2 = GENDER_FEMALE,
3 = GENDER_OTHER (aka GENDER_NEUTER)
string A_RACE "Race"
The race of the living.
If this attribute is set in an uninitialized living (i.e.
no race and no long description set yet), the living is
initialized with the default values from the RACEMASTER.
object P_RACE_OBJ "RaceObj"
If the A_RACE of the living is something official,
querying this property returns its race object (mostly
"/obj/race/<A_RACE>").
If the raceobject could not be loaded for a while, query
this property as 'QueryRaceObj(1)' to update the livings
notion of its race object.
string A_NAME "Name"
string SetName(string name, void|int no_sln)
string QueryName(void | mixed true_name)
The name of the living in proper capitalization.
Setting the name also sets A_SHORT and the gamedriver's
living_name to this name (the latter only if no_sln is
given as 0 or not given at all).
When queried from an A_INVIS living, "Someone" is returned
instead of the real name, unless a nonzero argument is
passed as <true_name>.
string P_REALNAME "RealName"
The 'real' name of the living, in all lowercase.
This attribute can't be set, instead it is hardcoded.
For livings it just returns A_NAME.
int A_ALIGN "Align"
The alignment of the living, a value from -1000..1000.
string A_MSGIN "MsgIn"
The message to give on normal entrance into a room.
The livings name will be prepended.
If the living is a ghost or a frog, a suiting message
is returned instead of the set A_MSGIN.
To unconditionally retrieve the real A_MSGIN, query for
the 'soft' property.
string A_MSGOUT "MsgOut"
The message to give on normal leave from a room.
The livings name will be prepended.
If the living is a ghost or a frog, a suiting message
is returned instead of the set A_MSGIN.
To unconditionally retrieve the real A_MSGIN, query for
the 'soft' property.
string A_MMSGIN "MMsgIn"
string A_MMSGOUT "MMsgOut"
As A_MSGIN/A_MSGOUT, only that these are used for
teleportations.
Though mainly for players, this soft attribute exists as well:
mixed A_DESCRIPTION "Description"
Either a string or an array of strings for the several states,
this is added after the long description.
Some supporting functions:
string QueryAlignString (int align)
Return a word describing the given <align>ment (like
'evil').
int UpdateAlign (int up)
Update the livings alignment by an other livings alignment
<up>. This will normally called by Die() in the victor.
string QueryGenderString ()
Return a string describing the livings gender.
string QueryPronoun ()
Return the livings personal pronoun: 'he', 'she' or 'it'.
string QueryPossessive ()
Return the livings possessive pronoun: 'his', 'her', or 'its'.
string QueryObjective ()
Return the livings objective pronoun: 'him', 'her', or 'it'.
The long and short descriptions modified on query according to
the state of the living. This is done by redefinition of the
resp. inherited functions.
If the optional parameter <what> of the functions is given,
the call will be passed to the inherited function and that
result returned. This will not be mentioned separately, so
keep it in mind :-).
string P_PRECONTENT "PreContent"
This is hardcoded to be "He/She/It carries:\n".
static string *_weapons (string possessive)
This is an auxiliary function.
It has to return an (possibly empty) array of which
weapons are wielded where, e.g.
({ "a twohander with <possessive> right hand" })
static string *_armours ()
This is an auxiliary function.
It has to return an (possibly empty) array of which
armours (or other things) are worn, e.g.
({ "a helmet", "a pair of boots", "a pair of sunglasses" })
static string *_equipment ()
This is an auxiliary function.
It has to return an (possibly empty) array of which
equipment the living uses, e.g.
({ "a torch", "a screwdriver(2)" })
static string *_hands ()
This is an auxiliary function.
It has to return an (possibly empty) array of which
things the living holds in his hands, e.g.
({ "a torch", "a flag" })
Excluded are armours, weapons and equipment.
static string *_long (void|string headstr)
This is an auxiliary function, used to build the
(Int)Long() description.
It has to return an array of \n-terminated strings, which
has to contain at least:
({ the given <headstr> (or "")
, the combined P_EXTRALOOKs of the carried objects
, a string describing the health and load of the living
, a string describing the armours/equipment worn
, a string describing the weapons wielded by which hand
, a string describing the equipment set
, a string describing the things held
})
This order is not mandatory, but recommended.
string Content ( void|string what
, mixed exclude
, void|object player
)
<exclude> got an additional meaning: if specified as
non-zero integer, weapons and armours are not contained in
the returned list.
string Short (void|string what)
string IntShort (void|string what)
Return the (internal) short description of the living.
If the living is invisible and this_player() is neither
not at least learner nor able to see invisibles
(P_SEE_INVIS != 0), 0 is returned.
If ::(Int)Short() returns an array containing a description
for the livings current state, that will be used.
Else a decent default message will be constructed, using
A_NAME and/or the existing ::(Int)Short().
string Long (void|string what)
string IntLong (void|string what)
Return the (internal) long description of the living.
If this_player() is not at least learner and the living is
invisible, 0 is returned.
If ::(Int)Long() returns an array containing a description
for the livings current state, that will be used as
<headstr>, else a decent default <headstr> will be
constructed, using A_NAME and/or the existing ::(Int)Long().
Result will be the imploded result of _long(<headstr>).
string ExaLong (void|string what)
string ExaIntLong (void|string what)
Return the (internal) long description of the living under
close examination.
This just returns (Int)Long(what).
The identification is done by a redefinition of id():
int id (string s)
Return non-zero if <s> identifies this object.
If the living is invisible, not called by itself and not
at least learner, 0 is returned.
Else, if <s> is accepted by the inherited id(), or matches
the A_NAME, the P_REAL_NAME or the A_RACE, non-zero is
returned to signal the success.
For ghosts or frogs, "ghost" resp. "frog" are accepted as
id as well.
If the current player is the queried living, the ids "me"
and "myself" are accepted.
A living can be classified using these functions:
int QueryIsLiving ()
Result is non-zero if the object is living by the means of
gameplay. This is to distinguish true livings from merely
technically living objects (like microphones).
int QueryIsNPC ()
Result is non-zero if the object is living and was never
interactive.
If P_IS_PET is set, QueryIsNPC returns 0.
int QueryIsPlayer ()
Result is non-zero if the object was or is an interactive
player.
nomask int QueryIsWizard ()
Result is non-zero if the object was or is a wizard
int P_IS_PET "IsPet"
Usually 0, this property has to be set to non-zero if the
living is a familiar (a fighting pet).
As sideeffect this causes QueryIsNPC to return 0, making
the pet attackable even for those NPCs which don't fight
other NPCs.
The module also implements the perceptional functions here
(since they have to go somewhere).
int P_BLIND "Blind"
When set to non-zero, the living is not able to see
anything, independant of the current lighting.
This is not a builtin property!
int P_SEE_INVIS "SeeInvis"
When set to non-zero, the living is able to see other
livings even when they are invisible.
If a player with this property set actually sees an invisible
buing is further governed by the P_SEE_INVIS_CHANCE property
(see below).
This is not a builtin property!
int P_SEE_INVIS_CHANCE "SeeInvisChance"
A value in the range 1..1000 giving the chance that a player
with P_SEE_INVIS set actually spots an invisible being.
A value of 1000 (as well as the default value of 0) allow
the player to see invisible beings on every look.
This is not a builtin property!
int CanSee (void|object env)
Check if the living can see in the <env>ironment (default
is the current environment), ignoring any P_BLIND setting.
The function checks the light levels and calls CanSeeHere()
in the <env>ironment.
Result is non-zero if it can see, else 0.
int CantSee (void|object env)
Check if the living can _not_ see in the <env>ironment (default
is the current environment), ignoring any P_BLIND setting.
The function checks the light levels and calls CanSeeHere()
in the <env>ironment.
Results:
< 0: it is too dark, result is the difference to A_IVISION.
= 0: the living can see.
> 0: it is too bright, result is the difference to A_UVISION.
vcarargs int CanSeeLiving (object|string liv, int distance)
Check if the living can see the other living <liv> (which
defaults to this_player()). Result is non-zero if it can,
and zero if it can't.
The function checks invisibility versus ghostness,
wizardness, and the ability to see invisible beings.
It does not care for the lighting, though.
distance= 0: Check in this room only
-1: Check in the whole mud
number: Check in a radius of this distance
mixed Search (mixed what, int mode)
mixed SearchM (mixed what, int mode, void|closure pred)
See /doc/concepts/search for a detailed documentation.
The function to catch the messages is also here:
void catch_msg (mixed *msg)
Catch and evaluate a message.
The data of MSG_SEE messages is told to the living
according to the lighting of its environment.
The text of MSG_HEAR messages is told to the living.
Other messages are ignored.
These functions are assumed as being available in other
modules:
/std/living/attributes : QueryAttr()
/std/living/body : QueryHP(), QueryMaxHP(), QueryHealthMsg()
/std/living/combat : QueryArmours(), QueryWeapons()
/std/living/hands : QueryHands()
/std/living/restrictions: DropOverload()
DESCRIPTION - /std/living/heart
Livings have an ongoing heartbeat, to trigger automatic
actions, both inside the livings and outside.
While this is a good thing because its easy to use and avoids
lots of call_outs, it results in a waste of cycles if the
heartbeats run w/o actually doing something and disabling the
swap out of the object.
This module solves the problem by using the generic heartbeat
mechanism to implement a on-need system. Instead of putting
all heartbeat actions into one function (and redefining it
in several layers), multiple functions can be registered with
this module to be called in the next heartbeat.
These registered functions have to return a non-zero value in
order to be called again in the next heartbeat. If they return 0,
they won't be called again unless re-registered.
mixed * P_HBHOOKS "HbHooks"
The list of functions to be called in the next heartbeat.
Each entry can be given as a string with the name of
the function to call, or as closure to execute.
The latter allows functions from external objects to
be called in the heartbeat as well.
void AddHeart (string hook)
void AddHeart (closure hook)
Add the <hook> to the list of functions.
Multiple calls with the same <hook> value are allowed.
However, do not call AddHeart() once with the functionname
as string, and once as closure - it will result in the
function to be called twice.
void RemoveHeart (string hook)
void RemoveHeart (closure hook)
Remove the <hook> from the list of registered functions,
if it is present at all.
int CheckHeart (string hook)
int CheckHeart (closure hook)
Check if <hook> is in the list of registered functions.
Return non-zero if yes, and 0 if not.
A second method to act in heartbeats is to define your owh
heart_beat() function (which inherits this modules heart_beat())
and control execution with this property:
int P_HEART "Heart"
Set to true if the toplevel heart_beat() shall be executed,
set to false if not.
Note that this property is just a hint for the heart module if
the heartbeat is needed or not; the arbitration has to be done
by yourself. Your heart_beat() function thus should look like
this:
void heart_beat() {
::heart_beat();
if (QueryHeart()) {
..your actions...
}
}
To make sure the heart is working propery, call this function:
void ValidizeHeart()
It checks some conditions and (re)starts the heartbeat when
necessary.
DO NOT CALL set_heart_beat() YOURSELF!
The internals of the heart module are are controlled by
these functions:
void create()
Initialize the heart module.
void heart_beat()
Call all registered functions.
int set_heart (int i)
Unconditionally performs a set_heart_beat(i) for the
living and returns the result.
You'll hardly need to call this.
DESCRIPTION - /std/living/moving
This module contains the functions used to move a living
around, or let it move by itself.
The function to go one step is:
int GoAway (void|int stunned)
Go away one room. Return non-zero on success, else 0 if no
exit were available.
This functions tries to go with the living through one of
the available exits. If possible, it tries to maintain the
direction which the last GoAway() used.
If <stunned> is non-zero, the GoAway() will succeed even
if the living is knocked-out.
It is used e.g. for emergencies:
void Whimpy ()
The living tries to run away, using GoAway(1).
If the living is knocked-out, it has a chance of 30% to run
away nevertheless.
The living is told approbiate messages.
Livings can be configured to walk around on their own. They do
this with a specific chance per heartbeat.
int A_GO_CHANCE "GoChance"
The chance (0..1000) that the living will go in one
heartbeat.
Setting the value to non-zero will restart the call_outs.
The walking is done by using self-starting call_outs:
void OneStep (int justSched)
If <justSched> is non-zero, just schedule the next attempt
to walk away, else go one step (using GoAway()) and then
schedule the next attempt.
If A_GO_CHANCE is zero, no next attempt is scheduled, of course.
The movement may be restricted to specific regions with the
property:
int P_ALLOWED_REGIONS "AllowedRegions"
The binary-or combination of REGION_xxxx values. If the
destination of a move() does not match at least one of the regions
allowed, the move is forbidden.
If this property is not set (ie. the value is zero), all regions
are allowed as destination.
To output approbiate messages both to the environment as to the
living itself, two functions from /std/thing/moving are
redefined:
int move (mixed dest, void|int method, void|mixed extra) {
Output approbiate messages if the living doesn't fit into
the <dest> room, or if the living used M_SPECIAL.
If the living is knocked-out, a M_GO or M_TELEPORT move
will fail (unless done from Whimpy()).
Also, for M_SPECIAL, M_GO and M_TELEPORT, interactive user
will be told the long room description by a call to
LookAfterMove() in the living object.
<extra> will be interpreted depending on the move-method:
M_SILENT and M_NOCHECK:
<extra> is ignored.
M_GO:
* <extra> is empty:
The room the living leaves will get:
"Name leaves.\n"
and the room where the living arrives:
"Name arrives.\n"
* simple string or array with one string:
The room the living leaves will get:
"Name leaves "+<extra>+".\n"
and the room where the living arrives:
"Name arrives.\n"
Only if it can be evaluated where someone came from
depending on <extra> the message will
different. Lets say <extra> is "north" then the
message will be:
"Name arrives from south.\n"
* array with two strings:
The room the living leaves will get:
"Name leaves "+<extra>[0]+".\n"
and the arrival-room will get:
"Name arrives "+<extra>[1]+".\n"
M_SPECIAL and M_TELEPORT:
* <extra> is empty:
M_TELEPORT:
"Name leaves in a puff of smoke.\n"
"Name arrives in a puff of smoke.\n"
M_SPECIAL:
"Name .\n"
"Name .\n"
* simple string:
The room the living leaves will get:
"Name "+<extra>+".\n"
and the room where the living arrives:
"Name "+<extra>+".\n"
* array with one string:
Here's a difference between M_SPECIAL and
M_TELEPORT. The leave-message is the same as if a
simple string is given. But the room, where the
living arrives will get a different message:
M_TELEPORT:
"Name arrives in a puff of smoke.\n"
M_SPECIAL:
"Name .\n"
* array with two strings:
The room the living leaves will get:
"Name leaves "+<extra>[0]+".\n"
and the arrival-room will get:
"Name arrives "+<extra>[1]+".\n"
* array with three strings:
Third entry will be told to the player:
"You "+<entry>[2]+".\n"
Of course the examples are only created for players with
the standard MsgIn/Out and MMsgIn/Out.
int remove ()
If the living is an interactive user, it is notified about
the destruct.
Then the living is just destructed.
These functions are assumed as being available in other
modules:
/std/living/actions : QueryStunned()
DESCRIPTION - /std/living/restrictions
Here the restrictions of the 'container' like nature of
livings are implemented.
It is mostly just a tweaking of the functions and builtin
properties inherited from /std/container/restrictions.
Additionally, the state of the living (normal, ghost or
frogged) has effects on the weights:
- normal livings can carry 10..70 (10..130) kg.
- ghosts have no own weight and can carry 1..13 g.
- frogs have half the normal weight and can carry 3..23 (3..39) kg.
int P_WEIGHT "Weight"
The body weight of the living is adapted according to
the livings state.
Note that the adaption works in both directions: the value
actually stored is always the full weight.
If setting the weight results in an overload, the HEART_BODY
heartbeat is started.
int P_MAX_WEIGHT "MaxWeight"
This can only be read, and returns a value depending on
the A_STR stat and the state of the living.
int P_LOAD "Load"
This read-only property returns in % the actual load of
the living in relation to the possible maximum, yielding a
value 0..200.
The 100%-point is determined by LOAD_LIMIT(P_MAX_CONTENT).
int P_MAX_CONTENT "MaxContent"
This read-only property returns in maximal load the
living can carry in its current state for a short time.
This is twice the load it can carry w/o penalties.
A function helps in dealing with too heavy loads.
void DropOverload (void|int howmuch)
If the livings carries more that it could do (e.g. after
being frogged), the function drops randomingly some
objects until the load is light enough again.
If <howmuch> >= 0, the living drops enough to reach the
normal load, or in case of > 0: half the normal load.
If <howmuch> is less than zero, everything is dropped.
Autoloaders and non-droppable objects are not dropped.
Two functions of container/restrictions need some fudging and
are thus redefined:
void notify_leave(mixed dest, void|mixed method, void|mixed extra)
The function outputs approbiate messages to a moved living if
it is taken, dropped or given away.
Everything else is done in the inherited function.
void notify_enter(object source, void|mixed method, void|mixed extra)
The function outputs approbiate messages to a moved living if
it is taken.
Everything else is done in the inherited function, or by
the 'giver'.
int prevent_leave(mixed dest, int method, void|mixed extra)
If the method is 'M_GET', ME_NO_LEAVE is returned, else
the inherited function is execute.
void allow_enter (int method, void|mixed extra)
If the method is 'M_PUT', ME_NO_ENTER is returned, else
the inherited function is execute.
Both functions also make additional messages if the weight of
the thing moved in or out would change the carried load by
more than LOAD_DTHRESH%.
If the living carries more than 100% load, every additional
weight causes a usage of the 'Str' stat (with automatic success).
To initialize the module, the lfun
void create()
must be called.
These functions are assumed as being available in other
modules:
/std/living/attributes : QueryAttr()la /std/living/description: QueryName(), QueryLState(), QueryInvis()
QueryPossessive()
/std/living/heart : QueryHeart()
/std/living/stats : QueryStr(), UseStat()
DESCRIPTION - /std/living/stats
The plain standard living implements a very simple version of
the stat/skill system: everything must be explicitely set, the
living is not able to learn by itself.
Therefore, just the absolute minimum is found here.
The following stat properties are builtin here:
int A_STR "Str"
The strength of the living.
int A_INT "Int"
The intelligence of the living.
int A_CON "Con"
The constitution of the living.
int A_QUI "Qui"
The quickness of the living.
int A_AGI "Agi"
The Agility of the living.
int A_CHA "Cha"
The charisma of the living.
int A_WIS "Wis"
The wisdom (magical knowledge) of the living.
The number of stats is available as NUMBER_STATS.
Every change of a stat starts the HEART_BODY heartbeat.
int QuerySpell(string spell)
int SetSpell(string spell,int value)
Query the spell of a living (0..1000) or set its spell
to the given value. The SetSpell() return the old value
of the spell.
mapping QuerySpells()
Return a mapping of all spells with their values. Mostly
for internal use
int QuerySkill(string skill)
int SetSkill(string skill,int value)
Query the skill of a living (0..1000) or set its skill
to the given value. The SetSkill() return the old value
of the skill.
mapping QuerySkills()
Return a mapping of all skills with their values. Mostly
for internal use
These functions are assumed as being available in other
modules:
/std/living/attributes: QueryAttr(), SetAttr()
/std/living/heart : GetHeart()
BUGS/TODO
Talent documentation, when talents got approved
/std/living:
InitLevel() should consider a P_RACE setting.
Also, the default values need to be checked, e.g. the
health rates should increase with the level.
InitDefault() doesn't care for races.
/std/living/actions:
A simple Actions-Delayhandling is needed.
/std/living/combat:
Defend(): the relation of target zones should be
checked. Maybe make them race-specific?
INHERITANCE TREE
std/living
|- std/living/moving
| `- std/thing/moving
|- std/living/attributes
|- std/living/body
|- std/living/chat
|- std/living/commands
|- std/living/description
| |- std/thing/description
| `- std/room/description
|- std/living/combat
|- std/living/heart
|- std/living/restrictions
| `- std/container/restrictions
| |- std/thing/restrictions
| `- std/room/restrictions
|- std/living/stats
|- std/room/items
`- std/base
SEE ALSO
attributes(C), combat(C), health(C), perception(C), search(C),
skills(C), base(S), combat(S), thing(S), equipment(S)