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/
/**
 * This is the basic effects module.
 * Extracted from Ember's living effects code by Deutha.
 * @changed 4-6-1997, Turrican
 * To use arrays of classes, and to allow function pointers.
 * @changed 17-10-1997 Olorin
 * Fixed a bug then adding an effect in the end() function of
 * another effect.
 * @changed 03-09-2002 Sandoz
 * Changed to get shadow objects from the shadows handler.
 * @see help::effects
 */

#include <effect.h>

class effect_shadow {
   object shad_ob;
   int idnum;
}

class effect_event {
   int inctime;
   mixed func;
   int eff_ob_num;
   mixed interval;
   int flags;
}

void delete_effect( int i );

// This keeps track of the effect number being called (beginning/end/etc.).
private nosave int current_enum;
private nosave int next_id;

#undef DEBUG

#ifdef DEBUG
nosave int logging_effects;
void set_logging_effects( int i ) { logging_effects = i; }
int query_logging_effects() { return logging_effects; }
#endif

// This stores shadows for corresponding effects as shadow_ob, idnum.
private nosave class effect_shadow *shadows;

// This stores effects currently active on player as "effect_ob_name", arg
private class effect *effs;

/*
 * effect event queue
 *
 *    This stores all effect events going to be happening...
 *
 *    time_from_previous_event, "function", eff_ob_num, interval, flags
 *
 * where flags can be
 *
 *   EE_REMOVE
 *   EE_CONTINUOUS
 *   EE_ONCE
 *
 * and interval can be
 *   number_of_secs
 *   ({ min_secs, max_secs })  ; "1" dice roll
 *   ({ min_secs, max_secs, n })   ; "n" dice rolls
 *
 * ee are added with
 *   submit_ee("function", interval, flags);
 *
 */
private class effect_event *eeq;

protected void create() {
    effs = ({ });
    shadows = ({ });
    eeq = ({ });
    next_id = 0;
    current_enum = -1;
#ifdef DEBUG
    logging_effects = 0;
#endif
} /* create() */

/**
 * This method removes all the current effects off the object
 * and sets everything back to the initial state.
 * @see add_effect()
 */
void reset_effects() {
    effs = ({ });
    shadows = ({ });
    eeq = ({ });
    current_enum = -1;
    remove_call_out("effect_timer");
#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) reset_effects:"
            "remove_call_out(\"effect_timer\")\n", time() % 1000,
            TO->query_name(), current_enum );
#endif
} /* reset_effects() */

/**
 * This method returns the outermost shadow on this object.
 * This is useful for making sure functions get called right
 * through the shadow chain.  If there are no objects being
 * shadows, this_object() is returned.
 * @return the outermost shadow object
 */
object affected_object() {
    object this, other;

    this = other = TO;

    while( objectp(other) ) {
        this = other;
        other = query_shadowing(this);
    }

    return this;

} /* affected_object() */

/**
 * This method freezes all the current effects so that they do
 * not continue to be processed.  Basicly it removes the call_out
 * for the effect_timer().  It also ensures the intrval for the
 * immediately pending effect
 * is correct.
 * @see effect_timer()
 * @see add_effect()
 */
void effect_freeze() {
    /* stop effect_timer call_out and put the remaining
     * time value into EEQ_INCTIME of first element */
    int timeleft;

    timeleft = remove_call_out("effect_timer");

#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) effect_freeze:"
            "remove_call_out(\"effect_timer\")=%d\n", time() % 1000,
            TO->query_name(), current_enum, timeleft );
#endif

    if( sizeof( eeq ) )
        eeq[ 0 ]->inctime = timeleft;

} /* effect_freeze() */

/** @ignore yes */
private void quit_save_effects( string func ) {
    int i, neffs;
    string effn;
    mixed arg;

    neffs = sizeof( effs );

    for( i = 0; i < neffs; i++ ) {
        effn = effs[ i ]->ob_name;
        arg = effs[ i ]->arg;
        if( pointerp(arg) && sizeof(arg) == 3 && arg[ 2 ] == BOGUS_OLD_ARG )
            arg = arg[ 1 ];
        if( effn )
            catch( call_other( effn, func, affected_object(), arg, i ) );
    }

} /* quit_save_effects() */

/**
 * @ignore yes
 * This method is called when a player actually saves.
 * It calls the quitting method on all the effects.
 */
