Copy and level up character - is this doable through scripting?

I had another idea for a sidequest in the module I’m working on at the moment. I would want to copy one of the companion characters, send the original off somewhere temporarily, and then also level up the copy of the character. I have a feeling this is not doable within NWN2 but I don’t know.

I would want the copy of the character to turn evil (ok, that’s easy) and attack the party, but since the party is quite big, I want the copy of the character to be way more powerful than the original (that’s why I want the level up). Sure, I could just make a copy of the blueprint of the character and spawn in that one, BUT it would be a lot cooler if the copy is a clear copy of how the character looks at that moment in the game with weapons, armor and all. I’m worried that the leveling up won’t just work at all, but as I am still not good at advanced scripting, I thought I’d ask here. This how I’ve made the script so far:

void main()
{
	
object oLoreen = GetObjectByTag("loreen");

location lLoreen = GetLocation(oLoreen);

CopyObject(oLoreen,lLoreen,OBJECT_INVALID,"loreenevil");

object oDestination = GetObjectByTag("lortowp");

AssignCommand(oLoreen, DelayCommand(0.2, JumpToObject(oDestination)));

//Then the copy is to change faction, attack the party and bla bla bla, but that I will add later, first I need the levelling up if this is to work as intended...

}
1 Like

auto-leveling depends on what the character has for its Package ( Packages.2da )

rough steps?

  • remove from party and copy
  • give a bunch of xp
  • then call ResetCreatureLevelForXP()
  • ???
1 Like

The starting package for this character is Default Swashbuckler.

Ok, when looking at the function ResetCreatureLevelForXP there is something I don’t understand. I choose what creature to reset, and then the amount of experience to give…eh…don’t get that.
If I first give a bunch of xp, then reset the level to zero and have to give a bunch of experience again, why would I need to give more xp first in the first place if everything is reset? I feel quite confused here…So the code at the moment (not finished by a long shot) looks kind of like this:

void main()
{
	
object oLoreen = GetObjectByTag("loreen");

location lLoreen = GetLocation(oLoreen);

CopyObject(oLoreen,lLoreen,OBJECT_INVALID,"loreenevil");

object oEvilLoreen = GetObjectByTag("loreenevil");

	if(GetIsObjectValid(oEvilLoreen))
	{

	GiveXPToCreature(oEvilLoreen,5000);

	ResetCreatureLevelForXP(oEvilLoreen,10000,TRUE);

	}

object oDestination = GetObjectByTag("lortowp");

AssignCommand(oLoreen, DelayCommand(0.2, JumpToObject(oDestination)));

}

Replace this:

	GiveXPToCreature(oEvilLoreen,5000);

	ResetCreatureLevelForXP(oEvilLoreen,10000,TRUE);

with this:

	int nXP = GetXP(oEvilLoreen) + 5000;	

	ResetCreatureLevelForXP(oEvilLoreen, nXP, TRUE);
2 Likes

Ah, ok! A question about that: Will the character levelup itself? What I mean is, when you have a companion that receives XP, you have to level that character up as a player. But at this time this evil character won’t be part of the party an thus needs to levelup herself. Does she do that with this script?

Yes, they will level-up automatically according to their package. The package determines what feats, skills, ability scores, spells they pick when they level-up. Note that the reset could change what feats, skills, ability scores, spells they originally had prior to the level-up. So they may not be exactly the same as their copy after the reset.

1 Like

Ah, yes, that makes sense. I tested it ingame and it worked great! Now, I just need to do a bit of tweaking. Thank you, travus!

1 Like

Uugh! Even though my first attempt was successfull I tried to tweak everything so that it looks a lot better in game, but now I don’t know what’s going on. The script I’m using won’t fire like it should. I’m getting a head ache from this. It’s to tiring.

Ok, so here’s what I did instead. The party is in a tavern. When they go out and certain things have happened, the character Loreen is copied, then is supposed to leave the party and teleport somewhere to another area, while the rest of the party moves outside the tavern. At the same time the copy EvilLoreen is to be spawned at a location of an NPC who is just outside the tavern. I put this script on OnUsed of the door exiting the tavern. However, nothing at all seems to happen. What I mean by that, is that the party just teleports outside the tavern, Loreen doesn’t leave the party and no EvilLoreen is copied and spawned.
I don’t know what I’m doing wrong, I must be missing something…Sigh… This script is a modified version of a script I got from kevL_s two years ago for my first module. I tried to take away things in the script for testing purposes but…

