The problem of pre-room-title reports (TADS adv3 solution)

Are there examples of games that discard the “standard” travel mechanics so as to avoid this kind of problem entirely? Like, for example, concluding travel by presenting the room description but not the room title if travel produced any output before looking around in the new location?

2 Likes

I haven’t played many games myself, to know whether there are. Certainly a person could suppress or modify how the room title is printed in those cases if they wanted to… I guess my project here is meant to address those games which plan to follow the adv3/adventure game standard pattern, since a certain class of old school players would probably like to see the room title each change for clarity, without having to forfeit things like travelDesc.
It seems like it could be a tough challenge to seamlessly integrate the announcement of the new location into the text, considering how many different sources can print a preroom report, and there’s no easy way to know which ones will fire on a given travel turn, and which one will be last in line… seems as if you’d just end up making a different type of transcript transform.

1 Like

Right, that’s why I was thinking about changing the room report itself (instead of twiddling anything that might be printed before it). I think that’s always going to come from the first stanza of Traveler.describeArrival(). So you could presumably either override it for me (if you only have one player character to worry about), or replacing/modifying the method globally.

I also wonder if you could make the “figure out what bits are pre-room title” logic simpler by putting a hook in describeArrival() that checks if gActor.isPlayerChar and if so iterates through the reports that have been generated up to that point, setting a flag on them, and then use a report manager run after the action to do the typesetting based on that.

2 Likes

@rileypb did something different with the room titles in Galaxy Jones… not sure if you saw that one during Spring Thing.

Personally, I’m content with the traditional style for a traditional parser-puzzle type of game, and I’m happy with how the bold works for drawing a little extra attention to the preroom reports. For my game at least, I don’t feel the need to explore any new approaches. I’m sure there’s endless possibilities that could be engineered for authors looking for a different presentation entirely.

The room title is printed from quite a tall call stack which indeed includes describeArrival, but more nearly from lookAround->lookAroundWithin->lookAroundName. Certainly you could silence lookAroundName if you were planning to scrap the standard room title, but it seems like you would need to integrate the destination into every travelDesc or something like that.
Are we just discussing abstract ideas, or are you planning on doing some major reconfiguring to your room titles?

2 Likes

For what I’m doing I’m considering even more radical options, like just outright changing how all travel-related output is handled.

This is more or less a continuation of a train of thought I touched on in a thread from last year where I have a bunch of cases where I basically want everything involved in travel to be handled by special logic.

I think you can do this sort of thing, mostly by intercepting describeArrival() (instead of doing things like changing Room.lookAroundWithin() like I discuss in that thread) because it’s part of all of the “normal” travel methods…basically everything except teleportation via directly calling Thing.moveInto() (which would require special handling and wouldn’t ping pre-room report travel-related stuff anyway).

This seems to be a general class of problem I run into a lot…where most of the time I want to handle things normally via the normal builtin logic and then also have special cases where basically none of the default behavior happens.

2 Likes

Ok, played around with this a LOT. Every time I tried to get tricky with it, eventually ran into the issue of lack of code context in the transcript. Pushed me back to some more modest tweaks that I’m pretty happy with. Though I have lost all perspective on if my solution is OBJECTIVELY good, or I’m just pot committed at this point. My changes:

// added styletag for pre-Room text header
// can also be used manually in TravelMessage, etc notifications if want explicit mood setting
travelHdrTag : StyleTag 'travelHdr' '<b><i>' '</b></i>\n';

modify preRoomTransform
	useBold = nil  // can still set to true, as before
	insertTxt = nil // defaults to 'en route to <roomname>' message.
    // insertTxt = 'o=o=o=o=o=o=o=o=o=o\n' // replace above line to set to std character string if desired

    applyTransform(tr,vec) {
        // JJMcC:  nothing changes below until the 'else' for useBold
        local bef = vec.indexWhich({f:f.messageText_=='<.roomname>'}); 
        if(bef && bef>2) { 
                //* Found a room title. Now look for a printable report before it.
            local rep = vec.valWhich({f:    dataType(f.messageText_)==TypeSString &&
				//* If you *want* the implicits etc. to be candidates
				//* for emboldening, comment out the following line.
                                        !f.messageText_.find('<.assume>') &&
 
                                        rexMatch(notInTag,f.messageText_)!=nil && 
                                        vec.indexOf(f)<bef });
            if(rep) {
					//* Found a printable report before the room title
				if(useBold) {
						//* We will embolden words at the beginning of the string

						//* First determine our length in terms of visible characters, and not tags and spaces
					local rlen = getPrintableLength(rep.messageText_);
					local vidx = vec.indexOf(rep);
					if(rlen<boldLength && rlen>7) { 
							//* Whole message is shorter than our boldLength: tags at beginning and end
						rep.messageText_ = addTags(rep.messageText_);
					}
					else if(rlen<8) { 
							//* The message we found is probably a single word. Embolden part of the next 
							//* message as well, if there is one.
						rep.messageText_ = addTags(rep.messageText_);
						local rep2 = vec.valWhich({f:   
													dataType(f.messageText_)==TypeSString && 
													rexMatch(notInTag,f.messageText_)!=nil && 
													vec.indexOf(f)<bef && 
													vec.indexOf(f)>vidx});
						if(rep2) rep2.messageText_ = addTags(rep2.messageText_,boldLength-rlen);
					}
						//* Else find soonest word ending after boldLength to insert </b>
					else rep.messageText_ = addTags(rep.messageText_,boldLength);
				}

                // JJMcC changes below
                //  DEFAULT is to add "en route to <roomname>" title above pre-room text
                //  can override by providing explicit insertTxt (above)
				else {
                    local headerTxt = (insertTxt == nil) ?
                        '<.travelHdr>\ven route to ' + vec[bef+2].messageText_ + '<./travelHdr>' : insertTxt;

						//* Insert either spaces or symbols before the report to draw attention.
						//* Try to find the first printable character for insertion point so we don't
						//* swallow any leading <.p>'s etc.
					local startIdx = (rexMatch(firstWordStart,rep.messageText_) ?? 1);

                    //  If a manual header provided in immediate text, do not prepend with 'en route'
                    if (!rep.messageText_.find('<.travelHdr>'))
                        rep.messageText_ = rep.messageText_.splice(startIdx,0,headerTxt);
				}
            }
        }
   }
