Leilukin

[TSL] Need help on the post-Rebuilt Jedi Enclave Visas scene

Recommended Posts

I'm currently working on a mod that triggers the Visas conversation on the Ebon Hawk after the Rebuilt Jedi Enclave sequence even if you play as a female Exile and the Disciple joins your party. My method is to modify the k_attonend and a_load006end script files, so the post-Rebuilt Jedi Enclave Visas scene (the related .dlg file is visasend.dlg) will always be triggered.

 

When I was testing the mod myself with TSLRCM 1.8.5, while I successfully triggered the Visas scene for a female Exile, the scene where the Disciple warned Carth/Cede about the Telos attack wasn't triggered because the visasend.dlg file doesn't trigger it.

 

I would like to know if Visas is spawned in this sequence, is it possible to trigger the Disciple interacting with Carth/Cede scene by scripting, or if modifying the visasend.dlg file is the only way to trigger it? I would prefer the former if possible.

 

Any help would be very much appreciated!

 

 

 

Here is the source of my modified scripts if it helps:

 

k_attonend:

 

 

// Prototypes
string sub3(int intParam1);
object sub2(int intParam1, string stringParam2);
void sub1();
 
string sub3(int intParam1) {
switch (intParam1) {
case 0:
return "atton";
break;
case 1:
return "baodur";
break;
case 2:
return "mand";
break;
case 11:
return "disciple";
break;
case 3:
return "g0t0";
break;
case 4:
return "handmaiden";
break;
case 10:
return "hanharr";
break;
case 5:
return "hk47";
break;
case 6:
return "kreia";
break;
case 7:
return "mira";
break;
case 8:
return "t3m4";
break;
case 9:
return "visasmarr";
break;
}
return "ERROR";
}
 
object sub2(int intParam1, string stringParam2) {
string string1;
if ((stringParam2 == "WP_gspawn_")) {
string string2 = sub3(intParam1);
if ((string2 == "ERROR")) {
return OBJECT_INVALID;
}
string1 = (stringParam2 + string2);
}
else {
string1 = stringParam2;
}
object object1 = GetObjectByTag(string1, 0);
object object3;
if (GetIsObjectValid(object1)) {
object3 = SpawnAvailableNPC(intParam1, GetLocation(object1));
if (GetIsObjectValid(object3)) {
SetCreatureAILevel(object3, 3);
return object3;
}
}
return OBJECT_INVALID;
}
 
void sub1() {
SetPartyLeader(0xFFFFFFFF);
int int2 = 0;
while ((int2 < 12)) {
if (IsNPCPartyMember(int2)) {
RemoveNPCFromPartyToBase(int2);
}
(int2++);
}
}
 
void main() {
if (GetLocalBoolean(OBJECT_SELF, 30)) {
return;
}
SetLocalBoolean(OBJECT_SELF, 30, 1);
sub1();
sub2(0, "WP_650END_ATTON_0");
object oAtton = GetObjectByTag("Atton", 0);
effect efDamage = EffectDamage((GetCurrentHitPoints(oAtton) - 1), 8, 0);
ApplyEffectToObject(0, efDamage, oAtton, 0.0);
sub2(9, "WP_650END_0");
SetGlobalNumber("000_PSYCHOTIC", 0);
AssignCommand(GetFirstPC(), ClearAllActions());
AssignCommand(oAtton, ClearAllActions());
AssignCommand(GetFirstPC(), ActionStartConversation(oAtton, "AttonEnd", 0, 0, 0, "", "", "", "", "", "", 0, 0xFFFFFFFF, 0xFFFFFFFF, 0));
}
 

 

 

 

a_load006end:

 

 

void main() {
object oWP_650END_PC_0 = GetObjectByTag("WP_650END_PC_0", 0);
object oWP_650END_0 = GetObjectByTag("WP_650END_0", 0);
object oPC = GetFirstPC();
DelayCommand(0.5, AssignCommand(oPC, ActionJumpToObject(oWP_650END_PC_0, 1)));
object oVisasMarr = GetObjectByTag("VisasMarr", 0);
DelayCommand(0.6, AssignCommand(oVisasMarr, ClearAllActions()));
DelayCommand(0.6, AssignCommand(oVisasMarr, ActionStartConversation(oPC, "visasend", 0, 0, 1, "", "", "", "", "", "", 0, 0xFFFFFFFF, 0xFFFFFFFF, 0)));
}
 

 

 

Share this post


Link to post
Share on other sites

You're on the right track, though I notice you cut out the gender check in a_load006end.nss.
 
About the only way I could see doing this without modifying any dialogs would be to modify a_load262.ncs. When you decompile it, you'll get this:
 


 

void main() {
	PlayMovie("TelMov15", 0);
	StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
}

 


 
The only good thing is that Disciple has to move for his cutscene. So we need to check his location compared to where he needs to be.
 


 

void main()
{
    // If Disciple is close to where he to be
    if(GetDistanceBetweenLocations(GetLocation(GetObjectByTag("WP_650END_1")),
       Location(Vector(54.839211, 59.720371, 1.8), 0.0)) > 2.0)
    {
        PlayMovie("TelMov15", 0);
        StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
    }
    else
    {
        object oDisciple = GetObjectByTag("Disciple", 0);
        DelayCommand(0.6, AssignCommand(oDisciple, ClearAllActions()));
        DelayCommand(0.6, AssignCommand(oDisciple, ActionStartConversation(oPC, "discend", 0, 0, 1, "", "", "", "", "", "", 0, 0xFFFFFFFF, 0xFFFFFFFF, 0)));
    }
}

 

 

Share this post


Link to post
Share on other sites

@Fair Strides

 

First of all, thanks for your reply!

 

I cut the gender check in a_load006end.nss on purpose, so Visas would always be the one who talked to the Exile after the Rebuilt Jedi Enclave sequence, regardless of the Exile's gender.

 

I tried to follow your suggestions on modifying a_load262.ncs, but when I tried to compile the script you suggested, I got this error:

 

Error: Undeclared identifier "oPC"
Error: Required argument missing in call to "ActionStartConversation"
Error: Required argument missing in call to "AssignCommand"
Error: Required argument missing in call to "DelayCommand"

 

 

If I understand it correctly, the script you suggested is meant to trigger the discend.dlg file, but I have no intention to do that in my mod. What I'm trying to achieve is to trigger discadm.dlg (the dialogue where Disciple warns Carth or Cede about the Telos attack) after the visasend.dlg conversation, provided the Disciple joins your party, so I'm intending to add a check if the Disciple joins your party in my script.
 
I've also discovered the script files used for discadm.dlg are discstart.ncs and discadm.ncs. When I decompile those files, I get these:
 
discstart.ncs:

void main() {
	AssignCommand(GetObjectByTag("Disciple", 0), ActionStartConversation(GetFirstPC(), "discadm", 0, 0, 0, "", "", "", "", "", "", 0, 0xFFFFFFFF, 0xFFFFFFFF, 0));
	SetGlobalFadeIn(1.0, 1.0, 0.0, 0.0, 0.0);
}


 

 

 
discadm.ncs:

void main() {
	SetGlobalFadeOut(0.0, 0.0, 0.0, 0.0, 0.0);
	AssignCommand(GetObjectByTag("Disciple", 0), ActionJumpToLocation(Location(Vector(54.83921, 59.72037, 1.8), 0.0)));
	if ((GetGlobalNumber("101PER_Revan_End") == 0)) {
		CreateObject(1, "carth_holo", GetLocation(GetObjectByTag("WP_carth", 0)), 0);
	}
	else {
		CreateObject(1, "Rep_Gen", GetLocation(GetObjectByTag("WP_carth", 0)), 0);
	}
	DelayCommand(0.2, AssignCommand(GetObjectByTag("carth_holo", 0), SetFacingPoint(GetPositionFromLocation(GetLocation(GetObjectByTag("Disciple", 0))))));
}

 

I wonder if I can include these scripts in my modified a_load262.ncs?

 

I also want to add a check if the Disciple joins your party to trigger discadm.dlg, something like this:

 

void main() {
	if (IsAvailableCreature(NPC_DISCIPLE)) {
		"Script to trigger discadm.dlg"
	}
	else {
		PlayMovie("TelMov15", 0);
                StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
	}
}

Share this post


Link to post
Share on other sites

In that case, just execute discstart.ncs and let the game do the rest. :D But you'll still need to check for where Disciple is.

 

 

 

void main()
{
    if (IsAvailableCreature(NPC_DISCIPLE))
    {
        // If Disciple is too far from where he should be, the conversation hasn't taken place yet...
        if(GetDistanceBetweenLocations(GetLocation(GetObjectByTag("WP_650END_1")),
           Location(Vector(54.839211, 59.720371, 1.8), 0.0)) < 2.0)
        {
            ExecuteScript("discstart", OBJECT_SELF);
        }
        else
        {
            PlayMovie("TelMov15", 0);
            StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
        }
    }
    else
    {
        PlayMovie("TelMov15", 0);
        StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
    }
}

 

