/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
new object $builder: $player;

var $channel_ui active_channels = #[];
var $channel_ui channel_dict = #[];
var $command_aliases command_aliases = [];
var $described prose = [];
var $foundation defined_msgs = #[["teleport", #[['branches, ["source", "dest", "actor"]]]]];
var $foundation msgs = #[["teleport", #[["actor", <$ctext_frob, [["You teleport to ", <$generator, ["dest", [], [], 'gen_dest]>, "."], #[['this, $builder]]]>], ["source", <$ctext_frob, [[<$generator, ["actor", [], [], 'gen_actor]>, " teleports to ", <$generator, ["dest", [], [], 'gen_dest]>, "."], #[['this, $builder]]]>], ["dest", <$ctext_frob, [[<$generator, ["actor", [], [], 'gen_actor]>, " teleports here from ", <$generator, ["source", [], [], 'gen_source]>, "."], #[['this, $builder]]]>]]]];
var $has_commands local = \
	#[["@realm?s", [["@realm?s", "", "@realm?s", 'realms_cmd, #[]]]], ["@mv|@move", [["@mv|@move", "*", "@mv|@move <object:>", 'move_cmd, #[[1, ['object_opt, []]]]]]],\
  ["@child?ren|@kids",\
    [["@child?ren|@kids", "*", "@child?ren|@kids <object>", 'children_cmd, #[[1, ['object, []]]]]]],\
  ["@par?ents",\
    [["@par?ents", "*", "@par?ents <object>", 'parents_cmd, #[[1, ['object, []]]]]]],\
  ["@build",\
    [["@build", "*", "@build <any:-conf?igure>", 'build_cmd, #[[1, ['any_opt, ["conf?igure"]]]]]]],\
  ["@attach",\
    [["@attach", "* to *", "@attach <any> to <descendant of $place>", 'attach_cmd, #[[1, ['any, []]], [3, ['descendant, [$place]]]]]]],\
  ["@destroy",\
    [["@destroy", "*", "@destroy <list object>", 'destroy_cmd, #[[1, ['list, ['object, []]]]]]]],\
  ["@dig",\
    [["@dig", "*", "@dig <any>", 'dig_cmd, #[[1, ['any, []]]]]]],\
  ["@teleport|@go",\
    [["@teleport|@go", "*", "@teleport|@go <any>", 'teleport_cmd, #[[1, ['any, []]]]]]],\
  ["@def-msg|@def-message",\
    [["@def-msg|@def-message", "*", "@def-msg|@def-message <any>", 'define_msg_cmd, #[[1, ['any, []]]]]]],\
  ["@undef-msg|@undef-message",\
    [["@undef-msg|@undef-message", "*", "@undef-msg|@undef-message <any>", 'undefine_msg_cmd, #[[1, ['any, []]]]]]]];
