tmi2_fluffos_v2/
tmi2_fluffos_v2/bin/
tmi2_fluffos_v2/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/ChangeLog.old/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/Win32/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/simuls/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/clone/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/command/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/data/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/master/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/log/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/compiler/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/efuns/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/operators/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/u/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/tmp/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/windows/
tmi2_fluffos_v2/lib/
tmi2_fluffos_v2/lib/adm/
tmi2_fluffos_v2/lib/adm/daemons/languages/
tmi2_fluffos_v2/lib/adm/daemons/network/I3/
tmi2_fluffos_v2/lib/adm/daemons/virtual/
tmi2_fluffos_v2/lib/adm/daemons/virtual/template/
tmi2_fluffos_v2/lib/adm/news/
tmi2_fluffos_v2/lib/adm/obj/
tmi2_fluffos_v2/lib/adm/obj/master/
tmi2_fluffos_v2/lib/adm/priv/
tmi2_fluffos_v2/lib/adm/shell/
tmi2_fluffos_v2/lib/adm/tmp/
tmi2_fluffos_v2/lib/cmds/
tmi2_fluffos_v2/lib/d/
tmi2_fluffos_v2/lib/d/Conf/
tmi2_fluffos_v2/lib/d/Conf/adm/
tmi2_fluffos_v2/lib/d/Conf/boards/
tmi2_fluffos_v2/lib/d/Conf/cmds/
tmi2_fluffos_v2/lib/d/Conf/data/
tmi2_fluffos_v2/lib/d/Conf/logs/
tmi2_fluffos_v2/lib/d/Conf/obj/
tmi2_fluffos_v2/lib/d/Conf/text/help/
tmi2_fluffos_v2/lib/d/Fooland/adm/
tmi2_fluffos_v2/lib/d/Fooland/data/
tmi2_fluffos_v2/lib/d/Fooland/data/attic/
tmi2_fluffos_v2/lib/d/Fooland/items/
tmi2_fluffos_v2/lib/d/TMI/
tmi2_fluffos_v2/lib/d/TMI/adm/
tmi2_fluffos_v2/lib/d/TMI/boards/
tmi2_fluffos_v2/lib/d/TMI/data/
tmi2_fluffos_v2/lib/d/TMI/rooms/
tmi2_fluffos_v2/lib/d/grid/
tmi2_fluffos_v2/lib/d/grid/adm/
tmi2_fluffos_v2/lib/d/grid/data/
tmi2_fluffos_v2/lib/d/std/
tmi2_fluffos_v2/lib/d/std/adm/
tmi2_fluffos_v2/lib/data/adm/
tmi2_fluffos_v2/lib/data/adm/daemons/
tmi2_fluffos_v2/lib/data/adm/daemons/doc_d/
tmi2_fluffos_v2/lib/data/adm/daemons/emoted/
tmi2_fluffos_v2/lib/data/adm/daemons/network/http/
tmi2_fluffos_v2/lib/data/adm/daemons/network/services/mail_q/
tmi2_fluffos_v2/lib/data/adm/daemons/network/smtp/
tmi2_fluffos_v2/lib/data/adm/daemons/news/archives/
tmi2_fluffos_v2/lib/data/attic/connection/
tmi2_fluffos_v2/lib/data/attic/user/
tmi2_fluffos_v2/lib/data/std/connection/b/
tmi2_fluffos_v2/lib/data/std/connection/l/
tmi2_fluffos_v2/lib/data/std/user/a/
tmi2_fluffos_v2/lib/data/std/user/b/
tmi2_fluffos_v2/lib/data/std/user/d/
tmi2_fluffos_v2/lib/data/std/user/f/
tmi2_fluffos_v2/lib/data/std/user/l/
tmi2_fluffos_v2/lib/data/std/user/x/
tmi2_fluffos_v2/lib/data/u/d/dm/working/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/smtp/
tmi2_fluffos_v2/lib/doc/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/interactive/
tmi2_fluffos_v2/lib/doc/driverdoc/concepts/
tmi2_fluffos_v2/lib/doc/driverdoc/driver/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/arrays/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/buffers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/compile/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/ed/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/filesystem/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/floats/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/functions/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/general/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/mappings/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/numbers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/parsing/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/constructs/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/preprocessor/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/types/
tmi2_fluffos_v2/lib/doc/driverdoc/platforms/
tmi2_fluffos_v2/lib/doc/mudlib/
tmi2_fluffos_v2/lib/ftp/
tmi2_fluffos_v2/lib/include/driver/
tmi2_fluffos_v2/lib/log/
tmi2_fluffos_v2/lib/log/driver/
tmi2_fluffos_v2/lib/obj/net/
tmi2_fluffos_v2/lib/obj/shells/
tmi2_fluffos_v2/lib/obj/tools/
tmi2_fluffos_v2/lib/std/adt/
tmi2_fluffos_v2/lib/std/board/
tmi2_fluffos_v2/lib/std/body/
tmi2_fluffos_v2/lib/std/fun/
tmi2_fluffos_v2/lib/std/living/
tmi2_fluffos_v2/lib/std/object/
tmi2_fluffos_v2/lib/std/shop/
tmi2_fluffos_v2/lib/std/socket/
tmi2_fluffos_v2/lib/std/user/
tmi2_fluffos_v2/lib/std/virtual/
tmi2_fluffos_v2/lib/student/
tmi2_fluffos_v2/lib/student/kalypso/
tmi2_fluffos_v2/lib/student/kalypso/armor/
tmi2_fluffos_v2/lib/student/kalypso/rooms/
tmi2_fluffos_v2/lib/student/kalypso/weapons/
tmi2_fluffos_v2/lib/u/l/leto/
tmi2_fluffos_v2/lib/u/l/leto/cmds/
tmi2_fluffos_v2/lib/www/errors/
tmi2_fluffos_v2/lib/www/gateways/
tmi2_fluffos_v2/lib/www/images/
tmi2_fluffos_v2/old/
tmi2_fluffos_v2/win32/
/*
 * File: /std/user.c
 * The user body is from the TMI-2 lib. Part of the bodies project and
 * implemented by Watcher@TMI-2 and Mobydick@TMI-2. The code contained
 * in this object is heavily based on code found in the original user.c
 *
 * The relevant code headers follow:
 * Original Authors: Sulam and Buddha @ The Mud Institute
 * Many other people have added to this as well.
 * This file has a long revision history dating back to the hideous
 * mudlib.n and is now probably not at all the same.
 * This file is part of the TMI distribution mudlib.
 * Please keep this header with any code within.
 *
 * 94-11-09 Leto added Beek's error handler code
 * 94-11-28 Leto added Beek's move_or code
 * 94-11-30 Leto added receive_snoop
 * 95-01-11 Blue modified receive_snoop slightly.
 */

