DestroyObject Question

I’m having a hell of a time destroying a placeable VFX! From what I understand, the object is supposed to be destroyed after the script is through running.
For OnUsed

object oPC = GetLastUsedBy();
    if (!GetIsPC(oPC)) return;

    // Apply a visual effect.
    ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect
    ( VFX_FNF_SOUND_BURST_SILENT ), GetNearestObjectByTag("SOUND_BURST"));

    // Begin sound que.
    AssignCommand(oPC, PlaySound("deep_horn"));
    DelayCommand(4.5, AssignCommand(oPC, PlaySound("pulse_machine")));

    DelayCommand(4.5, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "zep_lightshft045",
    GetLocation(GetWaypointByTag("STASIS_FIELD"))));

    DelayCommand(6.5, AssignCommand(oPC, PlaySound("sonar")));
    DelayCommand(10.5, AssignCommand(oPC, PlaySound("steamburst_01")));
    DelayCommand(12.0, AssignCommand(oPC, PlaySound("geiger")));
    DelayCommand(14.0, AssignCommand(oPC, PlaySound("ct_scan")));
    DelayCommand(16.5, AssignCommand(oPC, PlaySound("sonar")));

    DestroyObject(GetNearestObjectByTag("ZEP_LIGHTSHFT001"), 22.0);

    DelayCommand(21.5, AssignCommand(oPC, PlaySound("power_down_01")));
    return;
}

Everything fires as it should except to destroy the lightshaft. Need to get this thing gone!

Edit:
New code:

#include "nw_i0_2q4luskan"

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

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

    object oTarget = GetNearestObjectByTag("ZEP_LIGHTSHFT001");

    // Apply a visual effect.
    ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect
    ( VFX_FNF_SOUND_BURST_SILENT ), GetNearestObjectByTag("SOUND_BURST"));

    // Begin sound que.
    AssignCommand(oPC, PlaySound("deep_horn"));
    DelayCommand(4.5, AssignCommand(oPC, PlaySound("pulse_machine")));

    DelayCommand(4.5, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "zep_lightshft045",
    GetLocation(GetWaypointByTag("STASIS_FIELD"))));

    DelayCommand(6.5, AssignCommand(oPC, PlaySound("sonar")));
    DelayCommand(10.5, AssignCommand(oPC, PlaySound("steamburst_01")));
    DelayCommand(12.0, AssignCommand(oPC, PlaySound("geiger")));
    DelayCommand(14.0, AssignCommand(oPC, PlaySound("ct_scan")));
    DelayCommand(16.5, AssignCommand(oPC, PlaySound("sonar")));

    PrepForDestruction(oTarget);
    DestroyObject(oTarget, 22.0);

    DelayCommand(21.5, AssignCommand(oPC, PlaySound("power_down_01")));
    return;
}
Same result however.  Do I have it set up right?

Since I only work with NWN2 this might not help…or it might.
Whenever I try to destroy an object through a script I always do a function like this. It has helped me a lot of times when I haven’t been able to destroy an object at first:

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

Checking with the NWN Lexicon it appears all these functions are in NWN1 as well, so it might help you.

Are you getting the right object with your GetNearestObjectByTag call? Given that you are creating the object 4.5 seconds after the script runs I don’t think so.

You will need to delay the destruction and, most importantly, the GetNearestObjectByTag() call. I’d make a destroy_light_shaft() function that does it and then delay the call to that function in your main script.

Great call. Didn’t notice that. You are right.
Still, there is a delay to destroying the object already of 22 seconds. That won’t work you mean?

The delay there doesn’t matter because that’s a call to DestroyObject, which takes an object when the call is made not when the delay goes off. At the time that call is made GetNearestObjectByTag() is very likely returning OBJECT_INVALID (I’m only saying “very likely” in case he’s got another thing with that tag in the area, which is probably not the case but…). So there’s nothing to destroy.

Ah, I see.

That is not needed in NWN1. DestroyObject ignores this. Maybe eccept destructibility flag, but that cannot be set in toolset manually and is not commonly used in scripting either.

It’s mostly not needed in NWN2 either, but on rare occations I have noticed that when the script doesn’t destroy the object I want, then this might help. In this case though, it was as @meaglyn pointed out that the call is made before the delay and that was apparently the problem here.

Altered code:

