@create $room called "generic detailed room",detail

@chmod detail +rf

@prop detail.autoexit 1

@prop $detailed_room.listen_description
@prop $detailed_room.smell_description
@prop $detailed_room.feel_description
@prop $detailed_room.taste_description

@verb $detailed_room:"listen_self smell_self feel_self taste_self" tnt
@program $detailed_room:listen_self
desc = this.(verb[1..$-5] + "_description");
if (verb == "listen_self")
 report = "hear";
elseif (verb == "smell_self")
 report = "smell";
elseif (verb == "feel_self")
 report = "feel";
else
 report = "taste";
endif
if (desc)
  player:tell(desc);
else
  player:tell("You ", report, " nothing special.");
endif
.

@verb detail:look_self tnt
@program detail:look_self
{?brief = 0} = args;
player:tell();
player:tell(this:title());
if (!brief)
  desc = this:description();
  if (desc)
    player:tell_lines(desc);
  else
    player:tell("You see nothing special.");
  endif
endif
if(this.autoexit && player.autoexit)
  player:tell(this:autoexits());
endif
this:tell_contents(setremove(this:contents(), player), this.ctype);
.

@verb detail:autoexits tnt
@program detail:autoexits
exits = this:obvious_exits();
elist = " Exits: ";
if (exits)
  for n in (exits)
    if ($object_utils:isa(n, $exit))
      exname = "`2y" + n.name + "`00";
    elseif ($object_utils:isa(n, $two_way))
      index = this in n.exits == 1 ? 1 | 2;
      exname = "`2y" + n._aliases[index][1] + "`00";
    else
      exname = "`1rbad exit type (" + n + ")`00";
    endif
    if ($object_utils:has_property(n, "is_door") && n.is_door && n.is_closed)
      exname = "`1x[`2y" + exname + "`1x]`00";
    endif
    if (n == exits[$])
      elist = elist + exname + ".";
    else
      elist = elist + exname + ", ";
    endif
  endfor
else
  elist = elist + "`2wnone`00.";
endif
return elist;
.

@verb $detailed_room:here_huh tnt rxd
@program $detailed_room:here_huh
if ((verb = args[1]) in {"open", "close", "lock", "unlock"})
  exit = this:match_exit((target = args[2][1]));
  if ($recycler:valid(exit) && ($object_utils:has_callable_verb(exit, verb)))
    exit:(verb)();
    return 1;
  endif
  return 0;
endif
return pass(@args);
.

@verb $detailed_room:@exits none none none rd
@program $detailed_room:@exits
if (!$perm_utils:controls(valid(caller_perms()) ? caller_perms() | player, this))
  player:tell("Sorry, only the owner of a room may list its exits.");
elseif (this.exits == {})
  player:tell("This room has no conventional exits.");
else
  try
    for exit in (this.exits)
      try
        player:tell(exit:title(), " (", exit, ") leads to ", valid(exit.dest) ? exit.dest:title() | "???", " (", exit.dest, ") via {", $string_utils:from_list(exit.aliases, ", "), "}.");
      except (ANY)
        player:tell("Bad exit or missing .dest property:  ", $string_utils:nn(exit));
        continue exit;
      endtry
    endfor
  except (E_TYPE)
    player:tell("Bad .exits property. This should be a list of exit objects. Please fix this.");
  endtry
endif
.

@verb detail:tell_contents tnt
@program $detailed_room:tell_contents
{contents, ctype} = args;
if(!this.dark && contents != {} && ctype == 4)
  players = things = {};
  for x in (contents)
    if(is_player(x))
      players = {@players, x};
    else
      things = {@things, x};
    endif
  endfor
  if(!this.integrate)
    for t in (things)
      if($object_utils:has_readable_property(t, "look_msg"))
        player:tell($string_utils:pronoun_sub(t.look_msg, player, t, this));
      else
        player:tell("You see ", t:title(), " here.");
      endif
    endfor
  endif
  for p in (players)
    player:tell($string_utils:pronoun_sub(tostr("%N ", $gender_utils:get_conj("is", p), " here."), p));
  endfor
else
  pass(@args);
endif
.