// 'ga_teleport_glitch' - Script by kevL_s. Slightly modified by andgalf.
//
// dialog script. Assumes conversation between the PC and a Companion whose
// attempt to teleport will glitch. Also assumes that both the PC and the
// Companion will end up in the same module that the teleport starts in.
//
// NOTE: no checks are done for the validity of waypoints, etc.


const string sDEST_TELEPORTER = "lortowp"; // waypoint tag of the teleporter's destination
const string sDEST_PARTY      = "outsbbt"; // waypoint tag of the party's destination
const string sROSTER_LOREEN = "loreen";

void TeleportAll()
{

	object oEvilLoreen = GetObjectByTag("loreenevil");
		
	int nXP = GetXP(oEvilLoreen) + 30000;
		
	object oPC = GetFirstPC();	

	ResetCreatureLevelForXP(oEvilLoreen, nXP, TRUE);

    	object oLoreen2 = GetObjectFromRosterName(sROSTER_LOREEN);
    	RemoveRosterMemberFromParty(sROSTER_LOREEN, oPC, FALSE);

    	// second jump the companion to his/her destination waypoint
    	object oDestTeleporter = GetWaypointByTag(sDEST_TELEPORTER);
    	JumpToObject(oDestTeleporter);


    	// third jump the remaining party to their destination waypoint
    	object oDestParty = GetWaypointByTag(sDEST_PARTY);

    	object oFaction = GetFirstFactionMember(oPC, FALSE);
    	while (GetIsObjectValid(oFaction))
    	{
        	AssignCommand(oFaction, ClearAllActions());
        	AssignCommand(oFaction, JumpToObject(oDestParty));

        	oFaction = GetNextFactionMember(oPC, FALSE);
    	}


}


void main()
{
    object oPC = GetLastUsedBy();
	
	if(!GetIsPC(oPC)) return;
	
	oPC = SetOwnersControlledCompanion(oPC);

    // first remove the companion (conversation owner) who's doing the teleport
    // from the party
    //ClearAllActions();
	
	//if(GetGlobalInt("mirroraq"))
	//{
		object oNPC = GetObjectByTag("commonerbb5");	
		object oLoreen = GetObjectByTag("loreen");
		
		location lLoreen = GetLocation(oNPC);
	
		CopyObject(oLoreen,lLoreen,OBJECT_INVALID,"loreenevil");
		
		DelayCommand(1.0,TeleportAll());
	
	//}	
		
	//else
	//{
	
	//JumpPartyToArea(oPC, GetWaypointByTag("outsbbt"));
	
	//}	
		
	
}

I believe you need to “tell” the script who is going to jump.

So instead of

JumpToObject(oDestTeleporter);

try

AssignCommand(oLoreen2, JumpToObject(oDestTeleporter));

Ok, yes, that makes sense. I wonder why it worked before in kevL_s’ script though. But in the original script it was called from a conversation, so that could have something to do with it, I guess.

I’ll try your suggestion @4760. Thanks!

Well, I tested it, but it didn’t change much. The weird thing is that the script doesn’t seem to run at all. I can’t fathom why. I tried with having a companion click the door but it didn’t even switch to the main character which it is supposed to do. What am I missing here…?

Ok, I will test and put this script on the OnClick on the door instead and see what happens. (I’ll change oPC to GetClickingObject).

Ok, so this worked…kind of. Now another weird thing happens. Loreen teleported and Evil Loreen spawned, but once out of the tavern she attacks the party instantly. How could that be? I haven’t yet told her that she is evil (I haven’t changed the faction yet for her). Does it have something to do with her not being a part of the party and thus the game gets confused by what faction she should be part of? It’s so strange…

Edit: Crom, on Discord, told me that the copy probably has no faction, thus defaults to hostile, so I need to change/set her faction right away in the script it seems. I’ll try that and hopefully things will work now.

Edit2: Yes! Now it works!

2 Likes

Ok, we’re entering spoiler territory now for those who might want to play this module when it is finally released in a year or so, but I have to explain very thoroughly what’s going on here, and this only concerns a skippable sidequest so…