#include "nw_i0_2q4luskan"
void PrepForDestruction(object oTarget)
{
    SetPlotFlag(oTarget, FALSE);
    SetImmortal(oTarget, FALSE);
    AssignCommand(oTarget, SetIsDestroyable(TRUE, FALSE, FALSE));
}
void SureDestroy(object oObject)
{
    DestroyObject(GetNearestObjectByTag("ZEP_LIGHTSHFT001"), 22.5);
}
void main()
{
    object oPC = GetPCSpeaker();
    if (!GetIsPC(oPC)) return;

    object oTarget = GetNearestObjectByTag("ZEP_LIGHTSHFT001");

    // Apply a visual effect.
    ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect
    ( VFX_FNF_SOUND_BURST_SILENT ), GetNearestObjectByTag("SOUND_BURST"));

    // Begin sound que.
    AssignCommand(oPC, PlaySound("deep_horn"));
    DelayCommand(4.5, AssignCommand(oPC, PlaySound("pulse_machine")));

    DelayCommand(4.5, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "zep_lightshft045",
    GetLocation(GetWaypointByTag("STASIS_FIELD"))));

    DelayCommand(6.5, AssignCommand(oPC, PlaySound("sonar")));
    DelayCommand(10.5, AssignCommand(oPC, PlaySound("steamburst_01")));
    DelayCommand(12.0, AssignCommand(oPC, PlaySound("geiger")));
    DelayCommand(14.0, AssignCommand(oPC, PlaySound("ct_scan")));
    DelayCommand(16.5, AssignCommand(oPC, PlaySound("sonar")));

    DelayCommand(21.5, AssignCommand(oPC, PlaySound("power_down_01")));

    PrepForDestruction(oTarget);
    SureDestroy(GetNearestObjectByTag("ZEP_LIGHTSHFT001"));
}
Not sure if I did it right.  Sorry guys, I'm not a coder by any means.

Maybe change it to this (just a small alteration):

#include "nw_i0_2q4luskan"
void PrepForDestruction(object oTarget)
{
    SetPlotFlag(oTarget, FALSE);
    SetImmortal(oTarget, FALSE);
    AssignCommand(oTarget, SetIsDestroyable(TRUE, FALSE, FALSE));
}
void SureDestroy(object oObject)
{
    DestroyObject(GetNearestObjectByTag("ZEP_LIGHTSHFT001"));
}
void main()
{
    object oPC = GetPCSpeaker();
    if (!GetIsPC(oPC)) return;

    object oTarget = GetNearestObjectByTag("ZEP_LIGHTSHFT001");

    // Apply a visual effect.
    ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect
    ( VFX_FNF_SOUND_BURST_SILENT ), GetNearestObjectByTag("SOUND_BURST"));

    // Begin sound que.
    AssignCommand(oPC, PlaySound("deep_horn"));
    DelayCommand(4.5, AssignCommand(oPC, PlaySound("pulse_machine")));

    DelayCommand(4.5, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "zep_lightshft045",
    GetLocation(GetWaypointByTag("STASIS_FIELD"))));

    DelayCommand(6.5, AssignCommand(oPC, PlaySound("sonar")));
    DelayCommand(10.5, AssignCommand(oPC, PlaySound("steamburst_01")));
    DelayCommand(12.0, AssignCommand(oPC, PlaySound("geiger")));
    DelayCommand(14.0, AssignCommand(oPC, PlaySound("ct_scan")));
    DelayCommand(16.5, AssignCommand(oPC, PlaySound("sonar")));

    DelayCommand(21.5, AssignCommand(oPC, PlaySound("power_down_01")));

    DelayCommand(22.0,PrepForDestruction(oTarget));
    DelayCommand(22.5, SureDestroy(GetNearestObjectByTag("ZEP_LIGHTSHFT001")));
}

It’s up and running now. Thanks guys!

1 Like

That’s close :slight_smile:

The PrepForDestruction call has the same problem as the original code did. But it’s not needed since this object is neither immortal nor plot so it doesn’t matter. Also SureDestroy() does not need an object passed to it at all. There’s no need for the GetNearestObjectByTag(“ZEP_LIGHTSHFT001”) calls in the main script.
You could be the object oTarget = part in SureDestroy() and call PrepForDestruction from there if you wan to keep that part.

I should say that it is working because I attached it to a conversation and used GetPCSpeaker()
From the second conversation node I’m able to destroy the placeable through dialog.
It’s the first dialog node that isn’t firing properly. Still can’t destroy it through script 1.

