/*************************************************************************
* Possess.t: Using possessives as adjectives.
*
* By Garth Dighton, with help from Mike Roberts and Kevin Forchione on RAIF
* This module is freely distributable and modifiable, as long as the credits
* remain intact.
*
* This module enables players to refer to objects in the possession of actors
* with possessive adjectives: "ASK JANE ABOUT HER BOX, ASK BOB ABOUT GEORGE'S
* BOX", or similar.
*
* To use, each Actor should have the possessives property -- a list of
* single-quoted words which are added to the adjectives list of objects they
* carry. 'Her' and 'his' can be ignored.
* If any of these functions are overridden in your code, you'll have to make
* adjustments:
* thing.moveInto
* parseNounPhrase
* parseDisambig
* Functions added
* thing.Grab() -- Hook called when something is removed from an object's
* possession.
* thing.Accept() -- Hook called when somthing is moved into an object's
* possession.
* These functions are overridden in Actor to provides the necessary
* functionality. You can still add your own code, as long as the Actor code
* gets called
modify thing
// Add an Accept(obj) hook to moveInto:
moveInto(obj) =
{
local loc;
/*
* For the object containing me, and its container, and so forth,
* tell it via a Grab message that I'm going away.
*/
loc = self.location;
while (loc)
{
loc.Grab(self);
loc = loc.location;
}
if (self.location)
self.location.contents = self.location.contents - self;
self.location = obj;
if (obj) {
obj.contents = obj.contents + self;
loc = self.location;
while (loc)
{
loc.Accept(self);
loc = loc.location;
}
}
}
Grab(obj) = {}
Accept(obj) = {}
;
modify Actor
Grab(obj) = {
local i;
for (i = 1; i <= length(self.possessives); i++) {
delword(obj, &adjective, self.possessives[i]);
}
}
Accept(obj) = {
local i;
for (i = 1; i <= length(self.possessives); i++) {
addword(obj, &adjective, self.possessives[i]);
}
}
;
// Returns true if the word is a noun of the object
// Used to eliminate adjective-only matches. Necessary because parseNounList
// does _not_ seem to return the correct value for PRSFLG_ENDADJ!!!
isNounOf: function (word, obj)
{
local lst = getwords(obj, &noun);
if (find(lst, word)) return true; else return nil;
}
// parseNounPhrase function by Kevin Forchione and Mike Roberts, in response
// to a request for help made on rai-f.
// Modified to handle 'his' (though not 'my' or other possessives). Also
// modified to check that the objects exist within a valid person (ideally the
// current 'her' or 'him').
// Further modified to eliminate matches ending in adjectives if there's any
// other match.
parseNounPhrase: function(wordList, typeList, current_index,
complain_on_no_match, is_actor_check)
{
local i, next_index, next_token, ret, lst = [];
local pos = parserGetObj(PO_HER);
local found = nil;
local gender = &isHer;
local eliminateAdj = nil;
local lastword = nil;
/* let the parser handle noun phrases that don't start with 'her' */
if (wordList[current_index] != 'R' and wordList[current_index] != 'his')
return PNP_USE_DEFAULT;
/*
* 'her' is the last word of the noun phrase, so it is not
* being used as a possessive pronoun.
*/
if (length(wordList) == current_index)
return PNP_USE_DEFAULT;
/* set the next_index */
next_index = current_index + 1;
/* get the next word's type */
next_token = typeList[next_index];
/*
* If the next word is an article then 'her' is not being used as a
* possessive pronoun.
*/
if ((next_token & PRSTYP_ARTICLE) != 0)
return PNP_USE_DEFAULT;
/*
* If the next word cannot be an adjective, noun, or plural then
* 'her' is not being used as a possessive pronoun.
*/
if ((next_token & PRSTYP_ADJ) == 0
&& (next_token & PRSTYP_NOUN) == 0
&& (next_token & PRSTYP_PLURAL) == 0)
return PNP_USE_DEFAULT;
ret = parseNounList(wordList, typeList, next_index,
nil, nil, nil);
/*
* We don't have a syntactically valid noun phrase, so assume
* that 'her' is not being used as a possessive pronoun.
*/
if (ret == nil)
return PNP_USE_DEFAULT;
/*
* There are 2 cases to examine: no noun phrase or a syntactically
* valid noun phrase, but with no matching objects.
*/
if (length(ret) == 1)
/*
* No noun phrase. The first word isn't a noun, adjective,
* article, etc.
*/
if (ret[1] == current_index)
return PNP_USE_DEFAULT;
else {
goto noObjects;
}
/*
* We have a valid noun phrase, let's assume that 'her' is being
* used as a possessive. We'll restructure the return from
* parseNounList() so that it can be returned from
* parseNounPhrase().
*/
lst += ret[1];
lastword = wordList[ret[2][2]];
/* First we see if there are any objects within the current "her"
* object. If so, we will return those possible objects only.
*
* We also note if the match ended with a noun -- if so, we will later
* eliminate all adjective-only matches
*/
for (i = 3; i <= length(ret[2]); i += 2) {
if (not ret[2][i].isIn(pos)) continue;
lst += ret[2][i];
lst += ret[2][i+1];
if (isNounOf(lastword, ret[2][i])) eliminateAdj = true;
found = true;
}
/* If there weren't any valid objects within 'her', check for objects
* within _any_ female.
*/
if (not found) {
for (i = 3; i < length(ret[2]); i += 2) {
if (ret[2][i].location == nil or not ret[2][i].location.(gender))
continue;
lst += ret[2][i];
lst += ret[2][i+1];
if (isNounOf(lastword, ret[2][i])) eliminateAdj = true;
found = true;
}
}
if (not found) goto noObjects;
if (not eliminateAdj) return lst;
// Eliminate any match which did not end in a noun. We use "ret" instead
// of "lst" for the return value in this case.
ret = [] + lst[1];
for (i = 2; i <= length(lst); i+=2) {
if (isNounOf(lastword, lst[i])) {
ret += lst[i];
ret += lst[i+1];
}
}
return ret;
noObjects:
/*
* Syntactically valid noun phrase with no matching objects!
*/
"I don't see any ";
for (i = next_index; i <= length(wordList); ++i)
{
say(wordList[i]);
" ";
}
"here. ";
return PNP_ERROR;
}
parseDisambig: function(str, lst)
{
local i, tot, cnt;
local mystr = str;
// Remove possessives (R, his, my, and words ending in 's) from the noun
// phrase before asking. This is particularly necessary in the case of R,
// because "Which R box do you mean..." is particularly annoying!
local match = reSearch('R |his |my |%<%w*\'s%>', str);
if (match) {
// using length(str) for the length of the second substr will
// guarantee that it goes to the end-of-string. A hack, but easier
// than calculating.
mystr = substr(str, 1, match[1]-1) +
substr(str, match[1]+match[2], length(str));
}
"Which << mystr >> do you mean, ";
for (i = 1, cnt = length(lst) ; i <= cnt ; ++i)
{
lst[i].thedesc;
if (i < cnt) ", ";
if (i + 1 == cnt) "or ";
}
"?";
}
#ifdef VERSION
// versionTag for Jeff Laing's version.t module
possessiveVersion: versionTag
author = 'Garth Dighton'
func = 'possessive handling'
;
#endif VERSION