Question about faction change

Alright, that didn’t work. I modified the gb_comp_death to this. Maybe this is totally wrong, or it’s just that you need to have something on the OnPlayerDeath instead:

// gb_comp_death
/*
   OnDeath handler to queue KnockOut script for companions
*/
// ChazM 8/17/05
// BMA-OEI 12/19/05 knock out death system
// BMA-OEI 1/26/06 exit if owned and controlled
// BMA-OEI 2/27/06 DeathScript support, exit if not in PC faction
// BMA-OEI 2/28/06  
// NLC 7/14/08 - Increased NX2's hardcore quotient.

#include "ginc_companion"
#include "ginc_death"
#include "ginc_debug"
#include "x2_inc_switches"

const string VAR_GLOBAL_NX2_TRANSITIONS = "bNX2_TRANSITIONS";

void main()
{
	object oDead = OBJECT_SELF;
	string sTag = GetTag(oDead);
	
	object oNPC = GetObjectByTag("jasper");
	object oNPC2 = GetObjectByTag("assates");
	object oNPC3 = GetObjectByTag("thondarion");
	
	PrintString( "gb_comp_death: " + GetName(oDead) + " executing OnDeath event" );
	
	if( GetIsRosterMember(oDead) == TRUE && GetLastKiller() == oNPC )
    {
  	ExecuteScript("k_death_remove_gui", oDead);
    }

	else if( GetIsRosterMember(oDead) == TRUE &&GetLastKiller() == oNPC2)
    {
  	ExecuteScript("k_death_remove_gui", oDead);
    }
	
	else if( GetIsRosterMember(oDead) == TRUE && GetLastKiller() == oNPC3)
    {
  	ExecuteScript("k_death_remove_gui", oDead);
    }


	else
	{
	
	// Abort if dying character is owned PC (owned PCs should fire module OnDeath)
	if ( GetIsOwnedByPlayer( oDead ) == TRUE )
	{
		PrintString( "** gb_comp_death: " + GetName(oDead) + " is owned by a player. ABORT!" );
		return;	
	}
	
	// Check for additional death script
	string sDeathScript = GetLocalString( oDead, "DeathScript" );
	if ( sDeathScript != "" )	ExecuteScript( sDeathScript, oDead );

	// Abort if dying character is not in PC faction
	if ( GetIsObjectInParty( oDead ) == FALSE )
	{
		PrintString( "** gb_comp_death: " + GetName(oDead) + " not in " + GetName(GetFirstPC()) + "'s party. ABORT!" );
		return;
	}
	if( !GetGlobalInt( VAR_GLOBAL_NX2_TRANSITIONS ) )
	{
		//NX2 is HARDCORE! No auto-ressing!
		AssignCommand( oDead, KnockOutCreature( oDead ) );
	}
	else
	{
		if ( GetIsDeathPopUpDisplayed(oDead) == FALSE )
		{
			if ( GetIsPartyPossessible(oDead) == FALSE )
			{
				PrettyDebug( "** ginc_death: " + GetName(oDead) + "'s party wiped. displaying death screen", 30.0 );	
				ShowProperDeathScreen( oDead );
			}
		}
	}
	
	}
}

OK, tried another one. The first on death script was wrong, but I thought this would work. However, I placed it OnPlayerDeath and there still may be the problem with GetLastKiller not working. Here’s how the modified script for my module looks like (I think this OnPlayerDeath script is from Colorsfade initially):

// k_mod_player_death
/*
   Module OnDeath handler to queue KnockOut script for owned PCs
*/
// BMA-OEI 12/19/05
// BMA-OEI 2/28/06 -- DeathScript support
// BMA-OEI 8/10/10 -- Exit if not owned by player (e.g. possessed companions)
// MDiekmann 7/10/07 - Modified to cause game ending condition if caused by spirit eater death
// TDE 6/20/08 - Adapted script for NX2

#include "ginc_death" 
#include "ginc_debug"

void main()
{
	object oDead = GetLastPlayerDied();
	object oDead2 = GetFirstPC();
	object oTaik = GetObjectByTag("taik");
	PrintString( "k_mod_player_death: " + GetName(oDead) + " executing OnPlayerDeath event" );
	
	object oNPC = GetObjectByTag("jasper");
	object oNPC2 = GetObjectByTag("assates");
	object oNPC3 = GetObjectByTag("thondarion");
	
		if( GetIsDead(GetObjectFromRosterName("taik")) == TRUE && GetLastKiller() == oNPC )
    {
  		ShowProperDeathScreen( oDead2 );
    }

	else if( GetIsDead(GetObjectFromRosterName("taik")) == TRUE &&GetLastKiller() == oNPC2)
    {
  		ShowProperDeathScreen( oDead2 );
    }
	
	else if( GetIsDead(GetObjectFromRosterName("taik")) == TRUE && GetLastKiller() == oNPC3)
    {
  		ShowProperDeathScreen( oDead2 );
    }
	else
	{
	
	// Abort if dying character is not an owned PC
	if ( GetIsOwnedByPlayer( oDead ) == FALSE )
	{
		PrintString( "** k_mod_player_death: " + GetName(oDead) + " not owned by a player. ABORT!" );
		return;	
	}
	// Making sure, here.
	//ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(), oDead);
	AssignCommand( oDead, KnockOutCreature( oDead ) );
	
	// Check for additional death script
	string sDeathScript = GetLocalString( oDead, "DeathScript" );
	if ( sDeathScript != "" ) ExecuteScript( sDeathScript, oDead );	
	
	// Check if there are any members left for PC to possess
	if ( GetIsOwnedByPlayer(oDead) == TRUE || GetIsRosterMember(oDead) == TRUE )
	{
		if ( GetIsDeathPopUpDisplayed(oDead) == FALSE )
		{
			if ( GetIsPartyPossessible(oDead) == FALSE )
			{
				PrettyDebug( "*** NO ONE LEFT! ***", 30.0 );	
				ShowProperDeathScreen( oDead );
			}
		}
	}
	
	ExecuteScript("k_death_remove_gui", oDead);
	}
}

The curly brackets don’t completely line up. Sorry for that. It compiles though, just so you know.

Crom on Discord told me that GetLastKiller() returns OBJECT_INVALID just as I thought, but he said that GetLastDamager would work. So I did a revision of the script (for the OnPlayerDeath), but still no luck. Nothing happens:

// k_mod_player_death
/*
   Module OnDeath handler to queue KnockOut script for owned PCs
*/
// BMA-OEI 12/19/05
// BMA-OEI 2/28/06 -- DeathScript support
// BMA-OEI 8/10/10 -- Exit if not owned by player (e.g. possessed companions)
// MDiekmann 7/10/07 - Modified to cause game ending condition if caused by spirit eater death
// TDE 6/20/08 - Adapted script for NX2

#include "ginc_death" 
#include "ginc_debug"

void main()
{
	object oDead = GetLastPlayerDied();
	object oDead2 = GetFirstPC();
	object oTaik = GetObjectByTag("taik");
	PrintString( "k_mod_player_death: " + GetName(oDead) + " executing OnPlayerDeath event" );
	
	object oNPC = GetObjectByTag("jasper");
	object oNPC2 = GetObjectByTag("assates");
	object oNPC3 = GetObjectByTag("thondarion");
	
	if( GetIsDead(GetObjectFromRosterName("taik")) == TRUE && GetLastDamager(oTaik) == oNPC )
    {
  	   string sScript = GetLocalString(oDead2, "DeathScript");
        if (sScript != "") 
			ExecuteScript(sScript, oDead2);
        AssignCommand(oDead2, KnockOutCreature(oDead2));
    }

	else if( GetIsDead(GetObjectFromRosterName("taik")) == TRUE &&GetLastDamager(oTaik) == oNPC2)
    {
  	   string sScript = GetLocalString(oDead2, "DeathScript");
        if (sScript != "") 
			ExecuteScript(sScript, oDead2);
        AssignCommand(oDead2, KnockOutCreature(oDead2));
    }
	
	else if( GetIsDead(GetObjectFromRosterName("taik")) == TRUE && GetLastDamager(oTaik) == oNPC3)
    {
  	   string sScript = GetLocalString(oDead2, "DeathScript");
        if (sScript != "") 
			ExecuteScript(sScript, oDead2);
        AssignCommand(oDead2, KnockOutCreature(oDead2));
    }

	else
	{
	
	// Abort if dying character is not an owned PC
	if ( GetIsOwnedByPlayer( oDead ) == FALSE )
	{
		PrintString( "** k_mod_player_death: " + GetName(oDead) + " not owned by a player. ABORT!" );
		return;	
	}
	// Making sure, here.
	//ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDeath(), oDead);
	AssignCommand( oDead, KnockOutCreature( oDead ) );
	
	// Check for additional death script
	string sDeathScript = GetLocalString( oDead, "DeathScript" );
	if ( sDeathScript != "" ) ExecuteScript( sDeathScript, oDead );	
	
	// Check if there are any members left for PC to possess
	if ( GetIsOwnedByPlayer(oDead) == TRUE || GetIsRosterMember(oDead) == TRUE )
	{
		if ( GetIsDeathPopUpDisplayed(oDead) == FALSE )
		{
			if ( GetIsPartyPossessible(oDead) == FALSE )
			{
				PrettyDebug( "*** NO ONE LEFT! ***", 30.0 );	
				ShowProperDeathScreen( oDead );
			}
		}
	}
	
	ExecuteScript("k_death_remove_gui", oDead);
	}
}

hm. module OnPlayerDeath might not be the best place to handle it.

id be tempted to do it in the OnHeartbeat of the Area instead

// 'ag_area_hb'
/*
    area OnHeartbeat script.

    Checks if the companion-fighter is still valid and alive.
    If not, end Game.
*/

const string sTAGFIGHT = "tag_co_fighter"; // tag of companion fighting

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

    object oPc = GetFirstPC();
    if (GetArea(oPc) == OBJECT_SELF)
    {
        object oFighter = GetObjectByTag(sTAGFIGHT);
        if (!GetIsObjectValid(oFighter) || GetIsDead(oFighter))
        {
            SendMessageToPC(GetFirstPC(FALSE), ". failed");
            // end Game
        }
    }
}

/very rough

You could try putting Taik in a group and then setting a group death script like so:

//	Fire this from a convo or trigger prior to the fight.
//	This will fire an end-game if Taik dies.
 
#include "ginc_group"

void main()
{
	string sGroup = "group_name";	
	object oComp = GetObjectByTag("taik");
	
	GroupAddMember(sGroup, oComp);
	GroupOnDeathExecuteCustomScript(sGroup, "ga_end_game");
}

If Taik survives the fight then fire this script from some convo after the fight to clear the group:

#include "ginc_group"

void main()
{
	ResetGroup("group_name");
}
2 Likes

Ok. This was way trickier than I thought…

What is this message one sends here? Is that the death GUI? I don’t fully grasp when looking at the OnPlayerDeath script what is truly that gui.
With travus script I understand it better since there it seems to run the ga_end_game script, but then again, isn’t ga_end_game where the whole game just jumps to the main menu? I would like it if the GUI for “load game” and “you are dead” appears. I hope you understand what I mean. My initial thought was to use the ShowProperDeathScreen to get that GUI, but maybe I’m wrong.

Edit: Maybe that’s hardcoded and you can’t get the GUI to show unless the PC and all the companions die?

Edit 2: Tried travus’ script now, and even if it did work, it, as suspected, took you to the main menu if Taik dies.

Somehow it seems what I need to use, or what I’m looking for, when looking at ginc_death is the function ShowDeathScreen or ShowProperDeathScreen. But then I would need some function like “If group is dead” and I can’t quite find such a function in the Script Assist.

