Larian Banner: Baldur's Gate Patch 9
Previous Thread
Next Thread
Print Thread
Joined: Nov 2015
I
Ikul Offline OP
apprentice
OP Offline
apprentice
I
Joined: Nov 2015
Hi!
Yeah. Me again.

I've got a specific problem I can't solve right now:
I need an NPC to finish a conversation and then go somewhere. Currently, I can make the NPC go somewhere while in the conversation, but then it stays open and follows the NPC as they wander out of view.

Is there an elegant way to make this happen?

Joined: Jun 2013
old hand
Offline
old hand
Joined: Jun 2013
Yes there is.

IF
DialogEnded("DIALOGSTRINGNAMEHERE",_) //The _ is extremely important.
THEN
CharacterMoveToTrigger( ) ;


Also if that particular dialog file is used more than once you might need to set a character flag within the dialog to specify it's only for a certain event.


So something like this:
IF
DialogEnded("DIALOGSTRINGNAMEHERE",_) //The _ is extremely important.
AND
CharacterGetVarInteger(CHARACTER Character,"WalkAway",1) //set this in the dialog file.
THEN
CharacterMoveToTrigger ( ) ;
CharacterSetVarInteger(CHARACTER Character,"WalkAway",0); //reset the value


You can also kill the dialog with:
DialogRequestStop(STRING Dialog, CHARACTER Character);

But usually I think it's better to let the player hit end, which has the same effect as the above call.


I use this a TON in my mod for scripted events.

Last edited by SniperHF; 30/11/15 11:41 AM.
Joined: Nov 2015
I
Ikul Offline OP
apprentice
OP Offline
apprentice
I
Joined: Nov 2015
Yeah that's fairly elegant, thanks!

In the latest episode of Ikul's Stuff Not Working Quite As It Should, I actually tried DialogRequestStop and it didn't quite do the trick.

Joined: Jun 2013
old hand
Offline
old hand
Joined: Jun 2013
I'll have to check if/where I used that one, pretty sure I had it working at one point. Just didn't use it much.

Joined: Nov 2015
I
Ikul Offline OP
apprentice
OP Offline
apprentice
I
Joined: Nov 2015
Is there any reason you use
CharacterSetVarInteger
and not
DialogSetCharacterEvent
or
SetAndRememberDialogEvent?



Oh, figured out why DialogRequestStop didn't work.
Auto-completed to the wrong character. Similar names.

Last edited by Ikul; 30/11/15 11:42 AM.
Joined: Jun 2013
old hand
Offline
old hand
Joined: Jun 2013
I've never used dialog events or character events. I don't even really use charactervarintegers that much. Mostly stick to globalevents. Not too concerned with efficiency in these mod projects and it keeps my script more consistent and easier to root out problems.

I know that you can easily set Character Var Integers from the keyword editor.
ACTION SetCharacterFlag("WalkAway",0,1) 0 being the speaker index, and 1 being the integer.

And that those are easily captured in the story editor with GetVarInteger. VarInteger and CharacterFlag are the same thing, it's one of those inconsistencies between the story editor and the keyword editor.

So when you set the character flag in a node that's supposed to trip the story script for WalkAway, once the dialog ends it will query the flag and if it's 1 the character will walk away. I'm not sure if you can do that as easily with the other two types of events.


*I'm away from my editor at the moment and am going off memory, my details may be a little fuzzy.

Joined: Nov 2015
I
Ikul Offline OP
apprentice
OP Offline
apprentice
I
Joined: Nov 2015
You are a godsend and and a true font of information.
Is there a command analogous to getvarinteger for global flags?

Joined: Jun 2013
old hand
Offline
old hand
Joined: Jun 2013
Unfortunately no. There is however a workaround.

If you have the file "Greevers little helpers" in your story editor, there's a handy little section called global event memory.

If you don't have it all you need is this part:
Code
//REGION Global Event Memory
IF
GlobalEventSet(_String)
THEN
DB_GlobalEvent(_String);

IF
GlobalEventCleared(_String)
THEN
NOT DB_GlobalEvent(_String);
//END_REGION


Then you can do something like

IF
CharacterEnteredTrigger(_Player,TRIGGER_TRIGGER)
AND
DB_GlobalEvent("SomeGlobalEventString")
THEN
STUFF HAPPENS


I used this all the time, Greever is a scripter at Larian I assume and his helpers file is awesome.


Originally Posted by Ikul
true font of information.

Spending the last 9 months working with the editor will do that. I just wish Larian would get on the enhanced editor.. horsey