Share this post


Link to post
Share on other sites

In that case, just execute discstart.ncs and let the game do the rest. :D But you'll still need to check for where Disciple is.

 

 

 

void main()
{
    if (IsAvailableCreature(NPC_DISCIPLE))
    {
        // If Disciple is too far from where he should be, the conversation hasn't taken place yet...
        if(GetDistanceBetweenLocations(GetLocation(GetObjectByTag("WP_650END_1")),
           Location(Vector(54.839211, 59.720371, 1.8), 0.0)) < 2.0)
        {
            ExecuteScript("discstart", OBJECT_SELF);
        }
        else
        {
            PlayMovie("TelMov15", 0);
            StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
        }
    }
    else
    {
        PlayMovie("TelMov15", 0);
        StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
    }
}

 

May I ask how do I check for where Disciple is? I had compiled this script and tested it on my game but the discadm.dlg conversation still won't get triggered.

Share this post


Link to post
Share on other sites

You really shouldn't. As stated discadm.ncs he's jumped to that location instead of being there from the start.

Just having a disciple in party check should be sufficient, provided you actually run discadm AND discstart.

 

From the looks of your modified k_attonend he's not spawned in at all though, so he simply doesn't exist. Which makes it indeed very likely a proximity check fails :D

Share this post


Link to post
Share on other sites

You really shouldn't. As stated discadm.ncs he's jumped to that location instead of being there from the start.

Just having a disciple in party check should be sufficient, provided you actually run discadm AND discstart.

 

From the looks of your modified k_attonend he's not spawned in at all though, so he simply doesn't exist. Which makes it indeed very likely a proximity check fails :D

If I understand correctly, my problem was caused by me cutting the Disciple-related script in my modified k_attonend, so the Disciple didn't spawn on the Ebon Hawk at all, right?

 

Now, I tried to modify my k_attonend to spawn Visas as well as Disciple if he joins your party, like this:

 

 

// Prototypes
string sub3(int intParam1);
object sub2(int intParam1, string stringParam2);
void sub1();

string sub3(int intParam1) {
	switch (intParam1) {
		case 0:
			return "atton";
			break;
		case 1:
			return "baodur";
			break;
		case 2:
			return "mand";
			break;
		case 11:
			return "disciple";
			break;
		case 3:
			return "g0t0";
			break;
		case 4:
			return "handmaiden";
			break;
		case 10:
			return "hanharr";
			break;
		case 5:
			return "hk47";
			break;
		case 6:
			return "kreia";
			break;
		case 7:
			return "mira";
			break;
		case 8:
			return "t3m4";
			break;
		case 9:
			return "visasmarr";
			break;
	}
	return "ERROR";
}

object sub2(int intParam1, string stringParam2) {
	string string1;
	if ((stringParam2 == "WP_gspawn_")) {
		string string2 = sub3(intParam1);
		if ((string2 == "ERROR")) {
			return OBJECT_INVALID;
		}
		string1 = (stringParam2 + string2);
	}
	else {
		string1 = stringParam2;
	}
	object object1 = GetObjectByTag(string1, 0);
	object object3;
	if (GetIsObjectValid(object1)) {
		object3 = SpawnAvailableNPC(intParam1, GetLocation(object1));
		if (GetIsObjectValid(object3)) {
			SetCreatureAILevel(object3, 3);
			return object3;
		}
	}
	return OBJECT_INVALID;
}

void sub1() {
	SetPartyLeader(0xFFFFFFFF);
	int int2 = 0;
	while ((int2 < 12)) {
		if (IsNPCPartyMember(int2)) {
			RemoveNPCFromPartyToBase(int2);
		}
		(int2++);
	}
}

void main() {
	if (GetLocalBoolean(OBJECT_SELF, 30)) {
		return;
	}
	SetLocalBoolean(OBJECT_SELF, 30, 1);
	sub1();
	sub2(0, "WP_650END_ATTON_0");
	object oAtton = GetObjectByTag("Atton", 0);
	effect efDamage = EffectDamage((GetCurrentHitPoints(oAtton) - 1), 8, 0);
	ApplyEffectToObject(0, efDamage, oAtton, 0.0);
	if (IsAvailableCreature(NPC_VISAS)) {
		sub2(9, "WP_650END_0");
	}
	if (IsAvailableCreature(NPC_DISCIPLE)) {
		sub2(11, "WP_650END_0");
	}
	SetGlobalNumber("000_PSYCHOTIC", 0);
	AssignCommand(GetFirstPC(), ClearAllActions());
	AssignCommand(oAtton, ClearAllActions());
	AssignCommand(GetFirstPC(), ActionStartConversation(oAtton, "AttonEnd", 0, 0, 0, "", "", "", "", "", "", 0, 0xFFFFFFFF, 0xFFFFFFFF, 0));
}

 

 

 

 

