Custom boss script

Interesting. I’ve actually never used CombatCutscene. I very seldom use speak triggers though, and when I have, it’s always just with standard conversations. There’s still a lot I have to learn about NWN2 apparently.

In any case, I hope you manage to kill of the guardians the way you want to. Maybe - if you want to that is - can destroy them in the script just when the conversation is supposed to start? I guess that would be pretty simple (at least I hope so) with using the DestroyObject function. Maybe you could add that yourself?

2 Likes

Maybe something like this (I’m out on thin ice now since I’m still not that good with “while-loops”, sorry sensei @kevL_s , I know you’ve tried to teach me) but I think if you give your guardians the tags “guardian1”, “guardian2” etc. then this version of the script might work and they will be destroyed just before the conversation starts. Again, this is untested:


//Deleted faulty script

EDIT: Hmmm, no, it won’t work since the while loop will stop if there is a, for instance, “guardian3” who is already dead, but “guardian4” is alive. Now, how to get around that problem…

EDIT2: Darn it! I thought I could maybe have been clever by some way go over all objects with the tag “guardian” at the beginning of their string by perhaps using GetSubString or something, but my mind draws a blank. I’ll search among the forums and see if there’s an example where this is already done.

EDIT3: Ok, so here’s a new attempt. I looked at some of Lance’s code for sitting on chairs. Think I understood it and copied a bit and rewrote it slightly using the GetStringLeft function which I’ve never personally used in my own scripts before. Now, if all your guardians have the tag “guardian1” or “guardian3” or whatever (guardian plus a number) then everyone of them, I think and hope, should be erased before the conversation starts. I hope this works (but I don’t quite know what I’m doing now):

//::///////////////////////////////////////////////
//:: Default On Damaged
//:: NW_C2_DEFAULT6
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    If already fighting then ignore, else determine
    combat round
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Oct 16, 2001
//:://////////////////////////////////////////////

#include "hench_i0_ai"
#include "ginc_behavior"
#include "ginc_group"
#include "ginc_ipspeaker"
#include "x0_i0_assoc" // SetAssociateState()

/*
    OnDamaged script for the boss.
    Stops combat when the boss drops below 10 hitpoints. Starts a dialog.
*/

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


void main()
{

	object oDamager = GetLastDamager();
	object oWeaponUsed = GetLastWeaponUsed(oDamager);
	object oWeapon = GetObjectByTag("tagofweapon");
		
    int iHp = GetCurrentHitPoints(OBJECT_SELF);
	
	if (iHp < 10 && !GetLocalInt(OBJECT_SELF,"healedonetime"))
    {
       	AssignCommand(OBJECT_SELF, ClearAllActions(TRUE));

		int nCurrentHP;
    	int nHP;
		int nHealPercent = 100;
       	effect eFX;
		effect eVisual = EffectNWN2SpecialEffectFile("sp_cure_critical");
		
		SetLocalInt(OBJECT_SELF,"healedonetime",TRUE);
		
		object oPC = GetFirstPC();
		
		FloatingTextStringOnCreature("The boss has healed up.", oPC, FALSE);	
		
		nCurrentHP = GetCurrentHitPoints( OBJECT_SELF );
       	nHP = FloatToInt( IntToFloat(GetMaxHitPoints(OBJECT_SELF)) * IntToFloat(nHealPercent) / 100 ) - nCurrentHP;
        eFX = EffectHeal( nHP );
        ApplyEffectToObject( DURATION_TYPE_PERMANENT,eFX,OBJECT_SELF );
		ApplyEffectToObject(DURATION_TYPE_INSTANT, eVisual, OBJECT_SELF);
	
		return;
    }
	
	
    else if (iHp < 10 && GetLocalInt(OBJECT_SELF,"insidetrigger") && oWeaponUsed == oWeapon)
    {
        GroupChangeToStandardFaction("group_badguys", STANDARD_FACTION_COMMONER);
        ChangeToStandardFaction(OBJECT_SELF, STANDARD_FACTION_COMMONER);
		AssignCommand(OBJECT_SELF, ClearAllActions(TRUE));
		GroupClearAllActions("group_badguys", TRUE);
		 	
        object oPC = GetFirstPC(FALSE);
		
		int i = 1;
		object oGuardian = GetNearestObject(OBJECT_TYPE_CREATURE, oPC, i);
		
		while(oGuardian != OBJECT_INVALID)
		{
			
			if(GetStringLeft(GetTag(oGuardian), 8) == "guardian")
			{
       		 	PrepForDestruction(oGuardian);
				DestroyObject(oGuardian);	    
    		}
			
			i++; 
			oGuardian = GetNearestObject(OBJECT_TYPE_CREATURE, oPC, i);	
		
		}
		
		
        ClearPartyActions(oPC, TRUE);
		
		SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,GetObjectByTag("tagofcompanion"));
		SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,GetObjectByTag("tagofcompanion2"));
		SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,GetObjectByTag("tagofcompanion3"));
		SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,GetObjectByTag("tagofcompanion4"));
		
        CreateIPSpeaker("tagofthisboss", "bossconversation", GetLocation(oPC), 1.f);
		return;
    }
	

    int iFocused = GetIsFocused();
	
	// I've been damaged so no longer partially focused
	if (iFocused == FOCUSED_PARTIAL)
	{
		SetLocalInt(OBJECT_SELF, VAR_FOCUSED, FOCUSED_STANDARD); // no longer focused
	}
    if (iFocused == FOCUSED_FULL)
	{
        // remain focused
    }
	else if(GetFleeToExit())
	{
        // We're supposed to run away, do nothing
    }
    else if (GetSpawnInCondition(NW_FLAG_SET_WARNINGS))
    {
        // don't do anything?
    }
    else
    {
        object oDamager = GetLastDamager();
        if (!GetIsObjectValid(oDamager))
        {
        // don't do anything, we don't have a valid damager
        }
        else if (!GetIsFighting(OBJECT_SELF))
        {
            if ((GetLocalInt(OBJECT_SELF, HENCH_HEAL_SELF_STATE) == HENCH_HEAL_SELF_WAIT) &&
                (GetPercentageHPLoss(OBJECT_SELF) < 30))
            {
                // force heal
                HenchDetermineCombatRound(OBJECT_INVALID, TRUE);
            }
            else if (!GetIsObjectValid(GetAttemptedAttackTarget()) && !GetIsObjectValid(GetAttemptedSpellTarget()))
            {
//    Jug_Debug(GetName(OBJECT_SELF) + " responding to damage");
                if (GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL))
                {
                    HenchDetermineSpecialBehavior(oDamager);
                }
                else
                {
                    HenchDetermineCombatRound(oDamager);
                }
            }
        }
    }
    if(GetSpawnInCondition(NW_FLAG_DAMAGED_EVENT))
    {
        SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_DAMAGED));
    }


}