Another handy little script similar to that Greever one is this:

Code
IF
GlobalEventSet(_Event)
THEN
DebugBreak(_Event);



That will show in the log every time a global event happens. This can be extremely handy for debugging. Though it can also be annoying to have the log lighting up all the time so sometimes it should be commented out.

Joined: Nov 2015
I
Ikul Offline OP
apprentice
OP Offline
apprentice
I
Joined: Nov 2015
Okay so the character goes somewhere, and that's good, but here's another problem:
The character needs to go meet another character.

So I have something of the form:
IF
DialogEnded("NameOfDialog", _)
AND
CharacterGetVarInteger(CHARACTER_SomeGuy,"FlagGo",1)
THEN
CharacterMoveToCharacter(CHARACTER_SomeGuy, CHARACTER_TheOtherGuy, 1, "");

which is nice.
Now I need something to happen after he's arrived. If I add an instruction after this, it runs immediately after they depart rather than after arrival. So how do I do -that-?

Joined: Dec 2014
journeyman
Offline
journeyman
Joined: Dec 2014
Here's a script that I did a year back that does this.

Init section

Code
DB_AngeredSpirit("Aeril_WW_AngeredSpirit","Evil NPC","Aeril_WW_AngrySpirit");
DB_GaervConvinced("Aeril_WW_GaervSolution",CHARACTER_Aeril_SpiritOfTheForest,TRIGGER_Aeril_WW_DialogTrigger,"Aeril_TD_GaervWisp");
DB_GaervSpiritDrinks("Aeril_WW_GaervSpiritDrinks");
DB_GaervPlayerSpiritBottomsUp(CHARACTER_Aeril_GaervTable,"WaitForScreen","Aeril_WW_GaervSitDown",TRIGGER_Aeril_WW_GaervChairPlayer);
DB_GaervSitCount(0);

DB_GaervSpiritChairStartsDialog(ITEM_FUR_GaervChair2,"Aeril_TD_GaervWispTable",CHARACTER_Aeril_GaervTable,CHARACTER_Aeril_SpiritTable);

DB_GaervSpirit(CHARACTER_Aeril_Gaerv);
DB_GaervSpirit(CHARACTER_Aeril_SpiritOfTheForest);

DB_GaervSpiritDrinksItems(ITEM_FUR_GaervChair1);
DB_GaervSpiritDrinksItems(ITEM_FUR_GaervChair2);
DB_GaervSpiritDrinksItems(ITEM_FUR_GaervChair3);
DB_GaervSpiritDrinksItems(ITEM_CON_GaervCup1);
DB_GaervSpiritDrinksItems(ITEM_CON_GaervCup2);
DB_GaervSpiritDrinksItems(ITEM_CON_GaervCup3);
DB_GaervSpiritDrinksItems(ITEM_FUR_GaervLiquor);
DB_GaervSpiritDrinksItems(ITEM_FUR_GaervRoundTable);
ProcSetItemsOnStage(0);

DB_GaervSpiritTable(CHARACTER_Aeril_GaervTable);
DB_GaervSpiritTable(CHARACTER_Aeril_SpiritTable);
ProcSetDrinkGaervSpiritOnStage(0);

DB_SpiritSummons(CHARACTER_Aeril_WW_Summon1,TRIGGER_Aeril_WW_SpiritSummonSpawn1);
DB_SpiritSummons(CHARACTER_Aeril_WW_Summon2,TRIGGER_Aeril_WW_SpiritSummonSpawn2);
DB_SpiritSummons(CHARACTER_Aeril_WW_Summon3,TRIGGER_Aeril_WW_SpiritSummonSpawn3);
DB_SpiritSummons(CHARACTER_Aeril_WW_Summon4,TRIGGER_Aeril_WW_SpiritSummonSpawn4);
ProcSetSpiritSummonsOnStage(0);

DB_CharacterGain(CHARACTER_Aeril_WW_Summon1,0.10);
DB_CharacterGain(CHARACTER_Aeril_WW_Summon1,0.10);
DB_CharacterGain(CHARACTER_Aeril_WW_Summon1,0.10);
DB_CharacterGain(CHARACTER_Aeril_WW_Summon1,0.10);
DB_CharacterGain(CHARACTER_Aeril_SpiritOfTheForest,0.20);