void effects_saving() { quit_save_effects("saving"); }

/**
 * @ignore yes
 * This method is called when a player actually quits.
 * It calls the quitting method on all the effects.
 */
void effects_quiting() { quit_save_effects("quiting"); }

/**
 * This method restarts the internal call_out.
 * @see effects_freeze()
 * @see add_effect()
 */
void effect_unfreeze() {
    /* Restart the timer, checking that it hasn't been restarted
     * already by a nested unfreeze.
     */
    int time;

    if( sizeof( eeq ) ) {
#ifdef DEBUG
        if( logging_effects )
            log_file("EFFECT_WOBBLE", "%d:%s(%d) effect_unfreeze:"
                "call_out(\"effect_timer\", %d )\n", time() % 1000,
                TO->query_name(), current_enum, eeq[ 0 ]->inctime );
#endif
        time = find_call_out("effect_timer");
        if( time != -1 )
            remove_call_out("effect_timer");
        call_out("effect_timer", eeq[ 0 ]->inctime );
   }
} /* effect_unfreeze() */

/** @ignore yes */
protected int int_submit_ee( int eff_ob_num, mixed fun, mixed interval,
                             int flags ) {
    /*
     * This calculates next occurence of this ee and inserts it into the eeq.
     * Note that it can only be called sandwiched between freeze and
     * unfreeze calls.
     */
    int i, ntime;

#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) int_submit_ee:"
            "( %d, %O, %O, %s )\n", time() % 1000, TO->query_name(),
            current_enum, eff_ob_num, fun, interval,
            ({"once", "remove", "continuous"})[flags] );
#endif
    if( intp(interval) )
        ntime = interval;
    else if( pointerp(interval) ) {
        switch( sizeof(interval) ) {
         case 0:
           return -1;
         case 1:
           ntime = interval[0];
         break;
         case 2:
           ntime = interval[0] + random( interval[1] - interval[0] + 1 );
         break;
         case 3:
           ntime = 0;
           for( i = 0; i < interval[2]; i++ )
               ntime += interval[0] + random( interval[1] - interval[0] + 1 );
           ntime += interval[2] / 2;
           ntime /= interval[2];
         break;
         default:
           return -1;
        }
    } else
        return -1;

    /* If the flag is EE_REMOVE, and one exists for EEQ_EFF_OB already,
     * remove the old one.  This is for occasions when an EE_REMOVE is
     * called from merge_effect() */
    if( flags == EE_REMOVE ) {
        for( i = 0; i < sizeof(eeq); i++ )
            if( eeq[i]->eff_ob_num == eff_ob_num &&
                eeq[i]->flags == EE_REMOVE ) {
                eeq = delete( eeq, i, 1 );
                break;
            }
    }

    /*
     * This removes all scheduled ee's with the
     * specified flag and function.
     */
    if( ntime < 0 ) {
        if( flags != EE_REMOVE ) {
            for( i = 0; i < sizeof(eeq); i++ )
                if( eeq[i]->eff_ob_num == eff_ob_num &&
                    eeq[i]->flags == flags && eeq[i]->func == fun )
                    eeq = delete( eeq, i, 1 );
        }
        return 0;
    }

    for( i = 0; i < sizeof(eeq); i++ ) {
        /* goes in here! */
        if( ntime < eeq[i]->inctime ) {
            eeq[i]->inctime -= ntime;
            eeq = eeq[0..i-1] + ({ new( class effect_event, inctime : ntime,
                  func : fun, eff_ob_num : eff_ob_num, interval : interval,
                  flags : flags ) }) + eeq[i..];
            break;
        }
        ntime -= eeq[i]->inctime;
    }

    if( i == sizeof(eeq) && eeq ) {
        eeq += ({ new( class effect_event,
                       inctime    : ntime,
                       func       : fun,
                       eff_ob_num : eff_ob_num,
                       interval   : interval,
                       flags      : flags )
                       });
    } else if( !sizeof(eeq) ) {
        /* This shouldn't happen, but it does sometimes!
         * Note from Olorin: without testing for !sizeof(eeq), this
         * code mangles the cases where the queue exists and the event
         * already inserted by the for loop.
         */
        eeq = ({ new( class effect_event,
                      inctime    : ntime,
                      func       : fun,
                      eff_ob_num : eff_ob_num,
                      interval   : interval,
                      flags      : flags )
                      });
    }

    return i;

} /* int_submit_ee() */

/**
 * This submits an effect event schedule.  It will call "function name" in
 * your effect object after the interval specified in interval_spec, with
 * behaviour modified by flags.
 * interval_spec can be one of:
 * <dl><dt>n
 * <dd>time in seconds
 * <dt>({ m, n })
 * <dd>minimum time m seconds, maximum time n seconds, simple random
 *     distribution
 * <dt>({ m, n, r })
 * <dd>as for the ({ m, n }) case, except the random factor is effectively
 *     "rolled" r times ... the higher r is, the more likely the interval
 *     is to be close to (m + n)/2.
 * <dt>If the interval is a negative figure, any functions with the
 *     specified function name will be removed from the queue.
 *     This is useful in removing unwanted things from the effects queue.
 * </dl>
 * flags may be:
 * <dl>
 * <dt>EE_REMOVE
 * <dd>remove the effect from the player after the function call
 * <dt>EE_CONTINUOUS
 * <dd>do it repeatedly.  Default (EE_ONCE) is only do it once.
 * <dt>EE_ONCE
 * <dd>do it once
 * </dl>
 * These are defined in /include/effect.h
 * <p>
 * Only one EE_REMOVE can be in place at a time.  Subsequent EE_REMOVEs
 * will wipe previous ones.
 * <p>
 * NB:  submit_ee can ONLY be called from the effect object itself in the
 * course of a beginning/handler/end call, or from the effect shadow.
 * @see submit_ee2()
 * @see /include/effect.h
 * @see add_effect()
 * @param fun the function to call
 * @param interval the interval specifier
 * @param flags the flags for the event
 */
void submit_ee( mixed fun, mixed interval, int flags ) {
    /* public access point for int_submit_ee ... it uses current_enum or
       previous_object (for shadows) to determine what effect it refers to */
    int enumb;

#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) submit_ee:( %O, %O, %s )\n",
            time() % 1000, TO->query_name(), current_enum, fun, interval,
            ({"once","remove","continuous"})[flags] );
#endif

    if( ( enumb = current_enum ) < 0 ) {
        enumb = member_array( 1, map( shadows, (: $1 &&
            ((class effect_shadow)$1)->shad_ob == PO :) ) );
        if( enumb < 0 )
            error("Cannot submit from that object.");
    }

    effect_freeze();
    int_submit_ee( enumb, fun, interval, flags );
    effect_unfreeze();

} /* submit_ee() */

/**
 * This submits an effect event for a specific effect.
 * Apart from this it is the same as submit_ee()
 * @param enum the enum of the effecrt
 * @param fun the function to call
 * @param interval the interval specifier
 * @param flags the flagds associated with the event
 */
void submit_ee2( int enum, mixed fun, mixed interval, int flags ) {
    /* public access point for int_submit_ee ... it uses enum
     * to determine what effect it refers to (for those cases where
     * current_enum isn't defined)
     */

#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) int_submit_ee:"
            "( %d, %O, %O, %s )\n", time() % 1000,
            TO->query_name(), current_enum, enum, fun, interval,
            ({"once","remove","continuous"})[flags] );
#endif

    if( enum < 0 )
        error("Cannot submit from that object.");

    effect_freeze();
    int_submit_ee( enum, fun, interval, flags );
    effect_unfreeze();

} /* submit_ee() */

/**
 * This method returns the time until any EE_REMOVE effect
 * is expected to occur.  It will return -1 if no remove event
 * is scheduled.  If the flag is not set to true, the the
 * current enum is checked, otherwise the new_enum is checked.
 * @param flag if true, use new_enum not current_enum
 * @return the time until the remove event will occur,
 * or -1 if none is scheduled
 * @see submit_ee()
 * @see aff_effect()
 */
int expected_tt( int flag, int new_enum ) {
    int timeleft, enum;

    if( flag )
        enum = new_enum;
    else
        enum = current_enum;

    if( enum < 0 )
        return -1;

    if( !sizeof(eeq) )
        return -1;

    // Stop the event timer.
    effect_freeze();

    // Add up the incremental times in the queue until you get to the
    // EE_REMOVE for the one your asking about.
    foreach( class effect_event eff in eeq ) {
        timeleft += eff->inctime;
        if( eff->eff_ob_num == enum && eff->flags == EE_REMOVE ) {
            // Restart the event timer.
            effect_unfreeze();
            return timeleft;
        }
    }

    // Restart the event timer.
    effect_unfreeze();
    return -1;

} /* expected_tt() */