#define DEBUG

#include <config.h>
#include <login.h>
#include <commands.h>
#include <daemons.h>
#include <net/daemons.h>
#include <mudlib.h>
#include <flock.h>
#include <move.h>
#include <money.h>
#include <priv.h>
#include <driver/origin.h>
#include <uid.h>
#include <switches.h>
#include <messages.h>

/*
 * Some files are inherited by including user.h.
 */
#include <user.h>
#include <user2.h>
#include <logs.h>
#include <body.h>
#include <login_macros.h>

/*
 * This is only needed for consistency_check.
 */
#include <channels.h>
#include <domains.h>

/*
 * For receive_snoop
 */
#include <ansi.h>

inherit LIVING ;

/*
 * This is probably only a local hack.
 */
#ifndef CAP_NAME_MASTER_ONLY
#  undef CAP_NAME
#  undef LINK_CAP_NAME
#  define CAP_NAME capitalize(query("name"))
#  define LINK_CAP_NAME capitalize(link_data("name"))
#endif

#define HALL "/d/Fooland/hall"

/*
 * prototypes for local functions
 */
static varargs void complete_setup (string str);
static void die();
static int create_ghost();
int coins_carried();
void init_setup();
void destroy_autoload_obj();
varargs void execute_attack(int hit_mod, int dam_mod);
void call_user_dump(string type);

/*
 * Setup basic and command catch systems
 */
void basic_commands() {
    add_action("quit", "quit");
}

static void init_commands() {
    string path;

    add_action( "cmd_hook", "", 1 );

    path = query("PATH");

    if (!path) {
        if ( wizardp(this_object()) )
            path = NEW_WIZ_PATH;
        else
            path = USER_CMDS;

        set("PATH", path, MASTER_ONLY);
    }
}

/*
 * Setup standard user command hook system.  This system interfaces
 * with the cmd bin system, the environment's exits, and feeling entries.
 */
nomask static int cmd_hook(string cmd) {
    string file;
    string verb;
    int foo;
    mapping before, after;

    verb = query_verb();

    if (environment() && environment()->valid_exit(verb)) {
        verb = "go";
        cmd = query_verb();
    }

    file = (string)CMD_D->find_cmd(verb, explode(query("PATH"), ":"));

    if (file && file != "") {
#ifdef PROFILING
        before = rusage();
#endif
        foo = (int)call_other(file, "cmd_" + verb, cmd);

#ifdef PROFILING
        after = rusage();
              "/adm/daemons/profile"->log_cmd(verb,before,after);
#endif

        return foo;
    }

    if (environment() && environment()->query("quiet"))
        return 0;

#ifdef PROFILING
    before = rusage();
#endif

    foo = (int)EMOTE_D->parse(verb, cmd);

    if (foo) {
#ifdef PROFILING
        after = rusage();
        "/adm/daemons/profile"->log_cmd(verb,before,after);
#endif
        return foo;
    }

#ifdef PROFILING
    before = rusage();
#endif

#ifndef INTERMUD
    if (verb == "gwiz" || verb == "interwiz") {
        printf("Sorry, %s does not support intermud.\n",capitalize(mud_name()));
        return 1;
    }
#endif /* INTERMUD */

    foo = (int) CHANNELS_D -> parse_channel( verb, cmd );

#ifdef PROFILING
    if ( foo ) {
        after = rsuage();
        "/adm/daemons/profile" -> log_cmd( verb, before, after );
    }
#endif

    return foo;
}


/*
 * This function protects the object from being shadowed for
 * certain secure functions.
 */
nomask int query_prevent_shadow(object ob) {
    mixed *protect;
    int loop;

    protect = ({ "catch_tell", "receive_message", "do_alias", "do_xverb",
                 "do_substitution", "process_input", "tsh",
                 "do_nickname", "receive_snoop" });

    for (loop = sizeof(protect); loop--; )
        if (function_exists(protect[loop], ob))  return 1;

    return 0;
}

/*
 * Move the player to another room. Give the appropriate
 * message to on-lookers.
 * The optional message describes the message to give when the player
 * leaves.
 */
varargs int move_player(mixed dest, string message, string dir) {
    object prev;
    int res;

    prev = environment( this_object() );
    if ( res = move(dest) != MOVE_OK ) {
        message(MSG_INFO, "You remain where you are.\n", this_player());
        return res;
    }

    if (environment() && wizardp(this_object()) && query("display_path"))
        message(MSG_LOOK, sprintf("[%s]\n", file_name(environment())),
              this_object());

    if (message == "SNEAK") {
        set_temp("force_to_look", 1);
        command("look");
        set_temp("force_to_look", 0);
        return 0;
    }

    if (!query("invisible")) {

        if (message == 0 || message == "") {

            if (dir && dir != "") {
                message(MSG_MOVEMENT, (string)query_mout(dir) + "\n",
                      prev, this_object());
                message(MSG_MOVEMENT, (string)query_min() + "\n",
                      environment(), this_object());
            } else {
                message(MSG_MOVEMENT, (string)query_mmout() + "\n",
                      prev, this_object());
                message(MSG_MOVEMENT, (string)query_mmin() + "\n",
                      environment(), this_object());
            }
        } else {
            message(MSG_MOVEMENT, message + "\n", prev, this_object());
            message(MSG_MOVEMENT, (string)query_min() + "\n", environment(),
                  this_object());
        }
    }

    set_temp ("force_to_look", 1);
    command("look");
    set_temp("force_to_look", 0);


    /*
     * Follow/track mudlib support
     */
    if (!query("no_follow") && environment() != prev && prev)
        all_inventory(prev)->follow_other(this_object(), environment(), dir);

    return 0;
}