DB_JournalAdd("Aeril_WW_WispCompromise","Aeril_TheWoodOrTheWisps",0);
DB_JournalUpdate("Aeril_WW_WispCompromise","Aeril_TheWoodOrTheWisps","Update1",0);
DB_JournalUpdate("Aeril_WW_GaervTroubles","Aeril_TheWoodOrTheWisps","Update2",0);
DB_JournalUpdate("Aeril_WW_GaervBarter","Aeril_TheWoodOrTheWisps","Update3",0);
DB_JournalUpdate("Aeril_WW_GaervArrow","Aeril_TheWoodOrTheWisps","Update4",0);
DB_JournalUpdate("Aeril_WW_GaervCrafting","Aeril_TheWoodOrTheWisps","Update5",0);


DB_JournalUpdate("Aeril_WW_AngeredSpirit","Aeril_TheWoodOrTheWisps","Update6",0);
DB_JournalUpdate("Aeril_WW_WispLeaveForest","Aeril_TheWoodOrTheWisps","Update7",0);



Code section

Code

//BEGIN Anger Wisp
IF
	DialogCharacterEventSet(_Flag,_Character,_DialogInstance)
AND
	DB_AngeredSpirit(_Flag,_Faction,_DisplayText)
THEN
	DB_DoActionAfterDialogEndedAngerWisp(_DialogInstance,_Character,_Faction,_DisplayText);
	NOT DB_AngeredSpirit(_Flag,_Faction,_DisplayText);

IF 
	DialogEnded(_Dialog,_DialogInstance)
AND
	DB_DoActionAfterDialogEndedAngerWisp(_DialogInstance,_Character,_Faction,_DisplayText)
THEN
	CharacterSetFaction(_Character,_Faction);
	CharacterDisplayText(_Character,_DisplayText);
	ProcSpiritSummons(1);
	NOT DB_DoActionAfterDialogEndedAngerWisp(_DialogInstance,_Character,_Faction);

PROC
	ProcSetSpiritSummonsOnStage((INTEGER)_OnStage)
AND
	DB_SpiritSummons(_Summon,_Trigger)
THEN
	CharacterSetOnStage(_Summon,_OnStage);
	
PROC
	ProcSpiritSummons((INTEGER)_PlayAnimation,(CHARACTER)_Player)
AND
	DB_SpiritSummons(_Summon,_Trigger)
THEN
	ProcAugmentLevel(_Summon,_Player);
	CharacterAppearAtTrigger(_Summon,_Trigger,_PlayAnimation);


//END Anger Wisp

//BEGIN Gaerv Convinced to talk, moves to spirit

IF
	DialogCharacterEventSet(_Flag,_Character,_DialogInstance)
AND
	DB_GaervConvinced(_Flag,_NPC,_Trigger,_Dialog)
THEN
	DB_DoActionAfterDialogEndedMoveGaerv(_DialogInstance,_Character,_NPC,_Flag,_Trigger,_Dialog);
	NOT DB_GaervConvinced(_Flag,_NPC,_Trigger,_Dialog);

IF 
	DialogEnded(_Dialog,_DialogInstance)
AND
	DB_DoActionAfterDialogEndedMoveGaerv(_DialogInstance,_Character,_NPC,_Event,_Trigger,_DialogToLaunch)
THEN
	CharacterMoveToCharacter(_Character,_NPC,0,_Event);
	
IF
	CharacterEvent(_Character,_Event)
AND
	DB_DoActionAfterDialogEndedMoveGaerv(_DialogInstance,_Character,_NPC,_Event,_Trigger,_Dialog)
THEN
	DB_DoActionOnTriggerEnteredMoveGaerv(_Character,_NPC,_Trigger,_Dialog);
	TriggerRegisterForPlayers(_Trigger);
	CharacterFreeze(_Character);
	NOT DB_DoActionAfterDialogEndedMoveGaerv(_DialogInstance,_Character,_NPC,_Event,_Trigger,_Dialog);

IF
	CharacterEnteredTrigger(_Character,_Trigger)
AND
	DB_DoActionOnTriggerEnteredMoveGaerv(_NPC1,_NPC2,_Trigger,_Dialog)
AND
	_Character.isPlayer()
THEN
	CharacterUnfreeze(_NPC1);
	TriggerUnregisterForPlayers(_Trigger);
	ProcDoThreeSpeakerDialog(_Dialog,_NPC1,_NPC2,_Character);
	CharacterLookAtCharacter(_Character,_NPC2,0);
	NOT DB_DoActionOnTriggerEnteredMoveGaerv(_NPC1,_NPC2,_Trigger,_Dialog);

//END Gaerv Convinced to talk, moves to spirit

//BEGIN Gaerv, Spirit BottomsUp!