var $has_name name = ['prop, "Generic Builder", "Generic Builder"];
var $located location = $body_cave;
var $located obvious = 1;
var $location contents = [];
var $mail_list last_letter = 0;
var $mail_list letters = #[];
var $mail_list letters_index = #[];
var $mail_list mail = [];
var $mail_list notify = [$builder];
var $mail_list readers = [];
var $mail_list senders = 1;
var $mail_ui current = #[['location, 0], ['list, $builder]];
var $mail_ui subscribed = #[[$builder, [791485891, 0]]];
var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'core, 'command_cache, 'variables];
var $root inited = 1;
var $root managed = [$builder];
var $root manager = $builder;
var $root quota = 75000;
var $root settings = #[["home", $body_cave]];
var $thing gender = $gender_neuter;
var $user connected_at = 0;
var $user connections = [];
var $user formatter = $plain_format;
var $user last_command_at = 0;
var $user modes = #[];
var $user parsers = [$command_parser];
var $user password = "*";
var $user task_connections = #[];

protected method .attach_cmd() {
    arg cmdstr, cmd, source, prep, dest;
    var exit;
    
    (> .perms(caller(), 'command) <);
    if (!source) {
        source = .location();
    } else {
        catch any
            source = (> .match_environment(source) <);
        with
            return (traceback()[1])[2];
        if (!(source.is($place)))
            return (source.namef('ref)) + " is not a descendant of $place.";
    }
    catch ~abort, ~skip
        exit = (> .build_query_exit(dest, source) <);
    with
        return "Aborted.";
    catch any {
        exit.attach(source, dest);
        exit.configure(#[['named_name, 1]]);
    } with {
        .tell("Ack, unable to attach exit because:");
        .tell("  " + ((traceback()[1])[2]));
        (| exit.destroy() |);
        return;
    }
    return ("Successfully attached exit " + (exit.name())) + ".";
};

public method .bug_fixed() {
    arg list, subj, msg;
    var err, mail;
    
    list = (> $mail_lib.match_mail_recipient(list) <);
    mail = $mail_message.new_mail();
    mail.set_subject(subj);
    mail.set_text(msg);
    (> mail.send(list) <);
};

protected method .build_attach_exit() {
    arg exit, source, dest;
    var line;
    
    catch any {
        exit.attach(source, dest);
    } with {
        .tell(("Unable to attach " + (exit.name())) + " because: ");
        .tell("  " + ((traceback()[1])[2]));
        line = .prompt("Continue building? ");
        if (line in ["no", "n"])
            throw(~abort, "Aborted");
    }
};

protected method .build_cleanup() {
    arg @what;
    var obj;
    
    (> .perms(caller(), definer()) <);
    for obj in (what) {
        if (valid(obj[1]) && (obj[2])) {
            .tell(("Destroying " + ((obj[1]).name())) + "...");
            (| (obj[1]).destroy() |);
        }
    }
};

protected method .build_cmd() {
    arg cmdstr, cmd, args;
    var dest, source, exits, set, str, i;
    
    (> .perms(caller(), 'command) <);
    str = (args[1]).join();
    source = .location();
    args = args[2];
    if (!(| source.will_attach('source) |))
        return "This room is not publicly extendable.";
    
    // Establish the objects
    catch any {
        dest = (> .build_get_location(str) <);
        if (!(| (dest[1]).will_attach('dest) |)) {
            .tell(((dest[1]).name()) + " will not allow you to attach to it!");
            .build_cleanup(dest);
            return "** Build Aborted **";
        }
        exits = (> .build_get_exits(source, dest[1]) <);
        (> .build_attach_exit(exits[1], source, dest[1]) <);
        (> .build_attach_exit(exits[2], dest[1], source) <);
    } with {
        if (dest)
            .build_cleanup(dest);
        if (exits) {
            if (exits[1])
                .build_cleanup([exits[1], 1]);
            if (exits[2])
                .build_cleanup([exits[2], 1]);
        }
        if (error() == ~abort)
            return "** Build Aborted **";
        return [(traceback()[1])[2], "** Build Aborted **"];
    }
    .tell(("Completed building to " + ((dest[1]).name())) + ".");
    if ((i = "conf?igure" in (args.slice(1)))) {
        if (!((args[i])[3]))
            return "Skipping post-configuration.";
    }
    .tell("post-configuration..");
    
    // Flesh them out      
    set = #[['named_name, 1]];
    if (dest[2])
        (> (dest[1]).configure(set) <);
    if (exits[1])
        (> (exits[1]).configure(set) <);
    if (exits[2])
        (> (exits[2]).configure(set) <);
    return "Completed post-configuration.";
};

protected method .build_generate_exit() {
    arg pname, source, dest;
    var parent, exit, name;
    
    name = pname[1];
    [parent, exit] = (> .build_parse_name(name[1], $exit) <);
    name = replace(name, 1, exit);
    pname = replace(pname, 1, name);
    exit = (| source.match_environment(exit) |);
    if (exit) {
        if (exit.is($exit))
            throw(~stop, (((name[1]) + " is already an exit from ") + (source.name())) + ".");
        if (exit.source())
            throw(~stop, (exit.namef('ref)) + " is alread linked.");
        return exit;
    }
    catch any
        exit = (> parent.new() <);
    with
        throw(~stop, "Unable to create exit: " + ((traceback()[1])[2]));
    return .build_set_name(exit, @pname);
};

protected method .build_get_exits() {
    arg source, dest;
    var eleave, earrive, opp, t;
    
    // Get the exits
    catch any {
        eleave = (> .build_query_exit(source, dest) <);
        if (eleave) {
            // try really hard to figure this out
            if (!(opp = $place_lib.opposite_direction(eleave.name()))) {
                for t in (eleave.name_templates()) {
                    t = strsed(t, "[^a-z]+", "", "g");
                    if ((opp = $place_lib.opposite_direction(eleave.name())))
                        break;
                }
            }
            if (opp) {
                opp = strsed(explode(opp, "|")[1], "[^a-z]+", "", "g");
                earrive = (> .build_query_exit(dest, source, opp) <);
            } else {
                earrive = (> .build_query_exit(dest, source) <);
            }
        }
    } with {
        if (eleave) {
            .build_cleanup([eleave, 1]);
            if (earrive)
                .build_cleanup([earrive, 1]);
        }
        rethrow(error());
    }
    return [eleave, earrive];
};

protected method .build_get_location() {
    arg name;
    var dest, m, realm, parent, str;
    
    (> .perms(sender()) <);
    .build_hint(1);
    if (name) {
        if ((m = match_template(name, "to *")))
            name = m[2];
        name = (| $code_lib.parse_name(name) |);
    }
    while (!name) {
        catch any {
            name = (> .build_get_name("Destination: ") <);
            str = (name[1])[1];
            if ((m = regexp(str, "^ *\$([a-z0-9_]+) *(.*)$"))) {
                name = replace(name, 1, replace(name[1], 1, m[2]));
                catch ~namenf, ~symbol {
                    parent = (> lookup(tosym(m[1])) <);
                } with {
                    .tell((traceback()[1])[1]);
                    continue;
                }
                if (!(parent.is($place))) {
                    .tell(("Parent object " + (parent.namef('ref))) + " is not a place!");
                    continue;
                } else if ((!(parent.has_flag('fertile))) && (!(parent.is_writable_by(this())))) {
                    .tell(("Parent object " + (parent.namef('ref))) + " is not fertile!");
                    continue;
                }
            }
        } with {
            if (error() == ~skip) {
                .tell("You cannot skip this step!");
                continue;
            }
            rethrow(error());
        }
    }
    
    // first try to see if it already exists--only do this if they used
    // the direct $dbref too many conflicts otherwise.
    catch any {
        dest = (> $object_lib.to_dbref((name[1])[1]) <);
        (dest.is($place)) || throw(~place, (dest.namef('ref)) + " is not a place.");
        return [dest, 0];
    }
    
    // create it
    .build_hint(2);
    if (!parent)
        dest = (> ($place_lib.get_default('place)).spawn() <);
    else
        dest = (> parent.spawn() <);
    .build_set_name(dest, @name);
    .build_hint(4);
    catch any {
        realm = (> .build_query_realm(dest) <);
    } with {
        if (error() == ~skip)
            return dest;
        .build_cleanup([dest, 1]);
        rethrow(error());
    }
    
    // set the realm
    dest.set_setting_realm($place, "realm", realm);
    
    // okee, done
    return [dest, 1];
};

protected method .build_get_name() {
    arg prompt, @def;
    var name, out;
    
    .build_hint(3);
    while (!out) {
        name = (> .build_prompt(prompt, @def) <);
        if (!(out = (| $code_lib.parse_name(name) |)))
            .tell("Invalid name.");
    }
    return out;
};

protected method .build_hint() {
    arg n;
    
    (> .perms(sender()) <);
    if ((| .get_setting("experienced", $user) |))
        return;
    .tell($place_lib.build_hint(n));
};

protected method .build_parse_name() {
    arg str, class;
    var m, parent, what;
    
    if ((m = regexp(str, "^ *\$([a-z0-9_]+) *: *(.*)$"))) {
        parent = m[1];
        what = m[2];
        if (parent) {
            catch ~namenf, ~symbol
                parent = (> lookup(tosym(parent)) <);
            with
                throw(~stop, ("The object '$" + parent) + "' is not valid.");
            if (!(parent.is(class)))
                throw(~stop, ((("Parent object " + parent) + " is not ") + ((class.objname()).add_indefinite())) + "!");
        }
    } else {
        what = str;
    }
    if (!parent)
        parent = $place_lib.get_default(class.objname());
    return [parent, what];
};

protected method .build_prompt() {
    arg prompt, @def;
    var name, ans;
    
    [(def ?= "")] = def;
    while (!ans) {
        ans = .prompt(prompt);
        if (ans == "@skip")
            throw(~skip, "Skipped");
        if (ans == "@abort")
            throw(~abort, "Aborted");
        if (!ans) {
            if (!def)
                continue;
            ans = def;
        }
    }
    return ans;
};

protected method .build_query_exit() {
    arg source, dest, @args;
    var exit, line, name, def, from, str, m, parent;
    
    from = "from " + (source.name());
    line = (("Exit " + from) + " to ") + (dest.name());
    if (args) {
        def = args[1];
        line += (" [" + def) + "] ";
    } else {
        line += ": ";
    }
    while (!exit) {
        catch ~skip
            name = (> .build_get_name(line, def) <);
        with
            return 0;
        if (((name[1])[1]) == "@none")
            return 0;
        catch any
            exit = (> .build_generate_exit(name, source, dest) <);
        with
            .tell((traceback()[1])[2]);
    }
    return exit;
};

protected method .build_query_realm() {
    arg there;
    var line, realm, r, prompt;
    
    prompt = ((("What realm is " + (there.name())) + " in? [") + (((.location()).realm()).name())) + "] ";
    while (!realm) {
        line = (> .build_prompt(prompt, tostr((.location()).realm())) <);
        if (!line) {
            realm = (.location()).realm();
        } else if (line == "@realms") {
            r = ($place_lib.known_realms()).union($realm.descendants());
            .tell(["Realms:", ""] + ((realms.mmap('name)).prefix("    ")));
        } else {
            realm = $place_lib.match_realm(line);
            if (!realm) {
                .tell(("Unknown realm \"" + line) + "\", try @realms.");
            } else if (!(realm.is($realm))) {
                .tell((realm.namef('ref)) + " is not a realm, try @realms.");
                realm = 0;
            }
        }
    }
    return realm;
};

protected method .build_set_name() {
    arg obj, name, templates;
    var t, x;
    
    catch any {
        obj = obj.set_name(@name);
    } with {
        .tell("Unable to set name; " + ((traceback()[1])[2]));
        .tell("Setting name as " + (obj.objname()));
        obj = obj.set_name(tostr(obj.objname()));
    }
    for t in (templates)
        obj = obj.add_name_template(t);
    return obj;
};

protected method .children_cmd() {
    arg cmdstr, cmd, what;
    
    (> .perms(caller(), 'command) <);
    return [("Children of " + (what.namef('xref))) + ":"] + (._list_objects(what.children(), 'parents));
};

root method .core_builder() {
    // for now we dont core the bug system
    (| .del_command("@bug?s <any>", 'bug_cmd) |);
    (| .del_method('bug_cmd) |);
};

protected method .define_msg_cmd() {
    arg cmdstr, cmd, args;
    var what, opts, def, o, i, br, ref, compiler, eval, getter;
    
    (> .perms(caller(), 'command) <);
    [args, opts] = $parse_lib.opt(args, "b?ranches", "c?ompiler", "e?valuator", "g?etter");
    args = join(args);
    what = split(args, " *: *");
    if (listlen(what) != 2)
        return "Invalid message reference " + args;
    [def, what] = what;
    what = what.trim();
    if (match_regexp(what, "[=.]"))
        return ("You cannot set the default with " + cmd) + ".";
    def = (> .match_env_nice(def) <);
    if (!(def.is_writable_by(this())))
        return ("You cannot define messages on " + (def.namef('ref))) + ".";
    if (type(def) == 'frob)
        return "You cannot define messages on a frob.";
    catch any {
        (> def.define_msg(what) <);
        ref = (def + ":") + what;
        .tell(("Message " + ref) + " defined.");
    } with {
        return (traceback()[1])[2];
    }
    o = opts.slice(1);
    if ((i = "b?ranches" in o)) {
        br = (((opts[i])[4]).trim()).split(" *, *");
        catch any {
            def.set_msg_attr(what, 'branches, br);
            .tell("Message branches defined:");
            for i in (br)
                .tell((("  " + ref) + ".") + i);
        } with {
            .tell((traceback()[1])[2]);
        }
    }
    if ((i = "e?valuator" in o)) {
        eval = (opts[i])[4];
        if (!eval) {
            .tell("No evaluator specified with +evaluator=??");
        } else {
            eval = (> .match_env_nice(eval) <);
            if (!(eval.is($evaluator)))
                return "Invalid evaluator object: " + evaluator;
            catch any {
                def.set_msg_attr(what, 'evaluator, eval);
                .tell((("Set evaluator for " + ref) + " to ") + eval);
            } with {
                .tell((traceback()[1])[2]);
            }
        }
    }
    if ((i = "c?ompiler" in o)) {
        compiler = (opts[i])[4];
        if (!compiler) {
            .tell("No compiler specified with +compiler=??");
        } else {
            compiler = (> .match_env_nice(compiler) <);
            if (!(compiler.is($compiler)))
                .tell("Invalid compiler object: " + compiler);
            catch any {
                def.set_msg_attr(what, 'compiler, compiler);
                .tell((("Set compiler for " + ref) + " to ") + compiler);
            } with {
                .tell((traceback()[1])[2]);
            }
        }
    }
    if ((i = "g?etter" in o)) {
        getter = (opts[i])[4];
        if (!getter) {
            .tell("No getter specified with +getter=??");
        } else {
            getter = (> tosym(getter) <);
            catch any {
                def.set_msg_attr(what, 'getter, getter);
                .tell((("Set getter for " + ref) + " to ") + getter);
            } with {
                .tell((traceback()[1])[2]);
            }
        }
    }
};

protected method .destroy_cmd() {
    arg cmdstr, cmd, objs;
    var name, yes, obj;
    
    (> .perms(caller(), 'command) <);
    cmdstr = cmdstr.trim();
    if (((cmdstr.trim()) in ["@dest", "@destroy"]) && ((objs.length()) > 0)) {
        yes = .prompt(("Destroy '" + ((objs[1]).namef('xref))) + "'? ");
        if (!(yes in ["y", "yes"]))
            return "Ok, aborting..";
    }
    for obj in (objs) {
        catch any {
            name = obj.namef('xref);
            (> obj.destroy() <);
            if (valid(obj))
                .tell(("Unable to destroy " + name) + " immediately.");
            else
                .tell(("Sucessfully destroyed " + name) + ".");
        } with {
            return (traceback()[1])[2];
        }
    }
};

public method .dig_cmd() {
    arg cmdstr, cmd, args;
    var m, syn, name, dest, dnew, parent, name, lname, aname, leave, arrive, loc;
    
    (> .perms(caller(), 'command) <);
    syn = ["Syntax: `@dig <new place>` OR", "        `@dig <leaving exit>[;<arriving exit>] to <objref or new place>`", "You may also want to try @build."];
    if (!args)
        return syn;
    loc = .location();
    
    // are they essentially just @spawn'ing?
    if (!(m = match_template(args, "* to *"))) {
        if ((args[1]) == "$")
            return syn;
        if (!(name = (> $code_lib.parse_name(args) <)))
            return "Invalid name.";
        [parent, dest] = (> .build_parse_name((name[1])[1], $place) <);
        name = replace(name, 1, replace(name[1], 1, dest));
        catch any
            dest = (> parent.spawn() <);
        with
            return (traceback()[1])[2];
        .build_set_name(dest, @name);
        dest.set_realm(loc.realm());
        return ("New place created " + (dest.namef('ref))) + ".";
    }
    
    // can we extend from this room?
    if (!(| loc.will_attach('source) |))
        return "This room is not publicly extendable.";
    
    // do some basic parsing before we needlessly create anything
    [args, m, name] = m;
    [lname, (aname ?= "")] = split(args, " *; *");
    if ((!lname) || (!name))
        return syn;
    
    // create or parse the destination first
    if ((name[1]) == "$") {
        if (!(dest = (| lookup(tosym(substr(name, 2))) |)))
            return "Invalid object name.";
        if (!(dest.is($place)))
            return ("Destination " + (dest.namef('ref))) + " is not a place.";
    } else {
        if (!(name = (> $code_lib.parse_name(name) <)))
            return "Invalid name.";
        [parent, dest] = (> .build_parse_name((name[1])[1], $place) <);
        name = replace(name, 1, replace(name[1], 1, dest));
        catch any
            dest = (> parent.spawn() <);
        with
            return (traceback()[1])[2];
        .build_set_name(dest, @name);
        dest.set_realm(loc.realm());
        .tell(("Created new place " + (dest.namef('ref))) + ".");
        dnew = 1;
    }
    
    // now parse and attach the exits
    // first the leaving exit..
    if (!(name = (| $code_lib.parse_name(lname) |)))
        return ("Invalid exit name '" + lname) + "'";
    catch ~stop {
        leave = (> .build_generate_exit(name, loc, dest) <);
        (> .build_attach_exit(leave, loc, dest) <);
        .tell((("Attached exit " + (leave.name())) + " from ") + (loc.name()));
    } with {
        .tell((traceback()[1])[2]);
        return .build_cleanup([dest, dnew]);
    }
    
    // now the (optional) arrival exit
    if (aname) {
        if (!(name = (| $code_lib.parse_name(aname) |)))
            return ("Invalid exit name '" + cname) + "'";
        catch ~stop {
            arrive = (> .build_generate_exit(name, dest, loc) <);
            (> .build_attach_exit(arrive, dest, loc) <);
            .tell((("Attached exit " + (arrive.name())) + " from ") + (dest.name()));
        } with {
            .tell((traceback()[1])[2]);
    
            // thirty second leeway guess to if we created it or not
            // ... what I do to keep from wrapping
            dnew = dnew.or(((leave.created_on()) > (time() - 30)) ? 2 : 0);
            return .build_cleanup([dest, dnew.and(1)], [leave, dnew.and(2)]);
        }
    }
};

public method .format_messages() {
    arg target, @what;
    var messages, out, b, branches, m;
    
    messages = target.all_msgs();
    out = [];
    for m in ((messages.keys()).sort()) {
        branches = messages[m];
        if (branches.contains("general")) {
            out += [("  " + m) + " = "].affix((branches["general"]).uncompile());
            branches = dict_del(branches, "general");
        }
        for b in ((branches.keys()).sort())
            out += [((("  " + m) + ".") + b) + " = "].affix((branches[b]).uncompile());
    }
    return out;
};

protected method .move_cmd() {
    arg cmdstr, cmd, args;
    var what, dest, loc, fromto;
    
    (> .perms(caller(), 'command) <);
    what = args[1];
    args = args[2];
    if (args && ((args[1]) == "to"))
        args = delete(args, 1);
    if (!args)
        return ("You have to move " + (what.namef('ref))) + " somewhere.";
    dest = (> .match_env_nice(args.join()) <);
    catch any {
        loc = what.location();
        fromto = ((" from " + ((| loc.name() |) || $nowhere)) + " to ") + (dest.name());
        (| what.tell(((("You are suddenly yanked" + fromto) + " by ") + (.name())) + ".") |);
        (> what.move_to(dest) <);
    
        // hook into messages eventually
        (| loc.announce((what.name()) + " suddenly disappears.") |);
        (| dest.announce((what.name()) + " suddenly appears.", what) |);
        return (("You move " + (what.name())) + fromto) + ".";
    } with {
        (| what.tell(("You feel as if " + (.name())) + " was incapable of moving you, you did not go anywhere.") |);
        return (traceback()[1])[2];
    }
};

protected method .parents_cmd() {
    arg cmdstr, cmd, what;
    
    (> .perms(caller(), 'command) <);
    return [((("Parent" + ((((.parents()).length()) > 1) ? "s" : "")) + " of ") + (what.namef('ref))) + ":"] + (._list_objects(what.parents(), 'children));
};

protected method .realms_cmd() {
    arg cmdstr, cmd;
    var x, realms;
    
    (> .perms(caller(), 'command) <);
    realms = ($place_lib.known_realms()).union($realm.descendants());
    .tell(["Realms:", ""] + ((realms.mmap('name)).prefix("    ")));
};

public method .scan_bugs() {
    arg buglist;
    var bug, pos, choice, str, junk;
    
    .tell("");
    
    // No bugs? Return:
    if (!buglist) {
        .tell("There are no bugs in this category.");
        return;
    }
    pos = 1;
    while (choice != "X") {
        bug = buglist[pos];
    
        // Get the current bug obref.
        // Display the bug:
        .tell("");
        .menubar((("Report " + tostr(pos)) + " of ") + tostr(buglist.length()));
    
        // if (.preference('pagination) == 'on)
        //    .tell_paginated(bug.format().prefix(" "));
        // else
        .tell((bug.format()).prefix(" "));
        .menubar();
        str = "[RETURN] Next, [P]rev, [X] Exit, [C]laim, [F]ix, [D]ismiss, [A]ppend, [#]";
    
        // Allow admins to archive:
        //if (.is($admin))
        //    str = str + "[A]rchive";
        choice = .prompt(str + ": ");
        .tell("");
        if (choice == "@abort")
            throw(~aborted, "Aborted!");
        if (choice && ((choice[1]) == "~")) {
            .menubar("Execute Command");
            .execute_line(choice.subrange(2));
            .menubar();
            choice = 'do_nothing;
        }
        switch (choice) {
            case 'do_nothing:
                // Do nothing.
            case "A":
                // Append Text:
                .tell("Append text to report...");
                junk = .read();
                if (junk && (junk != 'aborted)) {
                    bug.set_text((bug.text()) + ["----------------------------------------------------------------------------", ("Addendum by " + (.name())) + ":", @junk]);
                    .tell("Text appended.");
                } else {
                    .tell("Aborted.");
                }
    
                // case "A":
                //     // Archive
                //     if (.is($admin)) {
                //         if (bug.fixed()) {
                //             bug.chparents($archived_bugs);
                //             .tell("Bug archived.");
                //         } else {
                //             .tell("You may only archive repaired bugs.");
                //             choice = 'no_scroll;
                //        }
                //     }
            case "D":
                // Dismiss
                .tell("Enter a description of the dismissal:");
                str = .read();
                if ((str == 'aborted) || (!str)) {
                    .tell("Aborted.");
                } else {
                    bug.set_text((bug.text()) + [("Addendum by " + (.name())) + ":", "", @str]);
                    $bug_handler.dismiss(bug);
    
                    // bug.chparents($dismissed_bugs);
                    .tell("Bug dismissed.");
                }
            case "P":
                // Previous
                choice = 'no_scroll;
                if (pos > 1)
                    pos--;
                else
                    .tell("You are at the beginning of the list.");
            case "F":
                // Fix:
                if ((bug.fixed()) && ((bug.owner()) != this())) {
                    .tell("This bug has already been marked as being fixed. If there are further problems, please submit another report.");
                } else {
                    if (bug.fixed()) {
                        .tell("This bug has already been repaired.");
                        continue;
                    }
                    .tell("You may directly [P]aste the contents of the bug report to the player bug");
                    str = .prompt("forum, [C]ompose a message to sent there, [RET] to do nothing, or e[X]it: ");
                    if (str == "P") {
                        .bug_fixed("*bug", (.name()) + " has repaired the following bug:", [@$mail_lib.indent_reply(bug.forum_format())]);
                    } else if (str == "C") {
                        junk = .read();
                        if (junk && (type(junk) == 'list)) {
                            //    .bug_fixed("*bugfix",  @junk);
                            .bug_fixed("*bugs", (.name()) + " has repaired the following bug:", [@$mail_lib.indent_reply(bug.forum_format())] + [@junk]);
                        }
                        bug.set_fixed(time());
                        .tell("You have marked this bug as being fixed.");
                    } else if (str == "x") {
                        bug.set_fixed(0);
                        .tell("Aborted.");
                    } else {
                        .bug_fixed("*bugs", (.name()) + " has repaired the following bug:", [@$mail_lib.indent_reply(bug.forum_format())]);
                        bug.set_fixed(time());
                        .tell("You have marked this bug as being fixed.");
                    }
                }
            case "C":
                // Claim:
                if (bug.owner()) {
                    if ((bug.owner()) != this()) {
                        .tell(((bug.owner()).name()) + " must relinquish ownership of this bug before you can claim it.");
                    } else {
                        bug.set_owner(0);
                        .tell("You have relinquished ownership of this bug.");
                    }
                } else {
                    bug.set_owner(this());
                    .tell("This bug report is now under your ownership.");
                }
            case "":
                // Next bug (works differently than scroll-to-next):
                if (pos < (buglist.length()))
                    pos++;
                else
                    .tell("You have reached the end of the list. Select 'x' to exit.");
            case "X":
                // Handler later.
            default:
                if (toint(choice)) {
                    choice = toint(choice);
                    if ((choice > 0) && (choice <= (buglist.length()))) {
                        pos = choice;
                        choice = 'no_scroll;
                    } else {
                        .tell("Number out of range.");
                    }
                } else {
                    .tell("Invalid selection.");
                }
        }
    
        // Scroll to the next bug unless we got CR, -, X:
        if (!(choice in ["", "-", "X", 'no_scroll])) {
            if (pos < (buglist.length())) {
                pos++;
            } else {
                .tell("You have reached the end of the list. Exiting.");
                return;
            }
        }
    }
};

protected method .teleport() {
    arg dest;
    var m, source, vars;
    
    source = .location();
    catch any {
        .move_to(dest);
    } with {
        .tell((traceback()[1])[2]);
        return 0;
    }
    vars = #[["$actor", this()], ["actor", .name()], ["$source", source], ["source", source.name()], ["$dest", dest], ["dest", dest.name()]];
    m = .eval_message("teleport", $builder, vars);
    dest.announce(m);
    source.announce(m);
};

public method .teleport_cmd() {
    arg cmdstr, com, dest;
    var loc, p;
    
    (> .perms(caller(), 'command) <);
    if (!dest) {
        .tell("Specify a destination.");
        return;
    }
    if (dest == "home")
        loc = .home();
    else
        loc = (| .match_environment(dest) |);
    
    // if we have still not found a location...
    if (!loc) {
        catch any {
            loc = $place_db.search(dest);
        } with {
            switch (error()) {
                case ~ambig:
                    .tell("Several rooms match that name: " + ((((traceback()[1])[3]).mmap('namef)).to_english()));
                case ~namenf:
                    .tell(("Unable to find place \"" + dest) + "\".");
                    return;
                default:
                    return (traceback()[1])[2];
            }
        }
    }
    if (!loc) {
        .tell(("Unable to find place \"" + dest) + "\".");
        return;
    }
    if (loc == (.location())) {
        .tell("You are already there!");
        return;
    }
    if (!(.teleport(loc)))
        .tell("Sorry.");
};

protected method .undefine_msg_cmd() {
    arg cmdstr, cmd, args;
    var what, opts, def;
    
    (> .perms(caller(), 'command) <);
    
    // it niggles the string enough to 'clean' up little mistakes
    [args, opts] = $parse_lib.opt(args, "b?ranches", "c?ompiler");
    args = join(args);
    what = split(args, " *: *");
    if (listlen(what) != 2)
        return "Invalid message reference " + args;
    [def, what] = what;
    what = what.trim();
    def = (> .match_env_nice(def) <);
    if (!(def.is_writable_by(this())))
        return ("You cannot define messages on " + (def.namef('ref))) + ".";
    catch any {
        (> def.undefine_msg(what) <);
        .tell(((("Message " + def) + ":") + what) + " undefined.");
    } with {
        return (traceback()[1])[2];
    }
};