private int clean_up_attackers() {
    mixed *attackers_tmp;
    int i;

    attackers_tmp = ({ });

    for (i = sizeof(attackers); i--; ) {
        /*
         * If he's dead, then forget about him entirely.
         */
        if (attackers[i] == 0 || !living(attackers[i]))
            continue;

        /*
         * If he's not here, then forget about him.
         */
        if (environment(attackers[i]) != environment(this_player()))
            continue;

        /*
         * If he's a ghost, we've done enough to him already :)
         */
        if ((int)attackers[i]->query_ghost() == 1) continue;

        /*
         * If we get this far, then we still want to be attacking him
         */
        attackers_tmp += ({ attackers[i] });
    }

    /*
     * Copy the tmp list over to the attackers list.
     */
    attackers = attackers_tmp;

    if (sizeof(attackers_tmp) == 0)
        any_attack = 0;

    return any_attack;
}

/*
 * Continue_attack is called from heart_beat..
 * here is where we can try to see if we're dead or in combat.
 */
void continue_attack() {
    /*
     * Check if this object has just died. if so, do the death stuff.
     */
    if (query("hit_points") < 0) {
        die();
        return;
    }

    /*
     * If there's no one to attack, then we are finished.
     */
    if (!any_attack)  return;

    /*
     * Call the clean_up_attackers function to see who's left. If it
     * returns 0, then there's no one left.
     */
    if (clean_up_attackers() == 0) {
        /*
         * No attackers in the room
         */
        message(MSG_COMBAT, "The combat is over.\n", this_player());
        return;
    }

    /*
     * Check to see if the player is doing something that prevents
     * him from making an attack.
     */
    if (query("stop_attack")>0) {
        message(MSG_COMBAT, "You are too busy to make an attack!\n",
              this_player());
        return;
    }

    /*
     * Check to see if we're under wimpy, and if so, run away.
     */
    if (query("hit_points")*100/query("max_hp") < this_player()->query("wimpy")) {
        run_away();
        return;
    }

    execute_attack();
}

static int loop, noise;

/*
 * This is the big, ugly CPU hogging function where the combat actually
 * takes place.
 */