PROC
	ProcSetItemsOnStage((INTEGER)_OnStage)
AND
	DB_GaervSpiritDrinksItems(_Item)
THEN
	ItemSetOnstage(_Item,_OnStage);

PROC
	ProcSetDrinkGaervSpiritOnStage((INTEGER)_OnStage)
AND
	DB_GaervSpiritTable(_Item)
THEN
	CharacterSetOnStage(_Item,_OnStage);

PROC
	ProcSetGaervSpiritOnStage((INTEGER)_OnStage)
AND
	DB_GaervSpirit(_Character)
THEN
	CharacterSetOnStage(_Character,_OnStage);

IF
	DialogCharacterEventSet(_Flag,_Character,_DialogInstance)
AND
	DB_GaervSpiritDrinks(_Flag)
THEN
	DB_DoActionAfterDrinks(_DialogInstance,_Character);
	NOT DB_GaervSpiritDrinks(_Flag);

IF
	DialogEnded(_Dialog,_DialogInstance)
AND
	DB_DoActionAfterDrinks(_DialogInstance,_Character)
THEN
	FadeToBlack(_Character,1.0,0);
	TimerLaunch(_Dialog,3000);

IF
	TimerFinished(_Event)
AND
	DB_DoActionAfterDrinks(_DialogInstance,_Character)
AND
	DB_GaervPlayerSpiritBottomsUp(_NPC,_TimerEvent,_DisplayText,_Trigger)
THEN
	TimerCancel(_Event);
	ProcSetItemsOnStage(1);
	ProcSetGaervSpiritOnStage(0);
	ProcSetDrinkGaervSpiritOnStage(1);
	CharacterTeleportToTrigger(_Character,_Trigger,"");
	FadeToBlack(_Character,1.0,1);
	TimerLaunch(_TimerEvent,3000);
	NOT DB_DoActionAfterDrinks(_DialogInstance,_Character);

IF
	TimerFinished(_TimerEvent)
AND
	DB_GaervPlayerSpiritBottomsUp(_NPC,_TimerEvent,_DisplayText,_Trigger)
AND
	DB_GaervSitCount(_Count)
AND
	IntegerSum(_Count,1,_Sum)
AND
	_Sum < 5
AND
	DB_NumberString(_Sum,_NumberAsString)
AND
	StringConcatenate(_DisplayText,_NumberAsString,_ResultingDisplayText)
THEN
	NOT DB_GaervSitCount(_Count);
	DB_GaervSitCount(_Sum);
	TimerCancel(_TimerEvent);
	CharacterDisplayText(_NPC,_ResultingDisplayText);
	TimerLaunch(_TimerEvent,6000);

IF
	CharacterUsedItem(_Character,_Item)
AND
	DB_GaervSpiritChairStartsDialog(_Item,_Dialog,_NPC1,_NPC2)
AND
	_Character.isPlayer()
AND
	DB_GaervPlayerSpiritBottomsUp(_N,_TimerEvent,_DT,_T)
THEN
	TimerCancel(_TimerEvent);
	ProcDoThreeSpeakerDialog(_Dialog,_NPC1,_NPC2,_Character);
	NOT DB_GaervSpiritChairStartsDialog(_Item,_Dialog,_NPC1,_NPC2);
	NOT DB_GaervPlayerSpiritBottomsUp(_N,_TimerEvent,_DT,_T);

//END Gaerv, Spirit BottomsUp!




The specific part that you want to check for is when the charater moves in a trigger :
CharacterEnteredTrigger(_Character,_Trigger)


Code
IF 
	DialogEnded(_Dialog,_DialogInstance)
AND
	DB_DoActionAfterDialogEndedMoveGaerv(_DialogInstance,_Character,_NPC,_Event,_Trigger,_DialogToLaunch)
THEN
	CharacterMoveToCharacter(_Character,_NPC,0,_Event);
	
IF
	CharacterEvent(_Character,_Event)
AND
	DB_DoActionAfterDialogEndedMoveGaerv(_DialogInstance,_Character,_NPC,_Event,_Trigger,_Dialog)
THEN
	DB_DoActionOnTriggerEnteredMoveGaerv(_Character,_NPC,_Trigger,_Dialog);
	TriggerRegisterForPlayers(_Trigger);
	CharacterFreeze(_Character);
	NOT DB_DoActionAfterDialogEndedMoveGaerv(_DialogInstance,_Character,_NPC,_Event,_Trigger,_Dialog);

