/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
new object $body: $located_location;

var $body actions = 0;
var $body available_body_parts = 0;
var $body body_parts = #[];
var $body interaction = 0;
var $body remote_command_cache = 0;
var $body wearing = [];
var $described prose = [];
var $has_commands local = \
	#[["wh?isper", [["wh?isper", "* to *", "wh?isper <any> to <any>", 'whisper_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]],\
  ["say",\
    [["say", "*", "say <any>", 'say_cmd, #[[1, ['any, []]]]]]],\
  ["to",\
    [["to", "* say *", "to <any> say <any>", 'to_say_cmd, #[[1, ['any, []]], [3, ['any, []]]]]]],\
  ["emote",\
    [["emote", "*", "emote <any>", 'emote_cmd, #[[1, ['any, []]]]]]],\
  ["quote",\
    [["quote", "*", "quote <any>", 'quote_cmd, #[[1, ['any, []]]]]]],\
  ["spoof",\
    [["spoof", "*", "spoof <any>", 'spoof_cmd, #[[1, ['any, []]]]]]],\
  ["pose",\
    [["pose", "*", "pose <any>", 'spoof_cmd, #[[1, ['any, []]]]]]],\
  ["think",\
    [["think", "*", "think <any>", 'think_cmd, #[[1, ['any, []]]]]]],\
  ["wear",\
    [["wear", "*", "wear <any>", 'wear_cmd, #[[1, ['any, []]]]]]],\
  ["remove|shed",\
    [["remove|shed", "*", "remove|shed <any>", 'remove_cmd, #[[1, ['any, []]]]]]],\
  ["@a?ction",\
    [["@a?ction", "*", "@a?ction <any>", 'action_cmd, #[[1, ['any, []]]]]]]];
