A placeable kills a creature

Howdy, I am once again calling upon the Heroes of the Vault.

I have a placeable that cast a spell at a creature when a player uses it, but when the creature dies, the player doesn’t receive xp. I think it is because the placeable is killing the creature not the player who uses the placeable.

What I think I need is an Ondeath script that will find the nearest PC and their faction members in the same area. Or some way to identify that the PC using the placeable is the killer of the creature.

I don’t know of a way or method, so any experienced scripters who know what I am trying to do, I would greatly appreciate the help.

Video of what I am doing

1 Like

Something like this perhaps? This is just a start and probably not that efficient and may not work:

On the placeable’s OnUsed:

void SetWeaponUser(object oLastUsed)
{

	SetLocalInt(oLastUsed,"weaponuser",TRUE);
	SetLocalInt(OBJECT_SELF,"used",TRUE);	
}

void main()
{

object oLastUsed = GetLastUsedBy();

	if(GetLocalInt(OBJECT_SELF,"used"))
	{

		object oArea = GetObjectByTag("theareatag");
		object oPC = GetFirstObjectInArea(oArea);

		while(GetIsObjectValid(oPC))
		{
		
			if(GetIsPC(oPC))
			{
				if(GetLocalInt(oPC,"weaponuser"))
				{
			
					SetLocalInt(oPC,"weaponuser",FALSE);
		
				}
			}
			
			else
			oPC = GetNextObjectInArea(oArea);
		}
		
	}


	
DelayCommand(2.0,SetWeaponUser(oLastUsed));

}

On the OnDeath of the creature killed:

void CheckForKiller()
{

object oArea = GetObjectByTag("theareatag");
object oPC = GetFirstObjectInArea(oArea);

		while(GetIsObjectValid(oPC))
		{
		
			if(GetIsPC(oPC))
			{
				if(GetLocalInt(oPC,"weaponuser"))
				{
			
					GiveXPToCreature(oPC,200);
					
				}
			}
			
			else
			oPC = GetNextObjectInArea(oArea);
		}

}


void DoTheCheck()
{

   	AssignCommand(GetModule(), CheckForKiller());

}

void main()
{

   	DelayCommand(3.0, DoTheCheck());
    ExecuteScript("nw_c2_default7", OBJECT_SELF);
				
}

Sometimes I’m not good at writing scripts from scratch, but maybe this will give you some ideas at least.
I’m sure someone more competent will come around and correct my mistakes here, but at least it’s a start for you.

2 Likes

I was unable to get the script to work, I’m probably just too ignorant on scripting to know how implement it. I think I did everything right… but I couldn’t tell if I did or didn’t.

I have altered the scenario a bit to get the creature in range so that the PC would have a higher percentage chance to get the last hit with melee or spell.

But until some skilled scripter with a kind soul like @andgalf takes pity on me, this is all I got for now.

riffing off Andgalf’s idea

put this bit in the placeable’s OnUsed script

	object oLastUser = GetLastUsedBy();
	object oCreature = GetNearestObjectByTag("creature"); // change to tag of monster
	SetLocalObject(oCreature, "weaponuser", oLastUser);

and this bit in the creature’s OnDeath script

	object oKiller = GetLocalObject(OBJECT_SELF, "weaponuser");
	if (GetIsObjectValid(oKiller))
	{
		GiveXPToCreature(oKiller, 200); // change to appropriate XP
	}

 
ps. not sure if that gives party XP or just to the PC …

3 Likes

@andgalf don’t delay a command in the OnDeath script, though i see what you were trying to do, i think it goes like this →

AssignCommand(GetModule(), DelayCommand(3.0, CheckForKiller()));

 
( ie. don’t delay a command that will be owned by the owner of the OnDeath script – immediately assign the delayed command out to the Module object )

2 Likes

Pretty sure nwn2 backend is like nwn1… If the pc isn’t doing anything, use their action queue to do the whatever from the placeable (damage, etc) or executescript. Then whatever else is used for xp/creature damage/death stuff should pick up the pc as the actor.

2 Likes

riffing off Lokey’s idea

add this to the OnUsed script (using default OnDeath script)

	object oLastUser = GetLastUsedBy();

	object oTarget // the monster
	int iDamage // the damage calculated
	effect eDamage = EffectDamage(); // fill out as appropriate
	AssignCommand(oLastUser, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget))

 
 
[edit] just had a thought, that the effect itself might need to be constructed by oLastUser. If so do the construction and application together in a subfunction and assign ownership/execution of the subfunction to oLastUser

3 Likes

Here is what I’m going with, but I don’t know if it will give the xp and item to the party faction. take a look see if the puzzle piece look right to you.
Thanks for the help!

//Ondeath sript 
//Thanks Andgalf & KevL_s