IF
	CharacterEnteredTrigger(_Character,_Trigger)
AND
	DB_DoActionOnTriggerEnteredMoveGaerv(_NPC1,_NPC2,_Trigger,_Dialog)
AND
	_Character.isPlayer()
THEN
	CharacterUnfreeze(_NPC1);
	TriggerUnregisterForPlayers(_Trigger);
	ProcDoThreeSpeakerDialog(_Dialog,_NPC1,_NPC2,_Character);
	CharacterLookAtCharacter(_Character,_NPC2,0);
	NOT DB_DoActionOnTriggerEnteredMoveGaerv(_NPC1,_NPC2,_Trigger,_Dialog);



This script contains more than what you asked but maybe it can help you answer some other questions you might or will have.

Joined: Jun 2013
old hand
Offline
old hand
Joined: Jun 2013
Originally Posted by Ikul
Okay so the character goes somewhere, and that's good, but here's another problem:
The character needs to go meet another character.

So I have something of the form:
IF
DialogEnded("NameOfDialog", _)
AND
CharacterGetVarInteger(CHARACTER_SomeGuy,"FlagGo",1)
THEN
CharacterMoveToCharacter(CHARACTER_SomeGuy, CHARACTER_TheOtherGuy, 1, "");

which is nice.
Now I need something to happen after he's arrived. If I add an instruction after this, it runs immediately after they depart rather than after arrival. So how do I do -that-?


I refer to this as "event chaining".

So on your last call "CharacterMoveToCharacter" you have a "" at the end. That is a field you can use to make character events.

So make it something like:
CharacterMoveToCharacter(CHARACTER_SomeGuy, CHARACTER_TheOtherGuy, 1, "ArrivedAtOtherGuy");


The event ArrivedAtOtherGuy will throw once the call Movetocharacter is complete.

Then add another statement:
IF
CharacterEvent(CHARACTER_SomeGuy,"ArrivedAtOtherGuy")
THEN
WHATEVER YOU WANT TO HAPPEN.

So just keep in mind that if the character never reaches the other guy, the next event will never happen. Like if he gets killed in between or the path finding bugs out.

This is further complicated if the other guy also moves around.

As a blunt method you could stop any actions "other guy" is doing by using CharacterFreeze() in the first statement. This is dangerous though because you need to make sure the unfreeze happens in the event SomeGuy fails to arrive.

If he's using a wander script or something you can change the priority on it which is safer.

Course if he's doing nothing then nothing to worry about.

Last edited by SniperHF; 04/12/15 03:31 PM.
Joined: Nov 2015
I
Ikul Offline OP
apprentice
OP Offline
apprentice
I
Joined: Nov 2015
Does CharacterFreeze also stop movement already in progress (i.e. if one character starts moving and then something happens that causes another to catch up to them mid-motion)?
If not, does anything do so?

Joined: Jun 2013
old hand
Offline
old hand
Joined: Jun 2013
TBH I don't remember 100% but I'm pretty sure CharacterFreeze will stop them dead, but once you unfreeze them they will immediately resume what they were doing.

CharacterFreeze has some problems though like a player cannot initiate dialog with the NPC any longer. Also I believe it stops the default dialog animations like boast,thankful, and such.

Just as a side note, freezing the player is extremely dangerous and potentially gamebreaking.



The alternative to freezing them is to flush their queue. There are two calls for this:
CharacterPurgeQueue
CharacterFlushQueue

I have no idea what the difference is, they have always worked interchangeably for me.

If you are only using the story editor then purge/flush will work fine provided some global thing doesn't trigger another action after you purge them.
The problem with those calls though is if you have any sort of systemic behavior purging the queue won't stop that for long. This is especially true if you are using anything charscript.

To solve this problem with my own charscripts I usually use the Statemanager.

You could alternatively use this call to stop charscript functions:
CharacterSetReactionPriority(CHARACTER Character, STRING Reaction, INT Priority)

The problem with that one is you have to keep track of all the different names you gave to the reactions. And if you are using larian charscripts like wander or patrol or something, well Larian has goofy names for their stuff to put it mildly.


My tutorial on the statemananger:
http://larian.com/forums/ubbthreads.php?ubb=showflat&Number=567495#Post567495


Joined: Nov 2015
I
Ikul Offline OP
apprentice
OP Offline
apprentice
I
Joined: Nov 2015
Aha!

I can't check it right now but from your description Purge/FlushQueue seem like they should do the trick.


Link Copied to Clipboard
Powered by UBB.threads™ PHP Forum Software 7.7.5