Question about faction change

So, I’ve got a situation where the all but one of the companions leave the PC, and then everyone is teleported to an indoor area, the PC to one waypoint, the companions to another, and the one single companion left in the party to a third waypoint. Then a creature attacks the one companion. I noticed that the companions that had left the party still attacks the hostile creature. My guess is that when leaving the party they become commoners, join the Commoner Faction that is, and that’s why they too automatically attacks the hostile creature.

Now, I would like the companions to join a made up faction of mine, called Bystander that is not hostile to Hostiles. I know a little bit about the faction pig thing, but I’ve managed to avoid that for the most part in my modules, and that is I believe due to NPCs who are Bystanders not changing factions and therefore it has worked so far.

I looked at the function ChangeFaction, and in the description there it says

// Make oObjectToChangeFaction join the faction of oMemberOfFactionToJoin.
// NB. ** This will only work for two NPCs **

I’m not sure what they mean by “This will only work for two NPCs”. Does it mean that just one NPC can join another NPCs faction? In that case I guess I can’t use this to change the companions to my custom faction Bystander unless I have a faction pig?

I think it depends on what their faction was before they joined the party (have a look at their blueprints or instances).

use the pig when ya gotta use the pig …

yep. You can’t use that function to either (a) remove a PC party member, or (b) add an NPC to the party.

so … remove the party members and roughly speaking on the next line of script, assign them to a faction/ to the pig’s faction.

1 Like

I’ve been struggling with another bug for a couple of hours now. Thankfully it got sorted out.

I tried with this function just to see what would happen and you’re right, it seems I indeed need a faction pig for this. I think I’ll maybe create a new faction altogether for this particular thing.

By the way, my companions are all Commoners when at the start of the game.

Maybe one can use script hidden on the faction pig…I don’t want it to be visible. Don’t remember if the faction pig needs to be in the same area as the others.
Edit: Ah, I see that’s what I did in my second module in an area.

iirc,

FactionPig
- scripthidden
- remove all AI scripts
- plot

(the stock factionpig works but i think it could be a bit better)

ie. Make the pig as ignorant as possible. All it is, is a creature-object with a Faction.

And, if it’s only needed in one area id keep it in that area, and use GetNearestObjectByTag()
but technically it could be anywhere in the module, and gotten w/ GetObjectByTag()

It’s really odd. I can’t get it to work. Don’t know what’s happening at the moment. I remove all the companions from the party and teleport them to three different places. Then when entering the area a script is supposed to run. For me it seems like it doesn’t run. I’ve put it on OnClientEnter. In this script the companions, now not part of the party (or roster if you will), and I change the faction for them, and then I change the faction of an NPC that is supposed to be hostile, but at the moment the NPC doesn’t become hostile. I don’t get it. I mean, if the ChangeFaction didn’t work, that I could understand, but that the NPC doesn’t change faction, that’s weird.

void main()
{

	object oPC = GetEnteringObject();
	
	if(!GetIsPC(oPC)) return;
	
	object oElvera = GetObjectByTag("elvera");
	object oCelestine = GetObjectByTag("celestine");
	object oLoreen = GetObjectByTag("loreen");
	object oAnto = GetObjectByTag("anto");
	
	object oFactionPig = GetObjectByTag("facpigobs");
	
	
	ChangeFaction(oElvera,oFactionPig);
	ChangeFaction(oCelestine,oFactionPig);
	ChangeFaction(oLoreen,oFactionPig);
	ChangeFaction(oAnto,oFactionPig);
	
	object oNPC = GetNearestObjectByTag("thondarion",oFactionPig); 
	
	ChangeToStandardFaction(oNPC,STANDARD_FACTION_HOSTILE);
	
}

debug …

// 'factionchange'