@verb detail:match_detail tnt
@program $detailed_room:match_detail
{alias, ?senses = {"look", "listen", "smell", "touch", "taste"}} = args;
senses = typeof(senses) == LIST ? senses | {senses};
match = $failed_match;
r = {};
for sense in (senses)
  senseprop = sense + "_details";
  for x in (`this.(senseprop) ! E_PROPNF => {}')
    if (alias in x[1])
      r = {@r, {sense, x in this.(senseprop)}};
      if (match == $failed_match)
        match = x in this.(senseprop);
      else
        match = $ambiguous_match;
      endif
    endif
  endfor
endfor
if (match != $failed_match)
  return {match, r};
endif
"checking for partial matches";
for sense in (senses)
  senseprop = sense + "_details";
  for x in (`this.(senseprop) ! E_PROPNF => {}')
    for y in (x[1])
      if (index(y, alias) != 1)
      elseif (match == $failed_match)
        r = {@r, {sense, x in this.(senseprop)}};
        match = x in this.(senseprop);
      else
        r = {@r, {sense, x in this.(senseprop)}};
        match = $ambiguous_match;
      endif
    endfor
  endfor
endfor
return {match, r};
.

@verb $detailed_room:display_detail tnt
@program $detailed_room:display_detail
{alias, sense} = args;
senseprop = sense + "_details";
{match, r} = this:match_detail(alias, sense);
if (match == $failed_match || match == $ambiguous_match)
  return match;
endif
player:tell_lines(this.(senseprop)[r[1][2]][2]);
return 1;
.


@prop $detailed_room.look_details {}
@prop $detailed_room.listen_details {}
@prop $detailed_room.smell_details {}
@prop $detailed_room.touch_details {}
@prop $detailed_room.taste_details {}

@verb $detailed_room:@de*tail any any any
@program $detailed_room:@detail
desc = `args[4..$] ! E_RANGE => iobjstr';
if (!$perm_utils:controls(player, this))
  player:tell("You don't have the permissions for that.");
  return E_PERM;
endif
"this manual unfolding is so we can properly handle the cases where 1) the alias has quoted spaces and 2) the alias is a preposition. args knows what's up, but :explode/:words fails in the first case and dobjstr fails in the second.";
sense = args[1];
if (args[2] != "as" || `args[3] == "as" ! E_RANGE => 0')
  aliaslist = args[2];
else
  aliaslist = "";
endif
if (!sense || (j = !(sense in {"look", "listen", "touch", "taste", "smell"})) || !aliaslist)
  if(!aliaslist && !j)
    msg = "If you want to set the room description, use @describe or @edit.";
  else
    msg = "";
  endif
  this:here_explain_syntax(caller, verb, args, msg);
  return;
endif
aliaslist = $string_utils:explode(aliaslist, ",");
dups = {};
for a in (aliaslist)
  {m, r} = this:match_detail(a, sense);
  if (m != $failed_match)
    dups = {@dups, a};
  endif
endfor
if (length(dups))
  player:tell("The keyword", length(dups) == 1 ? " " | "s ", $string_utils:english_list(dups), length(dups) == 1 ? " is" | " are", " already in use with that sense.");
  return E_PERM;
endif
this.(sense+"_details") = {@this.(sense+"_details"), {aliaslist, desc}};
player:tell("Detail for ", sense, " with alias", length(aliaslist) == 1 ? " " | "es ", "\"", $string_utils:english_list(aliaslist), "\" created in ", this:title(), " (", this, ")." );
.

@verb $detailed_room:@rmde*tail any any any
@program $detailed_room:@rmdetail
if (prepstr && !(prepstr in {"for", "from"}))
  "ack, detail w/ the name of a preposition";
  match = prepstr == "for" ? "for" | "from";
  if (i = match in args)
    alias = `$string_utils:from_list(args[1..i-1], " ") ! E_RANGE => ""';
    sense = `$string_utils:from_list(args[i+1..$], " ") ! E_RANGE => ""';
  else
    alias = argstr;
    sense = "";
  endif
elseif (prepstr == "for" || prepstr == "from")
  alias = dobjstr;
  sense = iobjstr;
else
  alias = dobjstr;
  sense = "";
endif
player:tell_lines({toliteral(args), alias, sense});
if (!$perm_utils:controls(player, this))
  player:tell("You don't have the permissions for that.");
  return E_PERM;
elseif (!alias)
  player:tell("Remove which detail?");
  return;
elseif (!sense)
  player:tell("Since you didn't specify, I'm assuming you mean all details called \"", alias, "\", no matter the sense.");
  sense = {"look", "listen", "smell", "touch", "taste"};
else
  if (!(sense in {"sight", "sound", "touch", "taste", "smell"}))
    this:here_explain_syntax(caller, verb, args);
    return;
  endif
  if (sense == "sight")
    sense = "look";
  elseif (sense == "sound")
    sense = "listen";
  endif
endif
{match, r} = this:match_detail(alias, sense);
if (match == $failed_match)
  player:tell("There's no detail called \"", alias, "\" to remove.");
  return;
endif
for i in (r)
  senseprop = i[1] + "_details";
  aliaslist = this.(senseprop)[i[2]][1];
  this.(senseprop) = setremove(this.(senseprop), this.(senseprop)[i[2]]);
  player:tell("Detail for ", i[1], " with alias", length(aliaslist) == 1 ? " " | "es ", "\"", $string_utils:english_list(aliaslist), "\" has been removed from ", this:title(), " (", this, ").");
endfor
.

@verb $detailed_room:here_explain_syntax tnt
@program $detailed_room:here_explain_syntax
{caller, verb, vargs, ?msg="", @args} = args;
r = 0;
if ($code_utils:verbname_match("@rmde*tail", verb))
  player:tell_lines({
"Syntax: @rmdetail <name> [from/for <sense>].",
"         Where <sense> is one of: sight sound touch taste smell."});
  r = 1;
elseif ($code_utils:verbname_match("@details", verb))
elseif ($code_utils:verbname_match("@de*tail", verb))
  player:tell_lines({
"Syntax: @detail <sense> <name[,alias,...,alias]> as \"<description>\".",
"         Where <sense> is one of: look listen touch taste smell."});
  r = 1;
endif
if(msg)
  if(r == 1)
    msg = $string_utils:space(9, " ") + msg;
  endif
  player:tell(msg);
  r = 1;
endif
return r;
.

@verb $detailed_room:@details none none none
@program $detailed_room:@details
if (!$perm_utils:controls(valid(caller_perms()) ? caller_perms() | player, this))
  player:tell("Sorry, only the owner of a room may list its details.");
  return E_PERM;
endif
output = 0;
limit = `player:linelen() ! ANY => 79';
for sense in ({"look", "listen", "taste", "smell", "touch"})
  for detail in (this.(sense+"_details"))
    start = $ansi_utils:slice(tostr(" ", sense, ": \"", typeof(detail[2]) == LIST ? detail[2][1] | detail[2], "\""), limit);
    if($ansi_utils:length(start) == limit)
      start = start[1..$-4] + "...`00\"";
    endif
    player:tell_lines({tostr($string_utils:english_list(detail[1]), ": "), start});
    output = output + 1;
  endfor
endfor
if (output == 0)
  player:tell("This room has no details.");
endif
.

@prop $detailed_room.echo_to {}
@prop $detailed_room.estart_msg "Elsewhere, "
@prop $detailed_room.eend_msg ""

@verb $detailed_room:"announce announce_all announce_all_but" tnt
@program $detailed_room:announce
pass(@args);
if(verb == "announce_all_but")
  {ignore, @text} = args;
else
  ignore = {};
  {@text} = args;
endif
try
  for target in (this.echo_to)
    if (valid(target) && target:is_unlocked_for(this))
      try
        if ($object_utils:isa(target, $detailed_room) || $object_utils:isa(target, $echo_room))
          target:announce_noecho(ignore, this:estart(), @text, this:eend());
        elseif ($object_utils:isa(target, #155))
          target:announce_all_e(this:estart(), @text, this:eend());
        else
          target:announce(this:estart(), @text, this:eend());
        endif
      except (ANY)
        continue target;
      endtry
    endif
  endfor
except (E_TYPE)
  this.owner:tell(this.name, " `00has a bad .echo_to property. It should be a list of rooms. Please fix this.");
endtry
.

@verb $detailed_room:announce_noecho tnt
@program $detailed_room:announce_noecho
":announce_noecho(LIST of targets to ignore, @text)";
"";
"This is $room:announce_all_but, but called to replace all :announce* verbs when dealing with an echo from another room, so we don't get infinitely bouncing echos.";
{ignore, @text} = args;
contents = this:contents();
for l in (ignore)
  contents = setremove(contents, l);
endfor
for listener in (contents)
  try
    listener:tell(@text);
  except (ANY)
    "Ignore listener with bad :tell";
    continue listener;
  endtry
endfor
.

@verb $detailed_room:"estart eend" tnt rxd
@program $detailed_room:estart
return this.(verb+"_msg");
.



"@prop detail.help_msg ""
"@edit detail.help_msg
"A detailed room is like a room, only better!
"
"It's got capacities for the following alternative behaviours:
" - automatic listing of exits, if .autoexits is on
" - unique descriptions of objects in room, if applicable (and .ctype = 4) <-- but it is very bad
" - details. use @detail or @rmdetail to add/remove details to rooms you own. The details will be visible on a "look <detail>", and are a way to create extra depth in a room without having everything you can look at be a seperate object. Use @details to see all the details on a room (but only if you're the owner)
" - echoes. set .echo_to to a list of rooms that will hear what happens in the room. set .estart_msg and .end_msg to alter how the message is displayed.
"
" (also, the l*ook code catches stuff like "look at foo on bar", which it doesn't normally)
"save
"done

"ideally will handle integration, later on.