make output text nouns clickable to send to command line?

george
15 Oct 2013, 15:48
Some of you might be familiar with the feature in iphonefrotz where you can tap on a word to send it to the command line. I'm interested in doing something like that with Quest. What would I need to code to make that happen?

From what I already know it seems like I might have to work with the existing functionality for drop-down verb menus. Also I'm not sure if it's possible to write a word to the command line without executing the command line. Any pointers?

jaynabonne
15 Oct 2013, 18:37
Since I'm not familiar with iphonefrotz, it might be helpful to describe the behavior you'd like to see. I get generally that you want to click on a word and have it enter/execute something at the command line, but I'm not sure of the specifics. Do you mean you'd click a word like "apple" and have it do "look apple", or would it be more like you'd have a word for "look" and have it execute look?

Quest has support for different styles of hyperlinks. You can have an "object" hyperlink which will pop up object's verb menu when clicked on.

msg("You see an {object:apple}.")

You can have a "command" link which will execute a command when clicked on.

msg("Would you like to {cmd:KillTroll:kill the troll} or {cmd:Escape:run for the hills}?")

In this case, you will see "kill the troll" and "run for the hills" as hyperlinks, and when clicked, they will execute the command KillTroll and Escape respectively.

Let me know if that is along the lines you're referring to. If you want to click on "apple" and have it enter "apple" at the command line, then you'd need to adapt the parser to handle object input like that- that is, a bare object name on its own (and still decide what you want it to do).

george
16 Oct 2013, 02:00
jaynabonne wrote:If you want to click on "apple" and have it enter "apple" at the command line, then you'd need to adapt the parser to handle object input like that- that is, a bare object name on its own (and still decide what you want it to do).


That is more what I'm going for. So you click on 'apple' and it enters it on the command line, without executing anything. To execute you would still press return or perhaps a button on the command line. So for example, you could type 'look', click on 'apple' in the room description, and it would send apple to the command line. Then you could press enter to do 'look apple'. Not that I'd want to do it this way! But just for a simple example.

jaynabonne
16 Oct 2013, 09:15
It could be done using some HTML and JavaScript code. If I get a little time, I could try to whip something up.

My take though: it might be a bit strange for people who are used to hyperlinks just *doing* something (whether it be execute a command or just bring up choices). I'm sure people could figure out, as long as you hyperlinks are consistently used, but it might be a bit jarring at first to have to type *and* click on things to complete a single command. I'm curious to see how it would work. :)

Pertex
16 Oct 2013, 12:12
jaynabonne wrote:but it might be a bit jarring at first to have to type *and* click on things to complete a single command. I'm curious to see how it would work.


Yes, but you could completely remove the typing if you could display a list off verbs in an extra bar next to the buttom of the screen. So you could first click a verb and then the object to create a valid command. You wouldn't need to use the verb menu of an object and you would get a game like the old scumm games like Maniac Mansion. Sounds great

george
16 Oct 2013, 15:11
Yes I agree for the simple case of look or go this would be a little weird, but I'm imagining it used for more complex actions. Still unsure of how it would all work but if we can get a working prototype then we can play with it.

So when you say this could be done using HTML and JS do we have to insert the word clicked into an HTML element (the command line) or something like that? Then in Quest when you press return or 'do command' button or whatever that will take whatever is in the element and execute it?

jaynabonne
16 Oct 2013, 15:59
Yes, that's exactly what I was thinking. :)

jaynabonne
16 Oct 2013, 23:17
Ok, here's a sample. Let me know if you need any improvements.

You need to send to Quest the InsertInputScript field contents somehow. I chose to make it a game string attribute and then send it using OutputTextNoBr. This needs to be done whenever the screen is cleared, unfortunately. If that is too onerous, we can create a .js file that you simply include in your game. It all depends on which you consider the lesser pain.

You then indicate a word is "clickable" by calling the function ClickText with the word. It will return a string to be output.


george
17 Oct 2013, 00:49
That's brilliant!

Here is maybe the trickier part. I'd like all the output text to have this functionality. Well, at least the nouns. And it would be nice if I didn't have to explicitly apply ClickText in every piece of game text I write. Is there a single function where all the game output text passes through that I could somehow hijack, such that I could parse the output text for game dictionary nouns (like apple, chair, etc.), and then apply ClickText to each noun?

Or maybe a different idea could work...can each noun's name be written as an expression, so that whenever it's printed, it's basically printed as the expression ClickText(noun)? I suppose that's not much better than what we have now though.

edit: So it didn't occur to me at first, but much of this machinery must be in Core right? So I'm seeing a few likely candidates for hijacking...ShowRoomDescription for example. Perhaps GetDisplayName is a better choice? ...or ProcessTextSection?

I'm a little in the weeds here, so any advice is appreciated.

second edit: Whoa, so I think I found it!

viewtopic.php?f=10&t=3547&p=23499&hilit=outputtext#p23499

