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

LATER LATER EDIT: I posted some updated code later in the thread, and am pasting it here now too over the old. The code block here should be good to go.

LATER EDIT: this code doesn’t cope with every situation perfectly. I suspect that an HTML tag is getting split somehow, but I haven’t looked into it yet. The only bug I’ve noticed so far is where the only pre-room report is an implicit announcement… the closing tag gets lost somewhere and the rest of the desc is in bold.
Second edit: I think <.p> markers are playing the same kind of havoc. [end edit]

I’m sharing a snippet of code to address what I expect will be a problem for more people than just myself. Whether I’m playing games myself, or reading transcripts of others playing my game, I find that it is very easy for the player to overlook reports/printed text that comes before a room title. In adv3, this text comes from things like travelDesc, enteringRoom, reportBefore, following NPCs and more.

This experimental solution allows the gamemaker to either auto-embolden the first several words of the pre-room-title reports, or to auto-insert extra spacing or eye-catching symbols before the report, according to the author’s taste.

Unfortunately this is only available for adv3, because I’m not aware of any abilities that adv3Lite has to buffer and rearrange outputted text.

//THIS USED TO BE SOMETHING ELSE... I've pasted in what I hope to be more correct
//code from the lower post

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
;
6 Likes

I am in the midst of taking a few weeks off to play Spring Thing games and have been away from the Code Zone for a bit. This was one of the last threads I read before TADS holiday. I was unsold on the problem statement at the time, but let me say this exact problem has bitten me at least 3 times in Spring Thing.

“You never told me that game!”
*check transcript*
“Eep, it’s in text prior to room title.”

So thanks for posting, will be my first stop when I get back.

2 Likes

I’m still refining my emboldening approach, which I think I am liking very well as I use it… I wouldn’t use the posted code as-is, I can already tell you a few other things it needs to handle…

2 Likes

Lol, thanks for heads up, will check with you in a week or so :]

2 Likes

I think I might include something like this in my post-comp release.

I didn’t put it in originally because I Am Prey was designed with screen readers first and visual readers second, so I think I was just habitually avoiding any use of ASCII symbols during development (with exception of the parkour bullet symbols, but I implemented a screen reader alternative there).

I’ll need to look over my code closely, because there are a lot of things that happen before the player goes into another room, sometimes, and it doesn’t just come from travel code, so I’ll need a way to check if the player’s action would lead to another room, and then place emboldened highlights before the following output text, and then before the next rooms describes itself. This way I can make sure that all systems which print during this period are contained within the highlights.

1 Like

I would be very curious to see how you deal with the situation in Lite. The transcript is what makes it work slick in adv3. I guess I’m also not sure of what your goals are… if you want every preroom report to be bold in entirety, then I guess that’s just a matter of adding a whole bunch of bold tags.
My situation, like you are saying, is that preroom reports can come from lots of different sources other than just travelDescs, and I only wanted the first few words of the topmost report to be bold. I was able to do that with the Transcript object, but I don’t know how on earth you’ll scan every turn in Lite to figure out what preroom reports will show and if so, which one will end up first in line. I’d really like to know, though, because I was hoping for there to be a similar solution for Lite users. I have been using the bold feature as I continue to develop my game, and I think I am really pleased with the effect so far.
Good luck with whatever you attempt!

1 Like

Yeah, I was just thinking of setting stuff up to get this effect:

> GO NORTH

********************************
(Output from subsystem 1)

(Bunch of stuff from subsystem 2)

(travelDesc)

(Other stuff that apparently prints late)
********************************

THE AVIARY (room title)
You are not sure if the room is square or round, because the...(etc)

EDIT: I also have not put too much planning into this yet, so I’m not claiming this is actually my solution.

1 Like

Are you going to have to use capture filters to determine which of those four sections are outputting on a given turn, to know where to insert bold and closing tags? Or are you using a different approach than emboldening the leading few words?

1 Like

Well… I don’t think I know what capture filters are, but I was gonna probably gonna have some method that queries an action, and returns true if the action is prepared to move the player.

Based on this return, I’ll output the first half of the tags before any other outputs, probably by modifying core Action logic.

And then based on the same query return, I’ll add the second half in Action.turnSequence, probably.

1 Like

StringCaptureFilter is one of the classes in the library. Theoretically, you can execute a group of methods, or perhaps a whole action turn, capturing any outputted text into a variable, where it can be inspected or modified before being displayed. You can temporarily install the filter on the main output stream at any time and then remove it after the call. Or leave it on permanently, and only make use of it when desired…

1 Like

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