;

Allows a few things:

  • Can manually specify pre-Room text headers with <.travelHdr>…<./travelHdr> tags, though depending on code run the risk of additional pre-Room text appearing before it. ie
    noteTraversal(traveler) {
        "<.travelHdr>Through the Mist<./travelHdr>
        You stumble uncertainly into the swirling mist.  ";
    }
  • Absent manual titles, all pre-Room text will be prefaced with en route to <Roomname>
  • That can also be overridden with a character string

I am going to carry this solution for a while. Will update if any additional tweaks (or gotchas) present themselves.

2 Likes

I’m uncertain if you’ve implemented this yet, but it might be good to have an easily-modified true/nil value to have these decorative highlights enabled or disabled. The reason being: A screen reader player would hear the pre-room-title report read out normally, but then the highlights would be read out like “oh equals-sign oh equals-sign oh equals-sign” etc.

So if this value is easy to modify, then when someone adds a screen reader yes-no prompt to their game, then they can easily add an instruction to the “using a screen reader” choice to disable the highlights.

EDIT:

Unless editing travelHdr solves the problem, but I have yet to learn how tag editing works.

2 Likes

I think what you describe is very manageable by overriding the preRoomTransform.insertTxt property. For my part, I have default set to text header, not pattern header. The reader warning on patterns is well taken.

Kind of orthogonal. Tags seem most useful to provide text formatting, and have the advantage (in this case) of surviving into the transcript as ‘flags’ of sorts.

2 Likes

Ok, I can’t promise I’m done fiddling, but I’m done fiddling FOR NOW. To recap, original code provided these capabilites:

  • By setting useBold, bolding the first boldLength characters in pre-Room text.
  • By setting insertText to some character string, that string becomes a title of sorts for pre-Room text, though useBold has precedence if both are set.

Additional capabilities in code below:

  • <.travelHdr><./travelHdr> tags so author can define specific pre-Room text Titles where desired. Where defined, preempts any other capabilities. See example usage here: The problem of pre-room-title reports (TADS adv3 solution) - #26 by jjmcc
  • Set multiSentenceOnly if you do not wish to emphasize single sentence pre-Room text. That ended up looking wonky to me.
  • If insertText and useBold both set to nil, auto-insert the canned header en route to <Roomname> before pre-Room text.

Required some text reordering, so just including the whole thing one more time.

/*
 *  johnyywz00 (John Ziegler) code to highlight transition text - so people know to read it!
 *      started with bold first boldLength characters or custom insertTxt
 *  JJMcC additional capabilities:  default 'en route' message, manual <.travelHdr> tags
 *      and optional multiSentence minimum criteria
 *
 */
