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

I’m just not sure how well embedded PauseForMores will work with capturing and then printing… I seemed to have some quirks with it in adv3 at least.

1 Like

Huh. I’ll probably look into this, then. Might make the work a easier. Thank you~

1 Like

Probably the easiest way is to use mainOutputStream.captureOutput method. I’m not sure this will solve your problems, but worth knowing about…

1 Like

Okay! I do believe this little gadget is somewhat better oiled now.
It will still bear some testing, I’m sure, but I seem to have overcome the glitches with angle-bracket tags. As before, you can draw attention to the first pre-room-title report by either emboldening or inserting a block of symbols/spaces, determined by the useBold property.
@jjmcc

preRoomTransform: TranscriptTransform
	useBold = true
	boldLength = 20
	insertTxt = 'o=o=o=o=o=o=o=o=o=o\n'
		//* 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
				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);
				}
				else {
						//* 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,insertTxt);
				}
            }
        }
   }
;

modify CommandTranscript
    transforms_ = static inherited + preRoomTransform
;
2 Likes

So… that two week break turned into two months! Have not been idle, I actually used Spring Thing momentum to power through two more rooms/puzzles in my WIP! Even weirder, I did not need to phone a friend the whole time, I figured stuff out on my own! Learning?

IAC, as I near completion of ANOTHER room, next on my list is to play with this.

Also, I am finding RemoteViewConnector and my sub-classed PassageSenseConnector ubiquitously useful. Gonna be embarrassing when no one bothers to >LOOK THRU DOORWAY

2 Likes

Cool! I love using it in my own game.

Did you ever play around with the bold thing?

Personally, after coding since mid-80s, I find the usage of style enchace the readability… Here the bold style evidencing the keywords/library point clearly how he solved the issue.

(I’m not sure if I have written clearly… not in style, in grammar & wording)

Best regards from Italy,
dott. Piergiorgio.

2 Likes

I did! The bolding seemed to work like a champ with one minor glitch: I think you are missing the final close-brace before the preRoomTransform semi colon. Worked fine when I added it.

preRoomTransform: TranscriptTransform
	useBold = true
	boldLength = 20
	insertTxt = 'o=o=o=o=o=o=o=o=o=o\n'
		//* 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) { 
              /*  JJMcC omitted all this for clarity
               */
        }
    } // JJMcC added this one
;

Code read like setting useBold = nil should cause to insert text, but that didn’t seem to work for me. Bold looked better anyway, so didn’t dive into that. I got a wild hare though, that I might want transition text titles (similar to room titles, but italic/bold ie Down the Stairs to the Landing) so started fiddling with that, acknowledging the trick you are using doesn’t have access to the initiating methods or variables.

It’s also a lot more intrusive to code (basically requiring a per-instance management), where yours is just automatic across the entire game. Currently, I am fiddling with <.tags> to see if I can get the benefits of your code when not specified, but more specific (and low code overhead) titling when I want it. Effectively, passing the text ‘author’ with magic flags your parser will use, that will either print an interstitial title if avail, or revert to default bolding behavior. Nothing to post here yet, but if I crack it will let you know.

2 Likes

JJ, thanks for the catch on the closing brace! Also for noting that insertTxt didn’t work… here was the glitch:

  //this v v
rep.messageText_.splice(startIdx,0,insertTxt);
  //should have been this v v
rep.messageText_ = rep.messageText_.splice(startIdx,0,insertTxt);

I made the edits to the code posted in this thread. I seem to have a pretty poor track record with ripping code out of my game and hastily repackaging it for general use without some hitches like that.

Will be interested to see what modifications and sophistications you make to the transform!

1 Like

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