OnDeath script checking multiple objects

Fooling around with the toolset, I wanted to make a OnDeath script that checks if multiple objects with the tag “asteroid1”, “asteroid2” etc. were destroyed. I have a similar script for this already, but it is very crude, so I tried to edit it to make it more elegant, but it doesn’t work at all, so I must be doing something wrong. The script compiles, but that’s all.

//Script for checking if all asteroids are gone - and then doing a journal entry

int CheckIfAllDead()
{
   
   int nRock = 1;
   object oRock = GetObjectByTag("asteroid" + IntToString(nRock));
   while(GetIsObjectValid(oRock) && (nRock < 12))
   {
        if(!GetIsDead(oRock)) 
	{
	return FALSE;
        }
        nRock = nRock + 1;
        oRock = GetObjectByTag("asteroid" + IntToString(nRock));
   }
	return TRUE;
}



void main()
{

	object oPC = GetFirstPC();

	if(CheckIfAllDead())
	{
	
	AddJournalQuestEntry("q_world",11,oPC);
	
	}

}

I place this on all the asteroid objects’ OnDeath in the area. There are 11 of those objects in total in the area.

What exactly is it not doing? Have you put any debug points in it to see what the values are? It looks like it should cycle through “asteroid1” --> “asteroid11”, see if the object is valid, see if the object is dead and return true is all 11 are valid and dead. Is that what you’re expecting? It looks like the biggest break point is probably the GetIsObjectValid check. If the object doesn’t exist (and nRock < 12), it’ll return TRUE, which is probably not what you want. Alternately, you can put a counter in before sending the TRUE back, or something similar.

int CheckIfAllDead()
{
    int nRock;
    object oRock = GetObjectByTag("asteroid" + IntToString(++nRock));
   
    while (GetIsObjectValid(oRock) && (nRock < 12))
    {
        if (!GetIsDead(oRock))
            return FALSE;
        else
            oRock = GetObjectByTag("asteroid" + IntToSring(++nRock));
    }

    return (nRock >= 12);
}

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

	if (CheckIfAllDead())
    	AddJournalQuestEntry("q_world", 11, oPC);
}
1 Like
// Place on each asteroid's OnDeath event.

void main()
{
	int n = GetGlobalInt("asteroid_destroyed") + 1; 	

	SetGlobalInt("asteroid_destroyed", n);
	
	if (GetGlobalInt("asteroid_destroyed") == 11)
	{
		AddJournalQuestEntry("q_world", 11, GetFirstPC());
	}
}
3 Likes
int CheckIfAllDead()
{
    object oRock;

    int nRock = 1;
    while (nRock <= 11)
    {
        oRock = GetObjectByTag("asteroid" + IntToString(nRock));
        if (GetIsObjectValid(oRock) && !GetIsDead(oRock))
        {
            return FALSE;
        }
        ++nRock;
    }
    return TRUE;
}

but id go w/ travus’ solution

2 Likes

Thanks for all the replies! Though I understand why your scripts work, I can follow along in the code, I still don’t quite get what I did wrong. I can’t wrap my head around it. I don’t quite get tinygiant’s version with return (nRock >=12); and the

isn’t that what I want? Don’t get it. As I’ve said so many times, my brain wasn’t made for scripting. I’m, for some reason too stupid for this kind of stuff, or my brain is wired differently, I don’t know. I guess I’ll read kevL_s’ script a few more times (like 20 or so) and compare with mine, and see if I can finally understand what I’m doing wrong. Travus’ script is neat, and I see what’s happening, though I would never in life be able to come up with a simple and effective code like that, and it’s a bit too different from mine to be able to analyze and compare.
It’s like…as soon as a script involves counting and a while loop my brain is just clueless. I can’t fathom why. It’s depressing.

When looking at my script again, it seems like I should be able to skip the text in italic:

nRock = nRock + 1;
oRock = GetObjectByTag(“asteroid” + IntToString(nRock));

Still, I can see that I run the GetIsObjectValid at the start of the while loop, and kevL_s’ script does it inside the while loop, so that I guess have something to do with mine not working, and it seems that that was what tinygiant was trying to say, but why that is important I can’t quite grasp.

Ok, now it gets weird. I tried kevL_s’ version and for some reason that didn’t work. No journal entry after last asteroid. Maybe there’s something else I’ve done wrong. I’ll try travus’ script now.

Edit: Tried travus’ script and still no journal entry. I must have made a mistake somewhere. Odd.

Edit2: I don’t know what’s going on. I have an OnDamage script too on each asteroid, that if the asteroid is hit with a certain kind of damage (electrical) it gets destroyed. I use DestroyObject in that script, and that script works. Maybe when I destroy the asteroid the game doesn’t interpret that as that it died?

Here’s the OnDamage script:

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



void main()
{

object oRock = OBJECT_SELF;
string sTag = GetTag(oRock);

	if(GetDamageDealtByType(DAMAGE_TYPE_ELECTRICAL) >0)
	{

	PrepForDestruction(oRock);
	DestroyObject(oRock);

	}

}