varargs void execute_attack (int hit_mod, int dam_mod) {
    int att_sk, def_sk, str, dex, ac, wc, hit_chance;
    string name, verb1, verb2, vname, damstr, damstr2, wepstr, *verbs;
    mixed *contents;
    object *prots;
    object weapon;
    int old_inv, s;
    string victim, posgender;
    int *damrange;

    /*
     * Check to see if primary target is dead, if so move to the next attack
     * in the attackers queue. If the attackers queue is empty, stop attack call.
     */
    if (attackers[0]->query("hit_points") < 0) {
        attackers -= ({ attackers[0] });
        if (!attackers || !sizeof(attackers))  return;
    }

    /*
     * Is the target being protected? If so, find the alternate target.
     */
    prots = attackers[0]->query("protectors");
    if (prots && sizeof(prots) > 0) {
        /*
         * Get rid of all ineligible protectors: dead or not present.
         */
        prots = filter_array(prots,"valid_protect", this_object());

        /*
         * If there are eligible protectors, then move the protector to the top
         * of the list, adding him if needed.
         */
        if (s = sizeof(prots)) {

            /*
             * Ok, I tried fixing it, but it is rather complex. In any case,
             * I do not think it is doing what it is supposed to do.
             * Someone mixed strings and objects all the way through... Leto
             */
//          victim = prots[random(s)];
            weapon = prots[random(s)];

            /*
             * re-using a variable to save memory - sorry bout that :(
             */
//          weapon = find_player(victim); //Leto
            ac = member_array(weapon,attackers);
            if (ac>-1) {
                attackers[ac]=attackers[0];
                attackers[0]=weapon;
            } else {
                attackers = ({ weapon }) + attackers;
                weapon -> kill_ob(this_object());
            }
        }
    }

    /*
     * hit_mod and dam_mod are modifiers that can be passed to the attack.
     * The heartbeat doesn't add them but you can make special attacks by
     * calling execute_attack directly. Be careful if you do so, you'll want
     * to also call kill_ob to make sure a fight starts...
     */
//  if (!hit_mod) hit_mod=0;
//  if (!dam_mod) dam_mod=0;

    /*
     * Collect the various statistics needed to get the hit chance and damage.
     */
    str = query("stat/strength");
    dex = attackers[0]->query("stat/dexterity");
    ac = attackers[0]->query("armor_class");
    weapon = query("weapon1");

    /*
     * If they don't have a weapon, they get their intrinsic combat skills.
     */
    if (!weapon) {
        /*
         * These are the numbers/strings for a unarmed user attack. They're
         * hard-coded but you could equally well set them as properties in the
         * user object.
         */
        wc = 2;
        dmin = 1;
        dmax = 3;
        verb1 = "swing at";
        verb2 = "swings at";
        wepstr = "fists";
        weapontype = "Blunt weapons";
    } else {
        /*
         * If we get here, then the player has a weapon, and we query the
         * weapon for its attack properties.
         * Does he have a second weapon? If so, use it on 20% of attacks.
         */
        if (query("weapon2") && random(5)==0) {
            weapon = query("weapon2");
        }
        wc = query("attack_strength");
        damrange = weapon->query("damage");
        dmin = damrange[0];
        dmax = damrange[1];
        verbs = allocate(2);
        verbs = weapon->get_verb();
        verb1 = verbs[0];
        verb2 = verbs[1];
        wepstr = (string)weapon->query("name");
        weapontype = capitalize(weapon->query("type")+" weapons");
    }

    name = query("cap_name");
    vname = attackers[0]->query("cap_name");
    posgender = possessive(query("gender"));

    /*
     * Check the attacker's attack skill and the defenders skill(s).
     *
     * If a player, check his weapons skills. Otherwise, use the attack skill
     * for a monster.
     */
    att_sk = query_skill(weapontype);

    /*
     * Ditto for the defender.
     */
    if (!(int)attackers[0]->query_monster()==1) {
        if (attackers[0]->query("armor/shield")) {
            def_sk = attackers[0]->query_skill("Shield defense");
        } else {
            def_sk = attackers[0]->query_skill("Parrying defense");
        }
    } else {
        def_sk = attackers[0]->query_skill("defense");
    }

    /*
     * This is the combat formula.
     * If you are using drunkenness, and want it to affect combat, then
     * call query("drunk"), which goes 1-25, and subtract it from the
     * hit chance.
     */
    hit_chance = 30 + str + att_sk + 3*wc - dex - def_sk - 3*ac + hit_mod;

    /*
     * Attacking invisible creatures is really rather difficult.
     */
    if ((int)attackers[0]->query("invisible")>0)
        hit_chance /= 5;

    /*
     * The hit chance is constrained to be between 2 and 98 percent.
     */
    if (hit_chance<2)  hit_chance = 2;
    if (hit_chance>98)  hit_chance = 98;

    /*
     * Improve the skills of the attacker and defender.
     *
     * The probability of the skill improving depends on the hit chance. If the
     * hit chance is 0 or 100, the skill does not improve. If the hit chance is
     * 50%, then the skill improves automatically. The closer the hit chance is
     * to 50%, the more likely the skill is to improve. This rewards players for
     * taking on monsters roughly equal in skill to themselves.
     */
    skill_improve_prob = hit_chance * (100-hit_chance) / 25;
    if (random(100)<skill_improve_prob) {
        improve_skill(weapontype,1);
    }
    if (random(100)<skill_improve_prob) {
        if (!attackers[0]->query_monster()) {
            if (attackers[0]->query("armor/shield")) {
                attackers[0]->improve_skill("Shield defense",1);
            } else {
                attackers[0]->improve_skill("Parrying defense",1);
            }
        }
    }

    /*
     * Get a list of all in the room who are listening to the battle.
     */
    contents = all_inventory(environment(this_object()));
    contents = filter_array(contents, "filter_listening", this_object());

    /*
     * This is the damage formula.
     * We have to calculate this first because we don't want to print messages
     * of the form "You hit for zero damage." If the damage is less than zero,
     * we print a "You miss" message regardless of the hit_chance roll.
     */
    damage = random(dmax-dmin+1)+dmin+str/8-1+att_sk/10-def_sk/5+dam_mod;

    /*
     * Before we print any messages, we need to make ourselves visible.
     * Otherwise the person we're attacking doesn't get the message. We
     * become invisible again after the messages are printed.
     * This is klugey but real real easy.
     */
    old_inv = query("invisible");
    set ("invisible", 0);

    /*
     * If positive damage, and the hit lands, then we do damage and print
     * the appropriate damage messages.
     */
    if (damage>0 && random(100)<hit_chance) {

        str = attackers[0]->query("hit_points");
        if (damage)  attackers[0]->receive_damage(damage);
        qs = objective((string)attackers[0]->query("gender"));

        /*
         * We print different damage messages based on how much damage was
         * done. You might want to make this system a little more interesting.
         * Go nuts.
         */
        switch (damage) {
            case 1:
                damstr = "scratch "+qs+".";
                damstr2 = "scratches "+qs+".";
                break;
            case 2..3 :
                damstr = "do light damage.";
                damstr2 = "does light damage.";
                break;
            case 4..6 :
                damstr = "hit.";
                damstr2 = "hits.";
                break;
            case 7..9 :
                damstr = "deliver a solid blow.";
                damstr2 = "delivers a solid blow.";
                break;
            case 10..15 :
                damstr = "hit hard!";
                damstr2 = "hits hard!";
                break;
            case 16..20 :
                damstr = "inflict massive damage!";
                damstr2 = "inflicts massive damage!";
                break;
            default :
                /*
                 * Mobydick just ran out of ideas at this point.  :)
                 */
                damstr = "whomp "+qs+"!";
                damstr2 = "whomps "+qs+"!";
        }


        /*
         * The routines below check to see if all the listeners really
         * want to hear how the battle is going (Watcher, 4/27/93).
         */
        if (!(query("noise_level") && damage < 2))
            message(MSG_COMBAT,
                  sprintf("You %s %s with your %s and %s\n",
                        verb1, vname, wepstr, damstr),
                  this_player());

        for (loop = sizeof(contents); loop--; ) {
            if (contents[loop])
                noise = (int)contents[loop]->query("noise_level");

            if (noise && (noise > 1 || (noise == 1 && damage < 2)))
                continue;

            message(MSG_COMBAT,
                  sprintf("%s %s %s with %s %s and %s\n",
                        name, verb2, vname, posgender,
                        wepstr, damstr2), contents[loop]);
        }

        if (attackers[0] &&
              !(attackers[0]->query("noise_level") && damage < 2))
            message(MSG_COMBAT,
                  sprintf("%s %s you with %s %s and %s\n",
                        name, verb2, posgender,
                        wepstr, damstr), attackers[0]);
    } else {
        /*
         * If we got here, it means we missed the hit roll, or did zero damage.
         */
        if (!query("noise_level"))
            message(MSG_COMBAT,
                  sprintf("You %s %s with your %s but you miss.\n",
                        verb1, vname, wepstr),
                  this_player());

        for (loop = sizeof(contents); loop--; )
            if (contents[loop] && !contents[loop]->query("noise_level"))
                message(MSG_COMBAT,
                      sprintf("%s %s %s with %s %s but misses.\n",
                            name, verb2, vname, posgender, wepstr),
                      contents[loop]);

        if (attackers[0] && !attackers[0]->query("noise_level"))
            message(MSG_COMBAT,
                  sprintf("%s %s you with %s %s but misses you.\n",
                        name, verb2, posgender, wepstr),
                  attackers[0]);
    }

    /*
     * Restore the old invisibility setting.
     */
    if (old_inv)
        set ("invisible", old_inv);

    return;
}

/*
 * This function filters out the living objects who are listening
 * to the present battle.
 */
int filter_listening(object obj) {
    if (obj == this_object() || obj == attackers[0])  return 0;
    return living(obj);
}

/*
 * This one filters dead or absent people from an array.
 */
int valid_protect (string str) {

    object foo;

    foo = find_player(str);
    if (!foo)  return 0;
    if (environment(foo) != environment(this_object())) {
        return 0;
    }
    if ((int)foo->query("hit_points")<0)  return 0;
    return 1;
}