/**
 * This method adds an effect onto the object.  The arg is passed
 * directly onto the function 'beginning' on the effect object.
 * If the result is non-zero then the return value of the begining
 * function is used instead of the arg and stored away.  If an
 * effect of the same type is already on the object then the
 * function 'merge_effect' will be called on the effect object.
 * If it returns a non-zero value then the arg for the initial
 * effect will be updated to the new value, otherwise both effects
 * will run with different arguments.
 * @see help::effects
 * @see submit_ee()
 */
void add_effect( string eff, mixed arg ) {
    string shad;
    object ob, effob;
    mixed hbf, res;
    int i, old_current_enum, enum;

#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) add_effect:(%s,%O)\n",
            time() % 1000, TO->query_name(), current_enum, eff, arg );
#endif

    if( !( hbf = (mixed)eff->query_secs_between_hb() ) &&
        ( hbf = (mixed)eff->query_heart_beat_frequency() ) && intp(hbf) )
        hbf *= 60;

    old_current_enum = current_enum;

    if( !effob = find_object(eff) )
        error("Bad effect object.");

    for( i = 0; i < sizeof(effs); i++ ) {
        if( effs[i]->ob_name == eff ) {
            if( function_exists("merge_effect", effob ) ) {
                current_enum = i;
                if( hbf ) {
                    if( res = (mixed)effob->merge_effect( affected_object(),
                        effs[i]->arg[1], arg ) )
                        effs[i]->arg[1] = res;
                } else {
                    if( res = (mixed)effob->merge_effect( affected_object(),
                        effs[i]->arg, arg ) )
                        effs[i]->arg = res;
                }
                current_enum = old_current_enum;
                return;
            }
        }
    }

    if( shad = (string)effob->query_shadow_ob() ) {
        ob = (object)SHADOW_H->get_shadow(shad);
        shadows += ({ new( class effect_shadow, shad_ob : ob,
                      idnum : next_id ) });
        ob->attach_to_player( affected_object(), next_id );
    } else {
        shadows += ({ new( class effect_shadow, shad_ob : 0,
                      idnum : next_id ) });
    }

    current_enum = sizeof(effs);
    enum = current_enum;

    effs += ({ new( class effect, ob_name : eff, arg : arg ) });

    if( function_exists("beginning", effob ) ) {
        if( res = (mixed)effob->beginning( affected_object(), arg, next_id ) )
            effs[enum]->arg = res;
    }

    next_id++;

    if( hbf ) {
        submit_ee("effect_heart_beat", hbf, EE_CONTINUOUS | EE_OLD );
        effs[enum]->arg = ({ 0, effs[<1]->arg, BOGUS_OLD_ARG });
    }

    current_enum = old_current_enum;

} /* add_effect() */

#define EFF_OB_NAME 0
#define EFF_ARG 1

#define EEQ_INCTIME 0
#define EEQ_FUNC 1
#define EEQ_EFF_OB 2
#define EEQ_INTERVAL 3
#define EEQ_FLAGS 4

#define EFF_SIZE 2
#define EEQ_SIZE 5

/** @ignore yes */
protected void convert_arrays() {
    int i, neffs, neeq;
    mixed old;

    if( sizeof(effs) && !classp(effs[0]) ) {
        old = effs;
        effs = allocate( neffs = sizeof(old) / EFF_SIZE );
        for( i = 0; i < neffs; i++ )
            effs[i] = new( class effect,
                ob_name : old[i*EFF_SIZE+EFF_OB_NAME],
                arg     : old[i*EFF_SIZE+EFF_ARG] );
    }

    if( sizeof(eeq) && !classp(eeq[0]) ) {
        old = eeq;
        eeq = allocate( neeq = sizeof(eeq) / EEQ_SIZE );
        for( i = 0; i < neeq; i++ )
            eeq[i] = new( class effect_event,
                inctime    : old[ i * EEQ_SIZE + EEQ_INCTIME ],
                func       : old[ i * EEQ_SIZE + EEQ_FUNC ],
                eff_ob_num : old[ i * EEQ_SIZE + EEQ_EFF_OB ],
                interval   : old[ i * EEQ_SIZE + EEQ_INTERVAL ],
                flags      : old[ i * EEQ_SIZE + EEQ_FLAGS ] );
    }

} /* convert_arrays() */

