Dump of #8855 (Player Class hacked with eval that does substitutions and assorted stuff)

@create #5803 named Player Class hacked with eval that does substitutions and assorted stuff:Player Class hacked with eval that does substitutions and assorted stuff,eval-test
@prop #8855."followable" 0 rc
@prop #8855."followers" {} r #3685
@prop #8855."follow_teleport" 0 rc
@prop #8855."paste_footer" "" rc
@prop #8855."paste_header" "" rc
;;#8855.("spoof_attribution") = "     --"
"#8855.("idle_messages") => E_PERM (Permission denied)
"#8855.("respond_to") => E_PERM (Permission denied)
;;#8855.("moved_by") = {}
;;#8855.("sessile") = 0
"#8855.("witnessing") => E_PERM (Permission denied)
;;#8855.("rooms") = {{"Living", #17}, {"Dining", #28}, {"Library", #1670}, {"Pool", #1428}}
"#8855.("messages_going") => E_PERM (Permission denied)
;;#8855.("mail_options") = {"sticky", {"@mail", "new"}}
"#8855.("current_message") => E_PERM (Permission denied)
"#8855.("messages") => E_PERM (Permission denied)
;;#8855.("features") = {#55317, #30203, #40842, #26787}
"#8855.("previous_connection") => E_PERM (Permission denied)
"#8855.("email_address") => E_PERM (Permission denied)
;;#8855.("help") = #16122
"#8855.("linebuffer") => E_PERM (Permission denied)
"#8855.("current_folder") => E_PERM (Permission denied)
"#8855.("all_connect_places") => E_PERM (Permission denied)
"#8855.("last_connect_place") => E_PERM (Permission denied)
"#8855.("lines") => E_PERM (Permission denied)
"#8855.("ownership_quota") => E_PERM (Permission denied)
"#8855.("password") => E_PERM (Permission denied)
"#8855.("size_quota") => E_PERM (Permission denied)
"#8855.("last_password_time") => E_PERM (Permission denied)
"#8855.("last_connect_attempt") => E_PERM (Permission denied)
"#8855.("key") => E_PERM (Permission denied)
;;#8855.("aliases") = {"Player Class hacked with eval that does substitutions and assorted stuff", "eval-test"}
;;#8855.("description") = "You see a player who looks like it has a Special Nifty eval command."
;;#8855.("object_size") = {27096, 1141286558}

@verb #8855:"eval(old)" this none this r #2487
@program #8855:eval(old)
"Should be wizard-owned.";
"A MOO-code evaluator.  Type `;CODE' or `eval CODE'.";
"  For the case where CODE is an expression, we need to prepend `return ' and append `;' to CODE before passing it to eval().  For statements, though, we don't want to make any changes before passing it to eval().  The code below assumes that CODE is an expression unless it either begins with a semicolon `;' or one of the MOO language keywords.";
"  If you set your .eval_time property to 1, you find out how many ticks and seconds you used.  Also allows you to have your own predefined variables like `here' and `me': These should be set with the @define and @undef commands.  To list your current @defines, use the @listdefs command.  Player.eval_ticks may be subtracted from the resulting tick count; the author never really followed that, and would appreciate any assistance on fixing it if it doesn't work.";
set_task_perms(player);
argstr = argstr + ";";
start = 1;
while (argstr[start] == " ")
  start = start + 1;
endwhile
end = start;
while (("A" <= (l = argstr[end])) && (l <= "Z"))
  end = end + 1;
endwhile
if ((argstr[start] == ";") || (argstr[start..end - 1] in {"if", "for", "fork", "return", "while"}))
  program = this:fixeval(argstr);
else
  program = this:fixeval("return " + argstr);
