Remapping between Standard/Literal/Topic Actions

Is there a word for ‘the allure of mechanical problem solving that causes the artistic component of your game to sputter and stall?’ I’ve just been calling them ‘shiny things.’ And man, I got them in spades.

The latest was the desire to implement a SEARCH FOR capablility ala >search pile of papers for deed Note that >search pile of papers might turn up the deed, or something else of interest. Ideally I’ve messaged things correctly so the player is hinted when there is more to find. IAC, SearchForAction.

Since SearchForAction shares a lot of the grammar of ConsultAboutAction, my first, braindead attempt was along the lines of

dobjFor(ConsultAction)
     maybeRemapTo(!ofKind(Consultable), SearchFor, self, IndirectObject)

It didn’t work, in either direction. The former wants to be a standard TIAction, whereas the latter is a library-defined TopicTAction. The error messages seemed to point to that disconnect being the source of my problems: singleIobj cannot be remapped to singleTopic and vice versa. I came up empty on trying to figure out how to support deeply overlapping grammar for the two command types, and instead found this thread: How To Remap Literal Action Into a Topic

That thread led me to this implementation, where ConsultAbout is the Master, that redirects to SearchFor when dobj is NOT a Consultable:

/* 
 *	SearchFor Action, to be able to search for specific stuff
 */
// accepts all Hidden objects as possibly in scope
//
class IobjResolverSearchFor : IobjResolver
	objInScope(obj) { return (obj.ofKind(Hidden) || inherited(obj)); }
;
DefineTIAction(SearchFor)
	actionAllowsAll = nil
    createIobjResolver (issuingActor, targetActor) {  // use resolver above
		return new IobjResolverSearchFor(self, issuingActor, targetActor);
	}
;
VerbRule(SearchFor)
    //  original syntax deeply intersected Consult About
    //('search' | 'dig'| 'li' | ('look' | 'l') ('in' | 'inside' | 'on') ) dobjList 'for' singleIobj
    //| ('find' | (('search' | 'dig'| 'look' | 'l') 'for')) singleIobj ('in' | 'inside' | 'on') dobjList : SearchForAction

    // reduced, unique-only syntax
    ('dig' | 'dig' 'in') dobjList 'for' singleIobj 
    | ('dig' | 'look' | 'l') 'for' singleIobj
      ('in' | 'inside' | 'on') dobjList : SearchForAction
    verbPhrase = 'search/searching (what) (for what)'
	askDobjResponseProd = nounList
    askIobjResponseProd = forSingleNoun
;
/*  Still need to figure out how to change this to more generic..
 *  below does not work
 *
modify VerbRule(ConsultWhatAbout)
     verbPhrase = 'search/searching (what) (for what)'
; */
modify Thing
	dobjFor(SearchFor) {  // results/action() override in 'searchable' instances
		preCond = [touchObj]
	}
	iobjFor(SearchFor) {
		preCond = []
		verify() {
            if (isIn(gActor))
                illogical('{You/he} {are} already holding {that iobj/him}.  ');
            if (canBeSeen) illogical('{It\'s iobj/he\'s} right here!  ');
            if ((!ofKind(Hidden) && seen) || discovered)
                illogical('{You/he} already know{s} where {that iobj/he} is.  ');
        }
	}
	cannotBeFoundHereMsg = 'It is unlikely {that iobj/he} could be found here.  '
    dobjFor(ConsultAbout) {
        verify() { }  // now everyone can be 'consulted': will remap to search
        action() {
        //  This is the meat of it.  Reassemble the original Topic text into a
        //   SearchFor command, and resubmit it so it will remap to an iobj
        //
            if (!ofKind(Consultable)) {
                local myName = '';

                // reassemble the Dobj words that resolved to self
                //
                foreach(local x in gAction.getDobjWords()) myName += x + ' ';

                // tokenize the replacement command and get the rest of line
                //
                local cmdTokenList = 
                    cmdTokenizer.tokenize('dig <<myName>> for ' + gTopicText);
                local nextCommandTokens =
                    gAction.tokenList.sublist(gAction.lastTokenIndex + 1);

                // retry parsing the edited token list - and followon command
                throw new RetryCommandTokensException(cmdTokenList + nextCommandTokens);
            }
            else inherited;
        }
    }
;

I still need to fix the ConsultWhatAbout prompt, and decide if I can live with
>consult pile of papers about deed
working just fine. But it struck me that the more general case of remapping between Action types could be of interest.

Also curious if maybe I’m missing a better way to manage overlapping grammar verbs.

2 Likes

I feel like I dealt with most of my weird grammar stuff a year or two ago, so it’s not really fresh in my mind… I’m pretty sure I did some kind of remapping from gObjs to literals/topics…

Finding a deed in a stack of general paperwork is a breeze, because its heading stands out. BROWSE PILE OF PAPER ought to work. OTOH, if the paperwork is all legal, a browsing can fail, even for a lawyer, so SEARCH in this case has sense, searching in a paperwork for a specific document means reading the paperwork for the specific document.
Lastly, search is needed when the paperwork is of the same nature:
SEARCH FOR OAKHILL MANSION DEED IN THE DEEDS FILE DRAWER
(btw, a true parser nightmare…)

so, handling of readables IS a potential issue (I’m actually working toward implementing a sizeable library in an IF centered about gaining knowledge & lore as “treasures”…)

