object utilities (#53)

(an instance of Generic Utilities Package made by The_Mayor)

     These routines are useful for finding out information about individual objects.
     
     Examining everything an object has defined on it:
      all_verbs (object) => like it says
      all_properties (object) => likewise
      findable_properties(object) => tests to see if caller can "find" them
      owned_properties (object[, owner]) => tests for ownership
     
     Investigating inheritance:
      ancestors(object[,object...]) => all ancestors
      descendants (object) => all descendants
      ordered_descendants(object) => descendants, in a different order
      leaves (object) => descendants with no children
      branches (object) => descendants with children
      isa (object,class) => true iff object is a descendant of class (or ==)
     
     Considering containment:
      contains (obj1, obj2) => Does obj1 contain obj2 (nested)?
      all_contents (object) => return all the (nested) contents of object
     
     Verifying verbs and properties:
      has_property(object,pname) => false/true according as object.(pname) exists
      has_verb (object,vname) => false/{#obj} according as object:(vname) exists
      has_callable_verb => same, but verb must be callable from a program
      match_verb (object,vname) => false/{location, newvname}
      (identify location and usable name of verb)



VERB SOURCE CODE:

has_property:
object = args[1];
prop = args[2];
if (prop in $code_utils.builtin_props)
    return valid(object);
else
    return !(!property_info(object, prop));
endif
.


all_properties:
what = args[1];
if (what.owner != caller_perms())
    set_task_perms(caller_perms());
endif
props = properties(what) || {};
while (valid(what = parent(what)))
    props = {@properties(what) || {}, @props};
endwhile
return props;
.


has_verb:
":has_verb(OBJ object, STR verbname)";
"Find out if an object has a verb matching the given verbname.";
"Returns {location} if so, 0 if not, where location is the object or the ancestor 
on which the verb is actually defined.";
object = args[1];
verbname = args[2];
while (E_VERBNF == (vi = verb_info(object, verbname)))
    object = parent(object);
endwhile
return vi ? {object} | 0;
.


has_callable_verb:
"Usage:  has_callable_verb(object, verb)";
"See if an object has a verb that can be called by another verb (i.e., that has its 
x permission bit set).";
"Return {location}, where location is the object that defines the verb, or 0 if the 
object doesn't have the verb.";
object = args[1];
verbname = args[2];
while (valid(object))
    if (index(verb_info(object, verbname)[2], "x") && verb_code(object, verbname))
        return {object};
    endif
    object = parent(object);
endwhile
return 0;
.


all_verbs:
what = args[1];
if (what.owner != caller_perms())
    set_task_perms(caller_perms());
endif
verbs = {};
while (valid(what))
    verbs = {@verbs(what) || {}, @verbs};
    what = parent(what);
endwhile
return verbs;
.


match_verb:
":match_verb(OBJ object, STR verb)";
"Find out if an object has a given verb, and some information about it.";
"Returns {OBJ location, STR verb} if matched, 0 if not.";
"Location is the object on which it is actually defined, verb is a name";
"for the verb which can subsequently be used in verb_info (i.e., no";
"asterisks).";
verbname = strsub(args[2], "*", "");
object = args[1];
while (E_VERBNF == (info = verb_info(object, verbname)))
    object = parent(object);
endwhile
return info ? {object, verbname} | 0;
.


isa:
":isa(x,y) == valid(x) && (y==x || y in :ancestors(x))";
what = args[1];
targ = args[2];
while (valid(what))
    if (what == targ)
        return 1;
    endif
    what = parent(what);
endwhile
return 0;
.


ancestors:
"Usage:  ancestors(object[, object...])";
"Return a list of all ancestors of the object(s) in args, with no duplicates.";
"If called with a single object, the result will be in order ascending up the inheritance 
hierarchy.  If called with multiple objects, it probably won't.";
ret = {};
for o in (args)
    what = o;
    while (valid(what = parent(what)))
        ret = setadd(ret, what);
    endwhile
endfor
return ret;
.


descendants descendents:
what = args[1];
kids = children(what);
result = {};
for x in (kids)
    result = {@result, @this:descendants(x)};
endfor
return {@kids, @result};
.


descendants_suspended descendents_suspended:
set_task_perms(caller_perms());
what = args[1];
kids = children(what);
result = {};
for x in (kids)
    result = {@result, @this:descendants_suspended(x)};
endfor
$command_utils:suspend_if_needed(0);
return {@kids, @result};
.


ordered_descendants:
r = {what = args[1]};
for k in (children(what))
    r = {@r, @this:(verb)(k)};
endfor
return r;
.


branches:
":branches(object) => list of all descendants of object which have children.";
if (kids = children(object = args[1]))
    s = {object};
    for k in (kids)
        s = {@s, @this:branches(k)};
    endfor
    return s;
else
    return {};
endif
.


branches_suspended:
":branches_suspended(object) => descendants of object having children.";
"this version calls suspend(0) as needed";
set_task_perms(caller_perms());
if (kids = children(object = args[1]))
    s = {object};
    for k in (kids)
        $command_utils:suspend_if_needed(0);
        s = {@s, @this:branches_suspended(k)};
    endfor
    return s;
else
    return {};
endif
.


leaves:
":leaves(object) => list of all childless descendents of object";
s = {};
for k in (children(args[1]))
    s = {@s, @this:leaves(k)};
endfor
return s || {args[1]};
.


leaves_suspended:
":leaves_suspended(object) => list of all childless descendents of object";
"this versions calls suspend(0) as needed";
set_task_perms(caller_perms());
s = {};
for k in (children(args[1]))
    $command_utils:suspend_if_needed(0);
    s = {@s, @this:leaves_suspended(k)};
endfor
return s || {args[1]};
.


contains:
"$object_utils:contains(obj1, obj2) -- does obj1 contain obj2?";
"";
"Return true iff obj2 is under obj1 in the containment hierarchy; that is, if obj1 
is obj2's location, or its location's location, or ...";
loc = args[1];
what = args[2];
while (valid(what))
    what = what.location;
    if (what == loc)
        return valid(loc);
        return 1;
    endif
endwhile
return 0;
.


all_contents:
"all_contents(object)";
"Return a list of all objects contained (at some level) by object.";
res = {};
for y in (args[1].contents)
    res = {@res, y, @$object_utils:all_contents(y)};
endfor
return res;
.


findable_properties:
"findable_properties(object)";
"Return a list of properties on those members of object's ancestor list that are 
readable or are owned by the caller (or all properties if the caller is a wizard).";
what = args[1];
props = {};
who = caller_perms();
while (what != $nothing)
    if ((what.r || (who == what.owner)) || who.wizard)
        props = {@properties(what), @props};
    endif
    what = parent(what);
endwhile
return props;
.


owned_properties:
"owned_properties(what[, who])";
"Return a list of all properties on WHAT owned by WHO.";
"Only wizardly verbs can specify WHO; mortal verbs can only search for properties 
owned by their own owners.  For more information, talk to Gary_Severn.";
what = anc = args[1];
who = ((c = caller_perms()).wizard && (length(args) > 1)) ? args[2] | c;
props = {};
while (anc != $nothing)
    for k in (properties(anc))
        if (property_info(what, k)[1] == who)
            props = listappend(props, k);
        endif
    endfor
    anc = parent(anc);
endwhile
return props;
.


property_conflicts:
":property_conflicts(object,newparent)";
"Looks for propertyname conflicts that would keep chparent(object,newparent)";
"  from working.";
"Returns a list of elements of the form {, @}";
"where  is list of descendents of object defining .";
if (!valid(object = args[1]))
    return E_INVARG;
elseif (!valid(newparent = args[2]))
    return (newparent == #-1) ? {} | E_INVARG;
elseif (!($perm_utils:controls(caller_perms(), object) && (newparent.f || $perm_utils:controls(caller_perms(), 
newparent))))
    "... if you couldn't chparent anyway, you don't need to know...";
    return E_PERM;
endif
"... properties existing on newparent";
"... cannot be present on object or any descendent...";
props = conflicts = {};
for o in ({object, @$object_utils:descendents_suspended(object)})
    for p in (properties(o))
        if (property_info(newparent, p))
            if (i = p in props)
                conflicts[i] = {@conflicts[i], o};
            else
                props = {@props, p};
                conflicts = {@conflicts, {p, o}};
            endif
        endif
        $command_utils:suspend_if_needed(0);
    endfor
    $command_utils:suspend_if_needed(0);
endfor
return conflicts;
.


descendants_with_property_suspended:
":descendants_with_property_suspended(object,property)";
" => list of descendants of object on which property is defined.";
"calls suspend(0) as needed";
object = args[1];
if ((caller == this) || (object.w || $perm_utils:controls(caller_perms(), object)))
    $command_utils:suspend_if_needed(0);
    if (property_info(@args))
        return {object};
    endif
    r = {};
    prop = args[2];
    for c in (children(object))
        r = {@r, @this:descendants_with_property_suspended(c, prop)};
    endfor
    return r;
else
    return E_PERM;
endif
.


locations:
"Usage:  locations(object)";
"Return a listing of the location hierarchy above object.";
ret = {};
what = args[1];
while (valid(what = what.location))
    ret = {@ret, what};
endwhile
return ret;
.


all_properties_suspended:
"Copied from object utilities (#3669):all_properties by Haakon (#2) Mon Dec 28 14:24:36 
1992 PST";
what = args[1];
if (what.owner != caller_perms())
    set_task_perms(caller_perms());
endif
props = properties(what) || {};
while (valid(what = parent(what)))
    props = {@properties(what) || {}, @props};
    $command_utils:suspend_if_needed(0);
endwhile
return props;
.


connected:
":connected(object) => true iff object is a connected player.";
"equivalent to (object in connected_players()) perhaps with less server overhead";
"use object:is_listening() if you want to allow for puppets and other non-player 
objects that still 'care' about what's said.";
return 1 + connected_seconds(@args);
.


isoneof:
":isoneof(x,y) = x isa z, for some z in list y";
what = args[1];
targ = args[2];
while (valid(what))
    if (what in targ)
        return 1;
    endif
    what = parent(what);
endwhile
return 0;
.



PROPERTY DATA: