Scripting question - modifying ginc_trigger

In my quest to get a bit better at scripting…I’m sure there are several here that knows the answer to this question. I tried for a bit myself but couldn’t get it to work the way I wanted.

I’m using a general trigger to trigger a conversation instead of SpeakTrigger. However, I really like SpeakTrigger and in conjunction with cutscene mode I’ve found that it works really well and mostly without bugs. Now, here’s the thing. I would like to use SpeakTrigger but depending on different situations the NPC that triggers the conversation will be different. That’s why I can’t use the SpeakTrigger in this case (since in the variables I put which NPC to be the owner of the conversation and I want different NPCs depending on different circumstances). I found that SpeakTrigger uses the function DoSpeakTrigger that is stored in ginc_trigger. Now to my question: How or where in the script can I modify the NPC string? Can I do a custom inlcude ginc_trigger script and somehow there point out that if this or that has happened (maybe by a global or local int) and that those different things changes which NPC string is to be used? (Gaah, this is hard to explain) Where in this case in the script do I point out the NPC string…?

To try and clarify, I would like to know where or how to change the NPC string in this script, in the ginc trigger or something like that, maybe by making a custom ginc_trigger script. When using SpeakTrigger you change this in variables of the trigger. But I would like to have two or three alternative NPC strings there, depending on what has happened before in the story. So where do I put that in the script…

If someone is able to understand what I need I’m grateful. I’m getting dizzy just trying to explain this…

DoSpeakTrigger() gets

string sSpeaker = GetLocalString(oTrigger, "NPC_Tag");
object oSpeaker = GetTriggerTarget(sSpeaker, oEnter);

(if sSpeaker is left blank, oEnter would start a dialog with itself)

You want/need to change the string_value of “NPC_Tag” on the trigger. At least that’s the way to do it without rewriting the stock start-dialog script ‘gtr_speak_node’.

eg. if you want “fred” to talk if the PC saved the damsel in the castle, when PC saves the damsel run a script that gets the trigger object and sets the value of “NPC_Tag” to “fred”

but if you want “frank” to talk if the PC slays the damsel, then when the PC slays the damsel run a script that gets the trigger object and sets the value of “NPC_Tag” to “frank”

in script that runs if damsel is saved:

object oTrigger = GetObjectByTag("unique_trigger_tag");
SetLocalString(oTrigger, "NPC_Tag", "fred");

in script that runs if damsel is slayed:

object oTrigger = GetObjectByTag("unique_trigger_tag");
SetLocalString(oTrigger, "NPC_Tag", "frank");

Another way it could be done, cross-module, is to set an int_variable on the PC, like

SetLocalInt(oPC, "bDamselStatusAlive", TRUE); // or FALSE

Then at the top of a custom ‘gtr_speak_node’ script, write a code like so:

if (GetLocalInt(oPC, "bDamselStatusAlive"))
    SetLocalString(OBJECT_SELF, "NPC_Tag", "fred");
else
    SetLocalString(OBJECT_SELF, "NPC_Tag", "frank");

 
The key to the issue is to set the “NPC_Tag” on the trigger by script instead of in the toolset.

ps. If your module supports multi-player, I guess that all PCs should be looped over, searching for the “bDamselStatusAlive” variable … note also that it doesn’t need to be a boolean. You could set up a pseudo-enum like so:

const string sDAMSELSTATE = "sDamselState";
const int iDAMSELSTATE_INDETERMINATE = 0;
const int iDAMSELSTATE_ALIVE = 1;
const int iDAMSELSTATE_DEAD = 2;
const int iDAMSELSTATE_ROMANCE = 3;

Then at the top of a custom ‘gtr_speak_node’ script, write a code like so:

switch (GetLocalInt(oPC, sDAMSELSTATE))
{
    case 0: // indeterminate
        SetLocalString(OBJECT_SELF, "NPC_Tag", "roger");
        break;
    case 1: // alive
        SetLocalString(OBJECT_SELF, "NPC_Tag", "fred");
        break;
    case 2: // dead
        SetLocalString(OBJECT_SELF, "NPC_Tag", "frank");
        break;
    case 3: // romance
        SetLocalString(OBJECT_SELF, "NPC_Tag", "damsel");
        break;
}

