Need help with Creature AI(editing Beholder code)

Information and Discussion Relating to NWN2 Scripting and Coding

Moderators: Moderator, Developer, DM

User avatar
Moridin
Retired Staff
Posts: 582
Joined: Thu Oct 18, 2012 10:14 pm
Location: SK, Canada

Need help with Creature AI(editing Beholder code)

Unread post by Moridin »

This is alot to look over, yet it doesn't seem to do anything ingame. Only the regular AI runs, and goes full on melee.
I need help figuring out why. It compiles so I know it works somewhat. Right now I'm using stock spells, once I get this working with regular spells, I'll begin making the rays. The drain is going to be individual, but to stay as close as possible to lore, I would try to make the drain also affect your items, though not permanently as in lore.

I am trying to make Gauth, the scourge of the trade of Sshamath. :mrgreen:
They feed on magic, and sometimes meat, and raid caravans to steal powerful magic items to drain their powers. The conclave cannot get rid of them since when you kill one, two more will grow from the corpse in a few weeks and magic is completely useless as well. :D
Hidden: show

Code: Select all

//::///////////////////////////////////////////////
//:: x2_s1_gauthatt
//:: Gauth Attack Spell Logic
//:: Copyright (c) 2003 Bioware Corp.
//:://////////////////////////////////////////////
/*

    This spellscript is the core of the Gauth's
    attack logic.

*/
//:://////////////////////////////////////////////
//:: Created By: Georg Zoeller
//:: Created On: 2003-08-28
//:://////////////////////////////////////////////


#include "x2_inc_gauth"



void main()
{
    int nApp = GetLocalInt(OBJECT_SELF,"gauth");
    object oTarget = GetSpellTargetObject();
    // Only if we are beholders and not beholder mages
 /*
    //* GZ: cut whole immunity thing because it was causing too much trouble
     if (nApp == 472 ||nApp == 401 || nApp == 403)
    {
        CloseAntiMagicEye(oTarget);
    }
  */

    // need that to make them not drop out of combat
    SignalEvent(oTarget,EventSpellCastAt(OBJECT_SELF,GetSpellId()));

    struct gauth_target_struct stTargets = GetRayTargets(oTarget);
    int nRay;
    int nOdd;
	int nRacial = GetRacialType(oTarget);
    if (stTargets.nCount ==0)
    {
        //emergency fallback
            GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_REPULSION,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_CONE_OF_COLD,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_LIGHTNING_BOLT,stTargets.oTarget1);
    }
    else if (stTargets.nCount ==1) // AI for only one target
    {
        nOdd=d4();
        if (nOdd == 1)
        {
            GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget1);
            if (d2()==1)
                GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget1);
        }
        if (nOdd == 2)
        {
            GauthDoFireBeam(GAUTH_RAY_CONE_OF_COLD,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_REPULSION,stTargets.oTarget1);
            if (d2()==1)
                GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget1);
        }
		if (nOdd == 3)
        {	
				GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget1);
				GauthDoFireBeam(GAUTH_RAY_LIGHTNING_BOLT,stTargets.oTarget1);
				if (d2()==1)
            		GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget1);
        }
		if (nOdd == 4)
        {
            GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_LIGHTNING_BOLT,stTargets.oTarget1);
            if (d2()==1)
                GauthDoFireBeam(GAUTH_RAY_CONE_OF_COLD,stTargets.oTarget1);
        }
        if (d3()==1)
        {
            GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget2);
			GauthDoFireBeam(GAUTH_RAY_REPULSION,stTargets.oTarget2);
        }
    }
    else if (stTargets.nCount ==2)
    {
        if (nOdd)
        {
            GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_REPULSION,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget2);
            GauthDoFireBeam(GAUTH_RAY_CONE_OF_COLD,stTargets.oTarget2);
            if (d2()==1)
                GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget2);
            GauthDoFireBeam(GAUTH_RAY_LIGHTNING_BOLT,stTargets.oTarget2);
        }
        else
        {
            GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget1);
            if (d2()==1)
                GauthDoFireBeam(GAUTH_RAY_LIGHTNING_BOLT,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget2);
            GauthDoFireBeam(GAUTH_RAY_CONE_OF_COLD,stTargets.oTarget2);
            GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget2);
        }
		if (d3()==1)
        {
            GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget3);
			GauthDoFireBeam(GAUTH_RAY_REPULSION,stTargets.oTarget3);
        }
    }
    else if (stTargets.nCount ==3)
    {
       if (nOdd)
       {
            GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_REPULSION,stTargets.oTarget1);
			GauthDoFireBeam(GAUTH_RAY_CONE_OF_COLD,stTargets.oTarget1);
			GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget2);
            GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget2);
			GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget2);
            GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget3);
            GauthDoFireBeam(GAUTH_RAY_LIGHTNING_BOLT,stTargets.oTarget3);
       }
       else
       {
            GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget2);
			GauthDoFireBeam(GAUTH_RAY_REPULSION,stTargets.oTarget1);
			GauthDoFireBeam(GAUTH_RAY_PARALYZATION,stTargets.oTarget3);
            GauthDoFireBeam(GAUTH_RAY_CONE_OF_COLD,stTargets.oTarget2);
            GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_WOUND,stTargets.oTarget2);
            GauthDoFireBeam(GAUTH_RAY_DRAIN,stTargets.oTarget1);
            GauthDoFireBeam(GAUTH_RAY_LIGHTNING_BOLT,stTargets.oTarget3);
			GauthDoFireBeam(GAUTH_RAY_CONE_OF_COLD,stTargets.oTarget3);
       }
    }




    // Only if we are beholders and not beholder mages
    if (nApp != 1)
    {
        OpenFeebleEye(oTarget);
    }
}



