Inheriting vocabWords?

I’m experimenting with writing an IF game in TADS3, but I keep trying to do things that apparently the programmers weren’t intending for me to try… I usually reserve that talent for playing IF games… but that’s neither here nor there… :laughing:

The game I am making is intended to be very dynamic with randomly generated NPCs. I’ve got most of the elements of it figured out… the only problems are noun-referencing the character’s components. There are other things I’d like to do, but I’ve pretty much come to the conclusion that they’re impossible :frowning:

At this point, all I really want to do is to make it so that “betty nose” (or whatever their name is) works the same as “betty’s nose” and “her nose” without making the automated response so bizarre. (see below for details)

This is the basic code as it would probably normally appear in TADS when crazy people like me aren’t over-thinking things… :nerd:

[spoiler][code]
class Nose: Component ‘nose’ ‘nose’
desc = "This is the description for {your/his} nose. "
isProperName=true
;
me: Actor
location = dormRoom
;

  • mynose: Nose ‘(my your) nose’ ‘your nose’
    ;
    her: Person ‘(her/Betty)’ ‘Betty’
    location = dormRoom
    ;
  • hernose: Nose ‘(her/Betty/Betty’s) nose’ ‘your nose’;
    [/code][/spoiler]
    Note: The “isProperName” is there because I couldn’t find another way to keep the parser from asking (i.e. when I type LOOK NOSE) “Which nose do you mean, the Betty’s nose, or the your nose?”

But what I’d really like to do is have the class handle every single thing it can. I’m almost there:

[spoiler][code]
class Nose: Component ‘nose’ ‘<<location.globalParamName>> nose’
desc = "This is the description for {your/his} nose. "
isProperName = true
;
her: Person ‘(her/Betty)’ ‘Betty’
location = dormRoom
globalParamName = ‘Betty’s’
;

  • hernose: Nose;
    [/code][/spoiler]

I initially tried to set a variable in Betty (noun = ‘Betty’s’) and referenced with <<location.noun>> but for some reason it would say “Which nose do you mean, Betty’s,her,Betty nose, or your nose?”.

I also tried using ‘(<<location.variable>>) nose’ in the vocabWords section, to add weak tokens… but I’d get an error at runtime: “nil object reference” (check the spoiler for the Stack error, if you’re curious).