endif
ticks = (ticks_left() - 13) - player.eval_ticks;
seconds = seconds_left();
value = eval(program);
seconds = seconds - seconds_left();
ticks = ticks - ticks_left();
if (value[1])
  val = value[2];
  if (typeof(val) == OBJ)
    player:tell("=> ", val, "  ", valid(val) ? ("(" + val.name) + ")" | ((a = $list_utils:assoc(val, {{#-1, "<$nothing>"}, {#-2, "<$ambiguous_match>"}, {#-3, "<$failed_match>"}})) ? a[2] | "<invalid>"));
  elseif (typeof(val) == ERR)
    player:tell("=> ", $code_utils:error_name(val), "  (", val, ")");
  else
    player:tell("=>  ", $string_utils:print(value[2]));
  endif
  if (player.eval_time && (!output_delimiters(player)[2]))
    player:tell("[used ", ticks, " tick", (ticks != 1) ? "s, " | ", ", seconds, " second", (seconds != 1) ? "s" | "", ".]");
  endif
else
  player:tell_lines(value[2]);
  nerrors = length(value[2]);
  player:tell(nerrors, " error", (nerrors == 1) ? "." | "s.");
endif
.

@verb #8855:"@define" any as any
@program #8855:@define
"Define <label> as <value>";
"eval will then substitute <value> for <label> in any code it's given.";
label = dobjstr;
value = iobjstr;
if ((!label) || (!value))
  player:tell("Usage: @define <label> as <value>");
else
  if (place = $list_utils:iassoc(label, player.eval_subs))
    player.eval_subs = listset(player.eval_subs, {label, value}, place);
  else
    player.eval_subs = listappend(player.eval_subs, {label, value});
  endif
  player:tell("Defined ", label, " as ", value, ".");
endif
.

@verb #8855:"@undef*ine" any none none
@program #8855:@undefine
"@undef <label>";
"Undefine a label for eval.  See eval and @define for more information.";
label = dobjstr;
if (!label)
  player:tell("Usage: @undef <label>");
else
  if (place = $list_utils:iassoc(label, player.eval_subs))
    player.eval_subs = listdelete(player.eval_subs, place);
    player:tell("Label ", label, " now undefined.");
  else
    player:tell("Label ", label, " not found.");
  endif
endif
.

@verb #8855:"@listdefs" none none none rxd
@program #8855:@listdefs
"List the definitions made with @define.";
labels = $list_utils:slice(player.eval_subs, 1);
values = $list_utils:slice(player.eval_subs, 2);
if (labels)
  maxlen = length("Label");
  for label in (labels)
    if (length(label) > maxlen)
      maxlen = length(label);
    endif
  endfor
  maxlen = maxlen + 1;
  player:tell($string_utils:left("Label", maxlen), "Value");
  player:tell($string_utils:left("-----", maxlen), "-----");
  for i in [1..length(player.eval_subs)]
    player:tell($string_utils:left(labels[i], maxlen), values[i]);
  endfor
  player:tell($string_utils:left("-----", maxlen), "-----");
else
  player:tell("No labels defined.");
endif
.

@verb #8855:"initialize" this none this
@program #8855:initialize
if ((caller == this) || $perm_utils:controls(caller_perms(), this))
  if (!this.eval_subs)
    this.eval_subs = {{"HERE", "player.location"}, {"ME", "player"}};
  endif
  return pass(@args);
else
  return E_PERM;
endif
.

@verb #8855:"'*" any any any rxd
@program #8855:'
"Usage:  '<player> <message>";
"Shortcut for page.";
if (!(who = verb[2..length(verb)]))
  player:tell("Usage:  '<player> <message>");
else
  if (argstr)
    argstr = tostr(who, " ", argstr);
  else
    argstr = who;
  endif
  this:page(who, @args);
endif
.

@verb #8855:"help_msg" this none this rx
@program #8855:help_msg
if (this != #8855)
  return this.help_msg;
else
  return this.help:get_topic(tostr(this));
endif
.

@verb #8855:"moveto" this none this rxd #3685
@program #8855:moveto
"Special moveto to move followers.";
"Set .followable = 0 to disallow people from following you.";
"Set .follow_teleport = 1 to allow people to follow you when you move by teleportation (e.g. home or @move).";
"@list-followers to see who's following you currently.";
"lose <player> will delete <player> from your .followers list.";
"moved the work to :do_followers";
if (this.followable && this.followers)
  call = callers();
  src = this.location;
  fork (1)
    this:do_followers(src, call);
  endfork
endif
return pass(@args);
.

@verb #8855:"fol*low" this none none rxd #3685
@program #8855:follow
"follow <player> will cause you to follow <player> everywhere <player> goes, as long as you can go there too.  If <player> teleports, you will only follow him if <player>.follow_teleport is nonzero.";
"";
"unfollow <player> will stop you from following <player>.";
if ((player in this.followers) || (player == this))
  player:tell(("You are already following " + this:title()) + "!");
elseif (!this.followable)
  player:tell(this:titlec(), " does not want to be followed right now.");
else
  this.followers = listappend(this.followers, player);
  player:tell(("You now follow " + this:title()) + ".");
  player.location:announce_all_but({player}, ((player:titlec() + " now follows ") + this:title()) + ".");
endif
.

@verb #8855:"unfol*low stop-fol*lowing don't-fol*low" this none none rxd #3685
@program #8855:unfollow
"follow <player> will cause you to follow <player> everywhere <player>";
"goes, as long as you can go there too.  If <player> teleports, you will";
"only follow him if <player>.follow_teleport is nonzero.";
"";
"unfollow <player> will stop you from following <player>.";
if (callers())
  player = caller;
endif
index = player in this.followers;
if (index)
  this.followers = listdelete(this.followers, index);
  player:tell(("You no longer follow " + this:title()) + ".");
  player.location:announce_all_but({player}, ((player:titlec() + " no longer follows ") + this:title()) + ".");
else
  player:tell(("But you don't follow " + this:title()) + "!");
endif
.

@verb #8855:"@list-fol*lowers" none none none rxd #3685
@program #8855:@list-followers
player:tell("You sense that ", $string_utils:english_list($list_utils:map_verb(player.followers, "title"), "no one"), (length(player.followers) < 2) ? " is " | " are ", "following you.");
.

@verb #8855:"lose" any none none rx #3685
@program #8855:lose
"'lose <name>' will get rid of <name>, if <name> is following you.";
"'lose all/every{thing,one,body}' is allowed also.";
losers = {};
if (!dobjstr)
  player:tell("Lose whom?");
elseif (!(dobj in player.followers))
  dobj = $string_utils:match(dobjstr, player.followers, "aliases");
  if (dobj == $failed_match)
    if (dobjstr in {"all", "everyone", "everything", "everybody"})
      losers = player.followers;
    else
      if ($object_utils:has_callable_verb(player.location, verb))
        player.location:(verb)();
      else
        player:tell("There's no one named \"", dobjstr, "\" following you.");
      endif
    endif
  elseif (dobj == $ambiguous_match)
    player:tell("\"", dobjstr, "\" could mean more than one person who is following you.");
  else
    losers = {dobj};
  endif
else
  losers = {dobj};
endif
for fol in (losers)
  player:tell("Losing ", fol:title(), ".");
  player.followers = setremove(player.followers, fol);
  fol:tell(player:titlec(), " lost you!");
  fol.location:announce_all_but({fol, player}, player:titlec(), " lost ", fol:title(), "!");
endfor
return losers;
.

@verb #8855:"@parent" any none none rxd
@program #8855:@parent
"@parent <thing> - only tell you one parent.";
if (!dobjstr)
  player:tell("Usage:  @parent <object>");
elseif (!$command_utils:object_match_failed(dobj = player:my_match_object(dobjstr), dobjstr))
  player:tell($string_utils:names_of({dobj, parent(dobj)}));
endif
.

@verb #8855:"_who's_followable" this none this rx
@program #8855:_who's_followable
"Called by \"who's followable\" and \"@who's followable in <room>\"";
where = args[1];
connected_players = connected_players();
if (where == player.location)
  where_name = "here";
  no_one = "No one else %l seems to be followable.";
else
  where_name = "in " + where.name;
  no_one = "No one seems to be followable in %l.";
endif
someone = "%w %a followable %l.";
who_all = {};
for thing in (where.contents)
  if (((is_player(thing) && (thing != player)) && (thing in connected_players())) && thing.followable)
    who_all = {@who_all, thing};
  endif
endfor
wo = this:feature("who's");
player:tell(wo:("who's_substitute")(who_all ? someone | no_one, $list_utils:map_arg(wo, "who's_title", who_all), where_name));
.

@verb #8855:"_who's_following_*" none none none rxd
@program #8855:_who's_following_
name = verb[18..length(verb)];
who = $string_utils:match_player(name);
if ($command_utils:object_match_failed(who, name))
  return;
elseif (!$object_utils:has_property(who, "followable"))
  player:tell($string_utils:pronoun_sub("%n is not followable.", who));
  return;
endif
where = args[1];
main = args[2];
followers = who.followers;
if (where == player.location)
  where_name = "";
  who_all = followers;
else
  where_name = " in " + where.name;
  who_all = $set_utils:intersection(where.contents, followers);
endif
if (main in followers)
  no_one = ("No one else%l seems to be following " + who:title()) + ".";
else
  no_one = ("No one%l seems to be following " + who:title()) + ".";
endif
someone = ("%w %a following " + who:title()) + "%l.";
wo = this:feature("who's");
player:tell(wo:("who's_substitute")(who_all ? someone | no_one, wo:("who's_titles")(who_all, 1), where_name));
.

@verb #8855:"@list(old)" this none this r #24442
@program #8855:@list(old)
"Like the standard @list, but allows you to specify ranges of lines:";
"  @list me:foo 14..16, ..9, 28..";
"lists lines 1-9, 14-16, and 28-$ of the verb.";
set_task_perms(player);
nargs = length(args);
if ((((nargs > 2) && (args[2] == "with")) && (index("parentheses", args[3]) == 1)) || (((nargs > 4) && (args[4] == "with")) && (index("parentheses", args[5]) == 1)))
  pflag = 1;
else
  pflag = 0;
endif
if ((((nargs > 2) && (args[2] == "without")) && (index("numbers", args[3]) == 1)) || (((nargs > 4) && (args[4] == "without")) && (index("numbers", args[5]) == 1)))
  nflag = 0;
else
  nflag = 1;
endif
range_st = ((-2 * nflag) + (2 * pflag)) + 4;
"if (nargs - 2 * pflag + 2 * nflag != 3 || !(spec = $code_utils:parse_verbref(args[1])))";
if (!(spec = $code_utils:parse_verbref(args[1])))
  player:notify("Usage:  @list <object>:<verb> [with parentheses] [without numbers]");
  return;
endif
object = $string_utils:match_object(spec[1], player.location);
if ($command_utils:object_match_failed(object, spec[1]))
  return;
endif
what = object;
vname = spec[2];
while ((what != $nothing) && ((code = verb_code(what, vname, pflag)) == E_VERBNF))
  what = parent(what);
endwhile
range_spec = $string_utils:from_list(args[range_st..nargs], " ");
if (range_spec)
  seq = $seq_utils:from_string(range_spec);
else
  seq = {1, length(code) + 1};
endif
if (code == E_VERBNF)
  player:notify("That object does not define that verb.");
elseif (typeof(code) == ERR)
  player:notify(tostr(code));
  "elseif (typeof(range = this:parse_range(length(code), {\"1-$\"}, 1, \"That verb has not been programmed.\", @args[range_st..nargs])) != LIST)";
elseif (typeof(seq) == ERR)
  player:notify(tostr("Range error: ", seq));
  "elseif (range[3] && !(nonum = \"nonum\" == $string_utils:trim(range[3])))";
  "player:notify(tostr(\"Don't understand this:  \", range[3]));";
else
  if (what != object)
    player:notify(tostr("Object ", object, " does not define that verb, but its ancestor ", what, " does."));
  endif
  info = verb_info(what, vname);
  vargs = verb_args(what, vname);
  if (index(vargs[2], "/"))
    vargs = listset(vargs, tostr("(", vargs[2], ")"), 2);
  endif
  player:notify(tostr(what, ":", info[3], "   ", $string_utils:from_list(vargs, " ")));
  lines = $seq_utils:tolist($seq_utils:intersection(seq, {1, length(code) + 1}));
  for line in (lines)
    if (!nflag)
      number = "";
    elseif (line < 10)
      number = tostr(" ", line, ":  ");
    else
      number = tostr(line, ":  ");
    endif
    player:notify(tostr(number, code[line]));
    $command_utils:suspend_if_needed(0);
  endfor
endif
.

@verb #8855:"bonked_msg" this none this rx #15550
@program #8855:bonked_msg
if (typeof(this.bonked_msg) != LIST)
  msg = this.bonked_msg;
elseif (length(this.bonked_msg))
  msg = this.bonked_msg[random(length(this.bonked_msg))];
endif
return $string_utils:pronoun_sub(msg || "");
.

@verb #8855:"@grep(old)" this none this r #24442
@program #8855:@grep(old)
set_task_perms(player);
if (prepstr == "in")
  pattern = dobjstr;
  objlist = $code_utils:eval_d(("return " + iobjstr) + ";");
  if (!objlist[1])
    player:tell("Had trouble reading `", iobjstr, "':  ");
    player:tell_lines(@objlist[2]);
    return;
  elseif (typeof(objlist[2]) == OBJ)
    objlist = {objlist[2..2]};
  elseif (typeof(objlist[2]) != LIST)
    player:notify(tostr("Value of `", iobjstr, "' is not an object or list:  ", $string_utils:print(objlist[2])));
    return;
  else
    objlist = objlist[2..2];
  endif
elseif (args && player.wizard)
  pattern = argstr;
  objlist = {};
else
  bracket = player.wizard ? "[]" | "";
  player:tell("Usage:  @grep <pattern> ", player.wizard ? "[in {objectlist}]" | "in {objectlist}");
  return;
endif
player:tell("Searching for verbs in ", iobjstr, " containing the string ", $string_utils:print(pattern), " ...");
player:tell();
$code_utils:find_verbs_containing(pattern, @objlist);
.

@verb #8855:"@paste(old)" any any any
@program #8855:@paste(old)
"Usage: @paste <prefix> <suffix>";
"Announce a series of entered lines to the room the player is in.";
"Before the lines are quoted, player.paste_header is run through";
"$string_utils:pronoun_sub(), and if the result contains the player's";
"name, it is used as a header.  Otherwise player.name centered in a";
"line of dashes is used.";
"A footer comes afterwards, likewise derived from player.paste_footer.";
"<prefix> and <suffix> are placed before and after each line.";
"";
"This verb is, as one might guess, designed for pasting text to MOO using";
"GnuEmacs or a windowing system.  You should remember that after you";
"have pasted the lines in, you must type . on a line by itself, or you'll";
"sit around waiting for $command_utils:read_lines() to finish _forever_.";
len = length(args);
prefix = len ? args[1] | "";
suffix = (len > 1) ? args[2] | "";
lines = $command_utils:read_lines();
header = $string_utils:pronoun_sub_secure(player.paste_header, "") || $string_utils:center(player.name, 75, "-");
player.location:announce_all(header);
for line in (lines)
  $command_utils:suspend_if_needed(0);
  player.location:announce_all((prefix + line) + suffix);
endfor
footer = $string_utils:pronoun_sub_secure(player.paste_footer, "") || $string_utils:center(player.name, 75, "-");
player.location:announce_all(footer);
.

@verb #8855:"@paste" any any any rxd
@program #8855:@paste
"Usage: @paste <prefix> <suffix>";
"Announce a series of entered lines to the room the player is in.";
"Before the lines are quoted, player.paste_header is run through";
"$string_utils:pronoun_sub(), and if the result contains the player's";
"name, it is used as a header.  Otherwise player.name centered in a";
"line of dashes is used.";
"A footer comes afterwards, likewise derived from player.paste_footer.";
"<prefix> and <suffix> are placed before and after each line.";
"";
"This verb is, as one might guess, designed for pasting text to MOO using";
"GnuEmacs or a windowing system.  You should remember that after you";
"have pasted the lines in, you must type . on a line by itself, or you'll";
"sit around waiting for $command_utils:read_lines() to finish _forever_.";
len = length(args);
prefix = len ? args[1] | "";
suffix = (len > 1) ? args[2] | "";
lines = $command_utils:read_lines();
to_tell = {$string_utils:pronoun_sub_secure(this.paste_header, "") || $string_utils:center(player.name, 75, "-")};
for line in (lines)
  to_tell = listappend(to_tell, (prefix + line) + suffix);
endfor
if (this.paste_footer)
  to_tell = listappend(to_tell, $string_utils:pronoun_sub($code_utils:verb_or_property(this, "paste_footer")));
else
  to_tell = listappend(to_tell, $string_utils:center(player.name, 75, "-"));
endif
for thing in (player.location.contents)
  "$command_utils:suspend_if_needed(0);";
  suspend(0);
  thing:tell_lines(to_tell);
endfor
player:tell("Done @pasting.");
.

@verb #8855:"do_followers" this none this rxd #58016
@program #8855:do_followers
"Special moveto to move followers.";
"Set .followable = 0 to disallow people from following you.";
"Set .follow_teleport = 1 to allow people to follow you when you move by teleportation (e.g. home or @move).";
"@list-followers to see who's following you currently.";
"lose <player> will delete <player> from your .followers list.";
if (caller != this)
  raise(E_PERM);
endif
{src, call} = args;
dest = this.location;
is_exit = 0;
for ex in ($list_utils:slice(call))
  if ($object_utils:isa(ex, $exit))
    exit = ex;
    is_exit = 1;
  endif
endfor
"Check for some pathological cases: we must either be connected, or else not a player, and we must be actually going somewhere reasonable.";
if (((((!is_player(this)) || $object_utils:connected(this)) && (!(dest in {$nothing, $limbo, src}))) && (src != $nothing)) && (!$object_utils:isa(dest, $generic_editor)))
  for follower in (this.followers)
    isp = is_player(follower);
    "It's not called 'stalk', so give up when disconnected!";
    if ((!$recycler:valid(follower)) || (isp && (!$object_utils:connected(follower))))
      this.followers = setremove(this.followers, follower);
    elseif (follower in src.contents)
      "Chase me! Chase me!";
      if (is_exit)
        fork (0)
          "clear callers() stack for paranoid exits.";
          set_task_perms(follower.owner);
          player = follower;
          follower:tell("You follow ", this:title(), ".");
          if (isp)
            "Ah, the last frame should be the exit name, duplicate";
            force_input(follower, call[$][2]);
          else
            exit:move(follower);
          endif
          if (follower.location != dest)
            follower:tell("Looks like ", this:title(), " got away!");
          endif
        endfork
      elseif (this.follow_teleport)
        "moveto was called by another verb (e.g. home)";
        fork (0)
          "again, clear callers() stack for paranoia";
          set_task_perms(follower.owner);
          player = follower;
          follower:tell("You follow ", this:title(), ".");
          loc = follower.location;
          follower.location:announce_all_but({follower}, follower:titlec(), " follows ", this:title(), ".");
          follower:moveto(this.location);
          if (follower.location == this.location)
            this:tell(follower:titlec(), " follows you.");
            follower.location:announce_all_but({follower, this}, follower:titlec(), " follows ", this:title(), ".");
          elseif (follower.location == loc)
            follower:tell("Looks like ", this:title(), " got away!");
            loc:announce_all_but({follower, this}, follower:titlec(), " looks bemused.");
          endif
        endfork
      endif
      $command_utils:suspend_if_needed(1);
    endif
  endfor
endif
.

@verb #8855:"moveto(old)" this none this rxd #3685
@program #8855:moveto(old)
"Copied from Player Class hacked with eval that does substitutions and assorted stuff (#8855):moveto by Doug (#3685) Tue Feb 16 09:06:32 1999 PST";
"Special moveto to move followers.";
"Set .followable = 0 to disallow people from following you.";
"Set .follow_teleport = 1 to allow people to follow you when you move by teleportation (e.g. home or @move).";
"@list-followers to see who's following you currently.";
"lose <player> will delete <player> from your .followers list.";
src = this.location;
retval = pass(@args);
if (this.followable && this.followers)
  call = callers();
  fork (1)
    dest = this.location;
    is_exit = 0;
    for ex in ($list_utils:slice(call))
      if ($object_utils:isa(ex, $exit))
        exit = ex;
        is_exit = 1;
      endif
    endfor
    connected_players = connected_players();
    "Check for some pathological cases: we must either be connected, or else not a player, and we must be actually going somewhere reasonable.";
    if (((((!is_player(this)) || (this in connected_players)) && (!(dest in {#-1, $limbo, src}))) && (src != #-1)) && (!$object_utils:isa(dest, $generic_editor)))
      for follower in (this.followers)
        "It's not called 'stalk', so give up when disconnected!";
        if ((!valid(follower)) || (is_player(follower) && (!(follower in connected_players))))
          this.followers = setremove(this.followers, follower);
        elseif (follower in src.contents)
          "Chase me! Chase me!";
          if ((!is_player(follower)) || (follower in connected_players))
            if (is_exit)
              follower:tell("You follow ", this:title(), ".");
              exit:move(follower);
            elseif (this.follow_teleport)
              "moveto was called by another verb (e.g. home)";
              follower:tell("You follow ", this:title(), ".");
              loc = follower.location;
              follower.location:announce_all_but({follower}, follower:titlec(), " follows ", this:title(), ".");
              follower:moveto(this.location);
              if (follower.location == this.location)
                this:tell(follower:titlec(), " follows you.");
                follower.location:announce_all_but({follower, this}, follower:titlec(), " follows ", this:title(), ".");
              elseif (follower.location == loc)
                loc:announce_all_but({follower, this}, follower:titlec(), " looks bemused.");
              endif
            endif
          endif
          if (follower.location != dest)
            follower:tell("Looks like ", this:title(), " got away!");
          endif
          $command_utils:suspend_if_needed(1);
        endif
      endfor
    endif
  endfork
endif
return retval;
.

"***finished***