Generic room

    This object determines the behavior of rooms.

    Informational public methods:

	dark()				True if room is dark
	link_ok()			True if room is link_ok
	exits()				Get exits

    Public methods to perform an action:

	tell_contents(s)		Tell contents a message
	verb_changed_inside()		Notification

    Owner methods:

	set_dark(val)			Set whether room is dark
	set_link_ok(val)		Set whether room is link_ok
	add_exit(obj)			Add an exit

    Commands:

	look_cmd()			Look at room
	say_cmd(s)			Say something to the room
	pose_cmd(s)			Emote something to the room

    Exit protocol methods:

	will_attach(actor)		Indicates impending exit attach
	did_attach(actor)		Indicates completed exit attach
	will_link(actor)		Indicates impending exit link
	did_link(actor)			Indicates completed exit link

    Private methods:

	invalidate_verb_caches()	Invalidate caches of contents

parent container
parent commands
object room

var room inited 1
var room exits []
var room dark 0
var room link_ok 0

eval
    .initialize();
    .set_name("Generic room");
    .add_command("l?ook", 'template, 'look_cmd);
    .add_command("\"*", 'pattern, 'say_cmd);
    .add_command(":*", 'pattern, 'pose_cmd);
    .set_fertile(1);
.

method init
    arg ancestors;

    (> pass(ancestors) <);
    if (definer() in ancestors) {
	exits = [];
	dark = 0;
	link_ok = 0;
    }
.

method dark
    return dark;
.

method set_dark
    arg val;

    if (!.is_owned_by(sender()))
	throw(~perm, "Sender not an owner.");
    dark = val ? 1 | 0;
.

method link_ok
    return link_ok;
.

method set_link_ok
    arg val;

    if (!.is_owned_by(sender()))
	throw(~perm, "Sender not an owner.");
    link_ok = val ? 1 | 0;
.

method exits
    return exits;
.

method add_exit
    if (!caller().is_agent('exit))
	throw(~perm, "Sender is not an agent of exit protocol.");
    exits = setadd(exits, sender());
.

method remove_exit
    if (!caller().is_agent('exit))
	throw(~perm, "Sender is not an agent of exit protocol.");
    exits = setremove(exits, sender());
.

method full_description
    arg [args];
    var obj, l;

    l = [];
    if (!dark) {
	for obj in (.contents()) {
	    if (!(obj in args))
		l = l + ["  " + obj.name()];
	}
    }
    return [.name(), .description()] + (l ? ["Contents:"] | []) + l;
.

method tell_contents
    arg str, [except];
    var obj;

    for obj in (.contents()) {
	if (!(obj in except))
	    (| obj.tell(str) |);
    }
.

method verb_changed_inside
    .invalidate_verb_caches();
.

method did_arrive
    arg [args];

    (> pass(@args) <);
    .invalidate_verb_caches();
    if (!dark && sender().is($user))
	.tell_contents(sender().name() + " has arrived.", sender());
.

method did_leave
    arg [args];

    (> pass(@args) <);
    .invalidate_verb_caches();
    if (!dark && sender().is($user))
	.tell_contents(sender().name() + " has left.", sender());
.

method did_connect
    if (!dark)
	.tell_contents(sender().name() + " has connected.", sender());
.

method did_disconnect
    if (!dark)
	.tell_contents(sender().name() + " has disconnected.", sender());
.

method invalidate_verb_caches
    var obj;

    if (sender() != this() || caller() != definer())
	throw(~perm, "Sender not this.");
    for obj in (.contents())
	(| obj.invalidate_verb_cache() |);
.

method look_cmd
    arg dummy;
    var actor;

    actor = sender();
    actor.tell(.full_description(actor));
.

method say_cmd
    arg str;
    var actor;

    actor = sender();
    .tell_contents(actor.name() + " says, \"" + str + "\"", actor);
    actor.tell("You say, \"" + str + "\"");
.

method pose_cmd
    arg str;
    var actor;

    actor = sender();
    if (str && str[1] == ":")
	.tell_contents(actor.name() + substr(str, 2));
    else
	.tell_contents(actor.name() + " " + str);
.

method will_attach
    arg obj;

    if (!.is_owned_by(obj))
	throw(~perm, "Object does not own room.");
.

method will_link
    arg obj;

    if (!link_ok && !.is_owned_by(obj))
	throw(~perm, "Object does not own non-link_ok room.");
.