EDIT4: Did some testing on my own and my script didn’t quite work with erasing the guardians. I’ve edited the script above now. I think it should work now.

4 Likes

woohoo, a while loop. Looks good :)

4 Likes

@andgalf I love your work! I might have to consider making you an NPC in my module haha. What’s your characters name? I’ll throw them in the Inn somewhere :slight_smile:

Thanks so much for doing that and testing it as well. Cool to hear you’re learning as you go as well, well done.

EDIT: i am on the home stretch of the first module. Hope to have it released in about two weeks.

2 Likes

@tfunke - I hope this latest version of the script can help with erasing your guardians that popped up again after the conversation for you. Please test on your end as one might never know if there are problems.

Hehe. I’m honored. Well, eh…I don’t know. One of my old character names from PnP roleplaying is Erconion, so maybe you could use that?

Yeah, I still have so much to learn when it comes to scripting. In this case I found that this script could help me as well (it could be handy in my module, I’m not sure at the moment), so it was good to be able to get this down.

EDIT: By the way, @kevL_s and @Lance_Botelle : I again found that I needed to use the PrepForDestruction function for the script to work in my testing. It should work with only DestroyObject. When I tested I just created 5 new characters in an area with tags like “guardian5” and “guardian2” and so forth. So I didn’t make them immortal, plot or anything like that, but still I needed that function. Any thoughts on this?

EDIT2: Also, @kevL_s - Could I have used GetSubString instead of GetStringLeft in my script for the purpose I needed? And how would I have done that in that case? Just trying to learn…
Another thing that I now see I haven’t done that @tfunke requested, was to fire the script when the boss character has less than 10 % of its health. I’m afraid I don’t know how to do that. Right now it runs when the HP is below 10 and that’s a bit different. And still I use the percentage when healing (but that’s because I stole that code from the ga_heal script) so I don’t quite know how that works. It seems to me that this should be rather easy to do, but… well, I’m a bit too dumb for that kind of thing obviously. :yum:

1 Like

yep →

if(GetSubString(GetTag(oGuardian), 0, 8) == "guardian")

[ but use GetStringLeft() ]

first define a constant (why not…)

const int iPCT_THRESHOLD = 10; // heal at percent HP