void main()
{
    SendMessageToPC(GetFirstPC(FALSE), "factionchange");

    object oPC = GetEnteringObject();

    if(!GetIsPC(oPC)) return;

    object oElvera = GetObjectByTag("elvera");
    object oCelestine = GetObjectByTag("celestine");
    object oLoreen = GetObjectByTag("loreen");
    object oAnto = GetObjectByTag("anto");

    object oFactionPig = GetObjectByTag("facpigobs");

    SendMessageToPC(GetFirstPC(FALSE), ". oElvera "    + (GetIsObjectValid(oElvera)    ? "VALID" : "INVALID"));
    SendMessageToPC(GetFirstPC(FALSE), ". oCelestine " + (GetIsObjectValid(oCelestine) ? "VALID" : "INVALID"));
    SendMessageToPC(GetFirstPC(FALSE), ". oLoreen "    + (GetIsObjectValid(oLoreen)    ? "VALID" : "INVALID"));
    SendMessageToPC(GetFirstPC(FALSE), ". oAnto "      + (GetIsObjectValid(oAnto)      ? "VALID" : "INVALID"));

    SendMessageToPC(GetFirstPC(FALSE), ". oFactionPig " + (GetIsObjectValid(oFactionPig) ? "VALID" : "INVALID"));


    ChangeFaction(oElvera,oFactionPig);
    ChangeFaction(oCelestine,oFactionPig);
    ChangeFaction(oLoreen,oFactionPig);
    ChangeFaction(oAnto,oFactionPig);

    object oNPC = GetNearestObjectByTag("thondarion",oFactionPig);

    SendMessageToPC(GetFirstPC(FALSE), ". oNPC " + (GetIsObjectValid(oNPC) ? "VALID" : "INVALID"));

    ChangeToStandardFaction(oNPC,STANDARD_FACTION_HOSTILE);

}

note that OnClientEnter fires only when a “player” enters …

Yes, I know. Travus said that in another thread. But the main character, the player, enters this area too at the same time so…that shouldn’t be a problem.

I’ll try your debug script.

1 Like

That’s really odd. Tried it now and the only thing that shows up on screen is “factionchange”. Maybe it’s wrong to use GetEnteringObject when doing a script on OnClientEnter? Or could it be since I teleport everyone there, that the first thing entering is not the PC but one of the companions?

This is an old script you did for me for my first module, kevL_s, that I’ve modified to fit here in this context. Maybe there’s something here that gives a clue as to what’s going on? This script runs at the end of a conversation, where the characters teleport to this indoor area, and then the OnClientEnter script runs (maybe I’ve somehow modified your script the wrong way? Though, to be fair, it kind of works since everyone shows up where they are supposed to):

// 'ga_teleport_glitch'
//
// dialog script. Assumes conversation between the PC and a Companion whose
// attempt to teleport will glitch. Also assumes that both the PC and the
// Companions will end up in the same module that the teleport starts in.
//
// NOTE: no checks are done for the validity of waypoints, etc.
//
// 2018 jan 7 - this version jumps Taik to a separate destination if in the
//              party. Player-control then switches to Zecane.

#include "x0_i0_assoc" // SetAssociateState()


const string sDEST_PC         = "pit_pc";   // waypoint tag of the PC's destination
const string sDEST_Z          = "pit_tai";      // waypoint tag of the Taik's destination
const string sDEST_COMPANIONS = "pit_party";  // waypoint tag of the Companions' destination

const float fSAFE = 5.f; // interval to allow the next area to fully load.


void Glitch(object oPC);
void Taik(object oPC);


// note: OBJECT_SELF is Speaker (not PcSpeaker)
void main()
{
    object oPC = GetPCSpeaker();

	if (!GetIsPC(oPC)) return;

	//int nInt;
	//nInt=GetLocalInt(oPC, "NW_JOURNAL_ENTRYzecane_duel_quest");

	//if (nInt ==  11)
   	//			return; 
	
    ClearAllActions(); // safety. Ends conversation
	
    FadeToBlack(oPC, FADE_SPEED_MEDIUM, fSAFE + 5.f); // single player

    DelayCommand(FADE_SPEED_MEDIUM, Glitch(oPC));
}


// Delayed to give time for the fade to finish.
void Glitch(object oPC)
{
    oPC = SetOwnersControlledCompanion(oPC); // get this done pronto.

    object oDestCompanions = GetWaypointByTag(sDEST_COMPANIONS);
	

	
    string sRoster;
    string sRosterListCurrent = ""; // roster who are removed from the party

    object oParty = GetFirstFactionMember(oPC, FALSE);
    while (GetIsObjectValid(oParty))
    {
        AssignCommand(oParty, ClearAllActions());

        if (GetIsRosterMember(oParty))
        {
           

            sRoster = GetRosterNameFromObject(oParty);
            if (sRoster != "taik")
            {
                sRosterListCurrent += sRoster + ";";

                RemoveRosterMemberFromParty(sRoster, oPC, FALSE);
                AssignCommand(oParty, JumpToObject(oDestCompanions));

                oParty = GetFirstFactionMember(oPC, FALSE);
                continue;
            }
        }

        oParty = GetNextFactionMember(oPC, FALSE);
    }

    if (sRosterListCurrent != "")
        SetLocalString(oPC, "CurrentRosterList", sRosterListCurrent);


    SetAssociateState(NW_ASC_MODE_STAND_GROUND, TRUE, oPC);
    AssignCommand(oPC, JumpToObject(GetWaypointByTag(sDEST_PC)));

    DelayCommand(fSAFE, Taik(oPC));
}