Best regards from Italy,
dott. Piergiorgio.

1 Like

It turns out there was a better way (at least for this application) after all. The above had quite a few kludgey workarounds, not the least of which due to me sometimes needing dobj to take the lead and other times iobj. After patching it the Nth time, I instead went with GlobalRemapping which is so far more elegant from the user’s perspective. In the case of GlobalRemap, it made more sense from user perspective to make SearchFor the default, so required some surgery on existing ConsultAbout syntax:

/* 
 *	SearchFor Action.  Note, IOBJ checks but DOBJ executes
 */
modify Thing
	dobjFor(SearchFor) {  // allow all
    	preCond = [touchObj]
        verify() { }
        action() { 
            if (gIobj.isIn(self)) {
                "You found it!  ";
                gIobj.discover();
                nestedAction(Take, gIobj);
            }
        }
	}
	iobjFor(SearchFor) {
		preCond = []
		verify() {
            if (isIn(gActor))
                illogical('{You/he} {are} already holding {that iobj/him}.  ');
            if (canBeSeen)
                illogical('{It\'s iobj/he\'s} right here!  ');
            if ((!ofKind(Hidden) && seen) || discovered)
                illogical('{You/he} already know{s} where {that iobj/he} is.  ');
        }
        check() { if (!isIn(gDobj)) failCheck(cannotBeFoundHereMsg); }
	}
	cannotBeFoundHereMsg = 'It is unlikely {that iobj/he} could be found here.  '
;

// accepts all Hidden objects as possible, including default intangible one below
//
class IobjResolverSearchFor : IobjResolver
	objInScope(obj) { return (obj.ofKind(Hidden) || inherited(obj)); }
;
DefineTIAction(SearchFor)
	actionAllowsAll = nil
    createIobjResolver (issuingActor, targetActor) {  //see below
		return new IobjResolverSearchFor(self, issuingActor, targetActor);
	}
;
VerbRule(SearchFor)
    ('dig' | 'dig' 'in' | 'li' | 'lb' | 'lt' | 'search' | //'lu'
    | ('look' | 'l' | 'search' ) ('in' | 'inside' | 'on' | 'under' | 'behind' | 'through') ) singleDobj 'for' singleIobj
    | ('find' | ('search' | 'dig' | 'look' | 'l') 'for') singleIobj
        ('in' | 'inside' | 'on' | 'under' | 'behind') singleDobj : SearchForAction
    verbPhrase = 'search/searching (what) (for what)'
	omitIobjInDobjQuery = true
    askDobjResponseProd = forSingleNoun
;
VerbRule(SearchWhatFor)
    ('find' | ('search' | 'dig'| 'look' | 'l') 'for') singleIobj : SearchForAction
    verbPhrase = 'search/searching (what) (for what)'
    whichMessageTopic = DirectObject
    construct()
    {
        /* set up the empty direct object phrase */
        dobjMatch = new EmptyNounPhraseProd();
        dobjMatch.responseProd = inSingleNoun;
    }
;
modify VerbRule(ConsultAbout)
    'consult' singleDobj ('on' | 'about') singleTopic
    // | 'search' singleDobj 'for' singleTopic
    //| 'search' 'in' singleDobj 'for' singleTopic
    | (('look' | 'l') ('up') //| 'for')
       // | 'find'
       // | 'search' 'for'
       | 'lu' | 'read' 'about' )
         singleTopic ('in' | 'on') singleDobj
    | ('look' | 'l') singleTopic 'up' 'in' singleDobj
    :
;
replace VerbRule(ConsultWhatAbout)
    (('look' | 'l') ('up' | 'for')
     // | 'find'
     // | 'search' 'for'
     | 'lu' | 'read' 'about') singleTopic
     | ('look' | 'l') singleTopic 'up' : ConsultAboutAction
    // changed from default to accomodate SEARCHFOR reusing this vocab
    verbPhrase = 'look/looking up (what) (in what)'  
    whichMessageTopic = DirectObject
    construct()
    {
        /* set up the empty direct object ploohrase */
        dobjMatch = new EmptyNounPhraseProd();
        dobjMatch.responseProd = inSingleNoun;
    }
;

This is the new bit, replacing complicated dobjFor(ConsultAbout) remapping

consultableSearchForRemap : GlobalRemapping
    getRemapping(issuingActor, targetActor, action) {
        if (action.ofKind(SearchForAction) ) {

    // NOTE, because command not yet parsed, must 'prefetch' DOBJ
    //  This requires specific object decode, so find Consultable that matches  
    //
          local dobjIsConsultable = (instanceWhich(Consultable,
                {x: action.canDobjResolveTo(x) }));

          // simpler if just one object in game
          //local dobjIsConsultable = (action.canDobjResolveTo(encyclopedia));

          if (dobjIsConsultable) { 

            local newAction = ConsultAboutAction.createActionInstance();
            newAction.setOriginalAction(action);

            local dobj = action.dobjMatch;
     // because want to recast iobj as Topic
            local iobj = newAction.reparseMatchAsTopic(action.iobjMatch, issuingActor, issuingActor);

            newAction.setObjectMatches(dobj, iobj);
            return [issuingActor, newAction];
        }
        return nil;
    }
;
4 Likes