Further testing revealed other strange things happening further on, which made me rethink the whole thing, and I did it in another way. This seemed to work flawlessly when testing a couple of times, but just before going to bed last night, when testing one last time, a game breaking bug appeared which got me a bit depressed to be honest.

So, here’s how I’ve done it now. When the party exits a tavern I run a script that first makes a copy of Loreen, then teleports normal Loreen to another area, and placing the copy outside the tavern. When exiting the tavern a dialogue is run which then leads to a confrontation with EvilLoreen. When her damage is below 15 a new conversation is run through CreateIPSpeaker. At the end of that conversation EvilLoreen runs off (and is destroyed). The party is then to search for her. Last night when testing, and I did run the module from the toolset by the way, when getting to the area where I teleported normal Loreen, she all of a sudden wasn’t there. So, for some reason the teleporting must have gotten screwed up or something. Here is the script placed at OnClick of the door when exiting the tavern. Maybe I’ve done something there that could cause this error (that only appeared this one time, but still…)


const string sDEST_TELEPORTER = "loreenevwp"; // waypoint tag of the teleporter's destination
const string sDEST_PARTY      = "outsbbt"; // waypoint tag of the party's destination
const string sROSTER_LOREEN = "loreen";

void TeleportAll()
{

		object oEvilLoreen = GetObjectByTag("loreenevil");
		object oLoreen = GetObjectByTag("loreen");
		
		int nXP = GetXP(oEvilLoreen) + 40000;
		
		object oPC = GetFirstPC();	

		ResetCreatureLevelForXP(oEvilLoreen, nXP, TRUE);
		
		SetImmortal(oEvilLoreen,TRUE);
		
		ChangeToStandardFaction(oEvilLoreen, STANDARD_FACTION_COMMONER);

		SetGlobalInt("evilfromt",1);
		
		effect eDamage = EffectDamage(40, DAMAGE_TYPE_SLASHING);
 
		ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oLoreen);

    	object oLoreen2 = GetObjectFromRosterName(sROSTER_LOREEN);
    	RemoveRosterMemberFromParty(sROSTER_LOREEN, oPC, FALSE);
	
    	// second jump the companion to his/her destination waypoint
    	object oDestTeleporter = GetWaypointByTag(sDEST_TELEPORTER);
    	AssignCommand(oLoreen2, JumpToObject(oDestTeleporter));

    	// third jump the remaining party to their destination waypoint
    	object oDestParty = GetWaypointByTag(sDEST_PARTY);

    	object oFaction = GetFirstFactionMember(oPC, FALSE);
    	while (GetIsObjectValid(oFaction))
    	{
        	AssignCommand(oFaction, ClearAllActions());
        	AssignCommand(oFaction, JumpToObject(oDestParty));

        	oFaction = GetNextFactionMember(oPC, FALSE);
    	}


}


void main()
{
    object oPC = GetClickingObject();
	
	if(!GetIsPC(oPC)) return;
	
	oPC = SetOwnersControlledCompanion(oPC);

    // first remove the companion (conversation owner) who's doing the teleport
    // from the party
    //ClearAllActions();
	
	if(GetGlobalInt("mirroraq") && !GetLocalInt(OBJECT_SELF,"Done"))
	{
		object oWP = GetObjectByTag("evillwp");	
		object oLoreen = GetObjectByTag("loreen");
		
		SetLocalInt(OBJECT_SELF,"Done",1);
		
		location lLoreen = GetLocation(oWP);
	
		CopyObject(oLoreen,lLoreen,OBJECT_INVALID,"loreenevil");
		
		DelayCommand(1.0,TeleportAll());
	
	}	
		
	else
	{
	
	JumpPartyToArea(oPC, GetWaypointByTag("outsbbt"));
	
	}	
		
	
}

One thing that has been on my mind about this is that maybe the waypoint tag is a tad bit too long. I think I’ve encountered such issues before. What do you script wizards think? My thought is to maybe place a script in the area where normal Loreen is supposed to be teleported to, and if she’s not there, just report through SendMessagetoPC that a serious error has occured. I don’t know. Here’s the script I’ve put on OnClientEnter of the area where normal Loreen is teleported (she is always part of the party except for this time in the module):