It seems like weird stuff always happen to me when doing stuff in the toolset.

Destroying an object and killing an object are not the same thing. I don’t think Destroying and object will fire a death event for that object. So in this case, you’ll probably want to combine Travus’ script into yours:

void main()
{
    object oRock = OBJECT_SELF;
    string sTag = GetTag(oRock);

	if (GetDamageDealtByType(DAMAGE_TYPE_ELECTRICAL) > 0)
	{
        PrepForDestruction(oRock);
        DestroyObject(oRock);

        int n = GetGlobalInt("asteroid_destroyed") + 1; 	
        SetGlobalInt("asteroid_destroyed", n);
	
        if (n >= 11)
        {
            AddJournalQuestEntry("q_world", 11, GetFirstPC());
            DeleteGlobalInt("asteroid_destroyed");
        }
	}
}

Also, just added the DeleteGlobalInt at the bottom in case you were resetting it later since I don’t know how your mod is setup. You don’t necessarily need that line, but if the situation is later reset so the asteroids can be destroyed again, you’ll need to delete “asteroid_destroyed” to restart the count.

1 Like

the problem, or incorrectness about putting GetIsObjectValid(oRock) as the while() condition is that, the instant that the loop hits the first invalid ‘rock’ (one that’s been destroyed, eg.) the loop is going to break and TRUE would be returned. Rocks should be iterated after said invalid rock or they won’t be considered at all …

The key is to iterate over all potential rocks and consider each (hence use of ints 1…11). As soon as the loop hits one that is both (a) valid and (b) not dead, then return false; there is at least that rock remaining.

But if the loop checks all potential rocks and finds that all are either (invalid or dead) return true. they’re dead jim.

 
you should use debug in scripts, andgalf – tell yourself exactly what the script is really doing,

eg -> if the script runs at all, how many times the loop loops, even the specific rock that may be breaking things …

ps. These are just notes if you wish to ponder them. I’d still go with travus’ / tinygiants idea (or actually maybe i wouldn’t personally but that’s neither here nor there atm.) just personal prefs, either way has minor advantages and disadvantages, it seem t’me

 
pps. and yeh, destroying a creature might not fire its OnDeath event … -> hint: a bit of debug could tell you whether it does or doesn’t right away.

2 Likes

Thanks, @kevL_s! That clarified things. I managed to get it to work by the way, by incorporating travus’ script into my OnDamage script.
I’ll try and get better at using debug stuff.

Edit: Sorry, didn’t see that you had posted also @tinygiant. Yes, that was exactly what I did, by the way, before going on here again and saw that you both had answered.

Well, the one I’m currently working on will probably never see the light of day, but who knows. I thought I was finished with module making when now my third module is in beta, but I couldn’t resist, so I started a fourth one. It’s actually a good feeling just fooling around without any preassure on oneself that it needs to be done and finished. Some things, I must say, are a lot easier now with the experience of doing three modules. It goes a lot more smoothly, even though I did mistakes in this script. Gotta try and use debug like SendMessageToPC more.

1 Like

Suggestion: Use squattingmonk’s sm-utils. It includes a debug utility. You want to print something to the log, it’s as simple as Debug("Print this to the log as a timestamped entry.");. Optionally, it can also go to DMs and/or the first PC (i.e. the guy testing the module).

Want it to go to the playing/testing PC in bright yellow? --> Notice("OMG, this is horrible.");

Want it in red? --> Error("Wow, now we're really screwed.");

You can set debug levels by object. For example, if you want to really debug a new area, you can put debug events left and right in the code. When it’s ready for deployment, you don’t have to delete all those debugs, just set a lower debug level on the object and all those debug messages will no longer appear in the log. New problem crop up in that area, reset the debug level higher and all those messages will suddently appear again.

https://github.com/squattingmonk/sm-utils

Want to go one step further? He has an entire nwscript-based event management system that allows you do control when/how/why events occurs. It’s a much-improved combination of NWN1’s HCR2 and NWN2’s CSF. The framework system includes sm-utils.

https://github.com/squattingmonk/nwn-core-framework

Ok, I might take a look at that.

By the way, the DeleteGlobalInt doesn’t seem to exist (I checked in the Script Assist under functions). Even though I don’t need to erase that global int, it could still be nice to be able to do that.

call SetGlobalInt() and set the value to 0

am pretty sure that erases it from globals …

Ah, yes, didn’t think about that, actually. That’s one way to do it. Any ints set to 0 (at least that’s my impression of the game, but you have way more knowledge about that) makes them disappear…or something similar at least.

if a local is set to 0 the variable is still cached on an object w/ value 0

but if a global is set to 0 then i think its entry no longer appears in “globals.xml” – rather like calling DeleteLocalInt() on a local … the variable goes ~poof~

Ah, ok. Good to know.

1 Like