// The transition to another area has to be complete before Zecane will part
// from the PC. So Zecane's jump is delayed by fSAFE sec.
void Taik(object oPC)
{
    object oTaik = GetObjectFromRosterName("taik");
    AssignCommand(oTaik, JumpToObject(GetWaypointByTag(sDEST_Z)));

    SetOwnersControlledCompanion(oPC, oTaik);
}

Wait a minute…If it is as you and travus say, that OnClientEnter only fires when a player enters the area, then we wouldn’t really need GetEnteringObject now would we? You should be able to switch it to GetFirstPC instead, right?

Edit: Tried it and now everything seems to work scriptwise. I have another problem now though with this scene, but that will have to wait until morning.

Edit 2: By the way, I removed the

if(!GetIsPC(oPC)) return;

too.

comment out these lines, i don’t think they’re helpful …

//  object oPC = GetEnteringObject();

//  if (!GetIsPC(oPC)) return;

just let the OnClientEnter run without checks … your module is SP iir, so it should fire only once and only for the player

 
ps. Perhaps you should be using GetFirstEnteringPC() in the OnClientEnter … but i don’t believe it’s even needed

 

okie doke

OK, well, as it seems to work now, I’ll leave it as it is. I’ll maybe come back here and ask some more questions about this scene, but I’ll see if I can figure out how to solve the issue I have now with the scene. This is very similar to the duel scene in my first module which worked fine with the PC nearby, but right now the hostile NPC goes after the PC instead of the character Taik which stands right in front of him. It’s so weird sometimes how NPCs behave. I mean, the most logical thing would be to attack the character in front of you, not run away and attack the PC.

Edit: I really need to sleep now but…maybe one could do some script to make the NPC ignore the PC? Something to put on his OnPerceived perhaps?

that is odd

the AI typically /spoiler alert

goes after the nearest enemy/threat.

It might help to explicitly call AssignCommand(oNPC, DetermineCombatRound(oTarget))

g’night Ag  ;)

Ah, ok. I’ll try with the DetermineCombatRound (I know I’ve used that function before when I want an enemy character attack right away, and I beleive I’ve also used another function called something like “AttackPC” or…I don’t remember the name of the function, will have to look it up)

Edit: What I meant was ActionAttack(oPC);

Eh…Uh…Now I’m getting scared. What’s going on here. I can’t find the function DetermineCombatRound in the script assist. Have I done something, put something in my override to cause this…? Loaded another module, emptied my override, and I still can’t find that function. I have a vague memory using this in my second module…but I could be wrong. Is it a function that only exists in NWN1?

Edit: Ah, now I calmed down. This function is part of the “nw_i0_generic” include it turns out.

Ok, I got everything working now. To hinder the PC from interfering I did Petrify(oPC); on him. This works really well. However, he is really frozen in the movement he did when he entered the area. That is of course logical. What I wonder is if there’s some other effect that hinders the PC and the player from doing anything with the character, but that still makes him stand still and move his head occasionally and so forth. Like he’s standing naturally, but you can’t get him into combat…

Hey, I actually got an idea now. Maybe you could make two new factions with faction pigs and assign this faction to the companion who is supposed to fight and the other to the opponent…I think I’ll try that instead.

Edit: Darn, you can’t do it like that. Of course you can’t. If you change the companion’s faction he’s no longer part of the player faction and then you can’t control him. Darn.

Tried with the paralyze effect instead on the PC but that looks exactly the same as the petrify effect. There’s no effect that makes the PC stand still, like Stand-Your-Ground but without the player being able to change this and attack a target?

Did you try “Daze” (but I think it only affects creature with 5 HD or less), “Domination” or “Charm”?

AssignCommand(oPC, ClearAllActions(TRUE));
SetCommandable(FALSE, oPC);

should work …

but MAKE SURE it gets set TRUE later! (no iffs ands or butts else borked game)

@4760 No, I haven’t tried Daze, Domination or Charm yet.

@kevL_s - Ok, I’ll try with your suggestion. Maybe I can do a SetCommandable(TRUE,oPC); as soon as the NPC dies?