#include "ginc_object"


void main()
{

object oMapPin = GetObjectByTag("toha_mn");
object oPC = GetFirstPC();

int nInt = GetLocalInt(oPC,"NW_JOURNAL_ENTRYq_summon");

if (nInt == 61 && !GetLocalInt(OBJECT_SELF,"MapPin"))
{
SetMapPinEnabled(oMapPin,1);
SetLocalInt(OBJECT_SELF,"MapPin",1);
}

if(GetGlobalInt("gallonspawned")) return;

SetGlobalInt("gallonspawned",1);

object oGallon = SpawnCreatureAtWP("gallon", "gallonwp");

object oLoreen = GetObjectByTag("loreen");
object oArea = GetObjectByTag("rolfamaro_region");
int nInt2 = GetLocalInt(oPC,"NW_JOURNAL_ENTRYq_mirror");

	if(GetArea(oLoreen) == oArea)
	{
	return;
	}

	else if (!GetFactionEqual(oPC,oLoreen) && nInt2 == 21)
	{

	SendMessageToPC(oPC,"A serious bug/error has occured. Please reload from before you exit the tavern in Bakestone Bay.");
		

	}


}

Ok, so after more testing, now the bug appears every time! I must have done something to cause this. At least the error message works, so I’ll leave that as it is. Maybe when destroying the copy (EvilLoreen) I also destroy the original somehow. That’s the only logical explanation for this…I’ll continue to search for clues to this behaviour…

I still don’t get it. Here’s the script that runs when EvilLoreen runs away. I can see no wrong with it:

void PrepForDestruction(object oTarget)
{
    SetPlotFlag(oTarget,FALSE);
    SetImmortal(oTarget,FALSE);
    AssignCommand(oTarget,SetIsDestroyable(TRUE,FALSE,FALSE));
}


void main()
{

    object oLoreen = GetObjectByTag("loreenevil");
    object oWP = GetObjectByTag("loreenexit");
	
   
    AssignCommand(oLoreen, ActionForceMoveToObject(oWP,TRUE));
	
	PrepForDestruction(oLoreen);
	
	DelayCommand(2.0, DestroyObject(oLoreen));

}

And sure, this works, but why on earth doesn’t normal Loreen teleport where she should teleport?

Wait a minute. I think I might have found the error in my first script:

object oDestTeleporter = GetWaypointByTag(sDEST_TELEPORTER);
    	AssignCommand(oLoreen2, JumpToObject(oDestTeleporter));

oLoreen2 is

GetObjectFromRosterName(sROSTER_LOREEN);

and at this moment she is no longer part of the roster. That must be it. I’ll just change it to oLoreen instead.

This is depressing. The problem persists. I don’t know what the heck is going on. On days like these I begin to question why I bother working with this game at all. :frowning_face:

Edit: Tried with removing the destroying of EvilLoreen but that did nothing. So the problem must lie within the first script. I don’t get it. I hope someone here can help me. :frowning_face:

Are you sure Loreen can stand so much damage? I mean, couldn’t she die in the process?

According to this, you can have up to 32 characters.
I guess you could even go to 64, based on this.

1 Like

Of course! You are totally right, 4760! When I’ve tested these few times I did it from the toolset and from the game with a level 1 character when this happened. I want her to be badly wounded, so that’s why I did it with that much damage, and the party will be on level 5 or 6 when this happens. I will try again like I did before with a level 6 character. Hopefully this is the problem. It has to be. (I really hope that’s the problem).

Thanks for trying to help, 4760!

It worked! That was the problem all along! And it is logical that I didn’t get this before, since I tested with a higher level character (the same level you will be at approximately when playing the game at this point), but I got lazy and thought I could take just a level 1 character and test from the toolset, since that was faster and I didn’t need to level up the whole party before testing. So lesson learned. You can’t be too lazy.

You start at level 3 in this adventure so she only had 32 HP or something like that when I spawned her into the game for testing. At level 4 I think she has 48 HP and you will have reached this level when getting to this point in the game (I was even at level 6 but I know how to solve all the sidequests of course), so I think it’s safe to keep the 40 HP damage.

Again, thank you @4760! You saved me! I was almost about to give up on this.