void create() {

    if (user_exists(getuid()))  return;

    /*
     * Until the user's name and id is set ... give it a temporary one.
     */
    set("name", "noname", MASTER_ONLY);
    set("id", ({ "noname" }));

    /*
     * We set EUID of 0 so that the login daemon can export the proper
     * UID onto the player. If you are running without AUTO_SETEUID, then
     * this has no effect, but under auto-EUID-setting it's important.
     * Also it makes it mildly harder for people to get themselves into
     * trouble by cloning user.c.
     */
    seteuid(0);


    /*
     * there's some standard properties that need to be locked so that
     * Joe Random Wizard can't break security by setting them on people
     * who haven't used them yet.
     */
    set("npc", 0, LOCKED);
    set("snoopable", 0, MASTER_ONLY);
    set("invisible", 0, MASTER_ONLY);
    set("short", "@@query_short");
    set("cap_name", capitalize(query("name")), MASTER_ONLY);
    set("title", "@@query_title", MASTER_ONLY);
    set("linkdead", "@@query_linkdead");
    set("age", 0, MASTER_ONLY);
    set("ghost", 0, MASTER_ONLY);
    set("shell", "none", MASTER_ONLY);
    set("user", 1, MASTER_ONLY);
    set("vision", "@@query_vision");
    set("harass", 0, OWNER_ONLY);
#ifdef NO_PKILL
    set("no_attack", 1, OWNER_ONLY);
#else
    set("no_attack",0, OWNER_ONLY);
#endif
    alias = ([ ]);

    /*
     * Complete the standard user attribute settings.
     */
    set("volume", MAX_VOLUME);
    set("capacity", MAX_CAPACITY);
    set("mass", USER_MASS);
    set("bulk", USER_BULK);
    set ("time_to_heal", 10);
    set("short", "@@query_short");
    set("channels", "", MASTER_ONLY);
    enable_commands();
}

void remove() {
    string euid;
    mixed *inv;
    int loop;

    if (previous_object()) {
        euid = geteuid(previous_object());
        if ( (euid != ROOT_UID) &&
              (euid != geteuid(this_object())) &&
              !adminp(euid) ) {
            message(MSG_SYSTEM, "You may not remove other players.\n",
                  this_player());
            return;
        }
    }
    free_locks(this_object());

    save_data();
    destroy_autoload_obj();

    inv = all_inventory(this_object());
    for (loop = sizeof(inv); loop--; )
        if (inv[loop]->query("prevent_drop"))
            inv[loop]->remove();

//  CMWHO_D->remove_user(this_object());
    if (link)  link->remove();
    living::remove();
}

static int in_de_quit_script;

varargs int quit(string str) {
    object *stuff, *inv;
    int i, j;

    if (!(origin() == ORIGIN_LOCAL || origin() == ORIGIN_DRIVER)
              && geteuid(previous_object()) != ROOT_UID)
        return 0;

    if (str) {
        notify_fail("Quit what?\n");
        return 0;
    }

    /*
     * If the #define is on, then save their location for starting next time.
     */
#ifdef REAPPEAR_AT_QUIT
    if (environment(this_player())) {
        set ("start_location", file_name(environment(this_player())));
    } else {
        set ("start_location", START);
    }
#endif

    /*
     * Free any outstanding file locks.
     */
    free_locks(this_object());

    /*
     * Get rid of any party memberships.
     */
//  check_team();
    PARTY_D->check_party(this_object());

    if ( wizardp( this_object() ) )   {
        string quit_script; // Pallando 93-02-11

        quit_script = user_path( query( "name" ) ) + ".quit";
        if ( file_size( quit_script ) > 0 ) {
            if (in_de_quit_script++)
                message(MSG_SYSTEM,
                      "Oi, stupid! Don't put `quit' in your ~/.quit file\n",
                      this_object());
            else
                call_other( this_object(), "tsh", quit_script );
            in_de_quit_script = 0;
        }
    }

    /*
     * If the "harass" logging is still on, then turn it off.
     */
    if (query("harass") > 0 ) {
        set("harass", 0);
        message(MSG_INFO, "Harass Log turned off.\n", this_object());
    }


    /*
     * If this is an invisible wizard, we let the invisibility stay on: but
     * if it's a player who cast the invisibility spell, then we want him
     * to be visible when he reappears.
     */
    if (!wizardp(this_object()))  set("invis", 0);

    if (link) {
//      link->set("last_on", time());
        link->set("ip", query_ip_name(this_object()));
    }

#ifdef QUIT_LOG
    if (link)
        log_file(QUIT_LOG, CAP_NAME + ": quit from " +
              query_ip_name(this_object()) + " [" +
              extract(ctime(time()), 4, 15) + "]\n");
    else
        log_file(QUIT_LOG, CAP_NAME +
              ": swept [" + extract(ctime(time()), 4, 15) + "]\n");
#endif

    /*
     * First save the user's personal data.
     */
    save_data();

    /*
     * Then destroy autoloading inventory.
     */
    destroy_autoload_obj();

    /*
     * Now drop everything droppable on the user.
     */
    inv = all_inventory( this_object() );
    inv = filter_array(inv, "inventory_check", this_object());

    if (inv && sizeof(inv))  command("drop all");

    /*
     * Announce the departure of the user.
     */
    if (this_object() && visible(this_object()))
        message(MSG_MOVEMENT, query("cap_name") + " left the game.\n",
              environment(), this_player());

    if (this_object() && interactive(this_object()))
        ANNOUNCE->announce_user(this_object(), 1);

#ifdef LOGOUT_MSG
    if (previous_object() == this_player() && this_player())
        message(MSG_INFO, LOGOUT_MSG, this_object());
#endif

    /*
     * Clean up a few loose ends before shutting down the user.
     */
//  CMWHO_D->remove_user(this_object());
    if (link)  link->remove();
    living::remove();

    return 1;
}


/*
 * This function determines if the user has anything droppable
 * when they quit the mud.
 */
static int inventory_check(object obj) {
    if (obj->query_auto_load())  return 0;
    if (!obj->query("short") || obj->query("prevent_drop"))  return 0;
    return 1;
}