Hmm, I think I got an idea now. Will try that out.

Edit: Sigh That didn’t work either. What I did was that I changed travus’ script to this:

//	Fire this from a convo or trigger prior to the fight.
//	This will fire an end-game if Taik dies.
 
#include "ginc_group"

void main()
{
	string sGroup = "group_name";	
	object oComp = GetObjectByTag("taik");
	
	GroupAddMember(sGroup, oComp);
	GroupOnDeathExecuteCustomScript(sGroup, "gui_asw3_showdeathscreen");
}

and the gui_asw3_showdeathscreen script looks like this:

#include "ginc_death"

void main()
{

object oPC = GetFirstPC();

ShowProperDeathScreen(oPC);

}

I though that would work but apparently not.

Ah, progress at last. When I did it like this, it worked, but you can’t press the load button:

#include "ginc_death"

void main()
{

object oPC = GetFirstPC();

ShowDeathScreen(oPC);

}

Ok, when looking at the function that seems easy to fix.

Yes!! Now it works. When doing the script like this it finally showed up as I wanted it to:

#include "ginc_death"

void main()
{

object oPC = GetFirstPC();

ShowDeathScreen(oPC,TRUE,FALSE,TRUE);

}

Try this new one. Make sure the name of this script is group_death:

//	group_death

//	Fire this from a convo or trigger prior to the fight.
//	This will fire a death screen if Taik dies.
 
#include "ginc_group"
#include "ginc_death"

void main()
{
	object oComp = GetObjectByTag("taik");
	
	if (!GetIsDead(oComp))
	{
		string sGroup = "group_name";
		
		GroupAddMember(sGroup, oComp);
		GroupOnDeathExecuteCustomScript(sGroup, "group_death");
	}
	
	else ShowProperDeathScreen(GetFirstPC(FALSE));
}

Still use the reset script after the fight.

@andgalf

Sorry I am a bit late to this post, and I think you are resolved (although I have not read every post), but to do this, all you need to do is setup some factions using the faction editor and preparing some faction objects to refer to.

The preparation involves setting up some “dumb” NPCs in an area not reachable by the player. Set the faction that you define in the editor on each dumb NPC and then apply the new factions to the NPCs you want to change with ChangeFaction.

When the companions leave the party, they become NPCs, so this faction function will work with them, but not with the Main PC. Setup factions where the “enemy” is neutral to all members not involved in fight and “player”, but hostile to the companion you want to fight and do the same with the companion involved in the fight with the “enemy”.

If and when you want the companions to rejoin the party, you can always change faction to something else again just prior joining, although they will be player faction upon joining anyway.

Although you appear to have it solved (having jumped to the end of this thread), I thought I 'd add this in case it helped any anyway.

EXAMPLE:

PIG 1: FACTION_ENEMY Set neutral to player faction and hostile to companion target. (Assign to enemy.)
PIG 2: FACTION_COMPANION Set neutral to player faction and hostile to companion’s enemy target. (Assign to companion.)

EDIT: Take care when using object oPC = GetEnteringObject(); as sometimes the object calling may not be a PC (as such). It could be from a GUI call by a player (if used like I do sometimes). So use a check line straight after like this … and do NOT stop the script just because it is not a PC, or you can see it will may not fire at all!

if(oPC == OBJECT_INVALID){oPC = OBJECT_SELF;}

Yes, there is an invisible “Collision box” that can be used from the placeables. NB: By default, this does not block line of sight. However, I have set up a new line in the placeables.2da with a copy of this with BlockLoS as true. I believe that should work, although I have never tried/used it in this way.

Thanks, Lance.

re. blocker things… In placeables under misc props there are collision boxes and balls that you can scale to any size thus making invisible walls. Give them a tag and you can easily destroy them when necessary. I’m not sure if you can shoot through them though.

If you can you might even be able to stick in anything and make it invisible, but I’ve never tried that.

See my post to andgalf above …

Thanks, Lance.