#include "nw_i0_2q4luskan"
void PrepForDestruction(object oTarget)
{
    SetPlotFlag(oTarget, FALSE);
    SetImmortal(oTarget, FALSE);
    AssignCommand(oTarget, SetIsDestroyable(TRUE, FALSE, FALSE));
}
void SureDestroy(object oTarget)
{
   DestroyObject(oTarget);
}
void main()
{
    object oPC = GetPCSpeaker();
    if (!GetIsPC(oPC)) return;

    object oTarget = GetNearestObjectByTag("STASIS_FIELD001");

    // Apply a visual effect.
    ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect
    ( VFX_FNF_SOUND_BURST_SILENT ), GetNearestObjectByTag("SOUND_BURST"));

    // Begin sound que.
    AssignCommand(oPC, PlaySound("deep_horn"));
    DelayCommand(4.5, AssignCommand(oPC, PlaySound("pulse_machine")));

    DelayCommand(4.5, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "zep_lightshft045",
    GetLocation(GetWaypointByTag("STASIS_FIELD"))));

    DelayCommand(6.5, AssignCommand(oPC, PlaySound("sonar")));
    DelayCommand(10.5, AssignCommand(oPC, PlaySound("steamburst_01")));
    DelayCommand(12.0, AssignCommand(oPC, PlaySound("geiger")));
    DelayCommand(14.0, AssignCommand(oPC, PlaySound("ct_scan")));
    DelayCommand(16.5, AssignCommand(oPC, PlaySound("sonar")));

    CreateItemOnObject("scanresults", GetObjectByTag("READ_OUT"), 1);

    DelayCommand(21.5, AssignCommand(oPC, PlaySound("power_down_01")));
}
Script #2
#include "nw_i0_2q4luskan"

void PrepForDestruction(object oTarget)
{
    SetPlotFlag(oTarget, FALSE);
    SetImmortal(oTarget, FALSE);
    AssignCommand(oTarget, SetIsDestroyable(TRUE, FALSE, FALSE));
}
void SureDestroy(object oObject)
{
   DestroyObject(GetNearestObjectByTag("STASIS_FIELD001"), 0.5);
}
void main()
{
    object oPC = GetPCSpeaker();

    AssignCommand(oPC, PlaySound("power_down_01"));

    object oTarget = GetNearestObjectByTag("STASIS_FIELD001");
    SureDestroy(GetObjectByTag("STASIS_FIELD001"));
}

I should say that isn’t working. Only on dialog script #2

I’m not sure I’m following, but if the first script in your last post is what’s on node 1 then I don’t see you calling destroy object on anything there. You need to call SureDestroy() at some point. Also you’ve changed from light shaft tags to stasis fields but you are still creating a light shaft placeable (and not changing its tag on create…). And the SureDestroy lost its GetNearstObjectByTag call.

Script 2 is working because you are not creating the stasis field object there. It’s already created so the GetNearestObjectByTag() in SureDestroy() finds an object to destroy. Since this one works then I guess the tag you need is “STASIS_FIELD001” so we’ll go with that.

But the point of the SureDestroy() function was to get the object then not beforehand so that the GNObT finds the object.

Script 1 edits:

void SureDestroy()
{
   object oTarget = GetNearestObjectByTag("STASIS_FIELD001");
   PrepForDestruction(oTarget);
   DestroyObject(oTarget);  // You don't need a delay here since we delayed the whole function call
}

then down in the main code

   DelayCommand(24.0, SureDestroy());  // don't need to pass oTarget - it's invalid here anyway

Script #2 could be a lot smaller. It doesn’t need the SureDestroy. (and neither one really needs the PrepForDestruction but I left that in script 1 for completeness).

#include "nw_i0_2q4luskan"

void main()
{
    object oPC = GetPCSpeaker();

    AssignCommand(oPC, PlaySound("power_down_01"));

    object oTarget = GetNearestObjectByTag("STASIS_FIELD001");
    DestroyObject(oTarget, 0.5);
}

It could be even simpler but this is so you can maybe see what I did better. Also I didn’t check if the include is even needed.

Thank you SO much! Working perfectly now.

What I’ve always done in circumstances like this is to use an object which I know has already been created as the place to hold the object pointer to the paired visual object which I’m going to have to destroy when the base object is destroyed. Since the CreateObject functions automatically return an object pointer, you only need to save that object pointer.
For example, the procedure for creation could go:
Create the base object.
Create the visual effect object.
Take the object pointer for the visual effect and save it on the base object using SetLocalObject.

For destruction:
Locate the base object.
Read the object pointer for the visual effect from the base object using GetLocalObject.
Give the DestroyObject command for the visual effect.
Give the DestroyObject command for the base object.
(The last two can be done in any order so long as they are in the same script since the DestroyObject command does not run until after the script ends).

The procedure is almost identical for visual effects which are applied to an invisible object. You either destroy the invisible object or, if it needs to remain, use the RemoveEffect function. In either case, finding the object works the same.