|
apprentice
|
OP
apprentice
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?
|
|
|
|
old hand
|
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.
|
|
|
|
apprentice
|
OP
apprentice
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.
|
|
|
|
old hand
|
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.
|
|
|
|
apprentice
|
OP
apprentice
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.
|
|
|
|
old hand
|
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.
|
|
|
|
apprentice
|
OP
apprentice
Joined: Nov 2015
|
You are a godsend and and a true font of information. Is there a command analogous to getvarinteger for global flags?
|
|
|
|
old hand
|
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:
//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. 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.. Another handy little script similar to that Greever one is this:
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.
|
|
|
|
apprentice
|
OP
apprentice
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-?
|
|
|
|
journeyman
|
journeyman
Joined: Dec 2014
|
Here's a script that I did a year back that does this. Init section
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
//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)
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.
|
|
|
|
old hand
|
old hand
Joined: Jun 2013
|
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.
|
|
|
|
apprentice
|
OP
apprentice
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?
|
|
|
|
old hand
|
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
|
|
|
|
apprentice
|
OP
apprentice
Joined: Nov 2015
|
Aha!
I can't check it right now but from your description Purge/FlushQueue seem like they should do the trick.
|
|
|
Moderated by Bvs, ForkTong, gbnf, Issh, Kurnster, Larian_QA, LarSeb, Lar_q, Lynn, Monodon, Raze, Stephen_Larian
|
|