Cleanup on Isle Bugsquat

I have several custom encounters involving several spawned beetles. The script I have is set to spawn a treant to help the PC once his/her hit points run below 25%.
Question is: Once the helper treant has completed his assistance with fighting the bugs, how do I disappear the treant using (VFX_FNF_NATURES_BALANCE). I’m having a rough go of it (no surprise). Any help would be awesome. Thanks guys.

Admission: Some (perhaps a lot of my code) maybe senseless. I was just tying functions I’d never used before. :frowning:

Code so far:

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

    if ( GetLocalInt(oPC, "DO_ONCE__" + GetTag(OBJECT_SELF)) )
         return;
         SetLocalInt(oPC, "DO_ONCE__" + GetTag(OBJECT_SELF), TRUE);

    string sCreature = "dla_treant";

    int nMaxHP = GetMaxHitPoints(oPC);
    int nCurHP = GetCurrentHitPoints(oPC);
    int nRatio = (100* nCurHP)/nMaxHP;

    if ( nRatio >= 25 )
    {
        object oSpawn;
        effect eVFX;

        string sCreature = "dla_treant";

        // Spawn Treant.
        eVFX = EffectVisualEffect(VFX_FNF_NATURES_BALANCE);
        oSpawn = CreateObject(OBJECT_TYPE_CREATURE, "dla_treant", GetLocation(oPC));
        DelayCommand(0.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX, oSpawn));

        object oCreature = GetNearestCreatureToLocation(CREATURE_TYPE_PERCEPTION, CREATURE_TYPE_IS_ALIVE, GetLocation(oPC),
        CREATURE_TYPE_RACIAL_TYPE, CREATURE_TYPE_REPUTATION);

        int nDead = GetIsDead(oCreature);

        object oTreant = StringToObject("dla_treant");
        if ( GetFactionAverageReputation(oTreant, oCreature) - 100 )
        {
            DelayCommand(3.0, AssignCommand(oTreant, ActionAttack(oCreature)));
        }
        if (GetIsDead(oCreature))
        {
            DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect
            ( VFX_FNF_NATURES_BALANCE ), oTreant));
            AssignCommand(oTreant, ActionSpeakString("Fare thee well adventurer..."));
            DestroyObject(oTreant, 5.0);
        }
     }
}

… in summation - After a hit point check of the PC, I need the treant to “leave”, after ALL of the bugs are dead.

The first thing is to script the correct events.

You have two cases:

  • the PC falls below 25% hp
  • all the bugs are dead

I believe that detecting damage to the PC is possible using the default script.

Since that will be entered often, it needs to do nothing if the treant already exists, or if the player is not in the bug fight, or has more than 25% hp. Otherwise create the treant.

Other events can be used to similar effect, but that’s the most timely.

The surest way of detecting that all the bugs are dead is to use the bug OnDeath script. You can use GetObjectByTag() in a loop to determine whether this is the last one, remembering to exclude corpses from the count.

Have a go at writing those two scripts, then we can help if they don’t work.

I’m sorry Proleric. I’m afraid I’m slightly out of my lane on this type of code. But - I did give it a go with the OnDeath script for the beetles. Tried making a loop, “I think”, Don’t laugh. Or do. :slight_smile: - I would. I am. :slight_smile:

Code:

 int nCount = 1;

    while ( GetLastDamager() == OBJECT_SELF )
    {
        if ( nCount < 1 )
        {
            object oObject = GetObjectByTag("DLA_TREANT_01");
            DelayCommand(0.5, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect
            ( VFX_FNF_NATURES_BALANCE ), oObject));
            DestroyObject(oObject, 2.0);
        }
    }
}

Try to keep in mind that I’ve never attmpted this before. It probably reads as nothing! Ha!
Yeah, tried the code out - now I get all sorts of respawns of the treant when a beetle dies. Not good.

Apparently this is an OnEnter script (based on the object oPC = GetEnteringObject(); part) with a flag to make it run once only.

The way I read the above part, the treant will be spawned only it the PC has more than 25% of the maximum hit points.

Then the treant will attack the creature nearest to the PC if it is not friendly to the treant:

if ( GetFactionAverageReputation(oTreant, oCreature) - 100 )

is equivalent to

if ( GetFactionAverageReputation(oTreant, oCreature) != 100 )