Nose.vocabWords() + 0x12 Nose.inheritVocab(mynose, obj#238d (Vector) [mynose,Nose]) + 0x2A mynose.inheritVocab(mynose, obj#238d (Vector) [mynose,Nose]) + 0x46 mynose.initializeVocab() + 0x15 func#361e1(mynose) + 0xE forEachInstance(VocabObject, function#000361e1) + 0x18 adv3LibPreinit.execute() + 0x44 adv3LibPreinit._execute() + 0x51 func#1a9f(adv3LibPreinit) + 0xE forEachInstance(PreinitObject, function#00001a9f) + 0x18 PreinitObject.classExec() + 0x38 _preinit() + 0x27 initAfterLoad() + 0x25 _mainCommon(['debug\\Weremechan.t3'], nil) + 0x10 _main(['debug\\Weremechan.t3']) + 0x12

Any suggestions? Or should I just give this up?

You’re running into trouble because the Nose class is really an object, and it doesn’t have a location (that property is set to nil). So attempts to access it will fail with the nil reference error during the PreInit phase when the class object’s vocabWords get placed into the grammar lookup table.

initializeThing gets called during PreInit too, so we can override the class method to make it initialize vocabulary based on the object’s location. (The parentheses around location.theName make it a weak token, meaning it will never work as a grammer match on its own, only when paired with “nose”.)

class Nose: Component
	name = 'nose'
	theName = (location.theNamePossAdj + ' ' + name)
	desc = "This is the description for <<theName>>. "
	initializeThing() {
		inherited();
		initializeVocabWith('(' + location.theName + ') ' + name);
	}
;

Mom: Person 'mom' 'Mom'
	"It's mom"
	isProperName = true
	isHer = true
;
+ Nose
;

me: Person 'me' 'me'
	"It's me! "
	isHim = true
;
+ Nose
;

That actually fixes it quite perfectly… though I need to study some more to figure WHY it worked so perfectly :smiley:

Since that question worked out so well… let me follow up with another question - any ideas why this coding isn’t handling {your/her} strings properly? Looking at Betty, it still says ‘your nose’. I thought it might be because we were under the same class… but changing my custom class’ root from “Actor” to “Person” doesn’t seem to resolve it.

It does show the proper variables associated with that character (mine is a different skin color from hers)… so it’s definately referencing the right location…

[spoiler][code]
class PlayerChar: Actor
desc = “Long-winded description based on various variables that are randomly generated within the character upon creation (well, actually I was calling the method via the intro text with <> <<gPlayer.rollStats>> <<her.rollStats>>, but I think I just learned a better way :D”
isHer = nil
isHim = nil
bodyColor = nil
etc…
rollStats()
{
bodyColor = rand([‘pale’,‘fair’,‘tanned’,‘rosy’,‘copper’,‘dark’,‘dark brown’,‘black’,‘ebon’]);
etc…
}
;
class Nose: Component
name = ‘nose’
theName = (location.theNamePossAdj + ’ ’ + name)
desc = "This is the description for {your/her} <<location.bodyColor>> nose. "
initializeThing() {
inherited();
initializeVocabWith(’(’ + location.theName + ') ’ + name);
}
;
me: PlayerChar
location = dormRoom
isHer = true
;

  • Nose;

her: PlayerChar ‘her/Betty’ ‘Betty’
location = dormRoom
isProperName = true
isHer = true
;

  • Nose;
    [/code][/spoiler]

The Message Parameter Substitution article goes into a lot of depth on this topic.

You’ll need to use a different format string and plug in an object selector to get output that makes sense:

desc = "This is the description for {the dobj/her}. "

That doesn’t quite work since you want another adjective in the output. Here it’s probably easiest to use theNamePossAdj from the location to build up the description.

desc = "This is the description for <<location.theNamePossAdj>> nose. "

Thank you for the help (sorry for the delay). I do have a follow-up question - is it possible to control the case when using those variables?

I’m now working on the descriptions and it’d be nice if I could start a sentence with “Her/Your nose” instead of always “Your/Betty’s” (which I am able to get using {The/her dobj} thanks to the name fix :slight_smile:

The variable I’d use for lower case appears to be <<location.itPossAdj>>… I even tried ItPossAdj, but it got upset :laughing:

I was under the impression that the parameter substitution simply took the hint about capitalization from how you chose to capitalize the case-insensitive keywords in the squiggle brackets.

So if you do this: “{You/He}”;
you get either ‘You’ or ‘He’ or ‘She’ coming out.
But if you do this: “{you/he}”;
you get either ‘you’ or ‘he’ or ‘she’ coming out.

A common desired outcome is this:
You want to print the NPC’s actual name when it’s third-person, but a pronoun when it’s in second-person talking about yourself.
For example: Janet is the name of an NPC, and Steven is the name of the player’s actor.
When Janet, an NPC, flips the switch you want it to print “Janet flips the switch.” but when you do it you want it to use a pronoun “You flip the switch” rather than using your name in third person like so: “Steven flips the switch”.

You can achieve this by using parameters that use the “theName” form, and defining Janet’s “isProperName” property to true. When you set “isProperName” to true, the parameter substitution will stop putting articles like “the” and “a” in front of the actor’s name. When using a construct that would normally come out as “A Janet” or “The Janet” it will just say “Janet”.

So you get this:
"{You/He} flip{s} the switch. ";
And when it’s you, it will say “You”, but when it’s Janet it will say “Janet”.

I’ve also found that sometimes it needs help realizing who the “{You/He}” refers to. Even when it’s not your turn and this is happening in an action performed by an NPC actor’s takeTurn() method, it still prints “You” sometimes. If this is happening you can tell it to stop doing that by using this construct:
“{You/He actor}”
which means “Pick the You or He form, assuming the subject is the current actor doing this action.”
I have no idea why that’s not just the default behavior anyway.

Exactly, library will print it all lowercase, first letter uppercase or whole uppercase depending on your hint as friltsar says. This behavior is implemented in adv3/output.t around line 1188.

But, this is just a standard object property not a “message parameter substitution”. Substitution is documented here: http://www.tads.org/t3doc/doc/techman/t3msg.htm But you can capitalize first letter by other means:

"\^<<location.itPossAdj>>";

More about this at the end of page: http://www.tads.org/t3doc/doc/sysman/strlit.htm