Object won't disappear, and not for the reason you think [SOLVED]

Hello NWN Community!

I needeth thy help. These days, I decided to create a henchman system. In order for it to work, a Creature named “Construct” is spawned and Tagged after the PC Player Name. After the PC talks with the said Creature, the Construct copies all the stats, sounds, name, feats, skills and inventory of the PC. An exact copy…
The problem is this. During that small gap between Creating the Construct and speaking with the Construct, the PC is free to do whatever he wants. So, after a bit of thought, I decided to put an ExecuteScript function inside a DelayCommand function :stuck_out_tongue: , in order to delete the Construct after a given time.

The first Script is simple. oPC is the Player, oSelf is the NPC (Who holds the shop that makes the Construct) and lTarget is used to set where the Creature will be spawned.
SetLocalString is called, in order to make a simple Text Appears check in the conversation with the NPC.
TakeGold… blah blah
And finally… The last piece of code.
Since the creature that is created gets its Tag changed to the PCPlayerName, I found it a must do - to save its Tag as a String as a Local Variable of the Area of Spawn.

object oPC = GetPCSpeaker();
location lTarget = GetLocation(GetWaypointByTag("HENCHMANSPAWN"));
object oSpawn = CreateObject(OBJECT_TYPE_CREATURE, "henchman", lTarget, TRUE, GetPCPlayerName(oPC));
object oSelf = OBJECT_SELF;


SetLocalString(oSelf, "PCName", GetPCPlayerName(oPC));
TakeGoldFromCreature(500000, oPC, FALSE);

object oConstruct = GetObjectByTag(GetPCPlayerName(oPC));
SetLocalString(GetArea(oSelf), "ConstructTag", GetTag(oConstruct));
DelayCommand(IntToFloat(60), ExecuteScript("silvmn_npc_avin6", oConstruct));

Simple enough right?

So once again, the second script…
lArea and oArea gets the area where the Creatures spawn.
ConstructTag string is used to get the Tag of the Creature.
And finally, oConstruct gets the Creature as an object.

Afterwards, if the name of the object oConstruct is “Construct”, that means that the Player hasn’t talked to the spawned Creature yet and the Construct hasn’t copied the name of the PC.
If the Player had talked with the Construct after it was spawned, the name of the oConstruct == Name Of PC.
So, I set the LocalString for the Text Appear check.
I assign as a command to the oConstruct to flag itself as DestroyAble.
And then I destroy it… but it won’t go away! It stays right there, waiting for the PC…
Why won’t you go away!!!

location lArea = GetLocation(GetWaypointByTag("HENCHMANSPAWN"));
object oArea = GetAreaFromLocation(lArea);

string ConstructTag = GetLocalString(oArea, "ConstructTag");
object oConstruct = GetObjectByTag(ConstructTag);

if (GetName(oConstruct, FALSE) == "Construct"){
    SetLocalString(GetObjectByTag("Silvmn_NPC_Avina"), "PCName", "!NULL");
    AssignCommand(oConstruct, ActionDoCommand(SetIsDestroyable(TRUE, FALSE, FALSE)));
    DestroyObject(oConstruct, 0.0f);
}

But, when I use the exact lines on a RemoveHenchman script, it works just fine…

object oHench = OBJECT_SELF;
SetFormerMaster(GetPCSpeaker(), oHench);
RemoveHenchman(GetPCSpeaker());
SetLocalInt(oHench, "IsTaken", 0);
AssignCommand(oHench, ActionDoCommand(SetIsDestroyable(TRUE, FALSE, FALSE)));
DestroyObject(oHench, IntToFloat(2));

I’ve tried using SendMessageToPC commands, just to check if the
GetName(oConstruct, FALSE) == “Construct” condition is met… Which it DOES!
For some reason, the DestroyObject function just will not work…

Sorry for the long post. Thank you for your time.

As a first pass guess I’d say the construct is not destroyable when you call destroyobject. The setisdestroyable done as an assigned action will not have happened yet. Try a short delay on the destroy like you have in the henchman script.

Also, I don’t think you need to do that as an Action. You could just do that in the assigncommand without the ActionDoCommand part. Or if you are running as the construct just do the SetIsDestroyable directly.

I agree with meaglyn as to a little delay - I almost always use the delay function just to make sure things clear before we destroy what we want.
When it comes to NWN, I am never completely sure if I have the object I am looking for without a little redundancy. When creating the construct, you can set it as a local object to the area (SetLocalObject(oArea, “Construct”, oConstruct), then you can easily get it with the second script.

@Mannast
Unbelievable!

Setting the DestroyObject with a delay of 2, worked! But that raises another question.
On the OnPhysicallyAttacked of the said Creature:

ExecuteScript("nw_ch_ac5", OBJECT_SELF);

object oPC = GetLastMaster();
object oHench = OBJECT_SELF;

ClearAllActions(TRUE);
if (GetLastAttacker(oHench) == oPC){
    SetLocalInt(oHench, "IsTaken", 0);
    AssignCommand(oHench, ActionDoCommand(SetIsDestroyable(TRUE, FALSE, FALSE)));
    DestroyObject(oHench);
}

The DestroyObject works instantly… Why? Is it because the script runs naturally (without an ExecuteScript)?

Thank you for your answers!

1 Like

@meaglyn is saying that in order to avoid any unneeded / problematic delays, you can replace this:

AssignCommand(oHench, ActionDoCommand(SetIsDestroyable(TRUE, FALSE, FALSE)));
DestroyObject(oHench, IntToFloat(2));

with this:

AssignCommand(oHench, SetIsDestroyable(TRUE, FALSE, FALSE));
DestroyObject(oHench); // default destruction delay is 0.0

It will make oHench destroyable immediately, allowing DestroyObject to drop it.

Or - if you need to call it often - create a wrapper function (click here)
void MyDestroyObject(object oObject=OBJECT_SELF, float fDelay=0.0)
{
    AssignCommand(oObject, SetIsDestroyable(TRUE, FALSE, FALSE));
    DestroyObject(oObject, fDelay);
}

then just go with MyDestroyObject(oHench).

For the OnPhysicallyAttacked one I’d have to guess the henchmen is already destroyable in that case. It’s not due to using ExecuteScript or not. There is almost no difference running code in a script to executing it in a separate one.

There is an outside chance that the ClearAllActions is allowing the SetIsdestroyable to run in time. But that would be very surprising. The destroyobject with no delay will run as soon as the script ends which should be before oHench gets to do even the first action in its queue. But I’ve been surprised before :slight_smile:

1 Like

that won’t work either
AssignCommand imposes a small delay by itself so the DestroyObject will run priot to SetIsDestroyable

Assign is not needed if this is creature event script as AssignCommand is used to move command from OBJECT_SELF to different object but in this case the object is the same

Yeah, there is a delay, but it appears that SetIsDestroyable is a special case (@meaglyn). It is assigned and executed before DestroyObject in my test script. Timing issue perhaps?

Since this function doesn’t accept a target parameter there’s no need for any extra checks (and caller must be valid anyway) so maybe the engine is quick about it.

Anyway, to avoid the issue, it should be enough to:

AssignCommand(oObj, SetIsDestroyable(TRUE));
AssignCommand(oObj, DestroyObject(OBJECT_SELF));

which should schedule them in order.

hmm maybe because DestryObject has also delay in fact…