Jump to content

Advanced Scripting/Dialogue Interactions


cmorgan

Recommended Posts

Hello - Domi has been kind enough to allow me to apprentice code on a small section of her work. Most of the quest I have been able to research, ask generic confirmation questions on forums, or copy from others examples. Unfortunately, this one section is way beyond anything I have examples for:

 

OUTLINE: 2 days after a previous encounter, Winski teleports onto the party wherever they are and initiates dialogue. Several options - the hard one to code is the "conflict" result: Winski summons 1 mephit for every partymember except player 1, and a killable but non-hostile Imp for the PC. Winski disappears, leaving the fighting to the summons - the Imp begins to cast a spell. If the Imp is killed, the spell stops, and the party talks about the encounter. If the Imp is not killed in time, Dynaheir (and if InParty("Minsc") are ("Dynaheir",DRYAD_TELEPORT) toast.

 

REQUEST FOR ASSISTANCE:

Encounter forced after a global timer expires, wherever the party is (triggered from which one of the _DYNAH .BCS?). Actor summoned onto party, dialogue, then

 

IF ~~ DYQU5W5.1

SAY ~Here, creatures, get the dark woman for your master. Kill everyone, but her and the boy.~

IF ~~ THEN DO ~SetGlobal("DYQUImpAttack","GLOBAL",1)

// I can add in here: ReallyForceSpell("WINSKI",DRYAD_TELEPORT)~ EXIT

END

 

I think I can use

CreateCreatureObjectOffset("X#MEPHT1",Player1,[100.100]) started at neutral, then ActionOverride to Enemy and Move to Player1 after Winski exits to bring on the heat, but now comes the hard part --

 

>> as many mephits are summoned as there are party members - 1 and an Imp.

Wow, how do I A. count players, B. subtract 1, and C. summon that variable # of .cres?

 

>>Imp is not hostile

OK, got that one - use the tp2 to set the allegiance to neutral - but how do I keep him neutral while being attacked? This is a "it's concentrating on casting, can't be intterrupted by party until killed" kind of thing.

 

>>and one of the mephits protects the Imp.

??

 

The big one, and I have absolutely no clue as to what to research/learn to do this:

 

The Imp casts (DisplayStringHead, probably one line every 5 sec,. possibly voiced lines) and if he is not killed by then - Dynaheir is taken away.

 

The Imp's Spell:

~line1 line2 line3 line4 line5 line6 line7 line8 line9 line10 line11 line12~

 

then

ONLY if the imp is not killed, do ReallyForceSpell("Dynaheir",DRYAD_TELEPORT) and InParty("Minsc") ReallyForceSpell("Minsc",DRYAD_TELEPORT)

ELSE

EXIT

 

Any help on what to research/learn to do this, I would truly appreciate! I am hoping to have a first draft of the completed work for her by the time she gets back in May.

Link to comment
You remove the part of the script that causes the CRE to go hostile when attacked. It may look like this:

 

IF
AttackedBy([GOODCUTOFF],DEFAULT)
Allegiance(Myself,NEUTRAL)
THEN
RESPONSE #100
 Enemy()
END

are Echon's suggestion from PPG; Avenger_teambg suggests that it would be better to remove the Attack() actions.

 

 

For the variable # myCre.cre, I may be able to come close to the requested result by

 

IF ~~ THEN REPLY ~Well then, little example code snippet, let slip the Dogs of War!~ DO ~CreateCreatureObjectOffset("myNeutralCreature",Player1,[50.100]) SetGlobalVariable("NeutralCreatureCasts","GLOBAL",1)~

IF ~InParty(Player2)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[100.100])~

IF ~InParty(Player3)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[100.50])~

IF ~InParty(Player4)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[60.60])~

IF ~InParty(Player5)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[70.100])~

IF ~InParty(Player6)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[40.90])~

EXIT

END

 

I think the InParty checks should return true only if an NPC is in the slot, right?

 

If I am on the right track, I still have to figure out how to do

"The Imp casts (DisplayStringHead, probably one line every 5 sec,. possibly voiced lines) and if he is not killed by then - Dynaheir is taken away.".

Link to comment

