![]() |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The idea with the shadow functionality is to mask one or more functions in a target object with others having the same name in a shadow object The purpose of this procedure is usually to modify the behaviour of the target object in some way.
Shadows are tricky in the respect that they are managed in runtime and not during compile time. This means that not only may shadows fail for various reasons which I'll go in to later, but they also unintentionally may cause the target object to stop working.
The basic rule is not to use shadows unless you really have to; they often are more trouble than they are worth. With that dire warning in mind, let us go on to the subject of creating and using them.
This in turn means that all functions in the shadowing object either has to be unique to the object itself, or be shadowable in the target object. Now, shadowing isn't completely without conditions:
query_prevent_shadow()
returning
the integer value 1
cannot be shadowed. NB! This is
determined by a call to the function query_allow_shadow()
in
/secure/master.c
, so this actually may work differently on
different muds.
nomask
.
Please notice that it's perfectly possible to declare the same variable globally in the shadow as is used in the target object, but that we then are talking about two different variables. This means that calls which are intercepted by the shadow will use the shadow-declared variable, and calls intercepted by the target object (to non-shadowed functions) will use its own local variable, perhaps producing conflicting results.
call_other()
are processed by the shadowing
mechanism. This means that any direct calls to a function in an object
will work just as usual and never notice the shadow function. This often
means that unless the creator of an object intended for a function to be
shadowed, it might both be using global variables directly (instead of
through public-declared wrapper functions) and be calling that function
directly instead of through call_other()
. Oh, I hope you remember
that constructions on the form obref->function(args)
is just
another way of writing call_other(obref, "function", args)
.
There's a few consequences of the above that are worth remembering.
As you already know, any object you want to bring into the game proper
has to be a derivate of the /std/object.c
object. If you create
an object without inheriting /std/object.c
or one of its
derivates, that object can never be inserted in to the inventory of any
other object, which in turn means that it never can appear in the
game. Since the /std/object.c
object contains a number of
unmaskable functions, this means that a shadow as such never can be
inserted into the game. Well, unless it inherits /std/object.c
itself, and only shadows some kind of non-insertable object, of
course. I suppose that this isn't impossible as such, just rather
unlikely.
Assume that an object has been shadowed by several objects, and that
the same function is shadowed in all objects, which shadow finally
gets the call? This is not an impossible situation if you're thinking
that I'm posing a trick question; since you're not shadowing a shadow,
but the original object, and since the function is shadowable there's
no preventing this from happening. The answer is that it's the last
function to shadow the object that 'wins'. Obviously this is not a
very good situation, so often the best way of making sure that your
object is the only final object to shadow another is to define the
function query_prevent_shadow()
to return 1. However, this
obviously makes it impossible for all other objects to shadow any
other function as well, so one has to be careful lest one interrupts
some other object from working.
By far the easiest way of creating a working shadow object is to
inherit the standard shadow object /std/shadow.c
and use
the functionality provided there. With that module you're not only
getting a standardized way of dealing with shadows that most other
object-managing objects recognize and can use, you're also getting a
number of handy functions that make sure that the shadow doesn't shadow
the same object twice by mistake, that autoloading shadows on players
automatically latch on to the player on login again as well as provide a
mechanism for removing the shadow.
Assume for example that you want to redefine the gender of a player
who's been affected by an evil spell of some kind. The function you
want to shadow is called query_gender_string()
and can be
found in /std/living/gender.c
. This is how you do it:
inherit "/std/shadow"; int start(string pl_name) { return(objectp(shadow_me(pl_name))); } int stop() { remove_shadow(); } string query_gender_string() { return "xxx"; } |
Please notice that I've added both a start and a stop function for
easy testing; these really aren't necessary as long as the shadow
somehow calls shadow_me()
with the target (either as a name
or an object reference) as argument.
The standard shadow module also provides easy access to the original
object. The variable shadow_who
is set to the original object, so
in the shadow object any call on the form shadow_who->function()
goes to the original object function, even if it is shadowed in the
shadow object.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |