skylib_mudos_v1/
skylib_mudos_v1/bin/
skylib_mudos_v1/bin/db/
skylib_mudos_v1/mudlib/banish/a/
skylib_mudos_v1/mudlib/banish/b/
skylib_mudos_v1/mudlib/banish/c/
skylib_mudos_v1/mudlib/banish/d/
skylib_mudos_v1/mudlib/banish/e/
skylib_mudos_v1/mudlib/banish/f/
skylib_mudos_v1/mudlib/banish/g/
skylib_mudos_v1/mudlib/banish/h/
skylib_mudos_v1/mudlib/banish/j/
skylib_mudos_v1/mudlib/banish/l/
skylib_mudos_v1/mudlib/banish/m/
skylib_mudos_v1/mudlib/banish/n/
skylib_mudos_v1/mudlib/banish/o/
skylib_mudos_v1/mudlib/banish/p/
skylib_mudos_v1/mudlib/banish/r/
skylib_mudos_v1/mudlib/banish/s/
skylib_mudos_v1/mudlib/banish/t/
skylib_mudos_v1/mudlib/banish/u/
skylib_mudos_v1/mudlib/banish/w/
skylib_mudos_v1/mudlib/cmds/
skylib_mudos_v1/mudlib/cmds/admin/
skylib_mudos_v1/mudlib/cmds/guild-race/
skylib_mudos_v1/mudlib/cmds/guild-race/crafts/
skylib_mudos_v1/mudlib/cmds/guild-race/magic/
skylib_mudos_v1/mudlib/cmds/guild-race/other/
skylib_mudos_v1/mudlib/cmds/living/broken/
skylib_mudos_v1/mudlib/cmds/player/group_cmds/
skylib_mudos_v1/mudlib/d/admin/
skylib_mudos_v1/mudlib/d/admin/room/
skylib_mudos_v1/mudlib/d/admin/room/we_care/
skylib_mudos_v1/mudlib/d/admin/save/
skylib_mudos_v1/mudlib/d/admin/text/
skylib_mudos_v1/mudlib/d/learning/TinyTown/buildings/
skylib_mudos_v1/mudlib/d/learning/TinyTown/map/
skylib_mudos_v1/mudlib/d/learning/TinyTown/roads/
skylib_mudos_v1/mudlib/d/learning/chars/
skylib_mudos_v1/mudlib/d/learning/functions/
skylib_mudos_v1/mudlib/d/learning/handlers/
skylib_mudos_v1/mudlib/d/learning/help_topics/
skylib_mudos_v1/mudlib/d/learning/help_topics/npcs/
skylib_mudos_v1/mudlib/d/learning/help_topics/objects/
skylib_mudos_v1/mudlib/d/learning/help_topics/rcs_demo/
skylib_mudos_v1/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/crowd/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/situations/
skylib_mudos_v1/mudlib/d/learning/save/
skylib_mudos_v1/mudlib/d/learning/school/
skylib_mudos_v1/mudlib/d/learning/school/add_sc/
skylib_mudos_v1/mudlib/d/learning/school/characters/
skylib_mudos_v1/mudlib/d/learning/school/general/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/basic_commands/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/edtutor/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_mudos_v1/mudlib/d/learning/school/items/
skylib_mudos_v1/mudlib/d/learning/school/npc_school/
skylib_mudos_v1/mudlib/d/learning/school/room_school/
skylib_mudos_v1/mudlib/d/learning/school/room_school/room_basic/
skylib_mudos_v1/mudlib/d/learning/school/room_school/situations/
skylib_mudos_v1/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_mudos_v1/mudlib/d/learning/text/
skylib_mudos_v1/mudlib/d/liaison/
skylib_mudos_v1/mudlib/d/mudlib/
skylib_mudos_v1/mudlib/d/mudlib/changes/
skylib_mudos_v1/mudlib/d/playtesters/
skylib_mudos_v1/mudlib/d/playtesters/effects/
skylib_mudos_v1/mudlib/d/playtesters/handlers/
skylib_mudos_v1/mudlib/d/playtesters/items/
skylib_mudos_v1/mudlib/d/sage/
skylib_mudos_v1/mudlib/doc/
skylib_mudos_v1/mudlib/doc/creator/
skylib_mudos_v1/mudlib/doc/driver/
skylib_mudos_v1/mudlib/doc/driver/efuns/arrays/
skylib_mudos_v1/mudlib/doc/driver/efuns/buffers/
skylib_mudos_v1/mudlib/doc/driver/efuns/compile/
skylib_mudos_v1/mudlib/doc/driver/efuns/filesystem/
skylib_mudos_v1/mudlib/doc/driver/efuns/floats/
skylib_mudos_v1/mudlib/doc/driver/efuns/functions/
skylib_mudos_v1/mudlib/doc/driver/efuns/general/
skylib_mudos_v1/mudlib/doc/driver/efuns/mappings/
skylib_mudos_v1/mudlib/doc/driver/efuns/mixed/
skylib_mudos_v1/mudlib/doc/driver/efuns/mudlib/
skylib_mudos_v1/mudlib/doc/driver/efuns/numbers/
skylib_mudos_v1/mudlib/doc/driver/efuns/parsing/
skylib_mudos_v1/mudlib/doc/known_command/
skylib_mudos_v1/mudlib/doc/login/
skylib_mudos_v1/mudlib/doc/lpc/basic_manual/
skylib_mudos_v1/mudlib/doc/lpc/intermediate/
skylib_mudos_v1/mudlib/doc/new/add_command/
skylib_mudos_v1/mudlib/doc/new/events/
skylib_mudos_v1/mudlib/doc/new/handlers/
skylib_mudos_v1/mudlib/doc/new/living/race/
skylib_mudos_v1/mudlib/doc/new/living/spells/
skylib_mudos_v1/mudlib/doc/new/object/
skylib_mudos_v1/mudlib/doc/new/player/
skylib_mudos_v1/mudlib/doc/new/room/guild/
skylib_mudos_v1/mudlib/doc/new/room/outside/
skylib_mudos_v1/mudlib/doc/new/room/storeroom/
skylib_mudos_v1/mudlib/doc/object/
skylib_mudos_v1/mudlib/doc/playtesters/
skylib_mudos_v1/mudlib/doc/policy/
skylib_mudos_v1/mudlib/doc/weapons/
skylib_mudos_v1/mudlib/global/
skylib_mudos_v1/mudlib/global/creator/
skylib_mudos_v1/mudlib/global/handlers/
skylib_mudos_v1/mudlib/global/virtual/setup_compiler/
skylib_mudos_v1/mudlib/include/cmds/
skylib_mudos_v1/mudlib/include/effects/
skylib_mudos_v1/mudlib/include/npc/
skylib_mudos_v1/mudlib/include/room/
skylib_mudos_v1/mudlib/include/shops/
skylib_mudos_v1/mudlib/net/daemon/
skylib_mudos_v1/mudlib/net/daemon/chars/
skylib_mudos_v1/mudlib/net/inherit/
skylib_mudos_v1/mudlib/net/obj/
skylib_mudos_v1/mudlib/obj/amulets/
skylib_mudos_v1/mudlib/obj/b_day/
skylib_mudos_v1/mudlib/obj/clothes/
skylib_mudos_v1/mudlib/obj/dwarmours/plate/
skylib_mudos_v1/mudlib/obj/dwclothes/transport/horse/
skylib_mudos_v1/mudlib/obj/dwscabbards/
skylib_mudos_v1/mudlib/obj/dwweapons/axes/
skylib_mudos_v1/mudlib/obj/dwweapons/chains/
skylib_mudos_v1/mudlib/obj/faith/symbols/
skylib_mudos_v1/mudlib/obj/fungi/
skylib_mudos_v1/mudlib/obj/gatherables/
skylib_mudos_v1/mudlib/obj/instruments/
skylib_mudos_v1/mudlib/obj/magic/
skylib_mudos_v1/mudlib/obj/media/
skylib_mudos_v1/mudlib/obj/misc/player_shop/
skylib_mudos_v1/mudlib/obj/monster/godmother/
skylib_mudos_v1/mudlib/obj/monster/transport/
skylib_mudos_v1/mudlib/obj/rings/
skylib_mudos_v1/mudlib/obj/spells/
skylib_mudos_v1/mudlib/obj/stationery/
skylib_mudos_v1/mudlib/obj/stationery/envelopes/
skylib_mudos_v1/mudlib/obj/stationery/papers/
skylib_mudos_v1/mudlib/obj/toys/
skylib_mudos_v1/mudlib/obj/vessels/
skylib_mudos_v1/mudlib/obj/weapons/swords/
skylib_mudos_v1/mudlib/save/autodoc/
skylib_mudos_v1/mudlib/save/leaflets/
skylib_mudos_v1/mudlib/save/mail/
skylib_mudos_v1/mudlib/save/new_soul/data/
skylib_mudos_v1/mudlib/save/parcels/
skylib_mudos_v1/mudlib/save/playerinfo/
skylib_mudos_v1/mudlib/save/players/d/
skylib_mudos_v1/mudlib/save/random_names/
skylib_mudos_v1/mudlib/save/random_names/data/
skylib_mudos_v1/mudlib/save/terrains/
skylib_mudos_v1/mudlib/save/terrains/tutorial_desert/
skylib_mudos_v1/mudlib/save/terrains/tutorial_grassy_field/
skylib_mudos_v1/mudlib/save/terrains/tutorial_mountain/
skylib_mudos_v1/mudlib/save/todo_lists/
skylib_mudos_v1/mudlib/secure/
skylib_mudos_v1/mudlib/secure/cmds/admin/
skylib_mudos_v1/mudlib/secure/cmds/lord/
skylib_mudos_v1/mudlib/secure/config/
skylib_mudos_v1/mudlib/secure/handlers/autodoc/
skylib_mudos_v1/mudlib/secure/handlers/intermud/
skylib_mudos_v1/mudlib/secure/include/global/
skylib_mudos_v1/mudlib/secure/save/
skylib_mudos_v1/mudlib/secure/save/handlers/
skylib_mudos_v1/mudlib/secure/std/classes/
skylib_mudos_v1/mudlib/secure/std/modules/
skylib_mudos_v1/mudlib/std/commands/
skylib_mudos_v1/mudlib/std/commands/shadows/
skylib_mudos_v1/mudlib/std/creator/
skylib_mudos_v1/mudlib/std/dom/
skylib_mudos_v1/mudlib/std/effects/
skylib_mudos_v1/mudlib/std/effects/external/
skylib_mudos_v1/mudlib/std/effects/fighting/
skylib_mudos_v1/mudlib/std/effects/priest/
skylib_mudos_v1/mudlib/std/effects/room/
skylib_mudos_v1/mudlib/std/environ/
skylib_mudos_v1/mudlib/std/guilds/
skylib_mudos_v1/mudlib/std/guilds/old/
skylib_mudos_v1/mudlib/std/languages/
skylib_mudos_v1/mudlib/std/languages/BACKUPS/
skylib_mudos_v1/mudlib/std/liquids/
skylib_mudos_v1/mudlib/std/npc/
skylib_mudos_v1/mudlib/std/npc/goals/
skylib_mudos_v1/mudlib/std/npc/goals/basic/
skylib_mudos_v1/mudlib/std/npc/goals/misc/
skylib_mudos_v1/mudlib/std/npc/plans/
skylib_mudos_v1/mudlib/std/npc/plans/basic/
skylib_mudos_v1/mudlib/std/npc/types/
skylib_mudos_v1/mudlib/std/npc/types/helper/
skylib_mudos_v1/mudlib/std/npcs/
skylib_mudos_v1/mudlib/std/outsides/
skylib_mudos_v1/mudlib/std/races/shadows/
skylib_mudos_v1/mudlib/std/room/basic/topography/
skylib_mudos_v1/mudlib/std/room/controller/
skylib_mudos_v1/mudlib/std/room/inherit/topography/
skylib_mudos_v1/mudlib/std/room/topography/area/
skylib_mudos_v1/mudlib/std/room/topography/iroom/
skylib_mudos_v1/mudlib/std/room/topography/milestone/
skylib_mudos_v1/mudlib/std/shadows/curses/
skylib_mudos_v1/mudlib/std/shadows/disease/
skylib_mudos_v1/mudlib/std/shadows/fighting/
skylib_mudos_v1/mudlib/std/shadows/healing/
skylib_mudos_v1/mudlib/std/shadows/magic/
skylib_mudos_v1/mudlib/std/shadows/poison/
skylib_mudos_v1/mudlib/std/shadows/rituals/
skylib_mudos_v1/mudlib/std/shadows/room/
skylib_mudos_v1/mudlib/std/shops/controllers/
skylib_mudos_v1/mudlib/std/shops/objs/
skylib_mudos_v1/mudlib/std/shops/player_shop/
skylib_mudos_v1/mudlib/std/socket/
skylib_mudos_v1/mudlib/std/soul/
skylib_mudos_v1/mudlib/std/soul/d/
skylib_mudos_v1/mudlib/std/soul/e/
skylib_mudos_v1/mudlib/std/soul/i/
skylib_mudos_v1/mudlib/std/soul/j/
skylib_mudos_v1/mudlib/std/soul/k/
skylib_mudos_v1/mudlib/std/soul/l/
skylib_mudos_v1/mudlib/std/soul/n/
skylib_mudos_v1/mudlib/std/soul/o/
skylib_mudos_v1/mudlib/std/soul/q/
skylib_mudos_v1/mudlib/std/soul/u/
skylib_mudos_v1/mudlib/std/soul/v/
skylib_mudos_v1/mudlib/std/soul/y/
skylib_mudos_v1/mudlib/std/soul/z/
skylib_mudos_v1/mudlib/std/stationery/
skylib_mudos_v1/mudlib/w/
skylib_mudos_v1/mudlib/w/default/
skylib_mudos_v1/mudlib/w/default/armour/
skylib_mudos_v1/mudlib/w/default/clothes/
skylib_mudos_v1/mudlib/w/default/item/
skylib_mudos_v1/mudlib/w/default/npc/
skylib_mudos_v1/mudlib/w/default/room/
skylib_mudos_v1/mudlib/w/default/weapon/
skylib_mudos_v1/mudlib/www/
skylib_mudos_v1/mudlib/www/download/
skylib_mudos_v1/mudlib/www/java/
skylib_mudos_v1/mudlib/www/secure/
skylib_mudos_v1/mudlib/www/secure/lpc/advanced/
skylib_mudos_v1/mudlib/www/secure/lpc/intermediate/
skylib_mudos_v1/v22.2b14-DSv10/
skylib_mudos_v1/v22.2b14-DSv10/ChangeLog.old/
skylib_mudos_v1/v22.2b14-DSv10/Win32/
skylib_mudos_v1/v22.2b14-DSv10/compat/
skylib_mudos_v1/v22.2b14-DSv10/compat/simuls/
skylib_mudos_v1/v22.2b14-DSv10/include/
skylib_mudos_v1/v22.2b14-DSv10/mudlib/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/clone/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/command/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/data/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/etc/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/include/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/inherit/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/inherit/master/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/log/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/compiler/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/efuns/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/operators/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/u/
skylib_mudos_v1/v22.2b14-DSv10/tmp/
skylib_mudos_v1/v22.2b14-DSv10/windows/
.DT
effects_tutorial
$MUDNAME$ Creator Help
effects_tutorial

.SH Name

.SI 5
Coding Effects - A Tutorial		      Mansarde, 22-jan-99
===========================		      -------------------
.EI

.SH Description

.SP 5 5
This documentation draws extensively on the existing "help effects" file,
however the aim of this tutorial is not to replace the that documentation but
to provide a practical guide to help get you started in actually coding an
effect.  It's aimed at people who are relatively competent at LPC, but who are
either new to effects or would like a quick refresher.
.EP

.SP 5 5
I would strongly recommend that you also read "help effects", since I have
not included a lot of the information that it contains, to try to keep this
tutorial a little shorter and to the point.
.EP


.SH Anatomy Of An Effect
.SH --------------------

.SP 5 5
A basic effect will normally require 5 functions:
.EP
.SI 5
  beginning()
  merge_effect()
  end()
  restart()
  query_classification()
.EI

.SP 5 5
Let's quickly run through what they do:
.EP

.SI 5
.SH [mixed/void] beginning (object player, mixed arg, int id)
.EI

.SP 10 5
This function is called immediately after add_effect(), the parameters are:
.EP

.SO 10 10 -25
	player	The player (or other object) that the effect has been added to.  Use this inside your effect, rather than this_player().
	arg	The arguments passed to the effect, generally things like its duration, strength, or whatever else you want.
	id	A unique id number for referring to yourself and your shadow (more about shadows later).  Normally, you won't need this.
.EO
.SI 5
.SH [mixed/void] merge_effect(object player, mixed old_arg, mixed new_arg, int id)
.EI

.SP 10 5
This function is called by add_effect if the your effect is already on
the object.  It allows you to control what happens when your effect is
"doubled up" - without this function the effects system will simply add
another copy of your effect to the object; normally that is not what you
want.
.EP

.SI 5
.SH void end (object player, mixed arg, int id)
.EI

.SP 10 5
This is is *always* called when your effect is removed, no matter what it
was that caused it to be removed.  The parameters are the same as for
beginning().
.EP

.SI 5
.SH void restart (object player)
.EI

.SP 10 5
This is called when the effect is restarted.  For example, if a player has
the effect on them when they log out, restart() will be called when they
log back in.
.EP

.SI 5
.SH string query_classification()
.EI

.SP 10 5
You MUST have this function in your effect.  It will return the dotted
classification string for your effect, "priest.shield", "npc.fighter",
"religious.fumble" or whatever.
.EP

.SH A Brief Note On Data Types
.SH --------------------------

.SP 5 5
You'll notice that in the above functions, the type for 'arg' in beginning,
merge_effect and end is given as "mixed".  However, you should always try to
avoid mixed data types wherever possible (anywhere, not just in effects), so
if the argument to your effect is an integer or a string, use "int arg" or
"string arg", as appropriate.  Often, the arguments to an effect really will
be mixed, in which case you have no real choice, but where you can, you should
specify the type.
.EP

.SP 5 5
Also, notice that the return type for beginning and merge_effect is
[mixed/void].  If you return anything from either of those two functions,
that will become the new argument[s] to the effect, so your return type should
be the same as that of your args.  However, if you don't wish to modify your
effect's arguments in one of those functions, just declare one or both as void,
and return nothing.
.EP

.SH submit_ee()
.SH -----------

.SP 5 5
One more thing that you'll almost certainly need in order to write an effect
is a call to submit_ee().  This will let you call functions on your effect at
timed intervals, do to interesting and exciting things.
.EP

.SP 5 5
It looks something like this:
.EP
.SI 5
player->submit_ee(string function, [int/int *] interval, int flags)
.EI

.SP 5 5
The parameters are:
.EP

.SO 10 10 -25
	function	The name of the function to call
	interval	How long until that function is called (see below)
	flags	EE_ONCE, do it once. The default, so you can just leave it out
		EE_CONTINUOUS, do it continuously
		EE_REMOVE, remove the effect from the object after the call
.EO

.SP 5 5
The interval can be a simple int, which is the time in seconds until the
function is called, or it can also be an array of ints:
.EP

.SO 10 20 -35
	({ min, max })	min time in seconds, max time in seconds. Simple uniform distribution.
	({ min, max, r })	min and max time in seconds, with the the result 'rolled' r times.  The higher the value of r, the closer the result will be to (min + max) / 2.
.EO

.SP 5 5
Only one EE_REMOVE can be in place at one time.  If you submit another, the
first is wiped and the new one used.
.EP

.SP 5 5
EE_ONCE, EE_CONTINUOUS, and EE_REMOVE are defined in /include/effects.h, so
you'll probably want
.EP
.SI 10
.SH  #include <effects.h>
.EI
.SP 5 5
at the top of your effect.
.EP

.SH Your First Effect
.SH -----------------

.SP 5 5
OK, you should now have a rough idea of what you'll need to make a simple
effect, so let's go ahead and code a simple effect to make someone stronger.
The effect will have one argument, the duration (in seconds), and will be
imaginatively classified "mansarde.tutorial.strength".
.EP

=====================================================================
#include <effect.h>

string query_classification() { return "mansarde.tutorial.strength"; }

void beginning (object player, int arg) {
  tell_object(player, "You feel your muscles grow in strength.\n");
  player->adjust_bonus_str(4);
  player->submit_ee( 0, arg, EE_REMOVE );
} /* beginning() */

int merge_effect (object player, int old_arg, int new_arg) {
  int time_left;
  time_left = (new_arg / 2) + player->expected_tt();
  player->submit_ee( 0, time_left, EE_REMOVE );
  return time_left;
} /* merge_effect() */

void end (object player) {
  tell_object(player, "Your muscles relax, and return to normal.\n");
  player->adjust_bonus_str(-4);
} /* end() */

void restart (object player) {
  tell_object(player, "You feel your muscles grow in strength.\n");
  player->adjust_bonus_str(4);
} /* restart() */
=====================================================================

.SP 5 5
OK, let's go through that effect and look at what it does.  We'll skip over
query_classification, since it's pretty straight forward.

Beginning has its return type set as void, because we don't want to change
the arg from there, and also has the "id" parameter missing, because we aren't
going to use it.  Notice that when we print a message to the player, we use
tell_object(player, ... ) rather than write(), because write() uses
this_player().

Remember that in an effect you never refer to the object that has the effect
on them with this_player(), because this_player() is frequently someone else.
For instance, if this effect were added from a spell then this_player() would
be the person casting the spell, not the target (unless they cast it on
themseleves :).  The point, though, is that it's generally uncertain who
this_player() will refer to, so you should avoid it, unless you're absolutely
100% certain that it will always be who you think it is.  Eg: if you have an
extra_look() function in your effect, you could use this_player() to refer to
the person doing the looking.

Next we increase their strength by 4, using adjust_bonus_str(), and set a
time for the effect to be removed with submit_ee().  The name of the function
in the submit_ee is 0, so it won't actually call a function, just remove the
effect.  Of course, end() will still be called, because it's always called
when the effect ends.  But specifying a function here would have allowed us
to call another function, before end().

Merge_effect extends the existing effect by half of the new duration.
Exected_tt() just returns how many seconds are left until our EE_REMOVE
submit_ee() will get called, in other words, how long until the effect is due
to be removed.	So if the original duration was 300 seconds, and after 100
seconds someone tries to add another 300 seconds:
.EP

.SI 5
time_left = (300 / 2) + 200 = 350
.EI

.SP 5 5
We then return time_left, so that it becomes the new arg for the effect, and
it shows up when the player is statted, or the args of the effect queried.

As a side note, some have claimed that there are problems with expected_tt,
however I'm not entirely certain what those problems are, and how you would
go about avoiding them.  Since there is no real alternative to expected_tt,
you're probably going to have to use it anyway, but just keep in mind that
it may not be 100% reliable.  Who knows, by the time you're reading this,
someone may have recoded the function, and it now works flawlessly. :)

End is pretty self-explanatory, we use tell_object() to print a message about
the player not feeling as strong any more, then we reduce their strength.

Restart is just like beginning, printing the "you feel stronger" message, and
increasing their strength.  The difference is that in restart, we don't need
to worry about resubmitting effect events, because those are taken care of
for us by the effects system.  When they log back in, the events that were
scheduled before they left will all still be there, and should just continue
as before.

Also notice that throughout the effect, we omitted parameters to functions if
we didn't use them.
.EP

.SH Was It Good For You?
.SH --------------------

.SI 5
Looking through the effect you should notice that there are some problems
with it, and some possible improvements.  First, we'll introduce another
effect function:
.EI

survive_death().

.SI 5
Normally, all effects are removed from a player when they die, but if you put
something like this in your effect:
.EI

int survive_death() { return 1; }

.SI 5
your effect will not be removed, and stay with the player as a ghost, and when
they're raised.  We don't want that in our effect, of course, so we do need
to be aware that the effect will get auto-removed when they die.  This
doesn't seem a problem, until you remember that end() *always* gets called
when the effect is removed, even by death.  In our case, this means that the
player will see "Your muscles relax, and return to normal." just after they
die.

Clearly, this would be a little silly, so we should add a check to end() to
make sure that the message only gets printed if they're still alive.  This
is something that's very easy to overlook when coding and testing an effect,
so try to watch out for it, and have end() print a different message to dead
people, or no message at all.  To check if your player is dead, just do a
check for the "dead" property:
.EI

if ( player->query_property("dead") ) { "He's dead, Jim." }

.SI 5
Something else that you would probably want to add, if you were coding an
effect like this, is a message printed to the room when the effect starts.
Something like
.EI

tell_room(environment(player), player->query_cap_name() +
	  " seems to grow in strength.\n", player);

.SI 5
alongside the messages to the player, in beginning and restart.  If someone's
muscles are growing and becoming stronger, Incredible Hulk style, is seems
natural that people standing nearby would notice.

Another possible addition might be a second argument, so that you can specify
how much extra strength to add, rather than always adding 4. This would make
your effect much more flexible and useful.

To do that you'll need to change the type of your "arg" parameters in
beginning, end, and merge_effect from int (single int) to int* (an array of
ints), and change the return type of merge_effect to int* as well.
You might also want to be really flash and change merge_effect so that as well
as extending the time, it also increases the str boost a little.

Finally, you could have a function that prints a message telling the player
how super-strong he or she feels every minute or so, using a continuous
submit_ee.  To do that you would need to add a line like this to beginning():
.EI

player->submit_ee("print_mess", 20, EE_CONTINUOUS);

.SI 5
This will call a function called print_mess() in your effect every 20 seconds,
for the duration of the effect.  Print_mess() could just be something like
this:
.EI

void print_mess( object player, mixed arg, int id ) {
  string* mess;
  mess = ({
	   "Your muscles pulse with strength.\n",
	   "You feel incredibly strong.\n",
	   "You feel that could tear up large trees with your bare hands.\n"
	 })
  tell_object(player, mess[random(sizeof(mess))];
  return;
} /* print_mess() */

.SI 5
I've included all the parameters that are passed for the sake of completeness,
you will probably not need all of them in your function.

You can see that this function just prints one of three messages, at random.
When you're printing messages to a player like this, it's always better to
have lots of different ones, rather han just one or two, since they tend to
become very irritating and boring in a very short period of time.
Remeber that you don't need to modify restart() to start those messages going
again, since this should be handled for you automagically.

So, having looked at that simple effect, let's move on to something more
advanced.
.EI


.SH Effects With Shadows
.SH ====================

.SI 5
The majority of effects on Discworld do things that cannot be acomplished with
an effect on its own, so they have a shadow object to accompany them.  Often,
most of the work will be done by the shadow, with the effect just there to
make sure that the shadow is added and removed at the right time, and
suitable messages printed to the player.
.EI

.SH What's A Shadow?
.SH ----------------

.SI 5
If you already know what a shadow object is, you could just skip straight to
The Next Section.  If not, read on :)

A shadow object is an object which redefines one or more functions on another
object, so that any calls to those functions are sent to the shadow, not the
real function.	It sounds tricky, but actually it's a fairly simple concept.

Let's take the Priest ritual called Aegis as an example.  This ritual halves
all damage taken by someone, and works by placing an effect (with a shadow)
on the target.	Inside the aegis shadow object is the following:
.EI

======================================================================
int adjust_hp( int number, object thing ) {
 /* It should be possible to heal while under aegis */
   if ( number >= 0 ){
      player->adjust_hp( number );
      return 1;
   }
   return (int)player->adjust_hp( number / 2, thing );
} /* adjust_hp() */
======================================================================

.SI 5
If a player (Mansarde, for the sake of this example) has the Aegis effect on
him, anything that tries to call adjust_hp() on him will not call the real
adjust_hp,  it will be intercepted by the shadowed adjust_hp.

So if something does this:
.EI

find_player("mansarde")->adjust_hp(-1000);

.SI 5
the call will go to the shadow, which will halve the number of hp to reduce,
then pass the call on to the normal adjust_hp function on the player.  This
means that by the time the real adjust_hp gets to see it, the number of HP to
remove has been halved.  You can still call the real adjust_hp() from within
your shadow, though.  If you do:
.EI

player->adjust_hp(300);

.SI 5
inside that shadow, it will not recursively call the function on the shadow,
as you might expect, but will instead call the real function, on the player
object.  In the case of a player having several shadow objects shadowing the
same function, each shadow function will call the next shadow function in turn
until it finally gets to the real function, or one of the shadow functions
returns without consulting the real function.  So if Mansarde has two aegis
shadows on him, and someone did:
.EI

find_player("mansarde")->adjust_hp(-1000);

.SI 5
the first aegis shadow would intercept the call, halve the number of hp to
remove, but when that first shadow called adjust_hp(), it would call the
second shadow, with a value of -500.  That second shadow will then halve the
number again, and pass it onto the real adjust_hp() function on the player.

Thus, with 2 aegis-style adjust_hp() shadow functions, a player will receive
only 1/4 of any damage.  All of this is handled for you, though, and you don't
need to lose any sleep worrying about other shadows interacting with yours,
as long as your shadow has been coded correctly, everything should be fine.

Some of you may have noticed, though, that this shadow is actually a bit
naughty.  If the number of hp >= 0, ie the person is being healed, this
function will do the healing as normal, then return 1.	This is wrong, because
adjust_hp() should in fact return the new number of hp.  The Aegis shadowed
adjust_hp() only returns the new hp if the player is being injured.

This is something you need to watch for when writing shadows, especially if
you're shadowing important functions:  make sure that you have the correct
arguments, and especially that you return the correct value.  If your shadow
has been written properly, it should look to the outside world exactly like
the real function.

Also, don't rely on what you _think_ the parameters and return values are,
from your experience of using the function in the past.  Find where the
function is defined, and make sure that your function has the same arguments
and return type.  What you do with those arguments will probably be
different, of course, and the actual value that you return may well be
different, but your function should be able to be used exactly like the real
one.  Any code which uses that function should not be broken by your shadow.

In this case, if someone had written code which depended on adjust_hp()
returning the new number of hp(), that code would not work correctly with
an aegised player.  Of course, by the time you read this, I will have modified
the Aegis shadow to return the correct value, but I included the previous,
incorrect code here as an example. :)

For a more info on shadows, see "help shadow".

Hopefully, the above will be enough for you to get an idea of what a shadow
is, and to follow the next example...
.EI

.SH The Next Section
.SH ----------------

.SI 5
...an effect to boost someone's crafts skills by 25%.

We'll classify this effect as "mansarde.tutorial.crafty" and give it one arg,
the duration.  As with the previous effect, you could modify it to have a
second arg, to specify how much of a boost you want.  For simplicity, we'll
just stick to a fixed 25% boost, though.

This effect will require two objects, an effect and the shadow.
First the effect:
.EI

======================================================================
#include <effect.h>

string query_classification() { return "mansarde.tutorial.crafty"; }

string query_shadow_ob() { PATH + "crafty_shad"; }

void beginning(object player, int arg) {
  tell_object(player, "You feel very crafty.\n");
  player->submit_ee(0, arg, EE_REMOVE);
} /* beginning() */