void main()
{
       // object oArea = GetObjectByTag("Area1");
	  // object oPC = GetFirstObjectInArea(oArea);
		object oKiller = GetLocalObject(OBJECT_SELF, "weaponuser");
		object oFM = GetFirstFactionMember(oKiller, FALSE);
	
	if (GetIsObjectValid(oKiller) && !GetAssociateType(oFM) && GetArea(oKiller) == GetArea(oFM))
	{
		GiveXPToCreature(oFM, 1200); // change to appropriate XP
		CreateItemOnObject("quest_sea_monster_scale", oFM);	// <-- put your item's resref here
		FloatingTextStringOnCreature("**You pick up one of the creature's scales.**", oFM);
	}
	
}
1 Like

@MagicalWisps - In your script here it doesn’t seem that you use oPC at all, so maybe you could skip the part with oPC.

1 Like

Yup, just realized it too, made edit.

1 Like

Yeah, I understand. It felt a bit iffy, but I didn’t know how to crack this nut properly. Thanks for clearing it out with the

I believe I was on the right track at least?

2 Likes

@kevL_s Gotta look into the whole SetLocalObject function which I still have a hard time wrapping my head around for some reason. SetLocalInt and SetGlobalInt is much more logical to me, even though I know that SetLocalObject should be equally logical. Sometimes I wonder how my brain is wired…

EDIT: Ok, reading the lexicon now…So…You store an object on another object and then give the whole thing a name? That way, you can store another object, like a creature, on a another creature, then in the script if you use GetLocalObject, you check on the original object who/what the other object is by checking the variable/name? I can hardly understand what I myself am saying, but I think I got it kind of right, lol.

EDIT2: The explanation in the Script Assist in NWN2 Toolset is a bit confusing to me, as nValue isn’t present:

// Set oObject's local object variable sVarName to nValue
void SetLocalObject(object oObject, string sVarName, object oValue);

And what confuses me a bit in the Lexicon is that the object put on the other object is considered a variable. I still think my own explanation, for my own messy brain, seems about right.

@MagicalWisps

PC is the killer (per the OnUsed script)
and you can assume that PC will be in the area with the monster
and he won’t be an associate (unless familiars can use the placeable, not so sure than they can…)
and i guess the user will be valid (since he just used the placeable) … so →

	object oKiller = GetLocalObject(OBJECT_SELF, "weaponuser");
	GiveXPToCreature(oKiller, 1200);
	CreateItemOnObject("quest_sea_monster_scale", oKiller);
	FloatingTextStringOnCreature("**You pick up one of the creature's scales.**", oKiller);

 
but i don’t know if you want to divide the XP up with Companions, or if you want to handle the possibility of a Companion using the placeable (said companion would get the item and XP)

1 Like

@andgalf yes you got it.

the thing that’s a bit confusing is that when you store a local_int, the value of the int is stored directly.

But when a local_object is stored it’s merely a pointer to the object itself … (you aren’t making a copy of the object and storing that, you’re just storing a pointer to an object)

So sometimes when retrieving the object, for example, it should be checked if its still a valid object ingame. In contrast, you never have to check the validity of a local_int (it can never go invalid, but a local_object could go invalid)

3 Likes

@kevL_s - Ah, I think it’s a bit clearer now. Like I said in my editing of my previous post though, I find the explanation in the Script Assist and the Lexicon to be adding to the confusion rather than clearing things up for me. Just by calling the other object oValue makes me think…“Eh…what?”.

And it’s totally logical to me, just as you said, that you must check if the object is invalid or not. I mean, the object could have been destroyed or killed, who knows.

1 Like

yeh, that seems to be the important thing : you can’t just conjure up an object into existence with GetLocalObject()

1 Like

A little set dressing, Still need to tighten up a few things.
Thanks guys!

3 Likes

lol cool :)

1 Like

@MagicalWisps - Looks really good!

1 Like

Here is onused script, added 6 second delay in use of the cannon and added textstring.

//Onused sript 
//Thanks Andgalf & KevL_s

void main()
{
    object oTarget;
	object oLastUser = GetLastUsedBy();
	object oCreature = GetNearestObjectByTag("quest_sea_creature_01"); // change to tag of monster
	SetLocalObject(oCreature, "weaponuser", oLastUser);
	
	if (!GetIsPC(oLastUser)) return;
	
		float fDelay = 6.0;
		
    if (GetLocalInt(OBJECT_SELF,"NW_DO_ONCE") != 0)
    {
       return;
    }
    
	 // Have us cast Fireball.
    oTarget = GetNearestObjectByTag("wp_cannon_01", OBJECT_SELF);
    ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE);
    SetLocalInt(OBJECT_SELF,"NW_DO_ONCE",1);
    DelayCommand(fDelay,SetLocalInt(OBJECT_SELF,"NW_DO_ONCE",0));
	FloatingTextStringOnCreature("**Reloading Cannon**", oLastUser);
	
}
1 Like