[TADS3] Conceptual questions

11 views
Skip to first unread message

NewKid

unread,
Feb 13, 2004, 10:02:23 AM2/13/04
to
OK, I've got myself up and running. I know where I want to go, but am
trying to get some concepts clear before I go too far down the wrong
paths.

First concerns ActorStates.
1] Am I correct in assuming that there can only be one of this class
in force for a given NPC at a time?
2] Why is ConversationReady state nested inside an InCoversation? It
is counterintuitive to me, especially since I can easily see more than
one conversation state (depending on NPCs current attitude toward
player), all stemming from the same ConversationReady behavior. There's
a programming reason for it, I'm sure, but I'm not seeing it.

Second:
I could never get this to work in TADS2, even with chatter.t: Let's
say we have a topic-type things, such as "advice". It is clear enough
to me that you can easily ASK for ADVICE, but can you say "Bob, GIVE ME
ADVICE", or "GIVE BOB ADVICE"?

Thanks in advance,
Cheers.

NewKid


----== Posted via Newsfeed.Com - Unlimited-Uncensored-Secure Usenet News==----
http://www.newsfeed.com The #1 Newsgroup Service in the World! >100,000 Newsgroups
---= 19 East/West-Coast Specialized Servers - Total Privacy via Encryption =---

Eric Eve

unread,
Feb 13, 2004, 1:31:05 PM2/13/04
to

"NewKid" <new...@killingtime.com> wrote in message
news:130220041002237739%new...@killingtime.com...

> OK, I've got myself up and running. I know where I want to go, but
am
> trying to get some concepts clear before I go too far down the
wrong
> paths.
>
> First concerns ActorStates.
> 1] Am I correct in assuming that there can only be one of this
class
> in force for a given NPC at a time?

Correct.

> 2] Why is ConversationReady state nested inside an
InCoversation? It
> is counterintuitive to me, especially since I can easily see more
than
> one conversation state (depending on NPCs current attitude toward
> player), all stemming from the same ConversationReady behavior.
There's
> a programming reason for it, I'm sure, but I'm not seeing it.

The InConversationState that a ConversationReadyState switches to
is the state held in its inConvState property. Conversely, the state
that an InConversationStates switches on termination of the
conversation is held in its nextState property. Nesting a
ConversationReadyState inside an InConversationState is simply a
coding convenience that causes the two states to be related to each
other at pre-initialization by pointing the inConvState of the
ConversationReadyState to the nesting InConversationState and the
nextState property of the InConversationState to the nested
ConversationReadyState. There's no need to use this nesting
mechanism if you don't want to; if you prefer, you can set the
nextState and inConvState properties explicitly in your code.

The system wasn't designed with the intention of relating multiple
InConversationStates to a single ConversationReadyState, though
presumably you could change the ConversationReadyState's inConvState
property dynamically in your code to achieve this effect. However,
TADS 3 provides a different mechanism for providing varying
responses with the emotional/cognitive state of an actor, and that
is the use of TopicGroups within InConversationStates.

So you might define:

enum neutral, friendly, hostile;

bob : Person 'bob' 'Bob'
...
attitude = neutral
...
;

+ bobTalking : InConversationState
specialDesc = "Bob is leaning on his broom, talking with you. "
;

++ bobSweeping : ConversationReadyState
specialDesc = "Bob is busily sweeping the shop. "
;

++ TopicGroup isActive=(bob.attitude==neutral) ;
+++ AskTopic @bob
"<q>How are you today?</q> you ask.\b
<q>Well enough.</q> he replies."
;

...

++ TopicGroup isActive=(bob.attitude==friendly);
+++ AskTopic @bob
"<q>How are you getting on?</q> you enquire.\b
<q>Very well!</q> he beams."
;
...

++ TopicGroup isActive=(bob.attitude==hostile);
+++ AskTopic @bob
"<q>So, how are you?</q> you wonder.\b
<q>What business is that of yours?</q> Bob snarls."
;

...

Note that we use the InConversationState and ConversationReadyState
to provide a description of what Bob is doing, so we don't want a
separate InConversationState depending on Bob's attitude, unless his
attitude makes a visible difference.


> Second:
> I could never get this to work in TADS2, even with chatter.t:
Let's
> say we have a topic-type things, such as "advice". It is clear
enough
> to me that you can easily ASK for ADVICE, but can you say "Bob,
GIVE ME
> ADVICE", or "GIVE BOB ADVICE"?
>
> Thanks in advance,
> Cheers.
>
> NewKid
>

