The first thing I did was move the it/him/her context checks from $foundation.match_environment() to $user.match_environment, as context is actually defined on $user. It ended up looking like this:
--- @program $foundation.match_environment() +access=pub -e=0 arg str; var obj, env, found, match, target; if (!str) throw(~objnf, "No object specified.", str); str = str.strip_article(); // Handle special cases. if (str in ["me", "my"]) { return this(); } else if (str[1] == "$" || str[1] == "#") { return (> $object_lib.to_dbref(str) <); } else if (str[1] == "~") { str = str.subrange(2); if (str[1] != "~") return $user_db.match_begin(str); } // Start matching found = []; env = .environment(); // special case ordinal references if ((match = $parse_lib.ordinal_reference(str))) return env.match_nth(@match); if ((match = $parse_lib.possessive_reference(str))) { if (match[1] == "me") obj = this(); else obj = (> env.match_object(match[1]) <); if (!obj.match_name(str)) { catch ~objnf, ~ambig, ~range { env = (| obj.contents() |) || []; if ((found = $parse_lib.ordinal_reference(match[2]))) return (> env.match_nth(@found) <); else return (> env.match_object(match[2]) <); } with { if (error() == ~objnf) throw(~objnf, obj.name() + " does not have " + match[2].add_indefinite() + "."); else if (error() == ~ambig) throw(~ambig, "\"" + str + "\" can match " + traceback()[1][3].mmap('namef, 'ref).to_english("", " or ") + "."); else throw(~objnf, obj.name() + "'s what?"); } } } catch any { return (> env.match_object(str) <); } with { if (error() == ~badfrob) { target = traceback()[1][3]; obj = frob_value(target)['location]; obj.del_frob_from_contents(target); throw(~badfrob, "Cleaned up bogus frob in your environment, try again.."); } if (error() == ~objnf) throw(~objnf, "You do not see " + str.add_indefinite() + " anywhere."); if (error() == ~ambig) { found = filter obj in (traceback()[1][3]) where (obj.name() == str); if (listlen(found) == 1) return found[1]; if (found) found = found.mmap('namef, 'ref).to_english("", " or "); else found = traceback()[1][3].mmap('namef, 'ref).to_english("", " or "); throw(~ambig, "\"" + str + "\" can match " + found + "."); } rethrow(error()); } . ---In preparation for work on $user.match_environment() I then created a new method called .long_fingers() on $user and $programmer. The return value of this method is then used in $user.match_environment() to determine whether or not to allow usage of $/#/~ (1 for yes, 0 for no):
--- @program $user.long_fingers() +access=pub -e=0 return 0; . @program $programmer.long_fingers() +access=pub -e=0 return 1; . ---Finally, I changed $user.match_environment's handling of context checks. Instead of simply returning the contents of a successful context match, it overwrites str with a successful match. This is then passed along to $foundation.match_environment(). It uses a .long_fingers() check to prevent objnums and objnames getting passed. It ended up looking like this:
--- @program $user.match_environment() +access=pub arg str; var match, gend; if (!str) throw(~objnf, "No object specified.", str); str = str.strip_article(); if ((str[1] == "$" || str[1] == "#" || str[1] == "~") && .long_fingers() == 0) throw(~objnf, "You do not see " + str.add_indefinite() + " anywhere."); if (str == "." && (match = (| context['last] |))) str = match; if ((match = (| (.remembered())[str] |))) str = match; if (str in ["it", "him", "her"] && (match = (| context[str] |))) str = match; match = (> pass(str) <); if (match.has_ancestor($thing)) { gend = (| match.gender() |); if (gend) context = context.add(gend.pronoun('po), str); } if (type(context) == 'dictionary) context = context.add('last, str); return match; . ---Update (4/5/2000): I forgot to mention it here, but you will also need to change the command template for @remember to "@remember <any> as <any>", as well as remove the .namef() calls from $user.remember_cmd(), $user.forget_cmd(), and $user.remembered_cmd().