var $has_commands shortcuts = #[["|*", ['quote_cmd, ["quote ", 1]]], ["\"*", ['say_cmd, ["say ", 1]]], ["%*", ['think_cmd, ["think ", 1]]], ["!*", ['spoof_cmd, ["spoof ", 1]]], [",*,*", ['esay_cmd, ["esay ", 1, " with ", 2]]], [":*", ['emote_cmd, ["emote ", 1]]], [".*", ['pose_cmd, ["pose ", 1]]], ["''*", ['to_say_cmd, ["to ", "", " say ", 1]]], ["'* *", ['to_say_cmd, ["to ", 1, " say ", 2]]]];
var $has_name name = ['uniq, "Generic Body", "the Generic Body"];
var $located location = $void;
var $located obvious = 1;
var $location contents = [];
var $root created_on = 796268969;
var $root fertile = 1;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core, 'command_cache];
var $root inited = 1;
var $root managed = [$body];
var $root manager = $body;
var $thing gender = $gender_neuter;

public method ._tell() {
    arg @args;
    var m;
    
    m = .manager();
    if ((m != this()) && ((m.location()) != (.location())))
        (| m.tell(@args.prefix(("<" + (.name())) + "> ")) |);
};

protected method .action_cmd() {
    arg cmdstr, cmd, string;
    var a, data, parse, out;
    
    (> .perms(caller(), 'command) <);
    if (!string) {
        out = [];
        if (actions)
            out = map a in (actions) to (pad(a[1], 25) + (a[2]));
        if ((data = ((.location()).prose()).get_var('details)))
            out += map a in (data) to ((pad(a[1], 25) + "look detail ") + (a[1]));
        if (!out)
            return "No actions registered.";
        return [strfmt("%25sCommand", "What"), strfmt("%25s-------", "----"), @out, "---"];
    }
    if (actions) {
        for a in (actions) {
            if ((a[1]).match_begin(string)) {
                data = a;
                break;
            }
        }
        if (data) {
            parse = $command_parser.parse(this(), data[2], $null_parser);
            (> .handle_parser_result(@parse) <);
            return;
        }
    }
    catch ~nodetail
        return (.location()).get_detail(string);
    .ptell(("No '" + string) + "' action registered.", #[['type, 'error]]);
};

public method .actions() {
    return actions;
};

protected method .add_interaction() {
    arg key, value;
    
    if (!interaction)
        interaction = #[];
    if (type(value) != 'list)
        value = [value];
    value = [time(), value];
    interaction = interaction.add(key, value);
};

public method .available_body_parts() {
    return available_body_parts || ['head, 'rleg, 'lleg, 'rarm, 'larm, 'torso];
};

public method .body_parts() {
    return body_parts;
};

public method .description() {
    arg flags;
    var ctext, what, w, o;
    
    o = [];
    ctext = (> pass(flags) <);
    if ((w = .wearing()))
        o += [((((.gender()).pronoun('psc)) + " is wearing ") + ((w.mmap('name)).to_english())) + "."];
    
    //   else
    //       o += [.gender().pronoun('psc) + " is naked, baring it all to the world."];
    ctext += [$ctext_frob.new_with(o)];
    return ctext;
};

public method .directed_tell() {
    arg what, type;
    
    ._tell(what);
    .send_event('social, sender(), type, what);
};

protected method .emote_cmd() {
    arg cmdstr, com, what;
    
    (> .perms(caller(), $user, $body) <);
    if (what && ((what[1]) == ":"))
        (.location()).announce((.name()) + (what.subrange(2)));
    else
        (.location()).announce(((.name()) + " ") + what);
};

public method .environment() {
    return pass() + (wearing || []);
};

protected method .esay_cmd() {
    arg cmdstr, cmd, how, prep, what;
    
    (> .perms(caller(), $user, $body) <);
    (.location()).announce((((((.name()) + " ") + (how.trim())) + ", \"") + (what.trim())) + "\"");
};

public method .event_notify() {
    arg event, origin, @args;
    
    if (caller() != $event_handler)
        throw(~perm, caller() + " is not $event_handler.");
    if (event == 'realm_announce)
        .tell_realm_announce(origin, args[1]);
};

protected method .handle_parser_result() {
    arg action, @more;
    var r, c;
    
    switch (action) {
        case 'error:
            ._tell(more[1]);
        case 'match, 'command:
            r = (> (more[1]).(more[2])(@more.subrange(3)) <);
            if (type(r) in ['list, 'frob, 'string])
                .ptell(r, #[['type, 'parser], ['command, more[2]]]);
        case 'failed:
            for c in (($place_lib.coordinate_shortcuts()).keys()) {
                if (action.match_template(c)) {
                    .tell(("There is no exit " + action) + " here.");
                    r = 1;
                }
            }
            if (!r)
                .tell(("I don't understand " + (action.chop((.actionlen()) - 22))) + ".");
        case 'ok:
            // do nothing, probably a null command
        default:
            ._tell("Unusual response from the parser: " + toliteral(more));
    }
};

public method .namef() {
    arg type;
    var str;
    
    switch (type) {
        case 'doing, 'nactivity, 'activity, 'titled:
            return .name();
        default:
            return (> pass(type) <);
    }
};

protected method .parse_interaction_reference() {
    arg targets, what, @userdb;
    var recip, target, msg, out;
    
    [(userdb ?= 0)] = userdb;
    targets = (targets && (targets.explode_list())) || [];
    if (!targets) {
        if (!(targets = (| interaction['objs] |)))
            throw(~stop, ("You must direct your " + what) + " to a target.");
        out = targets[2];
    } else {
        out = [];
        for recip in (targets) {
            target = 0;
            if (userdb)
                target = (| $user_db.match_begin(recip) |);
            if (!target) {
                catch ~objnf
                    target = (> .match_environment(recip) <);
                with
                    throw(~stop, (traceback()[1])[2]);
            }
            if (!(target.is($body)))
                throw(~stop, (target.namef('ref)) + " is not a valid recipient.");
            out = setadd(out, target);
        }
    }
    return out;
};

protected method .pose_cmd() {
    arg cmdstr, cmd, args;
    var action, targs, m, word, tail, i, str1, str2, str3, line, objs;
    
    (> .perms(caller(), $user, $body) <);
    if (!args) {
        (.location()).announce(.name());
        return;
    }
    action = args.word(1);
    if (" " in args) {
        args = substr(args, (" " in args) + 1);
        if ("and" in args) {
            targs = args.explode_english_list();
            word = targs.last();
            targs = delete(targs, listlen(targs));
            targs = map m in (targs) to ((| .match_environment(m) |) || m);
            if ((m = (| .match_environment(word) |))) {
                args = "";
                targs += [m];
            } else {
                args = substr(word, " " in word);
                m = word.word(1);
                targs += [(| .match_environment(m) |) || m];
            }
        } else {
            m = " " + (args.word(1));
            targs = [(| .match_environment(m) |) || m];
            args = (| substr(args, (" " in args) + 1) |) || "";
        }
    } else {
        .tell("You " + action);
        (.location()).announce((((.name()) + " ") + action) + "s", this());
        return;
    }
    
    // convert to ctext some day
    if (args) {
        str1 = strsed(args, " +my($| |\.)", " your%1");
        str1 = strsed(args, " +me($| |\.)", " yourself%1");
        word = (" " + ((.gender()).pronoun('pp))) + "%1";
        str2 = strsed(args, " +my($| |\.)", word);
        word = (" " + ((.gender()).pronoun('pr))) + "%1";
        str2 = strsed(args, " +me($| |\.)", word);
        str3 = str2;
    }
    objs = map m in (targs) to (((type(m) == 'objnum) && (m.name())) || m).to_english();
    .tell(((("You " + action) + " ") + objs) + args);
    
    // I'll do something better later
    for m in (targs) {
        line = (((.name()) + " ") + action) + "s ";
        if (type(m) == 'objnum)
            m.tell((line + strsub(objs, m.name(), "you")) + args);
    }
    line += objs + args;
    objs = filter m in (targs) where (type(m) == 'objnum) + [this()];
    (.location()).announce(line, @objs);
};

public method .ptell() {
    arg what, flags;
    
    .tell(what);
};

protected method .quote_cmd() {
    arg cmdstr, cmd, what;
    
    (.location()).announce(((.name()) + " | ") + what);
};

public method .register_action() {
    arg name, link;
    
    actions = dict_add(actions || #[], name, link);
};

protected method .remove_cmd() {
    arg cmd, cmdstr, what;
    
    (> .perms(caller(), $user, $body) <);
    what = (> .match_env_nice(what) <);
    if (!(what.is($wearable_frob)))
        return ("You are not wearing " + (what.name())) + ".";
    what = (> what.shed() <);
    return "You remove " + (what.name());
};

public method .reset_actions() {
    (| clear_var('actions) |);
};

protected method .say_cmd() {
    arg cmdstr, cmd, what;
    var type, how, idx;
    
    (> .perms(caller(), $user, $body) <);
    if (what)
        how = $code_lib.punctuation_type(what);
    else
        how = "say";
    (.location()).announce((((((.name()) + " ") + how) + "s, \"") + what) + "\"");
};

public method .set_body_part() {
    arg part, frob, param;
    
    if (sender().has_ancestor($wearable_frob))
        throw(~perm, "Sender must be $wearable_frob.");
    body_parts = body_parts.add(frob.new_with(part, param));
};

public method .shed() {
    arg what;
    
    if (caller() != $wearable_frob)
        throw(~wear, "You can only wear descendants of $wearable_frob.");
    wearing = setremove(wearing, what);
};

protected method .spoof_cmd() {
    arg cmdstr, cmd, what;
    var name;
    
    (> .perms(caller(), $user, $body) <);
    name = .name();
    if (!(((name + " ") in what) || ((" " + name) in what)))
        what = (what + "     -- ") + name;
    (.location()).announce(what);
};

public method .tell() {
    arg @args;
    
};

public method .tell_realm_announce() {
    arg realm, message;
    var loc, realm, propagator, prop_list, len, i;
    
    pause();
    loc = .location();
    if (!(propagator = loc.will_propagate()))
        return;
    prop_list = [propagator];
    realm = (.location()).realm();
    while (realm != $realm) {
        if (realm == realm) {
            len = prop_list.length();
            for i in [1 .. len] {
                message = message.propagate(prop_list[(len - i) + 1]);
                if (!message)
                    return;
            }
            .tell(message);
        }
        if (!(propagator = realm.will_propagate()))
            return;
        prop_list += [propagator];
        realm = (realm.parents())[1];
    }
};

public method .tell_traceback() {
    arg traceback;
    
    if ((.manager()) != this())
        (.manager()).tell_traceback(traceback);
};

protected method .think_cmd() {
    arg cmdstr, cmd, what;
    
    (> .perms(caller(), $user, $body) <);
    (.location()).announce((((.name()) + " . o O ( ") + what) + " )");
};

protected method .to_say_cmd() {
    arg cmdstr, com, who, prep, message;
    var targets, target, line;
    
    (> .perms(caller(), $user, $body) <);
    catch ~ambig
        targets = (> .parse_interaction_reference(who, "say") <);
    with
        return (traceback()[1])[2];
    .add_interaction('objs, targets);
    line = (((.name()) + " (to ") + ((targets.mmap('name)).to_english())) + ") ";
    if (message)
        line += ((message[1]) == ":") ? (message.subrange(2)) : (((($code_lib.punctuation_type(message)) + "s, \"") + message) + "\"");
    else
        line += "says, \"\"";
    for target in (targets)
        target.directed_tell(line, 'tosay);
    (.location()).announce(line, @targets);
};

public method .wear() {
    arg what;
    
    if (caller() != $wearable_frob)
        throw(~wear, "You can only wear descendants of $wearable_frob.");
    wearing = setadd(wearing || [], what);
};

protected method .wear_cmd() {
    arg cmd, cmdstr, what;
    
    (> .perms(caller(), $user, $body) <);
    what = (> .match_env_nice(what) <);
    if (!(what.is($wearable_frob)))
        return ("You cannot wear " + (what.name())) + ".";
    what = (> what.wear() <);
    return "You wear " + (what.name());
};

public method .wearing() {
    arg @args;
    var x, w;
    
    w = wearing || [];
    if (args && ('objects in args)) {
        for x in [1 .. w.length()]
            w = w.replace(x, class(w[x]));
    }
    return w;
};

protected method .whisper_cmd() {
    arg cmdstr, com, what, prep, who;
    var loc, targets, t, msg, recips;
    
    (> .perms(caller(), $user, $body) <);
    targets = (> .parse_interaction_reference(who, "whisper") <);
    .add_interaction('objs, targets);
    loc = .location();
    if (((targets.mmap('location)).compress()) != [loc]) {
        who = filter t in (targets) where ((t.location()) != loc);
        return ("You must be in the same place as " + (who.english_name_list())) + ", to whisper to them.";
    }
    msg = (((.name()) + " whispers, \"") + what) + "\"";
    for t in (targets)
        (| t.directed_tell(msg, 'whisper) |);
    recips = (targets.mmap('name)).to_english();
    loc.announce((((.name()) + " whispers to ") + recips) + ".", this(), @targets);
    .tell(((("You whisper, \"" + what) + "\" to ") + recips) + ".");
};

public method .will_move() {
    arg mover, place;
    
    // exits should always be able to pull "bodies" through them
    // this becomes sortof a big override returning, but ... *shrug*
    if (mover.is($exit))
        return;
    (> pass(mover, place) <);
};