make sense?

 
Note that you can’t simply bypass the “NPC_Tag”, at least not without copying and modifying DoSpeakTrigger() from ‘ginc_trigger’ – because DoSpeakTrigger() is also going to look at the trigger-object variables directly.

1 Like

Thanks for the reply kevL_s. I think this makes sense to me, actually.

I made a modified gtr_speak_node script that I used on a SpeakTrigger. It seems to be working just as I wanted it to. A question: It seems that the things with SetLocalString (that was the function I was looking for I realized, but I didn’t quite know how it worked in this case with the variables) overrides the the settings you put in the variables of the SpeakTrigger, right?

This is my modified gtr_speak_node script:

    #include "ginc_trigger"
    #include "ginc_var_ops"
    #include "ginc_influence"

    void main ()
    {
        object oPC = GetEnteringObject();
    	object oLoreen = GetNearestObjectByTag("loreen");
    	object oElvera = GetNearestObjectByTag("elvera");
    	int nInfluence = GetCompanionInfluence("loreen");
    	int nInfluence2 = GetCompanionInfluence("elvera");
    	
    	if(GetLocalInt(OBJECT_SELF,"Done")) return;
    	if (!GetIsPC(oPC)) return;
    	
    	SetLocalString(OBJECT_SELF, "Conversation","c_fbt_party_l");
    	SetLocalInt(OBJECT_SELF, "CutsceneBars",1);
    	
    	if(nInfluence >= 29)
    	{
    	SetLocalString(OBJECT_SELF, "NPC_Tag", "loreen");
    	
    		
    	SetCutsceneMode(oPC);
    	
    	SetLocalInt(OBJECT_SELF,"Done",1);
    	
    	DoSpeakTrigger(oPC);
       
    	}

    	else if(nInfluence2 >= 29)
    	{
    	SetLocalString(OBJECT_SELF, "NPC_Tag", "elvera");
    	
    	SetCutsceneMode(oPC);
    	
    	SetLocalInt(OBJECT_SELF,"Done",1);
    	
    	DoSpeakTrigger(oPC);
    	}
    	
    	else
    	{
    	return;
    	}
    	
    }
1 Like

SetLocalString in this case doesn’t just override the value of “NPC_Tag” … it literally replaces it. The variables that you set on the trigger in the toolset are just ordinary local-variables, directly affected by GetLocal*() and SetLocal*() calls in scripts,

/cheers

Great! Thanks!

I’m slowly getting to understand a tiny bit of doing scripts. Sometimes I don’t understand a thing because there’s some important part that I haven’t yet grasped fully, but more and more it becomes a bit clearer (as long as it is not too advanced) and as I have quite many scripts (done by you, Travus and Aqvilinus for the most part) to fall back to I’m not quite a total noob anymore.
I tried to help with a script on Discord, but that didn’t work at all even if it compiled in the toolset, and again I felt really dumb, but as it turned out when I got it explained to me, it was just a simple thing that I didn’t think about. It was a script that would run several times with the changes of an integer, and I didn’t realize that when running the same script several times, it doesn’t remember the ints previously written there. That’s why you have the SetLocalInt and SetGlobalInt to be able to transfer values between scripts. It’s all quite logical, but I didn’t think about that when I wrote the script.

The whole thing with case and break I have not wrapped my head around yet, even if I THINK I understand the concept of it at least. I guess I’ll just have to go back to your script tutorial and read slowly again.

you’re probly ready for it  :)

switch/case is another way of writing if/else blocks. i find them convenient and easier on my eyes … but they can evaluate ints only

( they might also be faster in extremely long statements )

Ah, I think I get it. So if the integer is set to 0 in your example (the damsel is indeterminate) then the one “owning” the dialog is roger. If the damsel is alive then fred owns the dialog etc.
So, it’s instead of writing:

if(GetLocalInt(oPC,"sDamselState") == 0)
{
SetLocalString(OBJECT_SELF, "NPC_Tag", "roger");
}

else if(GetLocalInt(oPC,"sDamselState")==1)
{
SetLocalString(OBJECT_SELF, "NPC_Tag", "fred");
}

yessir