Parsing inline string attributes not working?

SmohnJith
07 Oct 2018, 08:35

I have a template of sorts for an NPC called "Soldier". I want to scatter a few around some rooms randomly, so I make copies of the Soldier object and place them as I want. Now, a soldier can be male or female (assigned when spawned), so when I write the dialogue, I want to be able to do something along the lines of...

"(He / She) has nothing to say to you right now."

So, I write a print statement...

{this.article} has nothing to say to you right now.

But it doesn't work. {Soldier.article} works, but that grabs the generic article of the template "it", and not the article of the current soldier you are talking to who has had their gender set. Am I missing something really simple? Seems like there should be a way to do this without an inline if statement for every article usage, or separate prints for each gender!


mrangel
07 Oct 2018, 15:11

Are you sure you've set the article of the soldier correctly?

If you make the newly created soldiers inherit the types male or female, the article should be set automatically.

If you clone a "prototype" soldier or create a new object inheriting a soldier type, then you will have to manually set its gender, article, and possessive attributes. Changing gender does not automatically change the others.


SmohnJith
09 Oct 2018, 08:47

Yes, I'm changing the articles manually during spawn code. But I think you misunderstand my dilemma. for the sake of this example, pretend I spawned a copy of Soldier, and in the look at code of Soldier (and therefore my new copy), I put...

"{this.article} has nothing to say to you right now."

Literally evaluates to...

{this.article} has nothing to say to you right now.

The desired output would be (ignore capitalization for sake of simplicity)...

he has nothing to say to you right now.

Now, I can of course print an expression like...

this.article + " has nothing to say to you right now."

... and that works as desired, but I really don't want to write 5 paragraphs of text in this way when I could be using the article variable 20 or 30 times! So why does this not evaluate inline properly?


Dcoder
09 Oct 2018, 08:59

I'm guessing that should be:

"{=this.article} has nothing to say to you right now."

mrangel
09 Oct 2018, 12:33

Ugh ... I just wrote a response, but I wasn't paying attention and assumed it was in the object's "look" description.

The same issue seems to exist for all verbs: If the verb runs a script, it is invoked using do(), which creates the special variable this.

If the verb's response is a string, this is not created. You can only use expressions like {this.someattribute} in the text processor if you first set the attribute game.text_processor_this to be the object.

Here's a few options to deal with the problem:

Option 1 - Replace {this. with an object name when making a clone
One (rather ugly) solution would be to replace "this" with the actual name of the object. So after creating a new clone (assuming obj is a variable containing the new clone) you would do:

replacetext = "{" + obj.name + "."
foreach (attr, GetAttributeNames (obj, true)) {
  if (TypeOf (obj, attr) = "string") {
    value = GetAttribute (obj, attr)
    value = Replace (value, "{this.", replacetext)
    set (obj, attr, value)
  }
}

This would change the clone's verb responses, for example replacing "{this.article}" with "{soldier26.article}".

The problem with this method is that it will cause problems if you then try to make a clone from a clone (which is unlikely); and it could also be slow.

Option 2 - Make Quest set text_processor_this when running a verb
Fix the default verb. This is only possible in the desktop version of Quest, not the web version.
Open the type defaultverb (which should be there, but may be hidden. I'm not sure how easy editing types is).

That type will have a script attribute which contains quite a bit of code. Look for the lines:

        case ("string") {
          if (object2 = null) {
            msg (GetString(object, this.property))
          }

and change that to:

        case ("string") {
          if (object2 = null) {
            game.text_processor_this = object
            msg (GetString(object, this.property))
          }

That will make the special variable this available in all verb responses.

You may also want to modify the lookat command, inserting the line game.text_processor_this = object before the line msg (lookdesc) so that you can use this in an object's "look at" response.

Option 3 - make your verb set text_processor_this itself
this is always defined in script attributes. So you could make the verb a script that looks like:

game.text_processor_this = this
msg ("{this.article} has nothing to say to you right now.")

SmohnJith
09 Oct 2018, 20:18

For the sake of ease, I decided to use a function to evaluate character's attitude towards you and pick appropriate responses. Sticking "game.text_processor_this = currentCharacter", currentCharacter being the person you are talking to supplied as a function argument, just before the response code seems to be working like a charm. Thanks for helping me clear this up. Quite an unusual corner case to run into.


mrangel
09 Oct 2018, 20:48

It's something that comes up a lot; because variables are only available in the function or script they're declared in, not any other functions it calls, and that includes built in functions like the text processor.

If you want to make other variables available to the text processor, you can make game.text_processor_variables a dictionary. If you're using the text processor to display more complex text, this can save you a lot of time.