Text Processor Object Link Nesting Bug

This works:

msg ("{object:pencil}")

Then pencil will display as an object link.

This doesn't work, but should:

msg ("{object:{pencil.name}}")

{object:pencil} will be printed out.

I.e., nesting any {object.attribute} inside of {object:XXX} won't work. This means that you can only print object links with the text processor for specifically named objects, never attributes or variables.


The only other thing I could find that might be a workaround was "Local variables" found here:

http://docs.textadventures.co.uk/quest/text_processor.html

Couldn't get it to work and am not even entirely sure what this is supposed to do...


The text processor is fairly limited, and there's a lot of cases where nesting doesn't work properly; although I believe some of them were fixed in 5.8

In this case, the text processor looks for an object named {pencil.name}, doesn't find one, and because it's failed it ignores the {object: command and escapes the curly brackets before passing the string inside it to the text processor.

For the most part, the outermost text processor commands are processed first.

However, as of 5.8 if you do:

game.text_processor_variables = NewDictionary()
dictionary add (game.text_processor_variables, "obj", pencil)
msg ("I can see a {object:obj}.")

it will output I can see a pencil. which is probably what you wanted. The parameter passed to {object: can be either a variable or an object name, just like a bareword outside of the text processor.


Random aside Just thinking… maybe it would be more convenient to have something like:
  <function name="ProcessText" type="string" parameters="text, variables">
    data = NewDictionary()
    dictionary add (data, "fulltext", text)
    if (IsDefined("variables")) {
      if (not EndsWith (TypeOf (variables), "dictionary")) {
        variables = QuickParams ("this", variables)
      }
      if (EndsWith (TypeOf (game, "text_processor_variables"), "dictionary")) {
        vars = NewDictionary()
        foreach (key, game.text_processor_variables) {
          if (not DictionaryContains (variables, key)) dictionary add (vars, key, DictionaryItem (game.text_processor_variables, key))
        }
        foreach (key, variables) {
          dictionary add (vars, key, DictionaryItem (variables, key))
        }
        dictionary add (data, vars)
      }
      else {
        dictionary add (data, variables)
      }
    }
    else if (EndsWith (TypeOf (game, "text_processor_variables"), "dictionary")) {
      dictionary add (data, game.text_processor_variables)
    }
    text = ProcessTextSection(text, data)
    return (Replace(Replace(text, "@@@[email protected]@@", "{"), "@@@[email protected]@@", "}"))
  </function>

and pass the data dictionary to ParamsForTextProcessor and ObjectForTextProcessor.

The user could then do ProcessText ("The {object:thing} is blue.", QuickParams("thing", pencil)) or ProcessText ("The {object:this} is blue.", pen); while allowing text_processor_variables to be used for variables which you want to persist for multiple calls to ProcessText; as well as for backwards compatibility

Not sure why, but looking at this made me wonder about a slightly more intuitive (to me, at least) way the text processor could be made to handle variables.


To me, the text processor is only for description text boxes -- like for when we are just filling in room or object descriptions in the GUI and don't feel like switching it to a script just to print an attribute or two in the prose.

So, I very rarely use the text processor in a msg(), since it's just as easy (if not easier (and less stressful on Quest)) to just break in and out of a literal string with " +.

Example: msg("All of a sudden, and out of nowhere, a " + ObjectLink(pencil.name) + " appears!")

...but I'm thinking you may just be using a msg() in your example because it is merely an example? Or perhaps you just dig using the text processor...

Either way, I'm not judging.

I'm also assuming you are attempting to process pencil.name as an example. Your goal must be to use obj.name (with the variable obj, or something of that nature) in the code. Otherwise, I don't know why you wouldn't just do {object:pencil}, seeming how you already know the pencil's name.


Here are things that work for me (in Quest 5.8 and in Quest 5.7.2 (which is the version you (Dcoder) are using, if my calculations are correct)):

// Using text processor only
msg ("{object:pencil}")
msg("{=ObjectLink(GetObject(pencil.name))}")

// using whatever works
msg ("{object:" + pencil.name + "}")
msg (ObjectLink(pencil))
msg (ObjectLink(GetObject(pencil.name)))

Also, I'm pretty sure when the text processor sees {object: {whatever_string.here}}, it immediately prints whatever is returned by something very much like this:

ObjectLink(GetObject("{whatever_string.here}"))

Here is the actual function (ProcessTextCommand_Object) in Quest 5.7.2:

objectname = Mid(section, 8)
text = ""
colon = Instr(objectname, ":")
if (colon > 0) {
  text = Mid(objectname, colon + 1)
  objectname = Left(objectname, colon - 1)
}
if (not game.text_processor_this = null) {
  objectname = Replace(objectname, "this", game.text_processor_this.name)
}
object = GetObject(objectname)
if (object = null) {
  return ("@@@[email protected]@@" + ProcessTextSection(section, data) + "@@@[email protected]@@")
}
else {
  if (LengthOf(text) = 0) {
    text = SafeXML(GetDisplayAlias(object))
  }
  if (game.enablehyperlinks) {
    linkid = ProcessTextCommand_GetNextLinkId()
    colour = ""
    if (HasString(object, "linkcolour") and GetUIOption("UseGameColours") = "true") {
      colour = object.linkcolour
    }
    else {
      colour = GetLinkTextColour()
    }
    style = GetCurrentTextFormat(colour)
    return ("<a id=\"" + linkid + "\" style=\"" + style + "\" class=\"cmdlink elementmenu\" data-elementid=\"" + object.name + "\">" + text + "</a>")
  }
  else {
    return (text)
  }
}

Here's that same function in Quest 5.8 (it is a little different!):

  <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 = ObjectForTextProcessor(objectname)
    if (object = null) {
      return ("@@@[email protected]@@" + ProcessTextSection(section, data) + "@@@[email protected]@@")
    }
    else {
      if (LengthOf(text) = 0) {
        text = SafeXML(GetDisplayAlias(object))
      }
      if (game.enablehyperlinks) {
        linkid = ProcessTextCommand_GetNextLinkId()
        colour = ""
        if (HasString(object, "linkcolour") and GetUIOption("UseGameColours") = "true") {
          colour = object.linkcolour
        }
        else {
          colour = GetLinkTextColour()
        }
        style = GetCurrentTextFormat(colour)
        return ("<a id=\"" + linkid + "\" style=\"" + style + "\" class=\"cmdlink elementmenu\" data-elementid=\"" + object.name + "\">" + text + "</a>")
      }
      else {
        return (text)
      }
    }
    ]]>