then in the body →

	int iHpMax = GetMaxHitPoints();
	int iHpThreshold = (iHpMax * iPCT_THRESHOLD + 99) / 100; // round up.

	int iHp = GetCurrentHitPoints();
	if (iHp <= iHpThreshold)
	{
		// etc 
3 Likes

Occasionally, I have had to do the same thing. (Although some of the places I have used it, I probably do not require it now, but I have not had time to check.) This is just a guess at this stage, but there may be something happening at engine level where objects have been (or are) temporarily “protected” for some reason. Is the function still require if you delay the destruction by 0.1? You could try this and see if that prep function is still required:

E.g. DestroyObject(oGuardian, 0.1);

On the back of what KevL says, you can also use some existing functions for HP percentages:

GetHealthPercent: (ginc_ai)
GetHPPercentage: (nw_i0_generic)
GetPercentageHPLoss: (x0_i0_assoc) (*)

(*) This function name is a bit of a misnomer, because it actually returns the % amount of HPs remaining.

All three of these functions do the same sort of math that KevL shows in his function, but you may want to take a look at the functions in these libraries to see the workings.

Also, I note that GetHPPercentage does not make use of floats compared to the other two. In my experience, I have had some errors occur when I have not used floats in places similar to these sorts of calculations (if you need greater accuracy). Therefore, bear this in mind.

If it helps, think of GetSubString as the superior and more versatile version of both the GetStringLeft and GetStringRight. Whereas GetStringLeft and GetStringRight are used only to grab a section of the string at either end (left or right), the GetSubString can grab any section of a string, including the left and right sections.

GetStringLeft(“TESTSTRING”, 3); would return “TES”. (Here, 3 is just the first 3 letters.)
GetStringRight(“TESTSTRING”, 3); would return “ING”. (Here, 3 is just the last 3 letters.)

GetSubString(“TESTSTRING”, 0, 3); would return “TES”. ( * )
GetSubString(“TESTSTRING”, 7, 3); would return “ING”. ( * )

(*) Here, the first variable is where in the string to start from, and the second variable is how many we want from that starting position. The most important point to grasp when using GetSubString is that the count starts at 0 (zero) and not 1 (one). So, for further example …

GetSubString(“TESTSTRING”, 0, 1); would return “T”. ( ** )
GetSubString(“TESTSTRING”, 1, 1); would return “E”. ( ** )

( ** ) In both cases, we only want one letter (represented by the second integer variable), but in the first line we want the character starting at position zero (0) the first integer variable, whereas in the second line, we want the character starting at the first (1) position (represented by the first integer variable again), which is the one after position zero.

3 Likes

Wow it really is another language, isn’t it.

@andgalf Please don’t stress about the “below 10% health” thing, because for me (and I would imagine the players as well), it will make no difference. The difference might be an extra hit or two required by the party. The end result is the same, especially given the boss has had one chance to fully heal. You’ve done great, thanks.

EDIT: One Erconion (fixed with an o, not an a) the Scribe, coming right up.

I’ll test now and let you know.

3 Likes

@tfunke - Eh, not to be picky, but his name is Erconion not Erconian.

Still, I am humbled you wanting to name an NPC after one of my player characters.

That was a great explanation! Thanks, @Lance_Botelle !

4 Likes

I’m making a wild guess but I think that’s the Aussie/ Kiwi translation.

2 Likes

Ah…LOL!

1 Like

G’day g’day. Yes indeed, let’s lay the blame with that on my Aussieness, I’m ok with that.

3 Likes

Whingeing pom here ! You gave the game away with saying something was “on the nose”. I spent nearly a couple of years in Far North Queensland perfecting my banana bending and it’s the only place I’ve ever heard that expression.

3 Likes

@Tsongo lol, I had no idea that wasn’t a well known expression. We have some weird saying here in Australia, that’s for sure.
For everyone else: “on the nose”… means “a bit obvious”

1 Like

In the UK “on the nose” is a betting slang term relating to horse racing meaning to place a bet for the win only. There are other uk meanings but mostly they are little used nowadays.

TR

1 Like

Huh? I’m not a native english speaker but even I thought that “on the nose” was a common expression for “obvious”. I think I’ve heard that many times online and in films…I even think I have heard americans use that expression. So that’s wrong then?

1 Like

An equivalent uk expression with that meaning is “on the money”.

However I think we are just a tad off-topic now.

TR

Living in the UK myself, I have heard and used the term “on the nose” to mean something along the lines of “you got that spot on”. “You got that absolutely correct.” I.E. To mean “accurate”. E.G. You were “on the nose” with that comment.

I also just found this link… which suggests it may have come from boxing, the “nose” being a desired target.

1 Like