Oops ! I just saw it ! Sorry Lance.

1 Like

@travus - Since I’ve already resolved this, why would I need the new script?

Edit: Reading and trying to understand your new script I’m not sure it will work since you use ShowProperDeathScreen, and when I tried that in my own script nothing showed up. What I have now works though (your first script only modified), so I actually think I will stick with that, since I see no holes in my method with:

#include "ginc_group"

void main()
{
	string sGroup = "group_name";	
	object oComp = GetObjectByTag("taik");
	
	GroupAddMember(sGroup, oComp);
	GroupOnDeathExecuteCustomScript(sGroup, "gui_asw3_showdeathscreen");
}

and then the gui_asw3_showdeathscreen coded like this:

#include "ginc_death"

void main()
{

object oPC = GetFirstPC();

ShowDeathScreen(oPC,TRUE,FALSE,TRUE);

}

If you tell me otherwise, and I have missed something, then sure, but for now I’ll stick with what I’ve got instead.

@Lance_Botelle - Thanks for trying to help, although, practically everything you mentioned in your post I already know. Since I want to control the companion he has to be part of the PC party, and thus I can’t make him another faction. Making him another faction would have been ideal, since then the PC wouldn’t have been in combat stance then. Sadly this doesn’t seem to be possible if one is to control the companion. And talking about the wall you and @Tsongo mentioned I already mentioned I got help with this from Discord.

To quote myself:

This sounds a bit interesting though. But at the moment I feel like I want to move on from this scene, when I’ve finally got most of it to work. And I’m pretty sick and tired of it after testing it 30 times or so.

Edit: I changed my mind. I actually got a little curious how this would work. However, after adding a line copying the Collision Box and pasting it on line 10 000 in the placeables.2da, and setting BlockLoS to 1 (which I think represents True) it doesn’t show up in the toolset. I can’t find it under appearance in the blueprint. I gave it a unique label in the 2da. I called ASW3CollBox I believe. How does your line look in the 2da to make it showing up in the toolset? Oh, nevermind. Another one of those 2 placables.2da conflicts again. Forgot, and this is because there’s always so much testing things back and forth, I had had to use a walkmeshhelper hak, and then forgot to remove it.
I will try the collision box with blocked line of sight now.

OK, not a cutscene combat … my bad!

At least you have it sorted. Great!

Yes, it should be reasonably straightforward to setup … Let us know the result.

Lance.

1 Like

Tried it. Sorry to say it didn’t help. Maybe an archer won’t be able to fire arrows at the enemy (didn’t use an archer in my test), but that’s a minor issue I’ve already solved with EffectDominated, but the PC is still in combat stance, and that may be because he knows there’s a fight going on, I don’t know…

Yes, that is something else (possible detection) … but I suspect they cannot attack through it, which is what I thought would happen.

Anyway, you have it solved by the sounds of it, so that is what matters!
Thanks, Lance.

These last few days I played through my module to reach the point where I am now.

However, oddly enough the reset group thing @travus taught me didn’t seem to work. After this fight when taik is part of the party again, when fighting, if Taik dies, there is the game over screen, even though I reset the group. Don’t understand how this could be. Maybe I just didn’t hit “refresh” when saving the conversation where I run the reset group script…I don’t know. Here’s how the two scripts look in my module at the moment (I never used travus’ latest version he posted here, so maybe that’s it?). If you could help me again, it would be much appreciated:

//	Fire this from a convo or trigger prior to the fight.
//	This will fire an end-game if Taik dies. - Script by travus
 
#include "ginc_group"
#include "ginc_death"

void main()
{
	string sGroup = "taik_group";	
	object oComp = GetObjectByTag("taik");
	
	GroupAddMember(sGroup, oComp);
	GroupOnDeathExecuteCustomScript(sGroup, "gui_asw3_showdeathscreen");
} 

After the fight, this script is run in a conversation:

#include "ginc_group"

void main()
{
	ResetGroup("taik_group");
}

Have I done something wrong here?