</function>

Here's the new function that revised function uses:

  <function name="ObjectForTextProcessor" type="object" parameters="objectname">
    if (not game.text_processor_this = null and objectname = "this") {
      object = game.text_processor_this
      if (TypeOf(object) = "object") {
        return (object)
      }
    }

    if (not game.text_processor_variables = null and DictionaryContains (game.text_processor_variables, objectname)) {
      object = DictionaryItem(game.text_processor_variables, objectname)
      if (TypeOf(object) = "object") {
        return (object)
      }
    }
   
    return (GetObject(objectname))
</function>

BONUS

Things that do not work:

msg ("{object:{pencil.name}}") 
// Output: {object:pencil}
msg ("{object:{=GetObject(pencil.name)}}") 
// Output: {object:Object: pencil}
msg ("{object:{=ToString(GetObject(pencil.name))}}") 
// Output: {object:Object: pencil}
msg ("{object:{=ToString(GetDisplayName(GetObject(pencil.name)))}}") 
// Output: {object:a pencil}

Maybe the ProcessTextCommand_Object function should check for { in the section before trying to use GetObject()?


To me, the text processor is only for description text boxes -- like for when we are just filling in room or object descriptions in the GUI and don't feel like switching it to a script just to print an attribute or two in the prose.

Also for things where you don't want to create a global function for every possible message string.

For example: msg(weapon.critmessage) in a combat library, where you've loaded text_processor_variables with the name of the target, the attacker, and the damage dealt.


Log in to post a reply.

Support

Forums