IF ~~ THEN REPLY ~Well then, little example code snippet, let slip the Dogs of War!~ DO ~CreateCreatureObjectOffset("myNeutralCreature",Player1,[50.100]) SetGlobalVariable("NeutralCreatureCasts","GLOBAL",1)~
IF ~InParty(Player2)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[100.100])~
IF ~InParty(Player3)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[100.50])~
IF ~InParty(Player4)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[60.60])~
IF ~InParty(Player5)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[70.100])~
IF ~InParty(Player6)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[40.90])~
EXIT
END

 

That doesn't make sense syntactically as you can't nest conditions like that.

 

You could have something like:

 

IF ~InParty(Player6)~ THEN REPLY ~stuff~ DO ~SetGlobalVariable("NeutralCreatureCasts","GLOBAL",1)
CreateCreatureObjectOffset("myNeutralCreature",Player1,[50.100]) 
CreateCreatureObjectOffset("myHostileCreature",Player1,[100.100])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[100.50])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[60.60])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[70.100])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[40.90])~ EXIT
IF ~InParty(Player5)
!InParty(Player6) THEN REPLY ~stuff~ DO ~SetGlobalVariable("NeutralCreatureCasts","GLOBAL",1)
CreateCreatureObjectOffset("myNeutralCreature",Player1,[50.100]) 
CreateCreatureObjectOffset("myHostileCreature",Player1,[100.100])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[100.50])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[60.60])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[70.100])~ EXIT
IF ~InParty(Player4)
!InParty(Player5)
!InParty(Player6) THEN REPLY ~stuff~ DO ~SetGlobalVariable("NeutralCreatureCasts","GLOBAL",1)
CreateCreatureObjectOffset("myNeutralCreature",Player1,[50.100]) 
CreateCreatureObjectOffset("myHostileCreature",Player1,[100.100])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[100.50])~
CreateCreatureObjectOffset("myHostileCreature",Player1,[60.60])~ EXIT
...

 

Only one of these REPLY lines will turn up because of the conditions.

Link to comment

oh -- ok -- got it. What we are doing here is creating the largest contition, then backing it off one Player at a time. THANK YOU! Does my initial approach with nesting fail on regular dialog actions to; i.e. will

 

IF ~~ DYQU5.2

SAY ~something~

++ ~something~ + DYQU5.3

+ ~Class(Player1, THIEF_ALL)~ + ~something~ DO ~SetGlobal("X","Y",#) EXTERN ~_someone~

DYQU5W5.1

+ ~Class(Player1, BARD)~ + ~something~ DO ~SetGlobal("X","Y",#) EXTERN ~someone~ DYQU5W5.1

+ ~Class(Player1, FIGHTER_ALL)~ + ~*something ~ EXTERN ~_WINSKI~ DYQU5W5.1

END

 

work?

Link to comment

Yeah of course that will work. But what you were trying to do was have an IF ~conditions~ DO ~stuff~ in the middle of a DO ~loads of stuff~, which is why I called it nesting (i.e. something nested within something else). In your new example you're just attaching conditions to player responses which is fine.

 

Edit: Fuck, no you weren't. I've spotted a tilde I didn't see before. What you were doing was assuming that all of these...

 

IF ~InParty(Player2)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[100.100])~
IF ~InParty(Player3)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[100.50])~
IF ~InParty(Player4)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[60.60])~
IF ~InParty(Player5)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[70.100])~
IF ~InParty(Player6)~ DO ~CreateCreatureObjectOffset("myHostileCreature",Player1,[40.90])~

 

...would run if they are valid. They don't. Only the bottom-most valid one will run and it'll follow that line's transitions. Also, you didn't attach a transition to the ends of any them, so WeiDU wouldn't like it.

Link to comment

Yep, that is what I was assuming. I was saying I thought

 

IF X begins then check for

condition 1 if true then emit response 1

AND

condition 2 if true then emit response 2

AND

condition 3 if true then emit response 3

THEN go to Y.

 

NOW, if I "grokked", then the way I should think is

 

IF X begins then check for

condition 3 if true then emit response 3 OUT to Y

OR

condition 2 if true then emit response 2 OUT to Y

OR

condition 1 if true then emit response 1 OUT to Y

END

 

where only one response is followed, evaluated from the last condition upwards.

 

The Dialogue pattern above would have only one response, (e.g. FIGHTER_ALL based if Player1 was a fighter); the top line

++ ~something~ + DYQU5.3

wouldn't show up unless the class was not evaluated TRUE in condition check?

Link to comment

Well.

 

IF ~~ DYQU5.2
SAY ~something~
++ ~something~ + DYQU5.3
+ ~Class(Player1, THIEF_ALL)~ + ~something~ DO ~SetGlobal("X","Y",#) EXTERN ~_someone~
DYQU5W5.1
+ ~Class(Player1, BARD)~ + ~something~ DO ~SetGlobal("X","Y",#) EXTERN ~someone~ DYQU5W5.1
+ ~Class(Player1, FIGHTER_ALL)~ + ~*something ~ EXTERN ~_WINSKI~ DYQU5W5.1
END

 

As this are player responses then all valid responses will be displayed e.g. if they are fighter, just the first one (without conditions) and last one. However, if they are a fighter/thief the first, the second and last options will be displayed.

 

However:

 

IF ~~ DYQU5.2
SAY ~something~
IF ~Class(Player1, THIEF_ALL)~ DO ~SetGlobal("X","Y",#) EXTERN ~_someone~
DYQU5W5.1
IF ~Class(Player1, BARD)~ DO ~SetGlobal("X","Y",#) EXTERN ~someone~ DYQU5W5.1
IF ~Class(Player1, FIGHTER_ALL)~ EXTERN ~_WINSKI~ DYQU5W5.1
END

 

In this case we don't have player responses. We have straight EXTERN calls i.e. the player doesn't choose anything, it just automatically goes to the appropriate block. In this situation the first valid transition (bottom-up) is followed e.g. if they are fighter, its the FIGHTER_ALL one. If they are a thief, its the THIEF_ALL one. If they are a Fighter/Thief its the FIGHTER_ALL one as its first bottom-up.

Link to comment

Wicked cool. I think I understand. To confirm:

 

IF ~Class(Player1, BARD)~ THEN REPLY ~something~ DO ~SetGlobal("X","Y",#)~ EXTERN ~_WINSKI~ DYQU5W5.1

 

equals

 

if BARD then say "something" then SETGLOBAL then goto WINSKI and say "somethingelse" called by DYQU5W5.1 after the user hits return.

 

IF ~Class(Player1, BARD)~ DO ~SetGlobal("X","Y",#)~ EXTERN ~_WINSKI~ DYQU5W5.1

 

equals

 

if BARD then SETGLOBAL then goto WINSKI have him say "somethingelse" called by DYQU5W5.1 right away?

 

I appreciate the help - just watch out, 'cause any minute now I'm going to break out into "The Rain in Spain falls Mainly on the Plain", with me in the role of Eliza!

Link to comment

Yes thats it.

 

Modders tend to use IF ~~ THEN GOTO type lines to branch dialogue based on variables or randomness without the player saying anything. A decent example of this are NPC initiated flirts in the Flirt Pack. We get a generic opening line and then there are a series of IF ~~ THEN GOTO lines based on the random trigger, so you get a random flirt without any player choices (or having a different start of the dialogue for each flirt).

Link to comment

:p Grim Squeaker, thank you - I'm a happy guy. One last unrelated question before I head out to play around with 61 StartTimer(I:ID*,I:Time*) and 269 DisplayStringHead(O:Object*,I:StrRef*) to see about the imp's spell -- I'll try some stuff out before asking more questions about that.

 

Researching TakeItemReplace, I noticed you were exploring a workaround, swapping one amulet for another. If I leave off the Object,

IF ~~ DO ~TakeItemReplace("X#DYJOR2.ITM","X#DYJOR1.ITM")~

will the default behavior be to swap the item out from any player holding the item, or must the object be specified?

Link to comment

OK, perhaps I need to back up a step. I have about 90% of this coded, but still must be missing something basic --

 

This is the top of a combined .D file I am testing. APPEND shouldn't need a BEGIN; but

 

EXTEND_BOTTOM  ~_DRIZZT~ 10
IF ~Global("X#DynaJournal","GLOBAL",0)~ THEN DO ~SetGlobal("X#DynaJournal","GLOBAL",1)~ EXIT
END

APPEND ~_DRIZZT~
IF WEIGHT #4 ~!InParty("Dyanheir") !InMyArea("Dynaheir") Global("X#DynaJournal","GLOBAL",1) CombatCounter(0) !See([ENEMY]) See(Player1)~ THEN BEGIN DYQ0.0
SAY ~domi's text for drizzt here~
IF ~~ THEN DO ~GiveItemCreate("X#DYJOR1.ITM",Player1,1,1,0)~
IF ~InParty("Minsc")!Dead("Minsc")!StateCheck("Minsc",STATE_SLEEPING)~ EXTERN ~_BMINSC~ DYQ0.01
IF ~!InParty("Minsc")~ THEN REPLY ~Thank you. We will keep this with us until we find a scholar.~ EXIT
END
END //end append ~_DRIZZT~ X#DynaJournal=1  Item X#DYJOR1.ITM

 

Returns parse errors at

SAY ~domi's text for drizzt here~

IF ~~ THEN DO ~GiveItemCreate("X#DYJOR1.ITM",Player1,1,1,0)~

 

or if I write it

 

SAY ~domi's text for drizzt here~ DO ~GiveItemCreate("X#DYJOR1.ITM",Player1,1,1,0)~

 

I have clean code from BG1NPC, Eranon, and TGCep1 side by side in ConTEXT, and I can't see the syntax differences. Can anyone point a newbie to the obvious?

Link to comment

Right, you cannot do this:

 

SAY ~domi's text for drizzt here~ DO ~GiveItemCreate("X#DYJOR1.ITM",Player1,1,1,0)~

 

We're not doing a CHAIN here so all DO ~~ lines must be attached to a transition line (i.e. a player response or an IF ~~ THEN line).

 

IF ~~ THEN DO ~GiveItemCreate("X#DYJOR1.ITM",Player1,1,1,0)~

 

As I said before you cannot have an IF ~~ THEN line with no transition at the end. It has to go somewhere, whether it be EXTERNing, GOTOing or an EXIT. If you're intending that line to always play, it doesn't work like that as I covered above. Only one IF ~~ THEN line or player response (which is a type of IF ~~ THEN line) can be followed. Perhaps you meant:

 

IF ~InParty("Minsc")!Dead("Minsc")!StateCheck("Minsc",STATE_SLEEPING)~ DO ~GiveItemCreate("X#DYJOR1.ITM",Player1,1,1,0)~ EXTERN ~_BMINSC~ DYQ0.01
IF ~!InParty("Minsc")~ THEN REPLY ~Thank you. We will keep this with us until we find a scholar.~ DO ~GiveItemCreate("X#DYJOR1.ITM",Player1,1,1,0)~ EXIT

Link to comment

Serves me right for reading line after line of CHAIN examples, then going back to regular work.

 

Yes, your recoding was what I was aiming for: both conditions give the object; either Minsc replies or if Minsc is not in the party the player replies.

 

Got that DO needs to be set into the reply lines with a transition at the end --If I am not in a CHAIN (or I_C_T variant based on CHAIN), I need to work within

 

IF condition THEN BEGIN label (or if directly from a dialog sequence, IF nocondition label)

IF condition SAY text (or skip the IF condition and begin with SAY)

(no response from anyone = IF condition DO action TRANSITION)

IF condition THEN REPLY text DO action TRANSITION

END

 

 

I am still missing something about what truly constitutes a proper transition -not the words to use, but how they operate- studying and copying aside, my brain wants to go back to if

SAY ~text one and~

SAY ~text one continues here~

so it appears a linear form would work :

SAY ~ ~ DO ~ ~ SAY ~ ~ REPLY ~ ~ EXIT

That is what CHAIN seems to be doing; but it can't be. The initial SAY cannot have an action attached if there is no specific transition to the next line. I guess the transitions here are the jump to the next speaker?

 

borrowing from/adding to another post in the tutorials:

== KIDOJ ~We will go at once.~ DO ~ActionOverride("DCleric",EscapeArea())
AddJournalEntry(%Creating blah, blah, blah More blah stuff%,QUEST)~ 
== MYNPC ~InParty("Minsc")~ ~Come along now, big dude!~
END
++ ~Say text here~ EXIT
END

What are the transitions between KIDOJ, MYNPC, and Player1 response?

Link to comment

Okay first of all:

 

IF condition THEN BEGIN label (or if directly from a dialog sequence, IF nocondition label)
IF condition SAY text (or skip the IF condition and begin with SAY)
(no response from anyone = IF condition DO action TRANSITION)
IF condition THEN REPLY text DO action TRANSITION
END

 

You can't have conditions on SAY lines.

 

SAY ~text one and~
SAY ~text one continues here~

 

You can't have two consecutive SAY lines. The first one is SAY, any future ones are =

e.g.

SAY ~text one and~
= ~text one continues here~

 

Basically, what a CHAIN is a way to string together switch between dialogues. Here's a demonstration:

 

CHAIN IF ~Global("G#Bob","GLOBAL",0)~ THEN G#BOB chainstate
~Hello, my name is Bob.~
DO ~SetGlobal("G#Bob","GLOBAL",1)~
== G#JIM ~Really?  My name is Jim!~
== G#FRANK ~And mine is Frank!~
END
++ ~Yeah and my name is <CHARNAME>~ EXIT

 

That is identical to doing this:

 

APPEND G#BOB

IF ~Global("G#Bob","GLOBAL",0)~ THEN BEGIN state1
SAY ~Hello, my name is Bob.~
IF ~~ THEN DO ~SetGlobal("G#Bob","GLOBAL",1)~ EXTERN G#JIM state2
END

END

APPEND G#JIM

IF ~~ THEN BEGIN state2
SAY ~Really?  My name is Jim!~
IF ~~ THEN EXTERN G#FRANK state3
END

END

APPEND G#FRANK

IF ~~ THEN BEGIN state3
SAY ~And mine is Frank!~
++ ~Yeah and my name is <CHARNAME>~ EXIT
END

END

 

The reason CHAINs allow DO ~~ lines between the various bits of dialogue is to represnt what I have doing on in 'state1', the fact that between speakers you could have some actions.

 

In your example:

 

== KIDOJ ~We will go at once.~ DO ~ActionOverride("DCleric",EscapeArea())
AddJournalEntry(%Creating blah, blah, blah More blah stuff%,QUEST)~
== MYNPC ~InParty("Minsc")~ ~Come along now, big dude!~
END
++ ~Say text here~ EXIT
END

 

What I would call a 'transition line' is the bit that says:

 

++ ~Say text here~ EXIT

 

The transition in that is the bit that says EXIT i.e. saying where to go if you choose that option.

 

The bit that says...

 

DO ~ActionOverride("DCleric",EscapeArea())
AddJournalEntry(%Creating blah, blah, blah More blah stuff%,QUEST)~

 

...are actions which would be attached to a transition line were this not a CHAIN. They'd be like my 'SetGlobal("G#BoB","GLOBAL",1)' in 'state1' above.

 

Hope that helps.

 

Edit: Something to note in your example, however is the whitespace. Try not to think of this:

 

== KIDOJ ~We will go at once.~ DO ~ActionOverride("DCleric",EscapeArea())
AddJournalEntry(%Creating blah, blah, blah More blah stuff%,QUEST)~
== MYNPC ~InParty("Minsc")~ ~Come along now, big dude!~

 

Try to think of it as this:

 

== KIDOJ ~We will go at once.~ 
DO ~ActionOverride("DCleric",EscapeArea())
AddJournalEntry(%Creating blah, blah, blah More blah stuff%,QUEST)~
== MYNPC ~InParty("Minsc")~ ~Come along now, big dude!~

 

i.e. the actions are happening between the lines, rather than actually being attached to what the NPC is saying. Because WeiDU treats spaces and linebreaks the same way it doesn't make a difference, but its the way you picture that will help you.

Link to comment

Believe it or not, the lightning struck when you did the "whitespace" explanation-- light dawned, slapped head, and now I can go back through the examples.

 

Thank you VERY much. I'll head back, reread the WeiDU, Dialogue, and Chain tutorials in light of this, and recode!

Link to comment

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...