Assuming ADVICE is defined as a topic, then no, you can't GIVE BOB
ADVICE or say BOB, GIVE ME ADVICE (or at least you can, but you'll
be told that either you or Bob sees no advice here). There may be
several ways round this; one would be to devise a new class to deal
with this type of situation, for example:

class AdviceTopic : MultiLoc, Immovable 'advice' 'advice'
dobjFor(GiveTo)
{
preCond = []
verify() {inherited;}
check() {inherited;}
action() {inherited;}
}
iobjFor(AskFor)
{
preCond { return inherited;}
verify() {inherited;}
check() {inherited;}
action() {inherited;}
}
dobjFor(Default)
{
verify() { illogical('That\'s not something you can do with ' +
name); }
}
iobjFor(Default)
{
verify() { illogical('That\'s not something you can do with ' +
name); }
}
initialLocationClass = Room
;

Then you could define:

t_advice : AdviceTopic 'advice' 'advice';

And GIVE BOB ADVICE would work (provided you supply Bob with a
suitable GiveTopic). (This seems a bit of a kludge to me, perhaps
someone else can come up with something better).

-- Eric


Eric Eve

unread,
Feb 13, 2004, 3:08:50 PM2/13/04
to

"NewKid" <new...@killingtime.com> wrote in message
news:130220041002237739%new...@killingtime.com...
> Second:
> I could never get this to work in TADS2, even with chatter.t:
Let's
> say we have a topic-type things, such as "advice". It is clear
enough
> to me that you can easily ASK for ADVICE, but can you say "Bob,
GIVE ME
> ADVICE", or "GIVE BOB ADVICE"?
>
> Thanks in advance,
> Cheers.
>
Okay, I didn't handle BOB, GIVE ME ADVICE before. Probably the best
way to handle this is to define a StringPreParser object that
transforms X, GIVE ME Y into ASK X FOR Y. Here's one way to do it:

StringPreParser
doParsing(str, which)
{
local tempStr = str.toLower();
local comma, giveMe;
giveMe = tempStr.find('give me');
if(giveMe==nil)
return str;
comma = tempStr.find(',', giveMe - 3);
if(comma != nil && comma < giveMe)
{
str = 'ask ' + str.substr(1, comma-1) + ' for '
+ str.substr(giveMe + 8);
}
return str;
}
;

I'm sure this can be done more neatly using regular expressions, but
it was quicker for me to knock up the above than try to figure out
regular expressions! Note that this will work for commands like

BOB, GIVE ME ADVICE

But will be defeated by more complex commands like.

BOB, KISS SALLY, TAKE THE PEBBLE, DROP THE DIAMOND AND GIVE ME
ADVICE

-- Eric


Coorlim

unread,
Feb 13, 2004, 3:10:07 PM2/13/04
to

"Eric Eve" <eric...@NOSPAMhmc.ox.ac.uk> wrote in message
news:c0j55e$huv$1...@news.ox.ac.uk...

<SNIP>

Actualy, you can use both ASK BOB FOR ADVICE and GIVE BOB ADVICE using
ConvNode and SpecialTopic. You can even INSULT BOB or ASK BOB OUT FOR A
COFFEE if you were so inclined.