This attack will occur after 3 s, but after 1 s, the script will check if the creature is dead, so:

  • the treant for sure will never kill it before the condition is tested
  • the PC has less than 1 s to kill the creature or the treant will never disappear.

@4760 I think our posts are at crosspurposes - I’m suggesting that an OnEnter script is inappropriate because entering an area or trigger is not one of the two events that require action here. Better to start again by writing those two scripts.

@Nizidramaniit Taking a step back, it looks like you’d benefit from some scripting tutorials like those in the Lexicon or Tarot Redhand’s on the Vault. Also, Lilac Soul’s script generator is a good tool for understand how to make scripts before diving too deep.

I don’t immediately understand why your OnDeath script does anything at all. Assuming it’s running on the bug, OBJECT_SELF is the bug, whereas GetLastDamager() is either the PC or the treant, so they will never be equal - the script will do nothing.

I wonder whether you still have the unnecessary OnEnter script? Is it possible that every time you create a treant it fires OnEnter which creates another treant ad infinitum?

Counting on death could work something like this:

int n = 0;
int bAllDead = TRUE;
object oBug = GetObjectByTag("bug", n);

while (GetIsObjectValid(oBug))
{
  if (!(GetIsDead(oBug)) {bAllDead = FALSE; break;}
  oBug = GetObjectByTag("bug", ++n);
}

if (bAllDead) ...

This assumes that the bug tags are all “bug” and that they’re all in this area. Written on a phone so probably has typos.

Be careful with DelayCommand and the implicit delay in DestroyObject. The bug is dead, so it can’t act in the future. A safe way to instruct future events is AssignCommand(GetModule(), …) because the module continues to exist and can tell any other object what to do.

1 Like

I fully agree, I probably wasn’t clear with my comment.
That being said, I tried to explain what needed to be corrected (independently on which type of script would run) in the general code.

You are absolutely correct. I have no fundamentals. It’s like learning to drive with no brakes. I inevitably crash into a wall. :frowning: I’ll look into Tarot’s tutorials, for sure…
Where the script stands now - I can’t even get the treant to spawn correctly. Somehow the HP check isn’t working right. Also, I mistakenly used > when I should have used <.

  1. Greater than = Treant spawning before the battle.
  2. Less than = nothing happens at 25%. Anyway, I do appreciate all of the help from you guys.

I’ve ripped all of the scripts for this encounter apart. As It currently stands I have a fresh piece of paper…

1 Like

Suggested general reading order is -

TR’s Basics - Variables, Types and Functions (TBC)
TR’s Basics - Decisions
TR’s Basics - Mostly Operators
TR’s Basics - Boolean Algebra

Assuming that you are on windows, there are also a number of tutorials (not mine) to found in the offline NwN Lexicon 1.69. If you either can’t read the chm file on your machine or you want the most up-to-date version there is a link, under Related Projects on that page, to the online version of the Lexicon.

TR

The online version is recommended because EE has added a huge number of functions and some clarification. The tutorials are much the same.

You could call a script like this from the OnUse of the Treant:

/*   Script generated by
Lilac Soul's NWN Script Generator, v. 2.3

For download info, please visit:
http://nwvault.ign.com/View.php?view=Other.Detail&id=4683&id=625    */

//Put this script OnUsed
void main()
{

object oPC = GetLastUsedBy();

if (!GetIsPC(oPC)) return;

if (GetLocalInt(oPC, "bugkillcounter")!= 5)
   return;

object oTarget;
oTarget = GetObjectByTag("Treant");

AssignCommand(oTarget, ActionStartConversation(oPC, "TreantLeaves"));

}

In addition you’d have to create a “TreantLeaves” conversation file.

Clicking the Treant, the script would only trigger and start the “TreantLeaves” conversation after the PC has killed a predefined number of bugs (=locaiInteger). So only after the last bug has died, the PC can - reliefed but bewildered - thank the Treant and aks him why he helped out - to which the Treant can answer something (or not) and then walk away and vanish, scripted through the conversation file.

That way one script only needs to deal with the Treants appearance while the OnUse script takes care of the Treant leaving which makes both more reliable. And imho having even the smallest conversation with your saviour is alwas nice.

Thanks for the link. I was poking around this version of the Lexicon. Yeah, this is definitely a lot better than the download version.

Thanks TheStoryteller01. That gives me an idea…