/**
 * @ignore yes
 * This method is called after restore_object is done to init shadows etc.
 */
void init_after_save() {
    int i, neffs, *ee_exists;
    string shad, effn;
    object ob;
    mixed arg;

    convert_arrays();

    shadows = allocate( neffs = sizeof(effs) );

    for (i = 0; i < neffs; i++) {
        effn = effs[i]->ob_name;
        shadows[i] = new( class effect_shadow, idnum : next_id );
        if( catch( shad = (string)effn->query_shadow_ob() ) )
            continue;
        if( shad ) {
            ob = (object)SHADOW_H->get_shadow(shad);
            shadows[i]->shad_ob = ob;
            ob->attach_to_player( affected_object(), next_id );
        }

        current_enum = i;
        arg = effs[i]->arg;

        if( pointerp(arg) && sizeof(arg) == 3 && arg[ 2 ] == BOGUS_OLD_ARG )
            arg = arg[1];

        catch( effn->restart( affected_object(), arg, next_id++ ) );

    }

    ee_exists = allocate(neffs);

    for( i = 0; i < sizeof(eeq); i++ )
        ee_exists[eeq[i]->eff_ob_num] = 1;

    for( i = sizeof(ee_exists) - 1; i >= 0; i-- ) {
        if( catch( ob = load_object(effs[i]->ob_name) ) || !ob ) {
            delete_effect(i);
            continue;
        }
        if( !ee_exists[i] && !ob->query_indefinite() )
            delete_effect(i);
    }

    current_enum = -1;
    effect_unfreeze();

} /* init_after_save() */

/**
 * This method returns an array of effects matching the
 * classification.  The start part of the classification is
 * used to match, so passing "npc." into this function will
 * match all the effects which modify how an npc functions.
 * @param eff the classification to match
 * @return the array of enums corresponding to the effects
 * @see add_effect()
 */
int *effects_matching( string eff ) {
    int i, neffs, *match;
    string clas;
    object ob;

    match = ({ });
    neffs = sizeof(effs);

    for( i = 0; i < neffs; i++ ) {
        if( catch( ob = load_object(effs[i]->ob_name) ) || !ob )
            continue;

        if( !stringp( clas = (string)ob->query_classification() ) )
            continue;

        if( clas[0..strlen(eff)-1] == eff )
            match += ({ i });
    }

    return match;

} /* effects_matching() */

/**
 * This removes an effect from the player.  It uses the enum as
 * returned from sid to enum.  This is the number you see in
 * the stat of the object next to the effect.
 * @param i the enum to remove
 * @see add_effect()
 */
void delete_effect( int i ) {
    int id, j;
    object effect_object, shadow_object;
    string effect_file;
    mixed arg;

#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) delete_effect:(%d) "
            "sizeof(eeq)=%d\n", time() % 1000, TO->query_name(),
            current_enum, i, sizeof(eeq) );
#endif

    if( i < 0 || i >= sizeof( effs ) )
        return;

    id = shadows[i]->idnum;
    arg = effs[i]->arg;

    effect_freeze();

    for( j = 0; j < sizeof(eeq); j++ ) {
        if( eeq[j]->eff_ob_num == i ) {
            if( sizeof(eeq)-1 > j )
                eeq[j+1]->inctime += eeq[j]->inctime;
            eeq = delete( eeq, j--, 1 );
        } else {
            if( eeq[j]->eff_ob_num > i )
                eeq[j]->eff_ob_num--;
        }
    }

    effect_file = effs[i]->ob_name;
    effs = delete( effs, i, 1 );

    shadow_object = shadows[i]->shad_ob;
    shadows = delete( shadows, i, 1 );

    effect_unfreeze();

    /* Once all bookkeeping's done, it's safe to call end(), since we're
     * essentially outside the effects system.
     */
#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) delete_effect: before end\n",
            time() % 1000, TO->query_name(), current_enum );