SpecialTopic an object similar to AskTopic or TellTopic, and can only (I
believe) be used in a ConvNode. The examples below assume you understand
ConvNode (if not, theres a technical article about it at http://tads.org

Bob: Actor
;

+ ConvNode 'woes'; // all ConvNodes have names like this.
++ SpecialTopic 'ask Bob for advice' ['ask', 'Bob', 'for', 'advice']
"This is where asking bob for advice and his advice text goes!"
;

++SpecialTopic 'give Bob advice' ['give', 'Bob', 'advice']
//giving bob advice code
;

++SpecialTopic 'ask Bob out for a coffee' ['ask', 'Bob', 'out', 'for', 'a',
'coffee']
//Youve asked him out on a date, you crafty bastard!
;

Basicly, the 'ask Bob for advice' bit is what shows up if the player checks
out the ConvNode topic list. The bit in the brackets are the individual
words that must be entered in list format. None of them are vocabulary bits
that have to be defined elsewhere. After that,you drop in the response,
normally a block of text, but you can drop some code in there too. (I
forget the method name, but its the same for all the TopicEntries.

Hope that made sense,

Coorlim, who is a fan of NewKid's work


NewKid

unread,
Feb 14, 2004, 11:27:33 AM2/14/04
to
In article <c0j55e$huv$1...@news.ox.ac.uk>, Eric Eve
<eric...@NOSPAMhmc.ox.ac.uk> wrote:

> "NewKid" <new...@killingtime.com> wrote in message
> news:130220041002237739%new...@killingtime.com...

> Note that we use the InConversationState and ConversationReadyState
> to provide a description of what Bob is doing, so we don't want a
> separate InConversationState depending on Bob's attitude, unless his
> attitude makes a visible difference.

That makes sense. Of course, if his attitude was important enough for
me to make it affect all the topic responses, I'd want to make sure his
appearance and atmosphere reflected that as well. Am I rught in
remembering that you can put different atmospheres daemons in different
states?
Also, can you put some topics in the actor, rather than the state, for
convenience? I guess the actor would be bad, but I'd hate to duplicate
if there were a bunch of responses not affected by the mood.


> class AdviceTopic : MultiLoc, Immovable 'advice' 'advice'
> dobjFor(GiveTo)
> {
> preCond = []
> verify() {inherited;}
> check() {inherited;}
> action() {inherited;}
> }
> iobjFor(AskFor)
> {
> preCond { return inherited;}
> verify() {inherited;}
> check() {inherited;}
> action() {inherited;}
> }
> dobjFor(Default)
> {
> verify() { illogical('That\'s not something you can do with ' +
> name); }
> }
> iobjFor(Default)
> {
> verify() { illogical('That\'s not something you can do with ' +
> name); }
> }
> initialLocationClass = Room
> ;
>
> Then you could define:
>
> t_advice : AdviceTopic 'advice' 'advice';
>
> And GIVE BOB ADVICE would work (provided you supply Bob with a
> suitable GiveTopic). (This seems a bit of a kludge to me, perhaps
> someone else can come up with something better).

That was the way I kludged in TADS2 (essentially), I just hoped that
there was some better way in TADS3.

Thanks so far!

Eric Eve

unread,
Feb 14, 2004, 12:30:40 PM2/14/04
to

"NewKid" <new...@killingtime.com> wrote in message
news:140220041127337412%new...@killingtime.com...

> In article <c0j55e$huv$1...@news.ox.ac.uk>, Eric Eve
> <eric...@NOSPAMhmc.ox.ac.uk> wrote:
>
> > "NewKid" <new...@killingtime.com> wrote in message
> > news:130220041002237739%new...@killingtime.com...
>
>
> > Note that we use the InConversationState and
ConversationReadyState
> > to provide a description of what Bob is doing, so we don't want
a
> > separate InConversationState depending on Bob's attitude, unless
his
> > attitude makes a visible difference.
>
> That makes sense. Of course, if his attitude was important enough
for
> me to make it affect all the topic responses, I'd want to make
sure his
> appearance and atmosphere reflected that as well. Am I rught in
> remembering that you can put different atmospheres daemons in
different
> states?

You can override the takeTurn() method of different ActorStates, if
that's what you mean (in which case be sure to call inherited in the
overridden method). Alternatively, you can make the ActorState also
inherit from an EventList or TextList class as well (e.g. bobFuming:
ConversationState, ShuffledTextList) and supply an eventList or
textStrings property which will work much like an atmosphere list
(by default, ActorState.takeTurn calls doScript if the ActorState is
also a script object).

> Also, can you put some topics in the actor, rather than the state,
for
> convenience? I guess the actor would be bad, but I'd hate to
duplicate
> if there were a bunch of responses not affected by the mood.

Yes, you can, and that can be a useful thing to do. The thing to
watch, however, is that any matchable TopicEntry in an ActorState
will always take precedence over a TopicEntry in the actor. Thus,
for example, if you define a DefaultAskTellTopic on your bobFuming
state, none of the AskTopics or TellTopics you define in bob himself
will be reachable while bob is in the bobFuming state (which may
seem counterintuitive). If you want both ActorState dependent
DefaultTopics and TopicEntries on the actor, you may have to define
the DefaultTopics on the actor and then have then selected with
isActive=(getActor.curState==bobFuming) etc., which also seems
faintly kludgy to me.

[snip]

[concerning GIVE BOB ADVICE]


> That was the way I kludged in TADS2 (essentially), I just hoped
that
> there was some better way in TADS3.

Another alternative might be to define a new verb:

DefineTAction(Advise);

VerbRule(Advise)
('give' singleDobj 'advice') | ('advise' singleDobj)
: AdviseAction
verbPhrase = 'advise/advising (whom)'
;

You could then devise a new kind of TopicEntry object:

AdviceTopic: YesNoTopic
matchList = [adviceTopicObj]
;

adviceTopicObj : object;

You then need to put handling for the verb on the actor class:

modify Actor
dobjFor(Advise)
{
preCond = [canTalkToObj]
verify()
{
/* it makes no sense to ask oneself about something */
if (gActor == self)
illogical('{You/he} can\'t advise {yourself}. ');
}
action()
{
/* note that the issuer is targeting us with
conversation */
gActor.noteConversation(self);

/* let the current state handle it */
curState.yesNoFrom(gActor, adviceTopicObj); }

}
;

You'd also want to define default handling on Thing:

modify Thing
dobjFor(Advise)
{
verify() { illogical ('{You/he} cannot advise {that dobj/him}.
'); }
}
;

After all that you could put AdviceTopics in ActorStates and have
them response to ADVISE BOB or GIVE BOB ADVICE - but it's rather a
lot of work if you only want it once in your game!

As Coorlim noted, the other way you could handle it is via a
SpecialTopic in a ConvNode - which is fine provided you can ensure
that the ConvNode is active when the player wants to ask for advice.

> Thanks so far!
>
> NewKid

You're welcome,

Eric


NewKid

unread,
Feb 14, 2004, 10:44:03 PM2/14/04
to
In article <c0lm06$f15$1...@news.ox.ac.uk>, Eric Eve
<eric...@NOSPAMhmc.ox.ac.uk> wrote:

>
> You can override the takeTurn() method of different ActorStates, if
> that's what you mean (in which case be sure to call inherited in the
> overridden method). Alternatively, you can make the ActorState also
> inherit from an EventList or TextList class as well (e.g. bobFuming:
> ConversationState, ShuffledTextList) and supply an eventList or
> textStrings property which will work much like an atmosphere list
> (by default, ActorState.takeTurn calls doScript if the ActorState is
> also a script object).> > Also, can you put some topics in the actor, rather than the state,
> for
> > convenience? I guess the actor would be bad, but I'd hate to
> duplicate
> > if there were a bunch of responses not affected by the mood.
>
> Yes, you can, and that can be a useful thing to do. The thing to
> watch, however, is that any matchable TopicEntry in an ActorState
> will always take precedence over a TopicEntry in the actor. Thus,
> for example, if you define a DefaultAskTellTopic on your bobFuming
> state, none of the AskTopics or TellTopics you define in bob himself
> will be reachable while bob is in the bobFuming state (which may
> seem counterintuitive). If you want both ActorState dependent
> DefaultTopics and TopicEntries on the actor, you may have to define
> the DefaultTopics on the actor and then have then selected with
> isActive=(getActor.curState==bobFuming) etc., which also seems
> faintly kludgy to me.

Thanks. That explanation helps a LOT. I'd have been forever figuring
out what was happening with the defaults.....


>
> [snip]
>
> [concerning GIVE BOB ADVICE]
> > That was the way I kludged in TADS2 (essentially), I just hoped
> that
> > there was some better way in TADS3.
>
> Another alternative might be to define a new verb:
>
> DefineTAction(Advise);

> <snip>

This will also work, but I think I'd want BOTH ways of doing it. It
helps to cut down on Guess the Vocab issues.

> After all that you could put AdviceTopics in ActorStates and have
> them response to ADVISE BOB or GIVE BOB ADVICE - but it's rather a
> lot of work if you only want it once in your game!
>
> As Coorlim noted, the other way you could handle it is via a
> SpecialTopic in a ConvNode - which is fine provided you can ensure
> that the ConvNode is active when the player wants to ask for advice.

That's the problem, ConvNodes are pretty specific, from what I can see.
I'd rather have a more general case. Say your character is the exiled
King of Zortplotz, and you have a sidekick who is your Grand Vizier.
Having to go through hoops to get the Vizier in a mood to ADVISE would
be contrary to the Holy Writ of Mimesis.....

Coorlim

unread,
Feb 15, 2004, 12:01:46 AM2/15/04
to
<SNIPoRAMA>

> > As Coorlim noted, the other way you could handle it is via a
> > SpecialTopic in a ConvNode - which is fine provided you can ensure
> > that the ConvNode is active when the player wants to ask for advice.
>
> That's the problem, ConvNodes are pretty specific, from what I can see.
> I'd rather have a more general case. Say your character is the exiled
> King of Zortplotz, and you have a sidekick who is your Grand Vizier.
> Having to go through hoops to get the Vizier in a mood to ADVISE would
> be contrary to the Holy Writ of Mimesis.....
>
> NewKid
>
>

I dunnae about that... it all depends upon which point in the game that the
Vizier needs advice.

Some real world examples from games I'm working with, dumbed down to remove
spoilers.

Essentialy, there is a guardian barring passage through a hallway, but
ignoring the player unless the player attracts his attention (the player by
default is sneaking around unseen.) Once this is done, I activate the
following code to initiate his ConvNode:

Guardian.initiateConversation(nil, 'alert');

In this case, the actor's daemon activates it if he notices the player, and
the player has not yet defeated him.

The following code is nested into the actor directly, not into any state
(but his replies could have been moderated by a getCurState had he had any
additional states with a switch/case). The <.convstay> tag ensures that no
matter what we talk about, we stay in the 'alert' node, and therefore I can
continue to offer up the CHALLENGE GUARD special topic after I learn of it.

+ ConvNode 'alert'
;

++ AskTopic, StopTextList, SuggestedAskTopic @ Guardian
['<q>Why do you bar my passage?</q> you ask.
<.p>
{The guardian/he} replies placidly, <q>Anyone who wishes to pass must
best be in a game of wits.</q><.reveal gameStuff><.convstay><.topics> ',
'<q>If I defeat you in your game, I may pass?</q> you ask.
<.p>
<q>Indeed,</q> the guard responds. <q>you may. </q><.convstay>']
name = 'his job'
;

//<.reveal gameStuff> activates the challenge him option.
//<.topics> suggests the challenge activity, and any other suggested topics.
//SpecialTopics are automatically SuggestedTopics.

++ DefaultAnyTopic
"{The guardian/he} just watches you placidly. <.convstay>"
;

//if you leave this DefaultAnyTopic out, you can have access to all of the
other conversation pieces afforded by the ActorState.

++ SpecialTopic 'challenge him' ['challenge', 'him', 'guardian', 'guard']
topicResponse ()
{
"<q>Very well, what is your game, guardian?</q>
<.p>
The guard grins in anticipation.
<.p>
[Insert challenge description here.]";
Guardian.setCurState (GuardianDuel);
}
isActive = gRevealed('prizeStuff')
;

Note that after challenging the guard, we leave the ConvNode.

M.D. Dollahite

unread,
Feb 15, 2004, 2:33:55 PM2/15/04
to
>From: "Eric Eve"

>Probably the best
>way to handle this is to define a StringPreParser object that
>transforms X, GIVE ME Y into ASK X FOR Y.

The library already does that automatically.

changes.htm:
"The GiveTo handler changes a GiveTo command directed to another actor into an
AskFor command (BOB, GIVE ME THE CROWBAR becomes ASK BOB FOR CROWBAR)..."

I don't think there is actually any problem with ASKing FOR an abstract topic,
unless the library tries to verify that what you're asking for is an actual
Thing for some reason.

Eric Eve

unread,
Feb 15, 2004, 3:02:47 PM2/15/04
to

"M.D. Dollahite" <ryu...@aol.com.NOSPAM> wrote in message
news:20040215143355...@mb-m14.aol.com...

I'm not sure this library transformation does work with BOB, GIVE ME
ADVICE (assuming advice is defined as a Topic and not a Thing). In
fact, when I tried it just now it doesn't even work with a BOB, GIVE
ME CROWBAR type command. I think the reason is that the
transformation is defined in Actor.checkIobjGiveTo, but that the
handling for the GiveTo command (scope check, preconditions and so
forth) will have defeated the BOB, GIVE ME X command long before the
transformation takes place.

-- Eric


Reply all
Reply to author
Forward
0 new messages