I also tried to modify a_load262 again based on the script suggested by Fair Strides, but also make the script run both discadm and discstart, like this:

 

 

void main()
{
    if (IsAvailableCreature(NPC_DISCIPLE))
    {
        // If Disciple is too far from where he should be, the conversation hasn't taken place yet...
        if(GetDistanceBetweenLocations(GetLocation(GetObjectByTag("WP_650END_1")),
           Location(Vector(54.839211, 59.720371, 1.8), 0.0)) < 2.0)
        {
            ExecuteScript("discadm", OBJECT_SELF);
            ExecuteScript("discstart", OBJECT_SELF);
        }
        else
        {
            PlayMovie("TelMov15", 0);
            StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
        }
    }
    else
    {
        PlayMovie("TelMov15", 0);
        StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
    }
} 

 

 

 

However, this didn't solve my problems; when Visas talked to my female Exile, Disciple was standing near Visas, as seen in this screenshot; while the Visas conversation could proceed, discadm.dlg still wasn't triggered, so I would like to know what were the problems with my modified script? Thanks!

 

My a_load006end script is still the same as the one in my original post, if that helps.

Share this post


Link to post
Share on other sites

This is the part where you cheat and get sneaky. :) Visas and Disciple were meant to be a one-or-the-other scenario, so they spawn at the same waypoint.

 