/*



*/
Hidden: show

Code: Select all

//::///////////////////////////////////////////////
//:: Gauth AI and Attack Include
//:: x2_inc_gauth
//:: Copyright (c) 2003 Bioware Corp.
//:://////////////////////////////////////////////
/*

    Include file for several Gauth functions

*/
//:://////////////////////////////////////////////
//:: Created By: Georg Zoeller
//:: Created On: August, 2003
//:://////////////////////////////////////////////

#include "x0_i0_spells"

const int GAUTH_RAY_CONE_OF_COLD = 1;
const int GAUTH_RAY_LIGHTNING_BOLT = 2;
const int GAUTH_RAY_WOUND = 3;
const int GAUTH_RAY_PARALYZATION = 4;
const int GAUTH_RAY_REPULSION = 5;
const int GAUTH_RAY_DRAIN = 6;

struct gauth_target_struct
{
        object oTarget1;
        int nRating1;
        object oTarget2;
        int nRating2;
        object oTarget3;
        int nRating3;
        int nCount;
};



int   GetFeebleEyeMakesSense ( object oTarget );
void  OpenFeebleEye          ( object oTarget );
void  CloseFeebleEye         ( object oTarget );
int   GauthGetTargetThreatRating  ( object oTarget );
int   GauthDetermineHasEffect     ( int nRay, object oCreature );
void  GauthDoFireBeam             ( int nRay, object oTarget );

struct gauth_target_struct GetRayTargets ( object oTarget );

int GetFeebleEyeMakesSense(object oTarget)
{
    int bRet = TRUE;
    int nType;
	
	object oNear = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oTarget);
	float fDistance = GetDistanceBetween(oTarget, oNear);

    effect eTest = GetFirstEffect(oTarget);

    if (!GetIsEffectValid(eTest))
    {
      int nMag = GetLevelByClass(CLASS_TYPE_WIZARD,oTarget) + GetLevelByClass(CLASS_TYPE_SORCERER,oTarget) + GetLevelByClass(CLASS_TYPE_BARD,oTarget) + GetLevelByClass(CLASS_TYPE_RANGER,oTarget) + GetLevelByClass(CLASS_TYPE_PALADIN,oTarget);
      // at least 3 levels of magic user classes... we better use anti magic anyway
      if (nMag < 4)
      {
        bRet = FALSE;
      }
    }
		
    else
    {		
        while (GetIsEffectValid(eTest) && bRet == TRUE )
        {
            nType = GetEffectType(eTest);
            if (nType == EFFECT_TYPE_STUNNED || nType == EFFECT_TYPE_PARALYZE )
            {
                bRet = FALSE;
            }

            eTest = GetNextEffect(oTarget);
   		}
	}
	
	if(fDistance > 30.0 || !GetIsInCombat(oNear))
	{
		bRet = FALSE;
	}
    if (GetHasSpellEffect(805,oTarget)) // already feeblemind
    {
        bRet = FALSE;
    }

    return bRet;
}