#endif

    catch( effect_object = load_object(effect_file) );

    if( effect_object )
        if( pointerp(arg) && sizeof(arg) == 3 && arg[ 2 ] == BOGUS_OLD_ARG )
            effect_object->end( affected_object(), arg[1], id );
    else
        effect_object->end( affected_object(), arg, id );

#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) delete_effect: after end\n",
            time() % 1000, TO->query_name(), current_enum );
#endif

    // Kill the shadow if it exists.
    if( objectp(shadow_object) )
        shadow_object->remove_effect_shadow(id);

    if( !sizeof(effs) )
        TO->event_all_effects_deleted();

} /* delete_effect() */

/**
 * This method returns the effect number for the given effect id.  The
 * effect number is an internal nhandle used by delete_effect()
 * arg_of() set_arg_of() functions.  "id" is passed into the
 * begining/end etc functions on the effect object.
 * @param sid the effect id
 * @return the effect number
 * @see submit_ee2()
 * @see delete_effect()
 * @see add_effect()
 * @see arg_of()
 * @see set_arg_of()
 */
int sid_to_enum( int sid ) {
    return member_array( 1, map( shadows, (: classp($1) &&
        ((class effect_shadow)$1)->idnum == $(sid) :) ) );
} /* sid_to_enum() */

/**
 * This method turns an effect number into an effect id.
 * @see sid_to_enum()
 * @see add_effect()
 * @param enum the effect number
 * @return the effect id
 */
int enum_to_sid( int enum ) { return shadows[enum]->idnum; }

/**
 * This method returns the effect object for the given effect number.
 * @param enum the effect number
 * @return the effect object
 * @see arg_of()
 * @see add_effect()
 * @see sid_to_enum()
 */
string eff_of( int enum ) { return effs[enum]->ob_name; }

/**
 * This method returns the current arguments associated with the
 * given effect number.
 * @param enum the effect number
 * @return the arguments associated with the effect
 * @see add_effect()
 * @see set_arg_of()
 * @see sid_to_enum()
 */
mixed arg_of( int enum ) {
    mixed arg;

    if( sizeof(effs) <= enum )
        return 0;

    if( pointerp( arg = effs[enum]->arg ) && sizeof(arg) == 3 )
        if( arg[2] == BOGUS_OLD_ARG )
            return copy(arg[1]);

    return copy(arg);

} /* arg_of() */

/**
 * This sets the argument of the given effect to a new value.
 * @param enum the effect number to change
 * @param newarg the new argument value
 * @see sid_to_enum()
 * @see arg_of()
 * @see add_effect()
 */
void set_arg_of( int enum, mixed newarg ) { effs[enum]->arg = newarg; }

/** @ignore yes */
class effect *query_effs() { return effs; }
/** @ignore yes */
class effect_shadow *query_effect_shadows() { return shadows; }
/** @ignore yes */
class effect_event *query_eeq() { return eeq; }

/** @ignore yes */
void effect_timer() {
    int enum;
    object effect_object;
    mixed res, last_flags;
    class effect_event thisee;

#ifdef DEBUG
    if( logging_effects )
        log_file("EFFECT_WOBBLE", "%d:%s(%d) effect_timer:() "
            "sizeof(eeq)=%d\n", time() % 1000, TO->query_name(),
            current_enum, sizeof(eeq) );
#endif

    if( !sizeof(eeq) )
        return;

    thisee = eeq[0];
    eeq = eeq[1..];
    enum = current_enum = thisee->eff_ob_num;

    if( enum >= sizeof(effs) ) {
        effect_unfreeze();
        if( creatorp(TO) )
            tell_object( TO, "Effect number out of range, ignoring.\n");
        return;
    }

    if( thisee->flags & EE_CANCELLED ) {
        effect_unfreeze();
        return;
    }

    if( thisee->flags & EE_CONTINUOUS ) {
        last_flags = int_submit_ee( enum, thisee->func,
            thisee->interval, thisee->flags );
    }

    // Restart the timer.
    effect_unfreeze();

    catch( effect_object = load_object( effs[enum]->ob_name ) );

    if( !effect_object ) {
        if( creatorp(TO) )
            tell_object( TO, "Non-existent effect "+
                effs[enum]->ob_name+", deleting.\n");
        delete_effect(enum);
        current_enum = -1;
        return;
    }

    if( thisee->flags & EE_OLD ) {
        res = (mixed)effect_object->effect_heart_beat( TO,
            ++effs[enum]->arg[0], effs[enum]->arg[1], shadows[enum]->idnum );
        if( res == REMOVE_THIS_EFFECT )
            delete_effect(enum);
        else
            effs[enum]->arg[1] = res;
    } else {
        if( stringp(thisee->func) ) {
            res = call_other( effect_object, thisee->func, affected_object(),
                effs[enum]->arg, shadows[enum]->idnum, enum );
            if( res == REMOVE_THIS_EFFECT )
                delete_effect(enum);
            else if( res == CANCEL_THIS_EE )
                eeq[last_flags]->flags |= EE_CANCELLED;
        } else if( functionp(thisee->func) ) {
            res = evaluate( thisee->func, affected_object(), effs[enum]->arg,
                shadows[enum]->idnum, enum );
            if( res == REMOVE_THIS_EFFECT )
                delete_effect(enum);
            else if( res == CANCEL_THIS_EE )
                eeq[last_flags]->flags |= EE_CANCELLED;
        }
    }

    if( thisee->flags & EE_REMOVE )
        delete_effect(enum);

    current_enum = -1;

} /* effect_timer() */

/** @ignore yes */
mixed stats() {
    mixed ret;
    int i;
    object ob;

    ret = ({ });

    for( i = 0; i < sizeof(effs); i++ ) {
        if( catch( ob = load_object(effs[i]->ob_name ) ) || !ob ) {
            ret += ({ ({ "effect#" + i, " (buggered)"}) });
            continue;
        }

        if( intp(effs[i]->arg) || stringp(effs[i]->arg) )
            ret += ({ ({ "effect#" + i,
                effs[i]->ob_name->query_classification()+" ("+
                effs[i]->arg+")"}) });
        else
            ret += ({ ({ "effect#" + i,
                effs[i]->ob_name->query_classification()+" (complex)"}) });
    }

    return ret;

} /* stats() */

/**
 * This method removes all the effects as we die.  It calls the function
 * survive_death() on the effect objects themselves to determine if
 * they should be kept when the living dies.
 */
void effects_thru_death() {
    int i;
    object ob;

    i = sizeof( effs );

    while( i-- ) {
        if( catch( ob = load_object(effs[i]->ob_name) ) || !ob ||
            !ob->survive_death() )
            delete_effect(i);
    }

} /* effects_thru_death() */

/**
 * This method returns the current enum.
 * @return the current enum
 */
int query_current_effect_enum() { return current_enum; }

/**
 * This method is used to force the effects setup in the object to be a
 * specified value.  This is used in the effects effect which sets up
 * effects on someone using a shadow.  This should only be used on startup.
 * @param args the effects array to overwrite the old one with
 */
protected void set_effs( mixed args ) {
    int i, neffs;

    if( sizeof( args ) && !classp( args[ 0 ] ) ) {
        effs = allocate( neffs = sizeof( args ) / EFF_SIZE );
        for( i = 0; i < neffs; i++ )
            effs[ i ] = new( class effect,
                        ob_name : args[ i * EFF_SIZE + EFF_OB_NAME ],
                        arg     : args[ i * EFF_SIZE + EFF_ARG ] );
    } else {
        effs = args;
    }

} /* set_effs() */

/**
 * This method is used to force the effects setup in the object to be a
 * specified value.  This is used in the effects effect which sets up
 * effects on someone using a shadow.  This should only be used on startup.
 * @param args the effects array to overwrite the old one with
 */
protected void set_eeq( mixed args ) {
    int i, neeq;

    if( sizeof( args ) && !classp( args[ 0 ] ) ) {
        eeq = allocate( neeq = sizeof( args ) / EEQ_SIZE );
        for( i = 0; i < neeq; i++ )
            eeq[ i ] = new( class effect_event,
                       inctime    : args[ i * EEQ_SIZE + EEQ_INCTIME ],
                       func       : args[ i * EEQ_SIZE + EEQ_FUNC ],
                       eff_ob_num : args[ i * EEQ_SIZE + EEQ_EFF_OB ],
                       interval   : args[ i * EEQ_SIZE + EEQ_INTERVAL ],
                       flags      : args[ i * EEQ_SIZE + EEQ_FLAGS ] );
    } else {
        eeq = args;
    }

} /* set_eeq() */