/*
 * This procedure is called from the setup() function below.  It is
 * basically here to check that existing users get whatever new settings
 * they need to function in today's changing mudlib.
 */
void consistency_check() {
    int i,j;
    mapping doms, doms2;
    string *domlist;

    /*
     * if you think everyone has been "fixed" then what you put here should
     * moved to create() and taken out.
     * right now it's empty because hopefully everyone has been updated.
     */
//  set("no_attack",0,READ_ONLY);

    /*
     * I don't know why this was done.  I'm changing back to the
     * original _stopping_ player killing.  Blue.
     */
#ifdef NO_PKILL
    set("no_attack", 1, OWNER_ONLY);
#endif

    /*
     * Added by Leto to fix people with stupid vol/cap, causing
     * the quad bug to appear more often.
     */
    if (query("volume") < 0)  {
        message(MSG_SYSTEM,
              "\nVolume too small, fixed by consistency check.\n\n",
              this_object());
        set("volume",500);
        log_file("consistency_check",
              "Fixed negative volume of "+query("cap_name") + "\n");
    } else if (query("volume") > 10000) {
        message(MSG_SYSTEM,
              "\nVolume too large, fixed by consistency check.\n\n",
              this_object());
        set("volume",500);
        log_file("consistency_check",
              "Fixed too large volume of "+query("cap_name") + "\n");
    }
    if (query("capacity") < 0) {
        message(MSG_SYSTEM,
              "\nCapacity too small, fixed by consistency check.\n\n",
              this_object());
        set("capacity",5000);
        log_file("consistency_check",
              "Fixed negative capacity  of "+query("cap_name") + "\n");
    } else if (query("capacity") > 10000) {
        message(MSG_SYSTEM,
              "\nCapacity too large, fixed by consistency check.\n\n",
              this_object());
        set("capacity",5000);
        log_file("consistency_check",
              "Fixed too large capacity of "+query("cap_name") + "\n");
    }
    if (query("bulk") < 0) {
        message(MSG_SYSTEM,
              "\nBulk too small, fixed by consistency check.\n\n",
              this_object());
        set("bulk",1000);
        log_file("consistency_check",
              "Fixed negative bulk of "+query("cap_name") + "\n");
    } else if (query("bulk") > 10000) {
        message(MSG_SYSTEM,
              "\nBulk too large, fixed by consistency check.\n\n",
              this_object());
        set("bulk",1000);
        log_file("consistency_check",
              "Fixed too large bulk of "+query("cap_name") + "\n");
    }
    if (query("mass") < 0) {
        message(MSG_SYSTEM,
              "\nMass too small, fixed by consistency check.\n\n",
              this_object());
        set("mass",7500);
        log_file("consistency_check",
              "Fixed negative mass of "+query("cap_name") + "\n");
    } else if (query("mass") > 75000) {
        message(MSG_SYSTEM,
              "\nMass too large, fixed by consistency check.\n\n",
              this_object());
        set("mass",7500);
        log_file("consistency_check",
              "Fixed too large mass of "+query("cap_name") + "\n");
    }

    /*
     * we don't want folks to be snoopable when they first log in.
     */
    set("snoopable", 0, MASTER_ONLY);

// Leto added a check for 'leader'
// I'd really like to see this done at logoff time.....
if(query("leader")) delete("leader");
    /*
     * Added demotion from domains that no longer exist.  Blue, 950330.
     */
    if (query_link()->query("last_on") < 796700000) {
/* Commented out by Leto after Lucas reported it didn't work.
 * I'll debug it when i have the time
        doms = query_link()->query("domains");
        if (!doms)
            doms = ([ ]);
        doms2 = ([ ]);
        domlist = DOMAIN_LIST + ({ "primary" });
        for (i = 0, j = sizeof(domlist); i<j; i++) {
            if (doms[domlist[i]])
            doms2 += ([ domlist[i] : doms[domlist[i]] ]);
        }
        query_link()->set("domains", doms2);
   */ }

}

/*
 * This function is called when the player enters the game. It handles
 * news displays, player positioning, and other initial user setups.
 */
void setup() {
    string *news;
    int i, s;

    /*
     * Check to see if the user body has a "name"
     */
    if (!query("name"))  return;
    seteuid(getuid());

    /*
     * Initiate user shell setup protocal
     */
    init_setup();

    /*
     * Display last logon and logon site
     */
    if (link_data("last_on"))
        message(MSG_INFO,
              sprintf("\nLast logon:  %s from %s.\n\n",
                    ctime(link_data("last_on")), link_data("ip")),
              this_object());

    /*
     * Set termtype, info is in connection.c
     */
    set("termtype",query_link()->query("termtype"));

    debug("Setup: Running the consistency check.\n");

    consistency_check();  // A catch-all to upgrade old users

    debug("Setup: Setting special flags and reading news.\n");

    if (query("inactive"))   delete("inactive");

    CHANNELS_D -> initialize_user();

    if ( !query("cwd") ) set("cwd", "/doc");

    /*
     * Get the news from the news daemon and put it out line by line
     * to avoid overloading one write output.
     */
    news = explode( (string)MSG_D->display_news(), "\n");

    for (i = 0, s = sizeof(news); i < s; i++)
        message(MSG_INFO, news[i] + "\n", this_object());

    if (query("hushlogin")) { complete_setup(); return; }
#ifdef NO_LOGIN_PAUSE
    complete_setup();
    return;
#endif


    if (query("busy"))
        message(MSG_INFO, "\nYour busy flag is still on!\n", this_object());
    if (query("hide"))
        message(MSG_INFO, "You are still hidden. Not announced!\n",
              this_object());
    message(MSG_INFO,
          "Terminal type is "+query("termtype")+".\n\n"
          "[Press ENTER to continue]  ",
          this_object());

    input_to("complete_setup",2);

    return;
}

/*
 * Complete remainder of character setup after NEWS
 * has been read by the entering player
 */