void OpenFeebleEye (object oTarget)
{
   if (GetFeebleEyeMakesSense(oTarget))
   {
   		ClearAllActions();
        ActionCastSpellAtObject(805,GetSpellTargetObject(),METAMAGIC_ANY,TRUE,0, PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
   }
}

// being a badass gauth, we close our feeble eye only to attack with our eye rays
// and then reopen it...
void CloseFeebleEye(object oTarget)
{
    RemoveSpellEffects(805,OBJECT_SELF,oTarget); // Do you think I actually still need this? - Moridin
}

// stacking protection
int GauthDetermineHasEffect(int nRay, object oCreature)
{
  switch (nRay)
  {
       case GAUTH_RAY_PARALYZATION :     if (GetHasEffect(EFFECT_TYPE_PARALYZE,oCreature))
                                        return TRUE;

       case GAUTH_RAY_DRAIN :    if (GetHasEffect(EFFECT_TYPE_SPELL_FAILURE,oCreature))
                                        return TRUE;
  }

    return FALSE;
}


int GauthGetTargetThreatRating(object oTarget)
{
    if (oTarget == OBJECT_INVALID)
    {
        return 0;
    }

    int nRet = 20;

    if (GetDistanceBetween(oTarget,OBJECT_SELF) <5.0f)
    {
        nRet += 5;
		
		if(GetHitDice(OBJECT_SELF) <= GetHitDice(oTarget) - 3)
		{
			nRet += 3;
		}
    }
	if (GetDistanceBetween(oTarget,OBJECT_SELF) >15.0f)
    {
        nRet += 3;
    }

    nRet += (GetHitDice(oTarget)-GetHitDice(OBJECT_SELF) /2);
	
	if (GetLevelByClass(CLASS_TYPE_FIGHTER, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_BARBARIAN, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_MONK, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_RANGER, oTarget) > 3)
	{
		nRet += 3;
	}
	if (GetLevelByClass(CLASS_TYPE_ROGUE, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_WARLOCK, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_BARD, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_DRUID, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_PALADIN, oTarget) > 3)
	{
		nRet += 4;
	}
	if (GetLevelByClass(CLASS_TYPE_CLERIC, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_SORCERER, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_WIZARD, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_FAVORED_SOUL, oTarget) > 3 ||
		GetLevelByClass(CLASS_TYPE_SPIRIT_SHAMAN, oTarget) > 3)
	{
		nRet += 5;
	}

    if (GetPlotFlag(oTarget)) //
    {
        nRet -= 6 ;
    }

    if (GetMaster(oTarget)!= OBJECT_INVALID)
    {
        nRet -= 4;
    }
	
	if (GetHasEffect(EFFECT_TYPE_PARALYZE,oTarget))
	{
		nRet -= 8;
	}
	
	if (GetHasEffect(EFFECT_TYPE_CONFUSED,oTarget))
	{
		nRet -=8;
	}
	
	if (GetArcaneSpellFailure(oTarget) == 100)
    {
        nRet -=10;
    }
	
    if (GetIsDead(oTarget))
    {
        nRet = 0;
    }

    return nRet;
}


struct gauth_target_struct GetRayTargets(object oTarget)
{

    struct gauth_target_struct stRet;
    object oTarget1;
    object oTarget2;
    object oTarget3;
    int nTarget1;
    int nTarget2;
    int nTarget3;
    int nCount = 0;
	int nNth = 1;
	int nCheck = 0;
    if (oTarget != OBJECT_INVALID)
    {
		oTarget1 = oTarget;
        nCount ++;
        nTarget1 = GauthGetTargetThreatRating(oTarget1);

        oTarget2 =  GetNearestCreature(CREATURE_TYPE_IS_ALIVE,CREATURE_ALIVE_TRUE,oTarget1,nNth);
        while(GetIsObjectValid(oTarget2) && nCheck != 1)
        {
			if(GetIsFriend(oTarget1, oTarget2))
			{
            	nTarget2 = GauthGetTargetThreatRating(oTarget2);
            	nCount ++;
				nCheck = 1;
			}
			else
			{
				nNth++;
				oTarget2 =  GetNearestCreature(CREATURE_TYPE_IS_ALIVE,CREATURE_ALIVE_TRUE,oTarget1,nNth);
			}
        }
		nCheck=0;	
		nNth=1;
        oTarget3 =  GetNearestCreature(CREATURE_TYPE_IS_ALIVE,CREATURE_ALIVE_TRUE,oTarget2,nNth);
        while(GetIsObjectValid(oTarget3) && nCheck != 1)
        {
			if(GetIsFriend(oTarget1, oTarget3))
			{
            	nTarget3 = GauthGetTargetThreatRating(oTarget3);
            	nCount ++;
				nCheck = 1;
			}
			else
			{
				nNth++;
				oTarget3 =  GetNearestCreature(CREATURE_TYPE_IS_ALIVE,CREATURE_ALIVE_TRUE,oTarget2,nNth);
			}
        }
    }


    stRet.oTarget1 = oTarget1;
    stRet.nRating1 = nTarget1;
    if (oTarget2 != OBJECT_INVALID)
    {
        stRet.oTarget2 = oTarget2;
        stRet.nRating2 = nTarget2;
    }
    else
    {
        stRet.oTarget2 = oTarget1;
        stRet.nRating2 = nTarget1;
        nCount =2;
    }

    if (nCount ==3)
    {
        stRet.oTarget3 = oTarget3;
        stRet.nRating3 = nTarget3;
    }
    stRet.nCount =  nCount;


    return stRet;

}


void GauthDoFireBeam(int nRay, object oTarget)
{

    // don't use a ray if the target already has that effect
    if (GauthDetermineHasEffect(nRay,oTarget))
    {
        return;
    }

    int bHit   = TouchAttackRanged(oTarget,FALSE)>0;
    int nProj;
    switch (nRay)
    {
        case GAUTH_RAY_CONE_OF_COLD: nProj = 775;
                            break;
        case GAUTH_RAY_LIGHTNING_BOLT:    nProj = 851;
                            break;
        case GAUTH_RAY_REPULSION: nProj = 112367;
                            break;
        case GAUTH_RAY_WOUND: nProj = 391;
                            break;
        case GAUTH_RAY_DRAIN:  nProj = 727;
                            break;
        case GAUTH_RAY_PARALYZATION: nProj = 833;
                            break;

    }

    if (bHit)
    {
         ActionCastSpellAtObject(nProj,oTarget,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
    }
        else
    {
        location lFail = GetLocation(oTarget);
        vector vFail = GetPositionFromLocation(lFail);

        if (GetDistanceBetween(OBJECT_SELF,oTarget) > 6.0f)
        {

           vFail.x += IntToFloat(Random(3)) - 1.5;
           vFail.y += IntToFloat(Random(3)) - 1.5;
           vFail.z += IntToFloat(Random(2));
           lFail = Location(GetArea(oTarget),vFail,0.0f);

        }
        //----------------------------------------------------------------------
        // if we are fairly near, calculating a location could cause us to
        // spin, so we use the same location all the time
        //----------------------------------------------------------------------
        else
        {
              vFail.z += 0.8;
              vFail.y += 0.2;
              vFail.x -= 0.2;
        }
		object oIpoint = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_ipoint ", lFail, FALSE, "beh_ip_ray_targ");
        ActionCastSpellAtObject(nProj,oIpoint,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
		DestroyObject(oIpoint, 1.0);
    }


}
Characters:
Lomith Dy'nesen Journal
NegInfinity
Posts: 2449
Joined: Wed Feb 05, 2014 11:24 am

Re: Need help with Creature AI(editing Beholder code)

Unread post by NegInfinity »

You might want to mention where did you put that script. Because if it "doesn't work", it might happen because game doesn't run it at all.
The conclave cannot get rid of them since when you kill one, two more will grow from the corpse in a few weeks and magic is completely useless as well.
Hidden: show
Dump body into magma!
User avatar
Moridin
Retired Staff
Posts: 582
Joined: Thu Oct 18, 2012 10:14 pm
Location: SK, Canada

Re: Need help with Creature AI(editing Beholder code)

Unread post by Moridin »

NegInfinity wrote:You might want to mention where did you put that script. Because if it "doesn't work", it might happen because game doesn't run it at all.
Hidden: show
Dump body into magma!
I put the name of the script, x2_s1_gauthatt, as equal to a variable on the creature.
Hidden: show
That might work. :D You sort of have to keep the other 2 hundred or so of them away when you try it though. Since these Gauth are special and are sort of tribal.
Edit: Just looking at this, it would be better I think if I went closer to the original script instead of grouping the stalks together and separating the main eye.

It seems that I mostly have this working, mostly. All I need now is to figure out how to write 2 of their 7 abilites.
Characters:
Lomith Dy'nesen Journal
User avatar
Moridin
Retired Staff
Posts: 582
Joined: Thu Oct 18, 2012 10:14 pm
Location: SK, Canada

Re: Need help with Creature AI(editing Beholder code)

Unread post by Moridin »

Odd. The player is now caught in a loop of the powers:
1. Shapechange: Mindflayer
2. Eldritch Cone
3. Fireburst

I mentioned these spells nowhere in my code.
Characters:
Lomith Dy'nesen Journal
User avatar
Moridin
Retired Staff
Posts: 582
Joined: Thu Oct 18, 2012 10:14 pm
Location: SK, Canada

Re: Need help with Creature AI(editing Beholder code)

Unread post by Moridin »

I'm still working on this, but this is a question directed to anyone who knows about the spells removed from BGTSCC for reasons.

I want to know why bg_spell_repulsion was removed, if it worked like the spell Repulsion, and if it was buggy.

If it was just OP, I want to know which hakpak the spell came from or if it is custom, and how to gain access to it, so I can add it. Players will not be able to cast it though.
Characters:
Lomith Dy'nesen Journal
User avatar
Rasael
Retired Staff
Posts: 8096
Joined: Wed Jun 30, 2010 6:52 am
Location: Leiden, Netherlands

Re: Need help with Creature AI(editing Beholder code)

Unread post by Rasael »

'bg_spell_repulsion' is an empty script that I created but never filled with code because it quickly became obvious it would completely shutdown any affected AI creature. It'd be far too powerful. As far as I know its not a spell available in default NWN2, nor in any hakpacks or custom content. It was not removed from BGTSCC - it never went live.

Instead of repulsion I then wrote the following variants which work acceptably with the AI. We've tested and given the okay for them in our Quality Control group. But they haven't yet been implemented on the server.

http://bgtscc.wikia.com/wiki/Spell:_Hil ... sdirection
http://bgtscc.wikia.com/wiki/Spell:_Hil ... sdirection

http://bgtscc.wikia.com/wiki/Spell:_Has ... eshadowing
http://bgtscc.wikia.com/wiki/Spell:_Battle_Precognition
User avatar
Moridin
Retired Staff
Posts: 582
Joined: Thu Oct 18, 2012 10:14 pm
Location: SK, Canada

Re: Need help with Creature AI(editing Beholder code)

Unread post by Moridin »

Thankyou. This will get me away from where I stalled while making things. Hmmm, do you have any idea how to temporarily strip item properties so that they can be re-added later? I'll substitute repulsion with greater misdirection as that is basically what the creature is supposed to do.
Characters:
Lomith Dy'nesen Journal
User avatar
gedweyignasia
Custom Content
Posts: 1353
Joined: Sat Nov 23, 2013 11:27 pm
Location: EST/UTC-4

Re: Need help with Creature AI(editing Beholder code)

Unread post by gedweyignasia »

Moridin wrote:Thankyou. This will get me away from where I stalled while making things. Hmmm, do you have any idea how to temporarily strip item properties so that they can be re-added later? I'll substitute repulsion with greater misdirection as that is basically what the creature is supposed to do.
Iterate over its properties and create an array represent the properties, then remove the property. Write the array onto the item using this array library to store the list of properties. When you want to restore the item properties, just iterate through that array contained on the item and restore the item properties.

The first row will hold the ITEM_PROPERTY_* constants, and the rows below it represent any supplemental values (e.g. the +X for ITEM_PROPERTY_ENHANCEMENT_BONUS). Just remember to destroy the array once you've finished restoring the properties.

Return to “NWN2 Scripting”