Edit the k_attonend script to change the sub2 part that spawns Disciple so that the "" part is "mn_comm" (I looked at the waypoints in the .git file and found this one. It's out of the way in the Communications room). This will make sure we're good to go.

 

In the a_load262 script, try changing the second ExecuteScript to be this:

 

DelayCommand(0.25, ExecuteScript("discstart", OBJECT_SELF));

Share this post


Link to post
Share on other sites

This is the part where you cheat and get sneaky. :D Visas and Disciple were meant to be a one-or-the-other scenario, so they spawn at the same waypoint.

 

Edit the k_attonend script to change the sub2 part that spawns Disciple so that the "" part is "mn_comm" (I looked at the waypoints in the .git file and found this one. It's out of the way in the Communications room). This will make sure we're good to go.

 

In the a_load262 script, try changing the second ExecuteScript to be this:

 

DelayCommand(0.25, ExecuteScript("discstart", OBJECT_SELF));

Once again, thanks for your reply!  :D

 

I edited my k_attonend and a_load262 scripts as you suggested. While it solved the problem of Disciple getting in the way of Visas when the latter talked to my Exile, the discadm.dlg conversation still wasn't triggered unfortunately.

Share this post


Link to post
Share on other sites

In that case, debug time!

 

Add this to a_load262, before the Disciple distance check:

 

SendMessageToPC(GetFirstPC(), "Distance between Disciple and his needed location: " + FloatToString(GetDistanceBetweenLocations(GetLocation(GetObjectByTag("WP_650END_1")),
Location(Vector(54.839211, 59.720371, 1.8), 0.0)));

 

This will print a message to the Feedback screen in-game. We need to know what the answer is.

Share this post


Link to post
Share on other sites

In that case, debug time!

 

Add this to a_load262, before the Disciple distance check:

 

SendMessageToPC(GetFirstPC(), "Distance between Disciple and his needed location: " + FloatToString(GetDistanceBetweenLocations(GetLocation(GetObjectByTag("WP_650END_1")),

Location(Vector(54.839211, 59.720371, 1.8), 0.0)));

 

This will print a message to the Feedback screen in-game. We need to know what the answer is.

Just want to be sure, what I need to do is to edit a_load262 to be like this:

 

 

void main()
{
    if (IsAvailableCreature(NPC_DISCIPLE))
    {
        // If Disciple is too far from where he should be, the conversation hasn't taken place yet...
	SendMessageToPC(GetFirstPC(), "Distance between Disciple and his needed location: " + FloatToString(GetDistanceBetweenLocations(GetLocation(GetObjectByTag("WP_650END_1")),
Location(Vector(54.839211, 59.720371, 1.8), 0.0)));
        if(GetDistanceBetweenLocations(GetLocation(GetObjectByTag("WP_650END_1")),
           Location(Vector(54.839211, 59.720371, 1.8), 0.0)) < 2.0)
        {
            ExecuteScript("discstart", OBJECT_SELF);
            DelayCommand(0.25, ExecuteScript("discstart", OBJECT_SELF));
        }
        else
        {
            PlayMovie("TelMov15", 0);
            StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
        }
    }
    else
    {
        PlayMovie("TelMov15", 0);
        StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
    }
}

 

After testing my script, I check out the Feedback screen in-game and find the answer of "Distance between Disciple and his needed location:", is that correct?

Share this post


Link to post
Share on other sites

That is correct, but I'm a dummy. In both the SendMessageToPC and the GetDistanceBetweenLocations functions, change the "WP_650END_1" to "Disciple" and see if that changes anything.

Share this post


Link to post
Share on other sites

I added another ")" near the end of the SendMessageToPC function, otherwise when I compiled the script I would get a syntax error with the ";".

 

When I tested the script on my game, while I still didn't get the discadm.dlg conversation, I managed to get the answer of "Distance between Disciple and his needed location": 3.484300137

 

AcVaYh8.png

Share this post


Link to post
Share on other sites

This time, different problems came up:

 

Once the visasend.dlg conversation was over, the game returned to the Ebon Hawk and nothing happened, not even loading the TelMov15 movie like before. My Exile was able to move around the ship, and Visas, Dsciple and Atton were the only party members on the ship.

 

Disciple was in the communications room. Clicking him would start the discend.dlg conversation, after the conversation was over that's only when the discadm.dlg conversation happened and the game would proceed, except my Exile was seen in the cutscene since she was in the communications room.

 

 

Here are some screenshots:

 

http://i.imgur.com/1SH2BHv.jpg

http://i.imgur.com/A499Ftu.jpg

http://i.imgur.com/cRHq9Q2.jpg

Share this post


Link to post
Share on other sites

In that case, we can do it with either local variables or global ones, but I'm just trying to keep it as simple as possible since the guy wants to avoid editing any dialogs at all for some reason. Otherwise I'd just tell him to replace the a_load262 in Visas' dialog with both discadm on one node and discstart on the next one.

Share this post


Link to post
Share on other sites
I'm a lady, actually. :D

 

I want to avoid editing .dlg files as much as possible to increase my mod's compatibility with other mods, especially if I'm going to release my mod to the public, since as far as I know, mods that edit existing .dlg files are more common than ones that edit existing script files. I don't mind more complicated scripting as long as it's possible to achieve what I want without editing .dlg files.

Share this post


Link to post
Share on other sites

In that case I apologize. I'd add ", miss" to that, but I recently learned that not every woman likes being referred to that way.

 

If you're okay with editing the globalcat.2da file, then you can add a boolean called something like "LEI_650END_DISCTALK" and then edit your a_load262 this way:

 

 

 

void main()
{
    if (IsAvailableCreature(NPC_DISCIPLE) && GetGlobalBoolean("LEI_650END_DISCTALK") == FALSE)
    {
        SetGlobalBoolean("LEI_650END_DISCTALK", TRUE);
        ExecuteScript("discsadm", OBJECT_SELF);
        DelayCommand(0.25, ExecuteScript("discstart", OBJECT_SELF));
    }
    else
    {
        PlayMovie("TelMov15", 0);
        StartNewModule("262TEL", "FROM_006EBO", "", "", "", "", "", "");
    }
}

 

 

 

Edit: By the way, it took me forever to learn, but when posting the code snippets, if you set the Code Type to XML, you don't get the annoying color-coding.

Share this post


Link to post
Share on other sites

@Hassat Hunter: Because a_load262 is ran both after Visas' conversation and after Mical tells the Admiral about the attack on Telos. Leilukin is wanting to avoid modifying any dialog files, otherwise I'd just have her change the a_load262 to discadm and then add discstart on the next node.

Share this post


Link to post
Share on other sites
Hmmm, I wonder if discadm.dlg is hardcoded in some way so it can only be triggered after the discend.dlg conversation? I had tried many methods, including the ones that suggested in this thread, but I just can't get discadm.dlg to trigger at all.

 

I had even tried editing visasend.dlg to fire both discadm and discstart scripts, but I still got the problem of being stuck in the Ebon Hawk as mentioned in one of my previous posts. If discadm.dlg is really hardcoded that way and I'm going to release my mod, I will make a note about this issue for players who have TSLRCM installed and recruited Disciple.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.