static varargs void complete_setup (string str) {
    object ghost;
    string temp;
    mixed student_time;

    /*
     * This is here to permit an admin to shut down the game from the
     * "Press ENTER to continue" prompt. Sometimes this is helpful if there
     * is an object that is interfering with commands or otherwise wedging
     * the game, but will go away if the game is reset. It's not a security
     * problem because only admins can use it, and they could just log in
     * and use the shutdown command anyway...;)
     */
#ifdef SAFETY_SHUTDOWN
    if (adminp(getuid(this_object()))) {
        if (str=="shutdown") {
            CMD_SHUTDOWN->cmd_shutdown("0 because safety shutdown invoked.");
        }
    }
#endif

    message(MSG_INFO, "\n", this_object());

    link->set("last_on", time());
    link->set("ip", query_ip_name(this_object()));

    set("reply", 0);
    set("wreply", 0);

    debug("Complete_setup: Moving to the start location.\n");

    temp = getenv("START");
    if (!(temp && stringp(temp) && move(temp) == MOVE_OK)) {
        temp = query("start_location");
#ifdef REAPPEAR_AT_QUIT
        if (!(temp && stringp(temp) && move(temp) == MOVE_OK))
            move(START);
#else
            move (START);
#endif
    }


    call_out("save_me", 1);

    ANNOUNCE->announce_user(this_object(), 0);

    /*
     * This is commented out because of a problem with socket handling
     * by some versions of UNIX. Basically, if you make the call to USERID_D,
     * then the driver will call back to the user's host machine and ask for
     * the user's account name. Under some OS's (Ultrix and HP_UX to name two,
     * but there may be more) if the query returns "Host is unreachable", eg if
     * there is a firewall machine between the driver and the user's machine,
     * then the driver will break the user's connection, and anyone from that machine
     * will be unable to play the MUD.
     * You can undefine this at your own risk, but you'll be cutting off anyone
     * from a protected site, which means most .com addresses and a fair
     * smattering of other hosts, if your OS behaves this way. TMI-2's host
     * runs Ultrix, so we leave it commented out. A good 90% of hosts don't
     * support the user name query protocol anyway, so we're not losing that
     * much. It's your decision if you want to get the names of the other 10%
     * of users, or leave it commented out...
     */
//  USERID_D->query_userid();

    debug("Complete_setup: Checking to see if user is dead.\n");

    if (link_data("dead")) {
        ghost = create_ghost();
        move( VOID );
        set_temp("force_to_look", 1);
        ghost->force_me("look");
        set_temp("force_to_look", 0);
        message(MSG_INFO, "\nYou suddenly realize that you are still a ghost.\n",
              ghost);
        message(MSG_MOVEMENT, sprintf("%s enters the game.\n", query("cap_name")),
              environment(ghost), ({ ghost }));
        remove();
        return;
    }

    if (visible(this_object()))
        message(MSG_MOVEMENT, query("cap_name") + " enters the game.\n",
              environment(), this_player());

#ifdef LOGIN_LOG
    log_file(LOGIN_LOG, CAP_NAME + ": logged in from " +
          query_ip_name(this_object()) + " [" +
          extract(ctime(time()), 4, 15) + "]\n");
#endif

    set_temp("force_to_look", 1);
    command("look");
    set_temp("force_to_look", 0);

    student_time = STUDENT_D->query_time_left(query("name"));

    if (student_time != -1) {
        if (student_time<0)
            message(MSG_SYSTEM,
                  sprintf("\n%s\n\n",
                        blink(" [WARNING:  Your time period as a student has"
                              " ended.  You should copy any files\n"
                              "  you want to keep using ftpd.  "
                              "Type \"help ftpd\" if you need to.]")),
                  this_object());
        else
            message(MSG_SYSTEM,
                  "\n [You have " + bold(format_time(student_time, 1)) +
                  " left as a student]\n\n",
                  this_object());
    }
    debug("Complete_setup: Setup complete.\n");
}

/*
 * This function is called cyclically to save the user data
 * periodically, if AUTOSAVE is defined.
 */
static void autosave_user() {

    remove_call_out("autosave_user");
    call_out("autosave_user", AUTOSAVE);

    if (!wizardp(this_object()))
        message(MSG_INFO, "Autosave.\n", this_object());

    save_me();
}

void heart_beat() {
    int age;

    continue_attack();
    unblock_attack();
    heal_up();

#ifdef IDLE_DUMP
    if (this_object() && interactive(this_object()) &&
          !wizardp(this_object()) &&
          query_idle(this_object()) > IDLE_DUMP)
        call_user_dump("idle");
#endif

    /*
     * Add to the user's online age total.
     */
    age = query("age");

    if (!query_temp("last_age_set"))
        set_temp("last_age_set", time());

    age += (time() - query_temp("last_age_set"));
    set_temp("last_age_set", time());
    ob_data["age"] = age;
}


/*
 * This function returns whether the user is linkdead or not.
 */
nomask int query_linkdead() {  return !interactive(this_object());  }

static
void net_dead() {

    save_data();
    message(MSG_MOVEMENT,(string)query("cap_name") + " has gone net-dead.\n",
          environment(), this_object());
//  check_team();
    PARTY_D->check_party(this_object());
    ANNOUNCE->announce_user(this_object(), 3);
//  CMWHO_D->remove_user(this_object());
    set_heart_beat(0);

#ifdef LINKDEAD_DUMP
    call_out("call_user_dump", LINKDEAD_DUMP, "linkdead");
#endif

#ifdef NETDEAD_LOG
    log_file(NETDEAD_LOG, LINK_CAP_NAME + ": net-dead from " +
          query_ip_name(this_object()) + " [" +
          extract(ctime(time()), 4, 15) + "]\n");
#endif

    link->remove();
}

void restart_heart() {
    message(MSG_MOVEMENT, query("cap_name")+" has reconnected.\n",
          environment(), this_object());
    message(MSG_MOVEMENT, "Reconnected.\n", this_object());
    ANNOUNCE->announce_user(this_object(), 2);
    USERID_D->query_userid();
    set_heart_beat(1);
    set("inactive", 0);
    remove_call_out("call_user_dump");

#ifdef RECONNECT_LOG
    log_file(RECONNECT_LOG, CAP_NAME + ": reconnected from " +
          query_ip_name(this_object()) + " [ " +
          extract(ctime(time()), 4, 15) + "]\n");
#endif
}