preRoomTransform: TranscriptTransform
    multiSentenceOnly = true // set if only want to highlight multi-sentence pre-Room reports
	useBold = nil   // set to bold first boldLength characters, with no title.  Takes precedence over insertTxt
	boldLength = 20
	insertTxt = nil // pre-Room title character string.  if nil, 'en route to <roomname>' message prepended instead
    // insertTxt = 'o=o=o=o=o=o=o=o=o=o\n' // example alternate title
		//* Look for reports' messageText_ that has printable characters outside of angle bracket delimiters
    notInTag = static R'(%s|%v)*((<langle>[^>]+<rangle>)(%s|%v)*)*<AlphaNum|Punct>|^(%s|%v)*<AlphaNum|Punct>'
    wordEnd = static R'%>'
    lastWordEnd = static R'<AlphaNum>%s*((<langle>[^>]+<rangle>)(%s|%v)*)*$'
    firstWordStart = static R'(%s|%v)*((<langle>[^>]+<rangle>)(%s|%v)*)*<AlphaNum>'
    getPrintableLength(txt) { 
        local startIdx = rexMatch(firstWordStart,txt) ?? 1;    
        local res = rexSearch(lastWordEnd,txt);
        local endIdx = res ? res[1] : txt.length;           
        return endIdx - startIdx + 1;
    }
    addTags(txt,closeIdx?) {
        local startIdx = (rexMatch(firstWordStart,txt) ?? 1);    
        local endIdx, res;
        if(closeIdx) res = rexSearch(wordEnd,txt,closeIdx);
        else res = rexSearch(lastWordEnd,txt);
        endIdx = (res ? res[1] : txt.length) + 1;  
        txt = txt.splice(endIdx,0,'</b>');
        return txt.splice(startIdx,0,'<b>');
    }
    applyTransform(tr,vec) {
			//* The first report will typically be a CommandSepAnnouncement. If we find a room 
			//* title report, and it's not second in line, we will check the intervening reports for 
			//* one which has printable characters. We'll draw attention to it with bold or symbols.
        local bef = vec.indexWhich({f:f.messageText_=='<.roomname>'});
        if(bef && bef>2) {
                //* Found a room title. Now look for a printable report before it.
            local rep = vec.valWhich({f:    dataType(f.messageText_)==TypeSString &&
				//* I chose to skip implicit reports and object announcment reports as candidates
				//* for emboldening. So that in
				//* - - - - - - 
				//*    (first getting out of the iron maiden)
				//*    You leave the horrific chamber to the west, doing something very
				//*    noteworthy on the way...
				//*    
				//*            o=o=o ROOM TITLE o=o=o
				//*    This is the next room, where you probably missed the bit about
				//*    doing something noteworthy on your way here.
				//* - - - - - - -
				//* we will not embolden the implicit report, but instead will embolden 
				//* "You leave the horrific...".  If you *want* the implicits etc. to be candidates
				//* for emboldening, comment out the following line.
                                        !f.messageText_.find('<.assume>') &&
 
                                        rexMatch(notInTag,f.messageText_)!=nil && 
                                        vec.indexOf(f)<bef });
            if(rep) {
					//* Found a printable report before the room title
                    //* JJMcC detect and flag mult-sentence pre-report, if required
				local rlen = getPrintableLength(rep.messageText_);
    			local vidx = vec.indexOf(rep);
                local multiSentenceRegex = R'<period><Space|Newline>+<Alphanum>';
                local allPreRoomReport = '';
                local multiSentenceOk = true; // default, overridden if multiSentenceOnly set
                if (multiSentenceOnly) {
                    for (local i=vidx; i<bef; i++)
                        if (vec[i].messageText_ != nil)
                            allPreRoomReport += vec[i].messageText_.splice(vec[i].messageText_.length(),0,' ');
                            //"report[<<i>>] = .<<vec[i].messageText_>>.\n"; // DEBUG 
                    multiSentenceOk = (allPreRoomReport.find(multiSentenceRegex));
                }

                //* JJMcC modify if no manual <.travelHdr> AND clears multisentence requirement, if any
                if (!rep.messageText_.find('<.travelHdr>') && multiSentenceOk) {
    				if(useBold) {
						//* We will embolden words at the beginning of the string
						//* First determine our length in terms of visible characters, and not tags and spaces
					    if(rlen<boldLength && rlen>7) { 
							//* Whole message is shorter than our boldLength: tags at beginning and end
						    rep.messageText_ = addTags(rep.messageText_);
					    }
					    else if(rlen<8) { 
							//* The message we found is probably a single word. Embolden part of the next 
							//* message as well, if there is one.
						    rep.messageText_ = addTags(rep.messageText_);
						    local rep2 = vec.valWhich({f:   
													dataType(f.messageText_)==TypeSString && 
													rexMatch(notInTag,f.messageText_)!=nil && 
													vec.indexOf(f)<bef && 
													vec.indexOf(f)>vidx});
						    if(rep2) rep2.messageText_ = addTags(rep2.messageText_,boldLength-rlen);
					    }
						//* Else find soonest word ending after boldLength to insert </b>
					    else rep.messageText_ = addTags(rep.messageText_,boldLength);
				    }
				    else {  //* not useBold, so auto or defined header
                        local headerTxt = (insertTxt == nil) ?
                            '<.travelHdr>\ven route to ' + vec[bef+2].messageText_ + '<./travelHdr>' : insertTxt;
						//* Insert either spaces or symbols before the report to draw attention.
						//* Try to find the first printable character for insertion point so we don't
						//* swallow any leading <.p>'s etc.
					    local startIdx = (rexMatch(firstWordStart,rep.messageText_) ?? 1);
                        rep.messageText_ = rep.messageText_.splice(startIdx,0,headerTxt);
                    }
				}
            }
        }
    }
;
modify CommandTranscript
    transforms_ = static inherited + preRoomTransform
;
travelHdrTag : StyleTag 'travelHdr' '<b><i>' '</i></b>\n';

I am powerless before the siren call of shiny things.

2 Likes

Love it, glad to see folks building on my idea!

2 Likes