int merge_effect (object player, int old_arg, int new_arg) {
  int time_left;
  time_left = (new_arg / 2) + player->expected_tt();
  player->submit_ee( 0, time_left, EE_REMOVE );
  return time_left;
} /* merge_effect() */

void end (object player) {
  tell_object(player, "Your craftiness seems to have left you.\n");
} /* end() */

void restart (object player) {
  tell_object(player, "You feel very crafty.\n");
} /* restart() */

======================================================================

.SI 5
This effect is just about as simple as you can get.  It is almost the same
as the strength effect, minus the calls to adjust_bonus_str(), and with
different messages.  The main difference is the extra function:
.EI

string query_shadow_ob() { PATH + "crafty_shad"; }

(PATH would presumably be defined somewhere as the path for your shadows.)

.SI 5
This tells the effects system that our effect has a shadow attached, and what
its path is.  That one line is all you need to add a shadow to your effect.
The effects system will make sure that the shadow object is added and removed
from the player when the effect starts and ends, so you don't need to worry
about that.

All we need to do now, is actually write the shadow...
.EI

======================================================================
inherit "/std/effect_shadow";

int query_skill_bonus (string skill) {
  int true_bonus;
  true_bonus = player->query_skill_bonus( skill );

  if (skill[0..5] != "crafts") // not a crafts skill, so no modification
    return true_bonus;

  return true_bonus + (true_bonus / 4);
} /* query_skill_bonus() */
======================================================================

.SI 5
That wasn't so hard :)

We start out by inheriting /std/effect_shadow.	All effect shadows should
inherit this, as it has some support functions to handle adding and removing
it cleanly, plus the arg() function, which allows you to access the args for
the effect from within the shadow.

Then we have the definition of our shadowed query_skill_bonus() function.
We start off by finding out what the player's real skill bonus is in the
relevant skill, by calling the real query_skill_bonus, with the same
argument.  Once we have that, we next check whether the skill being queried
is a crafts skill, and if not we just return the true bonus that we got
earlier.  Otherwise, if the skill being queried is indeed a crafts skill,
we return the true bonus, plus the 25% boost.

This means that any call to query a non-crafts skill will work exactly as
before, and the skill bonus wil be unaffected, but all skills which have
"crafts" as their first 5 characters will recieve a 25% increase in bonus.

It's as easy as that.
.EI

.SH A Few General Pointers
.SH ----------------------

.SI 5
These are just some handy tips and things that are relevant when coding
effects.

1) Object variables will not behave as you'd expect.  Only one copy of the
   effect object is created, no matter how many people have the effect on
   them, so there will only be one instance of each object variable, not one
   per player.	This may be of some use to you, in some obscure case where you
   want to keep track of some data about the effect as a whole, but normally
   you will want to avoid object variables in effects.	To store data about
   a particular thing which has the effect on it, use an argument to the
   effect.

2) Call_outs. If you have an object which uses a lot of call_outs, especially
   at regular intervals, you should seriously consider impaling yourself on a
   sharp object, then modifying your object to use an effect instead.

   Although effects were originally intended as a way of temporarily modifying
   existing objects, they can be very useful to control behaviour which is
   part of the normal activities of your object, especially if this means
   getting rid of call_outs (which, as everyone knows, are the work of Satan :)

   The query_indefinite() function will help you here.	Normally, any effect
   which has no effect events (EEs) pending, including an EE to remove the
   effect, will automatically be deleted from the object by the effect system.
   To avoid having your effect wiped out, include something like this:
.EI

   int query_indefinite() { return 1; }

.SI 5
   This tells the effect system that your effect really is supposed to remain
   there indefinitely, even if it has no events pending, so it won't get
   removed.

3) Always, always, always put a comment at the top of your effect stating what
   the arguments are.  This is true even if you only have one arg, for the
   duration, but applies especially for effects which have several args.  It
   can be extremely annoying to have to wade through the code for an effect,
   trying to figure out what each of the args is supposed to be, and how
   they're used.  At the time of writing, a lot of effects do not have such a
   comment; this is not a good reason to create even more undocmented effects,
   however :)
.EI

.SH The Fat Lady Tunes Up
.SH ---------------------

.SI 5
That almost concludes the tutorial, and by this point you should be able to go
off and code your own effect to do almost whatever it is you want.  For
further reading, I highly recommend the contents of /std/effects and
/std/shadows, as well as "help effects", since it documents lots of effect
support functions that i've not listed here, to keep this shorter and because
they're not really necessary to get started.
.EI

.SH Laaaaaaaaa!
.SH -----------

.SI 5
The End.



Well, what're you waiting for?	Go and code yourself an effect!
.EI

.SH See Also
.SI 5
help shadow, help effect
.EI