void call_user_dump(string type) {
    if (this_player() && this_player() != this_object())  return;
    message(MSG_SYSTEM, "\nSorry, you have idled too long.\n", this_object());

    if (environment()) {
        if (type == "linkdead")
            message(MSG_MOVEMENT,
                  sprintf(
                        "A janitor suddendly appears and sweeps "
                        "%s into a nearby vortex.\n", query("cap_name")),
                  environment(), this_object());
        else
            message(MSG_MOVEMENT,
                  sprintf("%s has idled too long\n", query("cap_name")),
                  environment(), this_object());
    }

    quit();
}

static void die() {
    object killer, ghost, corpse, coins, *stuff;
    mapping wealth;
    string *names, name;
    int i, res, totcoins;

    /*
     * Set the user's killer variable
     */
    killer = query("last_attacker");
    if (!killer)  killer = previous_object();

    /*
     * If the wizard has themself set to "immortal", then
     * they cannot die ... stop death call.
     */
    if (wizardp(this_object()) && query("immortal")) {
        message(MSG_COMBAT, "Your immortality protects you from certain death.\n",
              this_object());
        return;
    }

    /*
     * If the user is already dead ... stop death call.
     */
    if (link_data("dead"))  return;

    /*
     * Bail out of any parties they may be involved in.
     */
//  check_team();
    PARTY_D->check_party(this_object());

    /*
     * Announce the user's death
     */
    message(MSG_INFO, "You have died.\n", this_object());
    message(MSG_INFO, query("cap_name") + " has died.\n", environment(),
          this_player());

    init_attack();

    /*
     * Setup corpse with user's specifics
     */
    corpse = clone_object("/obj/corpse");

    corpse->set_name(query("cap_name"));

    i = query("mass");
    if (i>0) corpse->set("mass", i);

    i = query("bulk");
    if (i>0) corpse ->set("bulk", i);

    i = query("capacity");
    if (i>0) corpse ->set ("capacity", i);

    i = query("volume");
    if (i>0) corpse ->set("volume", i);

    corpse->move(environment(this_object()));

    stuff = all_inventory(this_object());

    for (i = sizeof(stuff); i--; )
        if (!stuff[i]->query("prevent_drop") && !stuff[i]->query_auto_load())
            stuff[i]->move(corpse);

    wealth = query("wealth");
    if (wealth) {
        names=keys(wealth);
        for (i = sizeof(wealth); i--; ) {
            coins = clone_object(COINS);
            coins->set_type(names[i]);
            coins->set_number(wealth[names[i]]);
            totcoins = totcoins + wealth[names[i]];
            if (coins)
                coins->move(corpse);
        }
    }
    set ("wealth", ([ ]));

    res = query("capacity");
    set ("capacity", res + totcoins);

    if (killer)  name = (string)killer->query("cap_name");

#ifdef KILLS
    if (killer)
        log_file(KILLS, CAP_NAME + " was killed by " +
              (name ? name + " " : "") + "(" + file_name(killer) +
              ") [" + extract(ctime(time()), 4, 15) + "]\n");
    else
        log_file(KILLS, CAP_NAME + " was killed by something [" +
              extract(ctime(time()), 4, 15) + "]\n");
#endif

    /*
     * Switch user to ghost body
     */
    ghost = create_ghost();

    if (!ghost)  return;

    message(MSG_INFO,
          "\nYou have a strange feeling.\n"
          "You can see your own lifeless body from above.\n\n",
          ghost);

    if (killer) {
        ghost->set("killer_ob", killer);
        ghost->set("killer_name", (string)killer->query("name"));
    }

    /*
     * Use a call_out to make sure all the above calls have
     * completed their required processing (so we don't lose
     * this_object() before everything is done).
     */
    call_out("remove", 0);
}

string query_short() {
    if (query("name") == "noname")  return "Noname";

    if (!interactive(this_object()))
        return (query("title") + " [disconnected]");

    if (query("inactive"))
        return query("cap_name") + " is presently inactive";
    else if (this_player() && attackers && sizeof(attackers) &&
          environment(this_player()) == environment(this_object()))
        return query("cap_name") + " is attacking " +
              capitalize((string)attackers[0]->query("name")) + ".";
    else if (query_idle(this_object())>300) return query("title")+" (idle)";
        return query("title");
}

string query_title() {
    string str;

    if (!(str = getenv( "TITLE" )))
        str = query("cap_name");
    else if ( !sscanf(str, "%*s$N%*s") )
        str = query("cap_name") + " " + str;
    else
        str = replace_string( str, "$N", ""+ query("cap_name") );
    return str;
}

/*
 * not in use anymore?
 */
nomask void catch_msg(object source, string *msg) {
    int i, s;

    for (i = 0, s = sizeof(msg); i < s; i++)
        receive(msg[i]);
}

void hide(int i) {
    set_hide(i);
}

/*
 * support for debugging an error that a user encounters during this login
 */
private static mapping last_error;

void set_error(mapping m) {
    if (previous_object() != master())
        return;
    last_error = m;
}

mapping query_error() {
    if (file_name(previous_object()) == CMD_DBXFRAME ||
          file_name(previous_object()) == CMD_DBXWHERE)
        return last_error;
    else
        message(MSG_SYSTEM, "No permission to query error!", this_player());
}

/*
 * this driver apply is called when the user's environment is being
 * destructed; move the player to a safe place, or end up in limbo
 */
int move_or_destruct(object ob) {
    object old_env = environment();


    if (origin() != ORIGIN_DRIVER)  return 0; // I guess 0 Leto
    if (!ob && old_env != find_object(VOID))
        ob = find_object(VOID);
    if (!ob && old_env != find_object(HALL))
        ob = find_object_or_load(HALL);
    if (!ob) {
        /*
         * This is bad.  Try to save them anyway.
         */
        ob = clone_object("/std/room");
        if (!ob) {
            /*
             * we did our best ...
             */
            return 1;
        }
        ob->set_short("A Temporary room");
        ob->set_long("Something really nasty happened.\n");
        ob->set("light",1); // Nice to actually see the short and long;)
    }
    move(ob);
    if (environment() == old_env) {
        /*
         * we HAVE to move or we get dested.  Override weight checks etc
         */
        move_object(ob); // Leto (no longer need to pass this_object as arg)
    }
    return 0;
}

/* EOF */