In Quest 5.4, msg calls the OutputText function in CoreOutput.aslx so you can insert Log calls there etc. This would also be a good place to call some new Request perhaps, which could instruct the player UI to write to a transcript file.



Am I correct in thinking that if I modify OutputText I can get what I'm after? I'll have to give it a go...

third edit: wow, it works! That's awesome!

All I did was uppercase all output, doing the ClickText on nouns will be more involved, but does it seem like the basic principle should work with OutputText? Maybe I'm still better off doing it through GetDisplayName....

fourth (and final, I promise) edit: So I'm at a bit of a roadblock...If I modify the result of GetDisplayAlias with ClickText, I just get the HTML span in the output (which makes sense I suppose since GetDisplayAlias returns a string). Doing it in GetDisplayNameLink (as GetDisplayName seems to default to it) gives a weird result (I get a ClickText'd room name above all the output text). So, I might have to parse OutputText for nouns after all?

fifth edit (I know it's getting ridiculous): So I think I was confused before in wrapping the result of GetDisplayName with ClickText -- it wasn't a weird result, just normal Quest behaviour. In any case, no matter what I do, I can't get apple wrapped in ClickText properly (I've deleted all its display verbs and such so it should be just a normal word I guess).

jaynabonne
17 Oct 2013, 09:37
Would you be able to post what you have done so far? It might be a simple change from there.

What you are doing is similar to something I had done. I didn't go the "find all nouns" route because I wanted to highlight words that didn't necesssarily correspond to nouns. But I had a markup like: "You can see an {!apple!}." and it would then replace "{!" and "!}" with the appropriate parts. Of course, I was funneling all output through my own function (before hitting msg), so I had control. If you want to have this behavior with all text (even Quest generated) then you probably don't have much choice than to do a search. (I hope that won't be a big performance problem them searching the space of all visible objects for each word!)

george
17 Oct 2013, 15:11
I feel like applying the hehavior to all Quest text should work in theory, if I can find the right point in Quest's machinery to insert the function. I've investigated a bit more and I have an inkling of what's going on though. ClickText will wrap the span around its keyword, but depending on when you apply ClickText that keyword may already have markup around it. I think the inner markup has precedence if there are conflicting elements. I think that's happening with apple in the test since I can get all the output text but apple to have ClickText functionality (it's quite funny to send whole sentences to the command line with one click by the way! :) ).

I'll have some more time tonight to look into it.

george
18 Oct 2013, 06:23
Progress!

I made a very simple change to ProcessTextCommand_Object. This seems to be the function that deals with game objects in output. This is the function,


<function name="ProcessTextCommand_Object" type="string" parameters="section, data">
<![CDATA[
objectname = Mid(section, 8)
text = ""
colon = Instr(objectname, ":")
if (colon > 0) {
text = Mid(objectname, colon + 1)
objectname = Left(objectname, colon - 1)
}
object = GetObject(objectname)
if (object = null) {
return ("{" + ProcessTextSection(section, data) + "}")
}
else {
if (LengthOf(text) = 0) {
text = SafeXML(GetDisplayAlias(object))
}
if (game.enablehyperlinks) {
if (not HasInt(game, "lastlinkid")) {
game.lastlinkid = 0
}
game.lastlinkid = game.lastlinkid + 1
linkid = "verblink" + game.lastlinkid
colour = ""
if (HasString(object, "linkcolour") and GetUIOption("UseGameColours") = "true") {
colour = object.linkcolour
}
else {
colour = GetLinkTextColour()
}
style = GetCurrentTextFormat(colour)

// * * * I changed the following * * *

return ("<a id=\"" + linkid + "\" style=\"" + style + "\" class=\"cmdlink elementmenu\" data-elementid=\"" + object.name + "\">" + text + "</a>")
}
else {
return (text)
}
}
]]>
</function>


All I did was add jaynabonne's code to the anchor element. So I made it look like this,


"<a onclick=\"InsertInput((this.innerText || this.textContent))\" id=\"" + linkid + "\" style=\"" + style + "\" class=\"cmdlink elementmenu\" data-elementid=\"" + object.name + "\">" + text + "</a>"


So now to make an actual prototype to see how well this works in a real game! :) Of course, this is only part of the puzzle...I still need something that can parse all the text of descriptions and such for nouns (anything where the object name isn't generated by the Object I suppose).

Pertex
21 Oct 2013, 19:59
Hi Jay,
do you have an idea how to change InsertInput to send the created command to Quest so the command is executed?

jaynabonne
21 Oct 2013, 21:24
Do you mean when you click on "apple", it sends "apple" as the command? Or do you mean have something you can click on to execute the command when done assembling it with the various words?

jaynabonne
21 Oct 2013, 21:37
But if you just want to send what you click on as a command, then change the onclick to this:

onclick='sendCommand(this.innerText || this.textContent)'

Replace InsertText with sendCommand.

Pertex
22 Oct 2013, 18:44
Great, it works! Thanx