#include "no_inc_ptypes"

//support functions
void NO_ActionCastSpellAtLocation( int iSpell, location lTargetLocation, int iMetaMagic=METAMAGIC_ANY, int iCheat=FALSE, int iProjectilePathType=PROJECTILE_PATH_TYPE_DEFAULT, int iInstantSpell=FALSE, int iDefensive=TRUE )
{
	int iLevel = 14;
	int iConc = GetCombatConcentration();
	int iDefCast = FALSE;
	
	if ( iDefensive == TRUE && iConc >= iLevel && GetHostileCount( 5.0 ) > 0 )
	{
		iDefCast = TRUE;
	}
	SetActionMode( OBJECT_SELF, ACTION_MODE_DEFENSIVE_CAST, iDefCast );
	ActionCastSpellAtLocation( iSpell, lTargetLocation, iMetaMagic, iCheat, iProjectilePathType, iInstantSpell );
}	

void NO_ActionCastSpellAtObject( int iSpell, object oTarget, int iMetaMagic=METAMAGIC_ANY, int iCheat=FALSE, int iDomainLevel=0, int iProjectilePathType=PROJECTILE_PATH_TYPE_DEFAULT, int iInstantSpell=FALSE, int iDefensive=TRUE )
{
	int iLevel = 14;
	int iConc = GetCombatConcentration();
	int iDefCast = FALSE;
	
	if ( iDefensive == TRUE && iConc >= iLevel && GetHostileCount( 5.0 ) > 0 )
	{
		iDefCast = TRUE;
	}
	SetActionMode( OBJECT_SELF, ACTION_MODE_DEFENSIVE_CAST, iDefCast );
	ActionCastSpellAtObject( iSpell, oTarget, iMetaMagic, iCheat, iDomainLevel, iProjectilePathType, iInstantSpell );
}

void NO_ActionUseTalentAtLocation( talent tChosenTalent, location lTargetLocation, int iDefensive=TRUE )
{
	int iLevel = 14;
	int iConc = GetCombatConcentration();
	int iDefCast = FALSE;
				
	if ( iDefensive == TRUE && iConc >= iLevel && GetHostileCount( 5.0 ) > 0 )
	{
		iDefCast = TRUE;
	}
	SetActionMode( OBJECT_SELF, ACTION_MODE_DEFENSIVE_CAST, iDefCast );
	ActionUseTalentAtLocation( tChosenTalent, lTargetLocation );
}

void NO_ActionUseTalentOnObject( talent tChosenTalent, object oTarget, int iDefensive=TRUE )
{
	int iLevel = 14;
	int iConc = GetCombatConcentration();
	int iDefCast = FALSE;
				
	if ( iDefensive == TRUE && iConc >= iLevel && GetHostileCount( 5.0 ) > 0 )
	{
		iDefCast = TRUE;
	}
	SetActionMode( OBJECT_SELF, ACTION_MODE_DEFENSIVE_CAST, iDefCast );
	ActionUseTalentOnObject( tChosenTalent, oTarget );
}

void ActivateCombatMode( int iMode=0, object oE=OBJECT_SELF )
{
	int iM = 0;
	
	switch( iMode )
	{
		case FEAT_DIRTY_FIGHTING:
			iM = ACTION_MODE_DIRTY_FIGHTING;
			break;
		case FEAT_EXPERTISE:
			iM = ACTION_MODE_EXPERTISE;
			break;
		case FEAT_IMPROVED_EXPERTISE:
			iM  = ACTION_MODE_IMPROVED_EXPERTISE;
			break;
		case FEAT_FLURRY_OF_BLOWS:
			iM = ACTION_MODE_FLURRY_OF_BLOWS;
			break;
		case FEAT_POWER_ATTACK:
			iM = ACTION_MODE_POWER_ATTACK;
			break;
		case FEAT_IMPROVED_POWER_ATTACK:
			iM = ACTION_MODE_IMPROVED_POWER_ATTACK;
			break;
		case FEAT_RAPID_SHOT:
			iM = ACTION_MODE_RAPID_SHOT;
			break;
	}
	if ( GetActionMode( oE, iM ) == FALSE )
	{
		SetActionMode( oE, iM, TRUE );
	}
}

void DeactivateCombatModes( int iException=0, object oE=OBJECT_SELF )
{
	if ( iException != FEAT_DIRTY_FIGHTING && GetActionMode( oE, ACTION_MODE_DIRTY_FIGHTING ) == TRUE )
	{
		SetActionMode( oE, ACTION_MODE_DIRTY_FIGHTING, FALSE );
	}
	if ( iException != FEAT_EXPERTISE && GetActionMode( oE, ACTION_MODE_EXPERTISE ) == TRUE )
	{
		SetActionMode( oE, ACTION_MODE_EXPERTISE, FALSE );
	}
	if ( iException != FEAT_IMPROVED_EXPERTISE && GetActionMode( oE, ACTION_MODE_IMPROVED_EXPERTISE ) == TRUE )
	{
		SetActionMode( oE, ACTION_MODE_IMPROVED_EXPERTISE, FALSE );
	}
	if ( iException != FEAT_FLURRY_OF_BLOWS && GetActionMode( oE, ACTION_MODE_FLURRY_OF_BLOWS ) == TRUE )
	{
		SetActionMode( oE, ACTION_MODE_FLURRY_OF_BLOWS, FALSE );
	}
	if ( iException != FEAT_POWER_ATTACK && GetActionMode( oE, ACTION_MODE_POWER_ATTACK ) == TRUE )
	{
		SetActionMode( oE, ACTION_MODE_POWER_ATTACK, FALSE );
	}
	if ( iException != FEAT_IMPROVED_POWER_ATTACK && GetActionMode( oE, ACTION_MODE_IMPROVED_POWER_ATTACK ) == TRUE )
	{
		SetActionMode( oE, ACTION_MODE_IMPROVED_POWER_ATTACK, FALSE );
	}
	if ( iException != FEAT_RAPID_SHOT && GetActionMode( oE, ACTION_MODE_RAPID_SHOT ) == TRUE )
	{
		SetActionMode( oE, ACTION_MODE_RAPID_SHOT, FALSE );
	}
}

int DoHideInPlainSight( object oE=OBJECT_SELF )
{
	if ( GetHasFeat( FEAT_HIDE_IN_PLAIN_SIGHT, oE ) )
	{
		SetActionMode( oE, ACTION_MODE_STEALTH, TRUE );
		return TRUE;
	}
	return FALSE;
}

void DoFireBeholderRay( int iS, object oT )
{
	if ( iS < 776 || iS > 784 || iS == 781 || iS == 782 ) //not a valid beholder ray spell definition
	{
		return;
	}
	
	if ( TouchAttackRanged( oT ) == FALSE )
	{
		vector vT;
		location lT;
		int iF = 785 + Random( 3 );
		
		vT = GetPosition( oT ) + 1.0 * AngleToVector( IntToFloat( Random( 360 ) ) );
		lT = Location( GetArea( OBJECT_SELF ), vT, GetFacing( OBJECT_SELF ) );
		//ActionCastFakeSpellAtLocation( iS, lT );
		ActionCastSpellAtLocation( iF, lT, METAMAGIC_ANY, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE );
	}
	else
	{
		ActionCastSpellAtObject( iS, oT, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE );
	}
}

void DoMoveToLocation( location lDest, int bRun=FALSE )
{
    effect eVis;
    float fD = 4.0;
    float fMinDist = 15.0;

	//activate hide in plain sight while moving
	DoHideInPlainSight();

    if ( GetLocalInt( OBJECT_SELF, "TELEPORTER" ) && GetDistanceBetweenLocations( GetLocation( OBJECT_SELF ), lDest ) > fMinDist )
    {
        DoTeleport( lDest );
    }
    else if ( GetLocalInt( OBJECT_SELF, "FLIER" ) && GetDistanceBetweenLocations( GetLocation( OBJECT_SELF ), lDest ) > fMinDist )
    {
    	if ( !GetLocalInt( OBJECT_SELF, "DRAGONFLIGHTDEL" ) )
    	{
    		SetLocalInt( OBJECT_SELF, "DRAGONFLIGHTDEL", 1 );
    		DelayCommand( fD + 0.5, DeleteLocalInt( OBJECT_SELF, "DRAGONFLIGHTDEL" ) );
        	ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectDisappearAppear( lDest ), OBJECT_SELF, fD );
        }
    }
    else
    {
        ActionMoveToLocation( lDest, bRun );
    }
}

void DoMoveToObject( object oDest, int bRun=FALSE, float fDist=1.0f )
{
    location lDest;
    vector vT;
    float fD = 4.0;
    float fMinDist = 15.0;

	//activate hide in plain sight while moving
	DoHideInPlainSight();

    if ( GetIsObjectValid( oDest ) )
    {
        if ( GetLocalInt( OBJECT_SELF, "TELEPORTER" ) && GetDistanceBetweenLocations( GetLocation( OBJECT_SELF ), GetLocation( oDest ) ) > fMinDist )
        {
            vT = GetPosition( oDest ) + fDist * VectorNormalize( GetPosition( OBJECT_SELF ) - GetPosition( oDest ) );
            lDest = Location( GetArea( oDest ), vT, VectorToAngle( GetPosition( oDest ) - vT ) );
            DoTeleport( lDest );
        }
        else if ( GetLocalInt( OBJECT_SELF, "FLIER" ) && GetDistanceBetweenLocations( GetLocation( OBJECT_SELF ), GetLocation( oDest ) ) > fMinDist )
        {
        	if ( !GetLocalInt( OBJECT_SELF, "DRAGONFLIGHTDEL" ) )
        	{
            	vT = GetPosition( oDest ) + fDist * VectorNormalize( GetPosition( OBJECT_SELF ) - GetPosition( oDest ) );
            	lDest = Location( GetArea( oDest ), vT, VectorToAngle( GetPosition( oDest ) - vT ) );
            	SetLocalInt( OBJECT_SELF, "DRAGONFLIGHTDEL", 1 );
    			DelayCommand( fD + 0.5, DeleteLocalInt( OBJECT_SELF, "DRAGONFLIGHTDEL" ) );
            	ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectDisappearAppear( lDest ), OBJECT_SELF, fD );
            }
        }
        else
        {
            ActionMoveToObject( oDest, bRun, fDist );
        }
    }
}

void DoTeleport( location lLoc )
{
    effect eVis = EffectVisualEffect( VFX_IMP_GLOBE_USE );

    ActionCastFakeSpellAtObject( SPELLABILITY_SUMMON_CELESTIAL, OBJECT_SELF );
    ActionDoCommand( ApplyEffectAtLocation( DURATION_TYPE_INSTANT, eVis, GetLocation( OBJECT_SELF ) ) );
    ActionDoCommand( ApplyEffectAtLocation( DURATION_TYPE_INSTANT, eVis, lLoc ) );
    ActionDoCommand( ActionJumpToLocation( lLoc ) );
}

object GetTarget()
{
    object oS = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE );
    object oH = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE );
    object oT = OBJECT_INVALID;

	if ( !GetIsPerceived( oS, NO_PERCEPTION_SEEN ) )
	{
		oS = OBJECT_INVALID;
	}
	if ( !GetIsPerceived( oH, NO_PERCEPTION_HEARD ) )
	{
		oH = OBJECT_INVALID;
	}
	
	if ( GetIsObjectValid( oS ) && GetIsObjectValid( oH ) )
	{
		//target the closer of the two
		oT = GetDistanceBetween( OBJECT_SELF, oH ) < GetDistanceBetween( OBJECT_SELF, oS ) ? oH : oS;
	}
	else if ( GetIsObjectValid( oS ) )
	{
		//oH is invalid
		oT = oS;
	}
	else
	{
		//oS is invalid
		oT = oH;
	}
	return oT;
}

float DotProduct( vector v1, vector v2 )
{
    float fDP;

    fDP = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;

    return fDP;
}

location GetFlankLoc( object oT, location lL )
{
    vector vU;
    vector vT;
    vector vM;
    location lF;

    if ( GetIsObjectValid( oT ) )
    {
        vT = GetPosition( oT ) - GetPosition( OBJECT_SELF );
    }
    else
    {
        vT = GetPositionFromLocation( lL ) - GetPosition( OBJECT_SELF );
    }
    vM = VectorNormalize( AngleToVector( VectorToAngle( vT ) - 45.0 + 90.0 * IntToFloat( Random( 2 ) ) ) );
    vM = DotProduct( vT, vM ) * VectorNormalize( vM ) + GetPosition( OBJECT_SELF );
    lF = Location( GetArea( oT ), vM, VectorToAngle( vT - vM ) );

    return lF;
}

void DoEquipMelee( object oT )
{
    object oL = GetLocalObject( OBJECT_SELF, "LHAND" );
    object oR = GetLocalObject( OBJECT_SELF, "RHAND" );

    if ( GetIsObjectValid( oR ) || GetIsObjectValid( oL ) )
    {
        if ( GetIsObjectValid( oR ) )
        {
        	if ( !GetIsObjectValid( GetItemPossessor( oR ) ) )
        	{
        		//weapon is on the ground after being disarmed?
        		//try to pick it up
        		ActionPickUpItem( oR );
        		ActionEquipItem( oR, INVENTORY_SLOT_RIGHTHAND );
        	}
        	else if ( GetItemPossessor( oR ) == OBJECT_SELF && GetItemInSlot( INVENTORY_SLOT_RIGHTHAND ) != oR )
        	{
	            ActionEquipItem( oR, INVENTORY_SLOT_RIGHTHAND );
        	}
        }
        if ( GetIsObjectValid( oL ) )
        {
        	if ( !GetIsObjectValid( GetItemPossessor( oL ) ) )
        	{
        		//weapon is on the ground after being disarmed?
        		//try to pick it up
        		ActionPickUpItem( oL );
        		ActionEquipItem( oL, INVENTORY_SLOT_LEFTHAND );
        	}
        	else if ( GetItemPossessor( oL ) == OBJECT_SELF && GetItemInSlot( INVENTORY_SLOT_LEFTHAND ) != oL )
        	{
	            ActionEquipItem( oL, INVENTORY_SLOT_LEFTHAND );
        	}
        }
    }
    else if ( GetIsObjectValid( oT ) )
    {
        //nothing stored
        ActionEquipMostDamagingMelee( oT );
    }
    else
    {
        ActionEquipMostDamagingMelee();
    }
}

talent GetTalentSpell( int iCat, int iD )
{
	int iCR = 21;
	talent tT;
	talent iT; //reserve for invalid talent return
	
	while ( --iCR )
	{
		tT = GetCreatureTalentBest( iCat, iCR );
		if ( ( GetIsTalentValid( tT ) && GetIdFromTalent( tT ) == iD ) || !GetIsTalentValid( tT ) )
		{
			break;
		}
	}
	if ( GetIsTalentValid( tT ) && GetIdFromTalent( tT ) == iD )
	{
		return tT;
	}
	return iT;
}


void DoFightBroadcast()
{
    //broadcast fight "noise" at most once a second
    //respect silence spell
    int iDelay = 2;
    int iTime = GetTimeSecond();
    int iLast = GetLocalInt( OBJECT_SELF, "#LASTCREBC" );
    int iS = iTime < iLast ? iTime + 60 : iTime;
    int iVol = NO_CAN_USE_DM_CHANNEL ? TALKVOLUME_SILENT_SHOUT : TALKVOLUME_SILENT_TALK;
    
    //anti-spamloop delay
    if ( iS - iLast >= iDelay )
    {
    	//only broadcast noise if silence is not in effect
    	if ( !( GetEffectsOnObject() & NO_EFFECT_SILENCE ) )
    	{
	       SpeakString( "BC_FIGHTING", iVol );
	    }
       	SetLocalInt( OBJECT_SELF, "#LASTCREBC", iTime );
       	DeleteLocalInt( OBJECT_SELF, "#LASTREST" );
    }
}

int GetTimeSinceLastCombat()
{
    int iTime = GetTimeSecond();
    int iLast = GetLocalInt( OBJECT_SELF, "#LASTCREBC" );
    int iS = iTime < iLast ? iTime + 60 : iTime;
    int iT = iS - iLast;

    return iT;
}

void DoConditionalRestart( float fT=6.0 )
{
	DeleteLocalInt( OBJECT_SELF, "#RESTARTQUEUED" );
	if ( GetLocalInt( OBJECT_SELF, "#ACTIVE" ) && GetTimeSinceLastCombat() > FloatToInt( fT ) )
	{
		ClearAllActions();
		DeleteLocalInt( OBJECT_SELF, "#COMBATQUEUED" );
		//SetReadyStatus();
		DelayCommand( 0.1, DoQueueCombat( 8.0, 8.0 ) );
	}
}

void DoQueueShutdown( float fT=0.0 )
{
	if ( !GetLocalInt( OBJECT_SELF, "#QUEUESHUTDOWN" ) )
	{
		SetLocalInt( OBJECT_SELF, "#QUEUESHUTDOWN", 1 );
		DelayCommand( fT, ExecuteScript( "no_scr_shutdown", OBJECT_SELF ) );
	}
}

void DoQueueCombat( float fD=6.0, float fT=6.0 )
{
	if ( !GetLocalInt( OBJECT_SELF, "#ACTIVE" ) )
	{
		SetLocalInt( OBJECT_SELF, "#ACTIVE", 1 );
		DelayCommand( 0.1, ClearAllActions() );
	}
	if ( !GetLocalInt( OBJECT_SELF, "#COMBATQUEUED" ) )
	{
		SetLocalInt( OBJECT_SELF, "#COMBATQUEUED", 1 );
		DelayCommand( 0.2, ActionDoCommand( DoCombat() ) );
	}
	if ( !GetLocalInt( OBJECT_SELF, "#RESTARTQUEUED" ) )
	{
		SetLocalInt( OBJECT_SELF, "#RESTARTQUEUED", 1 );
		DelayCommand( fD, DoConditionalRestart( fT ) );
	}
}

void InitCombat( object oE=OBJECT_INVALID )
{
	int iT = GetLastActionTimestamp();
	
	if ( IsActive() == FALSE || GetCurrentAction() == ACTION_INVALID || iT > 8 )
	{
		ClearAllActions();
		SetIsActive( TRUE );
		DoCombat();
	}
}		

void DoVoiceChat( int iV=-1 )
{
	int iC = GetLocalInt( OBJECT_SELF, "#VCC_" + IntToString( iV ) );

	if ( iC == -1 || !CanAct() )
	{
		//do not do this voice chat
		return;
	}
	if ( iC == 0 )
	{
		//no specific setting for this voice chat, use default chance
		iC = GetLocalInt( OBJECT_SELF, "#VCC" );
	}
	if ( Random( 100 ) < iC )
	{
		//don't bother talking to ourselves, look for a nearby friend
		object oF = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, 1, CREATURE_TYPE_IS_ALIVE, TRUE );
		if ( GetIsObjectValid( oF ) && GetDistanceBetween( OBJECT_SELF, oF ) < 50.0 )
		{
			//there is a friendly nearby, do the voice chat
			int iD = iV;
			if ( iV == NO_VC_MELEE || iV == NO_VC_RANGED || iV == NO_VC_MELEEASSIST )
			{
				//custom voice chat handling
				iD = Random( 5 );
				switch( iD )
				{
					case 1:	iD = VOICE_CHAT_ATTACK;
					case 2: iD = VOICE_CHAT_BATTLECRY1;
					case 3: iD = VOICE_CHAT_BATTLECRY2;
					case 4: iD = VOICE_CHAT_BATTLECRY3;
					default: iD = VOICE_CHAT_TAUNT;
				}
			}
			PlayVoiceChat( iD );
		}
	}		
}

void DoMainCombatLoop()
{
	int iCnt = 1;
    string sPri = GetPriority( iCnt );
    int iPri = GetPriorityChance( iCnt++ );
    
    while ( sPri != "" )
    {
        if ( sPri == "+ATKMELEE" && Random( 100 ) < iPri && DoAttackMelee() ) //atkmelee
        {
            //PrintString( "+ATKMELEE: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+ATKRANGED" && Random( 100 ) < iPri && DoAttackRanged() ) //atkranged
        {
            //PrintString( "+ATKRANGED: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+COUNTERSPELL" && Random( 100 ) < iPri && DoCounterSpell() ) //counterspell
        {
            //PrintString( "+COUNTERSPELL: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+EVACAOE" && Random( 100 ) < iPri && DoEvacAOE() ) //evac AOEs
        {
            //PrintString( "+EVACAOE: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+REGROUP" && Random( 100 ) < iPri && DoRegroup() ) //regroup with allies
        {
            //PrintString( "+REGROUP: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+DEFSELF" && Random( 100 ) < iPri && DoDefendSelf() ) //apply defenses
        {
            //PrintString( "+DEFSELF: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+DEFSING" && Random( 100 ) < iPri && DoDefendSingle() ) //apply defenses to single
        {
            //PrintString( "+DEFSING: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+ENHANCESELF" && Random( 100 ) < iPri && DoEnhanceSelf() ) //enhance self
        {
            //PrintString( "+ENHANCESELF: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+ENHANCESING" && Random( 100 ) < iPri && DoEnhanceSingle() ) //enhance single
        {
            //PrintString( "+ENHANCESING: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+HELP" && Random( 100 ) < iPri && DoSpellHelp() ) //remove afflictions
        {
            //PrintString( "+HELP: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+HEAL" && Random( 100 ) < iPri && DoSpellHeal() ) //heal
        {
            //PrintString( "+HEAL: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+RAISE" && Random( 100 ) < iPri && DoSpellRaise() ) //raise
        {
            //PrintString( "+RAISE: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+BREACH" && Random( 100 ) < iPri && DoSpellBreach() ) //breach
        {
            //PrintString( "+BREACH: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+DIRECT" && Random( 100 ) < iPri && DoSpellDirect() ) //direct attack spell
        {
            //PrintString( "+DIRECT: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+TOUCH" && Random( 100 ) < iPri && DoTouch() ) //touch attack spell
        {
            //PrintString( "+TOUCH: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+AREA" && Random( 100 ) < iPri && DoSpellArea() ) //area attack spell
        {
            //PrintString( "+AREA: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+SUMMON" && Random( 100 ) < iPri && DoSpellSummon() ) //summon help
        {
            //PrintString( "+SUMMON: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+FEATENHANCE" && Random( 100 ) < iPri && DoFeatEnhance() ) //enhance via feat
        {
            //PrintString( "+FEATENHANCE: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+AVOIDMELEE" && Random( 100 ) < iPri && DoAvoidMelee() ) //avoid melee
        {
            //PrintString( "+AVOIDMELEE: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+TIMESTOP" && Random( 100 ) < iPri && DoTimeStop() ) //time stop
        {
            //PrintString( "+TIMESTOP: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+VIS" && Random( 100 ) < iPri && DoVision() ) //vision
        {
            //PrintString( "+VIS: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+BREATH" && Random( 100 ) < iPri && DoBreathWeapon() ) //breathweapon
        {
            //PrintString( "+BREATH: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+TURN" && Random( 100 ) < iPri && DoTurning() ) //turn undead
        {
            //PrintString( "+TURN: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+HEALSELF" && Random( 100 ) < iPri && DoHealSelf() ) //heal self
        {
            //PrintString( "+HEALSELF: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+GROUPENHANCE" && Random( 100 ) < iPri && DoSpellGroupEnhance() ) //group enhance
        {
            //PrintString( "+GROUPENHANCE: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+DISPELAOE" && Random( 100 ) < iPri && DoDispelPersAOE() ) //dispel hostile AOEs
        {
            //PrintString( "+DISPELAOE: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+DISPEL" && Random( 100 ) < iPri && DoDispelSingle() ) //dispel buffs on single enemy
        {
            //PrintString( "+DISPEL: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+DISMISSAL" && Random( 100 ) < iPri && DoDismissal() ) //dismiss enemy summons
        {
            //PrintString( "+DISMISSAL: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+MELEEASSIST" && Random( 100 ) < iPri && DoMeleeAssist() ) //assist friends in melee
        {
            //PrintString( "+MELEEASSIST: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+FLANK" && Random( 100 ) < iPri && DoFlank() ) //flank enemy
        {
            //PrintString( "+FLANK: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+GROUPHEAL" && Random( 100 ) < iPri && DoSpellGroupHeal() ) //use area heals
        {
            //PrintString( "+GROUPHEAL: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+MINDBLAST" && Random( 100 ) < iPri && DoMindBlast() ) //use mind blast psionics
        {
            //PrintString( "+MINDBLAST: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+BRAINEXTRACT" && Random( 100 ) < iPri && DoBrainExtraction() ) //Illithid brain extraction
        {
            //PrintString( "+BRAINEXTRACT: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+AVOIDENEMY" && Random( 100 ) < iPri && DoAvoidEnemies() ) //Move away from enemies
        {
            //PrintString( "+AVOIDENEMY: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+EYERAYS" && Random( 100 ) < iPri && DoBeholderRays() ) //Beholder eye rays
        {
            //PrintString( "+EYERAYS: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        else if ( sPri == "+CENTRALEYE" && Random( 100 ) < iPri && DoBeholderCentralEye() ) //Beholder's antimagic cone
        {
            //PrintString( "+CENTRALEYE: " + GetName( OBJECT_SELF ) );
            //return;
            SetLastAction( sPri );
            //SetLastActionTimestamp();
            break;
        }
        sPri = GetPriority( iCnt );
        iPri = GetPriorityChance( iCnt++ );
    }
}

//action functions
void DoCombat()
{
    //int iTime, iLast, iDiff;
    int iAct;
    float fDelay = 0.0;
    
	//delete queue marker
    DeleteLocalInt( OBJECT_SELF, "#COMBATQUEUED" );
	
    if ( GetIsDead( OBJECT_SELF ) )
    {
    	return;
    }

	//int iE = GetHostileCount( 50.0 );
	int iE = GetOmniscientHostileCount( 50.0 );
	if ( iE == 0 )
	{
		return;
	}

    //make combat noise for nearby listeners
    DoFightBroadcast();

    //don't bother running scripts if we can't do anything
    if ( !CanAct() )
    {
        ClearAllActions();
        //queue a delay before checking again
        fDelay += 0.1;
        DelayCommand( fDelay, ActionWait( 1.0 ) );
        //return;
    }
    else
    {
		//can take action
		//If creature is busy do not proceed
		iAct = GetCurrentAction();
		if ( iAct == ACTION_CASTSPELL || iAct == ACTION_HEAL || iAct == ACTION_ITEMCASTSPELL || iAct == ACTION_PICKUPITEM || iAct == ACTION_WAIT ||
			GetLocalInt( OBJECT_SELF, "#EYERAYS" ) == 1 )
		{
			//do nothing
		}
		else
		{
			//run the combat response table
			ClearAllActions();
			DoMainCombatLoop();
			SetLastActionTimestamp();
		}
	}
	DoHideInPlainSight(); //testing HIPS between actions

	if ( !CanAct() || ShortAction() )
	{
    	//DoQueueCombat( 8.0, 8.0 );
    	fDelay += 0.1;
    	DelayCommand( fDelay, ActionDoCommand( DoCombat() ) );
    }
}

int DoAttackMelee( object oT=OBJECT_INVALID )
{
    object oTarget;
    int iSpecial;
    int iMode;
    int iPen = 0;
    int iMelee = GetAttackerCount( 5.0 );
    int iCnt = 1;
    int iChat;
    location lLoc;
    vector vT, vU;
    object test;

	if ( GetIsObjectValid( oT ) )
	{
		oTarget = oT;
	}
	else if ( iMelee )
    {
        oTarget = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, iCnt++, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE );
        while ( GetIsObjectValid( oTarget ) && GetDistanceBetween( OBJECT_SELF, oTarget ) <= 5.0 && GetAttackTarget( oTarget ) != OBJECT_SELF )
        {
            oTarget = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, iCnt++, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE );
        }
    }
    else
    {
        oTarget = GetTarget();
    }

    if ( GetIsObjectValid( oTarget ) && ( GetObjectSeen( oTarget ) || GetObjectHeard( oTarget ) ) )
    {
        //PrintString( GetName( OBJECT_SELF ) + " melee attack " + GetName( oTarget ) + ": " + FloatToString( GetDistanceBetween( OBJECT_SELF, oTarget ) ) );
        DoEquipMelee( oTarget );
        DoVoiceChat( NO_VC_MELEE );
        //combat modes
        if ( iMode = SelectMeleeCombatModes( oTarget ) )
        {
        	iPen = GetCombatModeModifier( iMode );
        	DeactivateCombatModes( iMode );
        	ActivateCombatMode( iMode );
        	//ActionUseFeat( iMode, oTarget );
        	
        }
        //special melee attacks
        if ( iSpecial = GetBestMeleeSpecial( oTarget, iPen ) )
        {
            ActionUseFeat( iSpecial, oTarget );
        }
        else
        {
            ActionAttack( oTarget );
        }
        return TRUE;
    }
    else
    {
        if ( !GetIsObjectValid( oTarget = GetLastDamager() ) )
        {
            if ( !GetIsObjectValid( oTarget = GetLastAttacker() ) )
            {
                oTarget = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_IS_ALIVE, TRUE );
            }
        }
        if ( GetIsObjectValid( oTarget ) && GetDistanceBetween( OBJECT_SELF, oTarget ) <= 40.0 )
        {
        	/*
            vU = GetPosition( OBJECT_SELF );
            vT = GetPosition( oTarget ) - vU;
            vT = VectorMagnitude( vT ) * VectorNormalize( AngleToVector( VectorToAngle( vT ) - 45.0 + IntToFloat( Random( 90 ) ) ) );
            lLoc = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
            */
            lLoc = GetLocation( oTarget );
            DoMoveToLocation( lLoc, TRUE );
            return TRUE;
        }
    }
    return FALSE;
}

int DoAttackRanged( object oT=OBJECT_INVALID )
{
    object oTarget = ( GetIsObjectValid( oT ) ) ? oT : GetTarget();
    object oHurt;
    //talent tSpecial;
    int iSpecial;
    int iMode;
    int iPen = 0;
    int iAtk = GetAttackerCount();
    int iChat;
    location lLoc;

	//don't proceed if enemies are close
	//if creature should avoid melee that should be added as a priority
	//above this behaviour
	if ( GetHostileCount( 5.0 ) )
	{
		return FALSE;
	}

	//raging barbarians should not use ranged attack
	if ( GetHasFeatEffect( FEAT_BARBARIAN_RAGE, OBJECT_SELF ) || GetHasFeatEffect( FEAT_MIGHTY_RAGE, OBJECT_SELF ) )
	{
		//raging barbarians should skip ranged
		return FALSE;
	}
	
	if ( !GetLocalInt( OBJECT_SELF, "#RANGEDCAPABLE" ) )
	{
		if ( !GetHasRangedCapability() )
		{
			return FALSE;
		}
		SetLocalInt( OBJECT_SELF, "#RANGEDCAPABLE", 1 );
	}
	
    if ( GetIsObjectValid( oTarget ) && !iAtk )
    {
    	/* This should be done explicitly with the avoid melee behaviour
        if ( GetHostileCount( 5.0 ) )
        {
            if ( DoAvoidMelee() )
            {
                //PrintString( GetName( OBJECT_SELF ) + " avoiding melee before ranged attack" );
                return TRUE;
            }
        }
        */

        //PrintString( GetName( OBJECT_SELF ) + " ranged attack " + GetName( oTarget ) + ": " + IntToString( iAtk ) + FloatToString( GetDistanceBetween( OBJECT_SELF, oTarget ) ) );
        if ( !GetIsObjectValid( oT ) ) //not specifically told to attack a target
        {
        	if ( DoAbilityCheck( ABILITY_INTELLIGENCE, 5 ) &&
	            GetIsObjectValid( oHurt = GetMostDamagedAlly( 20.0, oTarget, TRUE ) ) )
        	{
	            oTarget = oHurt;
        	}
        }

        ActionEquipMostDamagingRanged( oTarget );
        DoVoiceChat( NO_VC_RANGED );
        //combat modes
        if ( iMode = SelectRangedCombatModes( oTarget ) )
        {
        	iPen = GetCombatModeModifier( iMode );
        	DeactivateCombatModes( iMode );
        	ActivateCombatMode( iMode );
        	ActionUseFeat( iMode, oTarget );        	
        }
        //special ranged attacks
        if ( iSpecial = GetBestRangedSpecial( oTarget, iPen ) )
        {
            ActionUseFeat( iSpecial, oTarget );
        }
        else
        {
            ActionAttack( oTarget );
        }
        return TRUE;
    }
    //PrintString( GetName( OBJECT_SELF ) + " aborting ranged attack: " + IntToString( iAtk ) );
    return FALSE;
}

int DoCounterSpell( object oT=OBJECT_INVALID )
{
    object oC = ( GetIsObjectValid( oT ) ) ? oT : GetNearestEnemyCaster();

    //do not counterspell if I have timestop running
    if ( GetHasSpellEffect( SPELL_TIME_STOP ) )
    {
        return FALSE;
    }
    //do not counterspell if I have attackers
    if ( GetAttackerCount( 40.0 ) )
    {
        return FALSE;
    }

    //dead/living status should be handled by GetNearestEnemyCaster()
    if ( GetIsObjectValid( oC ) )
    {
        //PrintString( GetName( OBJECT_SELF ) + " counterspelling " + GetName( oC ) );
        ActionCounterSpell( oC );
        return TRUE;
    }
    return FALSE;
}

int DoEvacAOE()
{
    vector vT;
    location lLoc;

    //don't bother if we have time stop
    if ( GetHasSpellEffect( SPELL_TIME_STOP ) )
    {
        return FALSE;
    }

    if ( GetAOECount( 7.5 ) )
    {
        vT = GetAOEEvacVector( GetAOEVector( 15.0 ) );
        lLoc = Location( GetArea( OBJECT_SELF ), vT, GetFacing( OBJECT_SELF ) );
        SetLocalLocation( OBJECT_SELF, "LASTLOC", GetLocation( OBJECT_SELF ) );
        DoMoveToLocation( lLoc, TRUE );
        return TRUE;
    }
    return FALSE;
}

int DoRegroup( object oT=OBJECT_INVALID )
{
    int iCnt = 0;
    int iE = 0;
    object oFriend;
    object oDistant = OBJECT_INVALID;
    vector vU, vT, vM;
    location lLoc;

	//check for order to regroup to a particular object
	if ( GetIsObjectValid( oT ) )
	{
		DoMoveToObject( oT, TRUE, 5.0 );
		return TRUE;
	}
	
    //don't bother if we have time stop
    if ( GetHasSpellEffect( SPELL_TIME_STOP ) )
    {
        return FALSE;
    }

    vU = GetPosition( OBJECT_SELF );
    vT = Vector( 0.0, 0.0, 0.0 );
    oFriend = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, ++iCnt, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE );
    while ( GetIsObjectValid( oFriend ) )
    {
        vT = vT + GetPosition( oFriend ) - vU;
        oFriend = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, ++iCnt, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE );
    }

    if ( VectorMagnitude( vT ) > 0.0 )
    {
        vT = vT / IntToFloat( iCnt - 1 );
    }

	if ( VectorMagnitude( vT ) == 0.0 )
	{
		oFriend = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, 1, CREATURE_TYPE_IS_ALIVE, TRUE );
		if ( GetIsObjectValid( oFriend ) && GetDistanceBetween( OBJECT_SELF, oFriend ) < 60.0 /*&& GetDistanceBetween( OBJECT_SELF, oFriend ) > 30.0*/ )
		{
			//we do have friends nearby but can't see them
			DoMoveToLocation( GetLocation( oFriend ), TRUE );
			return TRUE;
		}
	}
    if ( VectorMagnitude( vT ) > 25.0 )
    {
        if ( DoAbilityCheck( ABILITY_INTELLIGENCE, 5 ) )
        {
            //need to regroup, get movement vector
            vT = vT - 10.0 * VectorNormalize( vT );
            vM = vU + vT;
            lLoc = Location( GetArea( OBJECT_SELF ), vM, VectorToAngle( vT ) );
            DoMoveToLocation( lLoc, TRUE );
            //PrintString( GetName( OBJECT_SELF ) + " REGROUPING: (" + FloatToString( vM.x ) + "," + FloatToString( vM.y ) + ")" );
            DoVoiceChat( VOICE_CHAT_GROUP );
            return TRUE;
        }
    }
    else
    {
        //look for outliers
        iCnt = 0;
        oFriend = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, ++iCnt, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE );
        while ( GetIsObjectValid( oFriend ) )
        {
            oDistant = oFriend;
            oFriend = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, ++iCnt, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE );
        }
        if ( GetIsObjectValid( oDistant ) )
        {
            if ( GetDistanceBetween( OBJECT_SELF, oDistant ) > 30.0 )
            {
                DoMoveToObject( oDistant, TRUE, 10.0 );
                //PrintString( GetName( OBJECT_SELF ) + " finding OUTLIER " + GetName( oDistant ) );
                DoVoiceChat( VOICE_CHAT_GROUP );
                return TRUE;
            }
        }
    }
    return FALSE;
}

int DoDefendSelf()
{
    int iMagic = GetCasterCount( 40.0 );
    int iPhys = GetAttackerCount( 40.0 );
    int iPotAtk = GetHostileCount( 40.0 );
    int iSpell = 0;
    talent tSpell;
    struct sSpellDefStatus strMDef = EvaluateSpellDefenses();
    struct sPhysDefStatus strPDef = EvaluatePhysicalDefenses();

    if ( ( iPhys || iPotAtk ) && strPDef.iTotal < 4 )
    {
        /*
        if ( iSpell = GetBestPhysDefense() ) //again, no acid fog
        {
            tSpell = TalentSpell( iSpell );
        }
        */
        if ( iSpell = GetBestPhysDefenseSelf() )
        //if ( GetIsTalentValid( tSpell ) )
        {
            //PrintString( "PD: " + GetName( OBJECT_SELF ) + "(" + IntToString( iSpell ) + ")" );
            //ActionCastSpellAtObject( iSpell, OBJECT_SELF );
            NO_ActionCastSpellAtObject( iSpell, OBJECT_SELF );
            //ActionUseTalentOnObject( tSpell, OBJECT_SELF );
            return TRUE;
        }
    }
    if ( iMagic && strMDef.iTotal < 4 )
    {
        /*
        if ( iSpell = GetBestMagicDefense() ) //can assume acid fog (int val 0) won't be returned here
        {
            tSpell = TalentSpell( iSpell );
        }
        */
        if ( iSpell = GetBestMagicDefenseSelf() )
        //if ( GetIsTalentValid( tSpell ) )
        {
            //PrintString( "MD: " + GetName( OBJECT_SELF ) + "(" + IntToString( iSpell ) + ")" );
            //ActionCastSpellAtObject( iSpell, OBJECT_SELF );
            NO_ActionCastSpellAtObject( iSpell, OBJECT_SELF );
            //ActionUseTalentOnObject( tSpell, OBJECT_SELF );
            return TRUE;
        }
    }

    /*
    if ( iSpell = GetBestGenericProtection() )
    {
        tSpell = TalentSpell( iSpell );
    }
    if ( GetIsTalentValid( tSpell ) )
    {
        //ActionCastSpellAtObject( iSpell, OBJECT_SELF );
        ActionUseTalentOnObject( tSpell, OBJECT_SELF );
        return TRUE;
    }
    */
    return FALSE;
}

int DoDefendSingle( object oT=OBJECT_INVALID )
{
	object oD;
	int iSpell;
	struct sSpellDefStatus strM;
	struct sPhysDefStatus strP;
	
	oD = ( GetIsObjectValid( oT ) ) ? oT : GetLeastDefendedAlly( 20.0 );
	if ( GetIsObjectValid( oD ) )
	{
		strM = EvaluateSpellDefenses( oD );
		strP = EvaluatePhysicalDefenses( oD );
		if ( strM.iTotal < strP.iTotal )
		{
			if ( iSpell = GetBestMagicDefenseSingle( oD ) )
			{
				//PrintString( "DDSM: " + GetName( OBJECT_SELF ) + " -> " + GetName( oD ) + " (" + IntToString( iSpell ) + ")" );
				//ActionCastSpellAtObject( iSpell, oD );
				NO_ActionCastSpellAtObject( iSpell, oD );
				return TRUE;
			}
		}
		else
		{
			//strP.iTotal <= strM.iTotal
			if ( iSpell = GetBestPhysDefenseSingle( oD ) )
			{
				//PrintString( "DDSP: " + GetName( OBJECT_SELF ) + " -> " + GetName( oD ) + " (" + IntToString( iSpell ) + ")" );
				//ActionCastSpellAtObject( iSpell, oD );
				NO_ActionCastSpellAtObject( iSpell, oD );
				return TRUE;
			}
		}
	}
	return FALSE;
}
	
int DoEnhanceSelf()
{
	int iMinLvl = GetHitDice( OBJECT_SELF );
	iMinLvl = iMinLvl > 20 ? 20 : iMinLvl;
	iMinLvl = iMinLvl / 3;
    int iSpell = GetEnhanceSpellSelf( iMinLvl );
    vector vT, vU;
    location lM;
    //talent tSpell;

    if ( iSpell )
    {
        /*
        tSpell = TalentSpell( iSpell );
        if ( GetIsTalentValid( tSpell ) )
        {
            ActionUseTalentOnObject( tTest, OBJECT_SELF );
        }
        */
        //ActionCastSpellAtObject( iSpell, OBJECT_SELF );
        NO_ActionCastSpellAtObject( iSpell, OBJECT_SELF );
        if ( iSpell == SPELL_INVISIBILITY || iSpell == SPELL_IMPROVED_INVISIBILITY )
        {
        	//move a bit after casting invisibility on self
        	if ( VectorMagnitude( vT = GetHostileVector( 10.0 ) ) > 0.0 )
        	{
        		vU = GetPosition( OBJECT_SELF );
        		vT = 5.0 * AngleToVector( VectorToAngle( vT ) + 90.0 + IntToFloat( Random( 180 ) ) );
        		lM = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
        		DoMoveToLocation( lM, TRUE );
        		vT = 5.0 * AngleToVector( VectorToAngle( vT ) - 90.0 + IntToFloat( Random( 180 ) ) );
        		lM = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
        		DoMoveToLocation( lM, TRUE );
        	}
        }
        return TRUE;
    }
    return FALSE;
}

int DoEnhanceSingle( object oT=OBJECT_INVALID )
{
	int iMinLvl = GetHitDice( OBJECT_SELF );
	iMinLvl = iMinLvl > 20 ? 20 : iMinLvl;
	iMinLvl = iMinLvl / 3;
    int iSpell = 0;
    vector vT, vU;
    location lM;
	object oE = ( GetIsObjectValid( oT ) ) ? oT : GetLeastBuffedAlly( 20.0, TRUE );
	
	if ( GetIsObjectValid( oE ) )
	{
		if ( iSpell = GetEnhanceSpellSingle( iMinLvl, oE ) )
		{
			//PrintString( "DES: " + GetName( OBJECT_SELF ) + " -> " + GetName( oE ) + " (" + IntToString( iSpell ) + ")" );
			//ActionCastSpellAtObject( iSpell, oE );
			NO_ActionCastSpellAtObject( iSpell, oE );
			return TRUE;
		}
		if ( oE == OBJECT_SELF )
		{
			if ( iSpell == SPELL_INVISIBILITY || iSpell == SPELL_IMPROVED_INVISIBILITY )
        	{
        		//move a bit after casting invisibility on self
        		if ( VectorMagnitude( vT = GetHostileVector( 10.0 ) ) > 0.0 )
        		{
	        		vU = GetPosition( OBJECT_SELF );
        			vT = 5.0 * AngleToVector( VectorToAngle( vT ) + 90.0 + IntToFloat( Random( 180 ) ) );
        			lM = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
        			DoMoveToLocation( lM, TRUE );
        			vT = 5.0 * AngleToVector( VectorToAngle( vT ) - 90.0 + IntToFloat( Random( 180 ) ) );
        			lM = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
        			DoMoveToLocation( lM, TRUE );
        		}
        	}
		}
	}
	return FALSE;
}

/*
====================
DoSpellHelp:

Description: Find the ally with the most hampering effects on them and try to remove them.

Notes: Problem with this behaviour is that it always looks for most hampered ally. If it can't help that ally then
it will not help anyone. If there are less hampered allies that it could help it will never know because it goes
straight to the most hampered

Possible fix: First generate full list of possible help it can provide stored as bits and do bitwise AND against 
effects on each ally. If there are no matches at all, do not consider them. Store the ally with the most matches 
of available help vs hampering effects. Not yet tried.
====================
*/
int DoSpellHelp( object oT=OBJECT_INVALID )
{
    object oHurt;
    int iHelp;

    if ( GetHasHelpingAbility() )
    {
        oHurt = ( GetIsObjectValid( oT ) ) ? oT : GetMostHamperedAlly( 30.0, OBJECT_SELF, TRUE );
        if ( GetIsObjectValid( oHurt ) )
        {
            iHelp = GetBestHelp( oHurt );
            if ( iHelp )
            {
                SetLocalObject( oHurt, "HELPER", OBJECT_SELF );
                DelayCommand( 6.0, DeleteLocalObject( oHurt, "HELPER" ) );
                //PrintString( "HELP: " + GetName( OBJECT_SELF ) + " -> " + GetName( oHurt ) + "(" + IntToString( iHelp ) + ")" );
                //ActionCastSpellAtObject( iHelp, oHurt );
                NO_ActionCastSpellAtObject( iHelp, oHurt );
                return TRUE;
            }
            //PrintString( "HELP: fail " + GetName( OBJECT_SELF ) + " -> " + GetName( oHurt ) );
        }
    }
    return FALSE;
}

int DoSpellHeal( object oT=OBJECT_INVALID )
{
    //original heal code
    object oHurt;
    int iHeal, iCR;
    talent tHeal;
    int iMin = 8;

    if ( GetHasHealingAbility() )
    //if ( GetIsTalentValid( GetCreatureTalentBest( TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH, 20 ) ) )
    {
        oHurt = ( GetIsObjectValid( oT ) ) ? oT : GetLowestHPAllyNoHealer( 30.0, iMin, OBJECT_SELF, TRUE );
        if ( GetIsObjectValid( oHurt ) )
        {
        	/* 
        	// Need to use this hack with talents because GetHasSpell will pick up spontaneous casting ability
        	// but ActionCastSpell* cannot do the spontaneous casting, which leaves the creature daydreaming
        	// rather than acting
        	*/
            iHeal = GetBestHeal( oHurt, iMin );
            tHeal = GetTalentSpell( TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH, iHeal );
            //if ( iHeal )
            if ( GetIsTalentValid( tHeal ) )
            {
            	//PrintString( "HEAL: " + GetName( OBJECT_SELF ) + " -> " + GetName( oHurt ) + " (" + IntToString( iHeal ) + ")" );
                SetLocalObject( oHurt, "#HEALER", OBJECT_SELF );
                DelayCommand( 6.0, DeleteLocalObject( oHurt, "#HEALER" ) );
                //ActionCastSpellAtObject( iHeal, oHurt );
                //ActionUseTalentOnObject( tHeal, oHurt );
                NO_ActionUseTalentOnObject( tHeal, oHurt );
                return TRUE;
            }
        }
    }
    return FALSE;
}

int DoSpellRaise( object oT=OBJECT_INVALID )
{
    //original raise code
    object oHurt;
    int iRaise;

    if ( GetHasRaisingAbility() )
    {
        oHurt = ( GetIsObjectValid( oT ) && GetIsDead( oT ) ) ? oT : GetNearestDeadAllyNoRaiser( 30.0, OBJECT_SELF, TRUE );
        if ( GetIsObjectValid( oHurt ) )
        {
            iRaise = GetBestRaise( TRUE );
            if ( iRaise )
            {
                SetLocalObject( oHurt, "#RAISER", OBJECT_SELF );
                DelayCommand( 6.0, DeleteLocalObject( oHurt, "#RAISER" ) );
                //ActionCastSpellAtObject( iRaise, oHurt );
                NO_ActionCastSpellAtObject( iRaise, oHurt );
                return TRUE;
            }
        }
    }
    return FALSE;
}

int DoSpellBreach( object oT=OBJECT_INVALID )
{
    int iCnt = 1;
    int iSpell = 0;
    int iMaxLvl = 0;
    int iMaxNum = 0;
    struct sSpellDefStatus strMDef;
    struct sPhysDefStatus strPDef;
    object oSub = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, iCnt++, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN );
    object oTarget = OBJECT_INVALID;

    //add breach check before doing search?
    if ( GetIsObjectValid( oT ) )
    {
    	oTarget = oT;
    }
    else
    {
    	while ( !GetIsObjectValid( oTarget ) && GetIsObjectValid( oSub ) )
    	{
	        if ( !GetIsDead( oSub ) && !GetIsObjectValid( GetLocalObject( oSub, "BREACHER" ) ) )
        	{
	            strMDef = EvaluateSpellDefenses( oSub );
            	strPDef = EvaluatePhysicalDefenses( oSub );
	
            	if ( strMDef.iTotal + strPDef.iTotal > iMaxLvl )
            	{
	                oTarget = oSub;
                	iMaxLvl = strMDef.iTotal + strPDef.iTotal;
            	}
        	}
        	oSub = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, iCnt++, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN );
    	}
    }

    if ( GetIsObjectValid( oTarget ) )
    {
        if ( iSpell = GetBestBreach( iMaxLvl ) )
        {
            //ActionCastSpellAtObject( iSpell, oTarget );
            NO_ActionCastSpellAtObject( iSpell, oTarget );
            SetLocalObject( oTarget, "BREACHER", OBJECT_SELF );
            DelayCommand( 4.0, DeleteLocalObject( oTarget, "BREACHER" ) );
            return TRUE;
        }
    }
    return FALSE;
}

int DoSpellDirect( object oT=OBJECT_INVALID )
{
    int iSpell = -1;
    int iMinLvl = 0;
    float fRange = 25.0;
    object oHurt;
    object oTarget;
    float fRad;
    float fRatio;
    location lLoc;
    talent tSpell;

    //go for least defended, failing that go for most damaged
    //in practice GetLeastMagicDefendedEnemy should always return someone if any
    //enemies are visible to caller
    if ( GetIsObjectValid( oT ) )
    {
    	oTarget = oT;
    }
    else if ( !GetIsObjectValid( oTarget = GetLeastMagicDefendedEnemy( fRange ) ) )
    {
        oTarget = GetTarget();
        if ( DoAbilityCheck( ABILITY_INTELLIGENCE, 5) )
        {
            oHurt = GetMostDamagedAlly( 20.0, oTarget, TRUE );
        }
        if ( GetIsObjectValid( oHurt ) && GetObjectSeen( oHurt, OBJECT_SELF ) )
        {
            oTarget = oHurt;
        }
    }

    //open up spell level selection range
    iMinLvl = GetHitDice( oTarget ) / 3;

    //PrintString( GetName( OBJECT_SELF ) + " DS" + IntToString( iMinLvl ) );
    if ( GetIsObjectValid( oTarget ) && GetObjectSeen( oTarget, OBJECT_SELF ) )
    {
        iSpell = GetDirectSpell( oTarget, FALSE, iMinLvl );
    }
    if ( iSpell > -1 && ( fRad = GetAreaSpellRadius( iSpell ) ) > 0.0 )
    {
        //direct spell has radius effect
        //this code should be unnecessary now
        fRatio = GetFriendFoeRatio( GetLocation( oTarget ), fRad );
        if ( fRatio < GetFriendFoeTolerance() )
        {
            iSpell = GetDirectSpell( oTarget, TRUE, iMinLvl );
        }
    }
    //Testing immunity checks
    //result: feh, doesn't work properly, 100% immunity to damage type doesn't count as "immunity". go figure.
    //SpeakString( "IT " + GetName( oTarget ) + " " + IntToString( FortitudeSave( oTarget, 1000, SAVING_THROW_TYPE_FIRE ) ) );
    
    /* Spell/Talent Issue
    if ( iSpell > -1 )
    {
        tSpell = TalentSpell( iSpell );
    }
    */
    if ( iSpell > -1 )
    {
        //ActionCastSpellAtObject( iSpell, oTarget );
        NO_ActionCastSpellAtObject( iSpell, oTarget );
        //ActionUseTalentOnObject( tSpell, oTarget );
        return TRUE;
    }
    return FALSE;
}

int DoTouch( object oT=OBJECT_INVALID )
{
	int iSpell = -1;
    int iMinLvl = 0;
    float fRange = 20.0;
    object oHurt;
    object oTarget;

    //go for least defended, failing that go for most damaged
    //in practice GetLeastMagicDefendedEnemy should always return someone if any
    //enemies are visible to caller
    if ( GetIsObjectValid( oT ) )
    {
    	oTarget = oT;
    }
    else if ( !GetIsObjectValid( oTarget = GetLeastMagicDefendedEnemy( fRange ) ) )
    {
        oTarget = GetTarget();
        if ( DoAbilityCheck( ABILITY_INTELLIGENCE, 5 ) )
        {
            oHurt = GetMostDamagedAlly( 10.0, oTarget, TRUE );
        }
        if ( GetIsObjectValid( oHurt ) && GetObjectSeen( oHurt, OBJECT_SELF ) )
        {
            oTarget = oHurt;
        }
    }

    //open up spell level selection range
    iMinLvl = GetHitDice( oTarget ) / 3;

    //PrintString( GetName( OBJECT_SELF ) + " TS" + IntToString( iMinLvl ) );
    if ( GetIsObjectValid( oTarget ) && GetObjectSeen( oTarget, OBJECT_SELF ) )
    {
        iSpell = GetTouchSpell( oTarget, iMinLvl );
    }
    
    /* Spell/Talent Issue
    if ( iSpell > -1 )
    {
        tSpell = TalentSpell( iSpell );
    }
    */
    if ( iSpell > -1 )
    {
        //ActionCastSpellAtObject( iSpell, oTarget );
        NO_ActionCastSpellAtObject( iSpell, oTarget );
        //ActionUseTalentOnObject( tSpell, oTarget );
        return TRUE;
    }
	return FALSE;
}

int DoSpellArea()
{
    int iCnt;
    int iMinLvl = 0;
    int iSpell = -1;
    int iHostiles = GetHostileCount( 40.0 );
    float fRad;
    float fRange;
    float fRatio;
    float fDist;
    float fMinSearch = 0.0;
    float fMaxSearch;
    vector vU;
    vector vT;
    vector vS;
    location lMark;
    object oTarget;
    object oD;
    talent tSpell;

	if ( !iHostiles )
	{
		return FALSE;
	}
	
	//estimate average enemy hit dice to get an idea of what spell level range we should consider
	iMinLvl = GetSafeAverageEnemyLevel();
	
	if ( iHostiles == 1 )
	{
		oTarget = GetTarget();
		if ( !GetIsObjectValid( oTarget ) )
		{
			return FALSE;
		}
		else 
		{
			fMaxSearch = GetDistanceBetween( OBJECT_SELF, oTarget );
			iSpell = GetAreaSpell( Vector( 0.0, 0.0, 0.0 ), FALSE, iMinLvl, fMaxSearch );
			if ( iSpell == -1 )
			{
				return FALSE;
			}
			fRad = GetAreaSpellRadius( iSpell );
			vT = GetPosition( oTarget ) - GetPosition( OBJECT_SELF );
		}
	}
	else if ( iHostiles > 1 )
	{		
		//fMaxSearch = VectorMagnitude( GetHostileVector( 40.0 ) );
		/*
		oD = GetMostDistantEnemy( 40.0 );
		if ( GetIsObjectValid( oD ) )
		{
			fMaxSearch = GetDistanceBetween( OBJECT_SELF, oD );
		}
		else
		{
			//apparently no enemies spotted, should be impossible at this point
			fMaxSearch = 40.0;
		}
		*/
		fMaxSearch = GetAverageDistanceToEnemy( 40.0 );
		//SpeakString( "ADtE: " + FloatToString( fMaxSearch ) + " AEL: " + IntToString( iMinLvl ) );
		iSpell = GetAreaSpell( Vector( 0.0, 0.0, 0.0 ), FALSE, iMinLvl, fMaxSearch );
		if ( iSpell < 0 )
		{
			//SpeakString( "No spell" );
			return FALSE;
		}
		//SpeakString( "Spell: " + IntToString( iSpell ) );
		fRad = GetAreaSpellRadius( iSpell );
		vT = GetAreaTarget( iSpell, fRad, fMinSearch, fMaxSearch );
		vS = GetAverageEnemySaveInArea( vT, fRad );
		//SpeakString( "AES: " + FloatToString( vS.x ) + " " + FloatToString( vS.y ) + " " + FloatToString( vS.z ) );
	}

	//PrintString( GetName( OBJECT_SELF ) + " AS" + IntToString( iMinLvl ) );
	
	fRange = GetSpellRange( iSpell );
	vU = GetPosition( OBJECT_SELF );
	//single target case
	if ( iHostiles == 1 )
	{
		lMark = GetLocation( OBJECT_SELF );
		if ( GetIsDiscriminantSpell( iSpell ) )
		{
			if ( fRange == 0.0 )
			{
				//ActionCastSpellAtLocation( iSpell, lMark );
				NO_ActionCastSpellAtLocation( iSpell, lMark );
			}
			else
			{
				//ActionCastSpellAtObject( iSpell, oTarget );
				NO_ActionCastSpellAtObject( iSpell, oTarget );
			}
			return TRUE;
		}
		else
		{
			if ( fRange == 0.0 )
			{
				fRatio = GetFriendFoeRatio( lMark, fRad );
				if ( fRatio < GetFriendFoeTolerance() )
				{
					iSpell = GetAreaSpell( Vector( 0.0, 0.0, 0.0 ), TRUE, iMinLvl, fMaxSearch );
					if ( iSpell < 0 )
					{
						//fRatio not acceptable, no discriminant spells
						return FALSE;
					}
				}
				//either fRatio is okay or we have a discriminant spell
				fRange = GetSpellRange( iSpell );
				if ( fRange == 0.0 )
				{
					//ActionCastSpellAtLocation( iSpell, lMark );
					NO_ActionCastSpellAtLocation( iSpell, lMark );
				}
				else
				{
					//ActionCastSpellAtObject( iSpell, oTarget );
					NO_ActionCastSpellAtObject( iSpell, oTarget );
				}
				return TRUE;
			}
			fRatio = GetFriendFoeRatio( GetLocation( oTarget ), fRad );
			if ( fRatio < GetFriendFoeTolerance() )
			{
				iSpell = GetAreaSpell( Vector( 0.0, 0.0, 0.0 ), TRUE, iMinLvl, fMaxSearch );
				if ( iSpell < 0 )
				{
					return FALSE;
				}
			}
			//either fRatio is okay or we have a discriminant spell
			fRange = GetSpellRange( iSpell );
			if ( fRange == 0.0 )
			{
				//ActionCastSpellAtLocation( iSpell, lMark );
				NO_ActionCastSpellAtLocation( iSpell, lMark );
			}
			else
			{
				//ActionCastSpellAtObject( iSpell, oTarget );
				NO_ActionCastSpellAtObject( iSpell, oTarget );
			}
			return TRUE;
		}	
	}
	//multiple target case
	if ( iHostiles > 1 )
	{		
    	//PrintString( "AREA: " + GetName( OBJECT_SELF ) + FloatToString( fMaxSearch ) );        
        if ( VectorMagnitude( vT ) == 0.0 )
        {
        	//SpeakString( "No target" );
        	return FALSE;
        }
        /*
        if ( 5 - GetAbilityModifier( ABILITY_INTELLIGENCE ) > 0 )
        {
            lMark = Location( GetArea( OBJECT_SELF ),
                    vU + vT + VectorNormalize( AngleToVector( IntToFloat( Random( 360 ) ) ) ) * IntToFloat( Random( 5 - GetAbilityModifier( ABILITY_INTELLIGENCE ) ) ),
                    GetFacing( OBJECT_SELF ) );
        }
        else
        {
            lMark = Location( GetArea( OBJECT_SELF ), vU + vT, GetFacing( OBJECT_SELF ) );
        }
        */
        lMark = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
        //SpeakString( GetName( OBJECT_SELF ) + " AS(" + IntToString( iSpell ) + ")" );
        if ( !GetIsDiscriminantSpell( iSpell ) )
        {
            fRatio = GetFriendFoeRatio( lMark, fRad );
            //SpeakString( "AS FFR" + FloatToString( fRatio ) );
            if ( fRatio < GetFriendFoeTolerance() )
            {
                iSpell = GetAreaSpell( Vector( 0.0, 0.0, 0.0 ), TRUE, iMinLvl, fMaxSearch ); //get discriminant area spell if possible
                if ( iSpell < 0 )
                {
                    //SpeakString( GetName( OBJECT_SELF ) + " !iDisc " + FloatToString( fRatio ) );
                    return FALSE;
                }
                //PrintString( GetName( OBJECT_SELF ) + " iDisc " + FloatToString( fRatio ) );
            }
            else
            {
                //SpeakString( GetName( OBJECT_SELF ) + " FFOK " + FloatToString( fRatio ) );
            }
        }
        //either friendfoe ratio is acceptable or we have a discriminant spell
        //if the spell is lightning or chain lightning target it at nearest creature
        //do this sometimes with delayed blast fireball
        if ( iSpell == SPELL_LIGHTNING_BOLT || iSpell == SPELL_CHAIN_LIGHTNING || iSpell == SPELL_GEDLEES_ELECTRIC_LOOP )
        {
            oTarget = GetFirstObjectInShape( SHAPE_SPHERE, fRad, lMark );
            while ( GetIsObjectValid( oTarget ) )
            {
                if ( GetObjectType( oTarget ) == OBJECT_TYPE_CREATURE || GetIsPC( oTarget ) )
                {
                    if ( GetObjectSeen( oTarget ) && !GetIsDead( oTarget ) && GetIsEnemy( oTarget ) )
                    {
                        //PrintString( "AS LCL " + GetName( oTarget ) );
                        break;
                    }
                }
                oTarget = GetNextObjectInShape( SHAPE_SPHERE, fRad, lMark );
            }
            if ( GetIsObjectValid( oTarget ) ) //should always be valid
            {
                //PrintString( "AS CL" + GetName( oTarget ) );
                //ActionCastSpellAtObject( iSpell, oTarget );
                NO_ActionCastSpellAtObject( iSpell, oTarget );
                return TRUE;
            }
        }
        //don't cast an area spell right next to myself
        //maybe later add something to see if I am immune to the spell
        //and cast anyway if so
        //Spells with range of 0 should be okay to cast
        else if ( GetIsDiscriminantSpell( iSpell ) || 
        	( GetSpellRange( iSpell ) > 0.0 && VectorMagnitude( vT ) > fRad ) ||  GetSpellRange( iSpell ) == 0.0 ||
        	IsCone( iSpell ) )
        {
            //ActionCastSpellAtLocation( iSpell, lMark );
            NO_ActionCastSpellAtLocation( iSpell, lMark );
            return TRUE;
        }
    }
    return FALSE;
}

int DoSpellSummon()
{
    int iMinLvl = GetAverageEnemyLevel();
    int iSpell = GetSummonSpell( iMinLvl );
    int iA;
    vector vT, vA;
    object oTarget = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE );

    if ( iSpell && !GetLocalInt( OBJECT_SELF, "#SUMMONDEL" ) && !GetIsObjectValid( GetAssociate( ASSOCIATE_TYPE_SUMMONED ) ) )
    {
    	/*
		vT = AngleToVector( GetFacing( OBJECT_SELF ) - 90.0 + IntToFloat( Random( 180 ) ) );
        //testing addition to stop summoning creatures into AOEs
        if ( GetAOECount( 10.0 ) )
        {
            vT = GetAOEVector( 10.0 );
            vT *= -1.0;
        }
        //end test code
        vT = 3.0 * VectorNormalize( vT );
        vT = GetPosition( OBJECT_SELF ) + vT;
        ActionCastSpellAtLocation( iSpell, Location( GetArea( OBJECT_SELF ), vT, GetFacing( OBJECT_SELF ) ) );
        */
        //ActionCastSpellAtLocation( iSpell, GetLocation( OBJECT_SELF ) );
        NO_ActionCastSpellAtLocation( iSpell, GetLocation( OBJECT_SELF ) );
        SetLocalInt( OBJECT_SELF, "#SUMMONDEL", 1 );
        DelayCommand( 6.0, DeleteLocalInt( OBJECT_SELF, "#SUMMONDEL" ) );
        DelayCommand( 6.0, SpeakString( "BC_FIGHTING", TALKVOLUME_SILENT_TALK ) );
        return TRUE;
    }
    return FALSE;
}

int DoSpellCharm()
{
    //included in DoSpellDirect()
    return FALSE;
}

int DoFeatEnhance()
{
    //NOTE: should be okay to rely on enhance feats to be non-zero as Alertness should never be returned
    int iFeat = GetEnhanceFeat();

    if ( iFeat )
    {
        ActionUseFeat( iFeat, OBJECT_SELF );
        return TRUE;
    }
    return FALSE;
}

int DoDragonFlight()
{
	//unused due to lack of re-entry animation
	/*
    int iCnt = 1;
    object oTarget = OBJECT_INVALID;

    if ( GetLocalInt( OBJECT_SELF, "FLIER" ) && GetHostileCount() > 3 )
    {
        oTarget = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, iCnt++, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN );
        while ( GetIsObjectValid( oTarget ) )
        {
            if ( Random( 5 ) && GetDistanceBetween( OBJECT_SELF, oTarget ) > 10.0 )
            {
                break;
            }
            oTarget = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, iCnt++, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN );
        }
        if ( GetIsObjectValid( oTarget ) )
        {
            DoMoveToObject( oTarget );
            return TRUE;
        }
    }
    */
    return FALSE;
}

/*
int DoAvoidMelee()
{
    vector vT;
    location lLoc;

    //don't bother if we have time stop
    if ( GetHasSpellEffect( SPELL_TIME_STOP ) )
    {
        return FALSE;
    }

    //if ( GetAttackerCount( 5.0 ) )
    if ( GetHostileCount( 5.0 ) )
    {
        vT = GetHostileEvacVector( GetHostileVector( 15.0 ) );
        if ( VectorMagnitude( vT ) > 5.0 )
        {
        	SetLocalLocation( OBJECT_SELF, "#LASTHOTSPOT", GetLocation( OBJECT_SELF ) );
            SetLocalFloat( OBJECT_SELF, "#LASTAMANGLE", VectorToAngle( vT ) );
            vT = GetPosition( OBJECT_SELF ) - vT;
            lLoc = Location( GetArea( OBJECT_SELF ), vT, VectorToAngle( vT ) );
            DoMoveToLocation( lLoc, TRUE );
            return TRUE;
        }
    }
    return FALSE;
}
*/
int DoAvoidMelee()
{
	vector vT;
	location lD;
	
	if ( GetHasSpellEffect( SPELL_TIME_STOP, OBJECT_SELF ) == TRUE )
	{
		//don't bother if we have time stop, dish out some pain instead
		return FALSE;
	}
	
	if ( GetHostileCount( 5.0 ) )
	{
		vT = ( -1.0 * ( 10.0 + IntToFloat( Random( 20 ) ) ) ) * VectorNormalize( GetHostileVector( 20.0 ) );
		lD = Location( GetArea( OBJECT_SELF ), vT + GetPosition( OBJECT_SELF ), VectorToAngle( vT ) );
		if ( GetLocalInt( OBJECT_SELF, "#LASTDEST" ) )
		{
			if ( GetDistanceBetweenLocations( GetLocalLocation( OBJECT_SELF, "#LASTDEST" ), GetLocation( OBJECT_SELF ) ) < 5.0 )
			{
				//close enough to target location
				DeleteLocalInt( OBJECT_SELF, "#LASTDEST" );
				DeleteLocalLocation( OBJECT_SELF, "#LASTDEST" );
			}
			else if ( GetDistanceBetweenLocations( GetLocalLocation( OBJECT_SELF, "#LASTDEST" ), GetLocation( OBJECT_SELF ) ) > 5.0 )
			{
				//didn't make it close enough to our last desired location for some reason
				vT = ( 10.0 + IntToFloat( Random( 20 ) ) ) * VectorNormalize( AngleToVector( VectorToAngle( vT ) - 90.0 + 180 * IntToFloat( Random( 2 ) ) ) );
				lD = Location( GetArea( OBJECT_SELF ), vT + GetPosition( OBJECT_SELF ), VectorToAngle( vT ) );
			}
		}
		SetLocalInt( OBJECT_SELF, "#LASTDEST", 1 );
		SetLocalLocation( OBJECT_SELF, "#LASTDEST", lD );
		DoMoveToLocation( lD, TRUE );
		/*XXX this queueing now handled by main combat loop
		ActionDoCommand( DelayCommand( 1.0, DoCombat() ) );
		*/
		return TRUE;
	}
	return FALSE;
}
		
int DoTimeStop()
{
    int iEnemy = GetHostileCount( 40.0f );

    if ( iEnemy )
    {
        if ( GetHasSpell( SPELL_TIME_STOP ) && !GetHasSpellEffect( SPELL_TIME_STOP ) )
        {
            //ActionCastSpellAtObject( SPELL_TIME_STOP, OBJECT_SELF );
            NO_ActionCastSpellAtObject( SPELL_TIME_STOP, OBJECT_SELF );
            return TRUE;
        }
    }
    return FALSE;
}

int DoVision()
{
	object oS;
	int iCnt = 1;
	int iSpell = 0;
	
	if ( !GetHasVisionSpells() )
	{
		return FALSE;
	}
	
	oS = GetVisionDeprived( 20.0 );
	if ( GetIsObjectValid( oS ) )
	{
		iSpell = GetVisionSpellNeeded( oS );
		if ( iSpell )
		{
			//oS should be creature that needs vision, iSpell is the spell needed
			SetLocalObject( oS, "#VISION", OBJECT_SELF );
			DelayCommand( 6.0, DeleteLocalObject( oS, "#VISION" ) );
			//ActionCastSpellAtObject( iSpell, oS );
			NO_ActionCastSpellAtObject( iSpell, oS );
			return TRUE;
		}
	}
	return FALSE;
}

int DoBreathWeapon()
{
    int iSpell = GetBreathWeapon();
    int iHostiles;
    float fRad = RADIUS_SIZE_LARGE;
    float fMinSearch = 2.5;
    float fMaxSearch = fRad;
    vector vT;
    object oT;
    location lMark;
    int iMeta= METAMAGIC_ANY;
    int iCheat = TRUE;
	
	if ( iSpell && GetLocalInt( OBJECT_SELF, "#BDEL" ) == 0 )
	{
		iHostiles = GetHostileCount( 50.0 );
		if ( iHostiles == 1 )
		{
			//we have a breath weapon ready to go, single target
			oT = GetTarget();
			if ( oT != OBJECT_INVALID )
			{
				//ActionCastSpellAtObject( iSpell, oT, iMeta, iCheat );
				NO_ActionCastSpellAtObject( iSpell, oT, iMeta, iCheat, 0, PROJECTILE_PATH_TYPE_DEFAULT, FALSE, FALSE );
				SetLocalInt( OBJECT_SELF, "#BDEL", 1 );
				DelayCommand( RoundsToSeconds( d4() ), DeleteLocalInt( OBJECT_SELF, "#BDEL" ) );
				return TRUE;
			}
		}
		else if ( iHostiles > 1 )
    	{
    		//we have a breath weapon ready to go, multiple targets
        	vT = GetAreaTarget( iSpell, fRad, fMinSearch, fMaxSearch );
        	if ( VectorMagnitude( vT ) > 0.0 )
        	{
	            lMark = Location( GetArea( OBJECT_SELF ), GetPosition( OBJECT_SELF ) + vT, GetFacing( OBJECT_SELF ) );
        		//ActionCastSpellAtLocation( iSpell, lMark );
        		NO_ActionCastSpellAtLocation( iSpell, lMark, iMeta, iCheat, PROJECTILE_PATH_TYPE_DEFAULT, FALSE, FALSE );
        		SetLocalInt( OBJECT_SELF, "#BDEL", 1 );
        		DelayCommand( RoundsToSeconds( d4() ), DeleteLocalInt( OBJECT_SELF, "#BDEL" ) );
        		return TRUE;
        	}
        }
    }
    return FALSE;
}

int DoTurning()
{
    int iTurn = GetHasFeat( FEAT_TURN_UNDEAD );
    vector vT = Vector( 0.0, 0.0, 0.0 );
    location lLoc;

    if ( !iTurn )
    {
        return FALSE;
    }

    vT = GetTurningVector();
    if ( VectorMagnitude( vT ) > 0.0 )
    {
        vT = GetPosition( OBJECT_SELF ) + vT;
        lLoc = Location( GetArea( OBJECT_SELF ), vT, VectorToAngle( vT ) );
        DoMoveToLocation( lLoc, TRUE );
        ActionUseFeat( FEAT_TURN_UNDEAD, OBJECT_SELF );
        return TRUE;
    }
    return FALSE;
}

int DoSpellCone()
{
	//moving this over to DoSpellArea()
	/*
    object oT;
    int iSpell, iMinLvl;
    float fRad = 10.0;
    vector vT, vU, vX;
    location lLoc, lMove;

    if ( GetIsObjectValid( oT = GetTarget() ) )
    {
        iMinLvl = GetAverageEnemyLevel() / 3;
    }

    if ( iSpell = GetConeSpell( iMinLvl ) )
    {
        vU = GetPosition( OBJECT_SELF );
        vT = GetAreaTarget( iSpell, fRad );
        if ( VectorMagnitude( vT ) > 0.0 )
        {
            //extend slightly past center of area
            vT = vT + 2.5 * VectorNormalize( vT );
            lLoc = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
            ActionCastSpellAtLocation( iSpell, lLoc );
            return TRUE;
        }
    }
    */
    return FALSE;
}

int DoHealSelf()
{
    //Testing via talents, having to go via random seems kind of wasteful
    //Look for another way to do this
    int iHeal;
    int iHealTalent;
    int iCR;
    int iFeat;
    int iDam;
    int iP;
    int iLimit = 0;
    talent tHeal, tP;
    object oP, oHeal, oHealer;
    event evHeal;

    oHealer = GetLocalObject( OBJECT_SELF, "#HEALER" );
    if ( GetIsObjectValid( oHealer ) )
    {
        if ( GetDistanceBetween( OBJECT_SELF, oHealer ) < 10.0 )
        {
            //got a healer who is close, don't double up, healing is on the way
            return FALSE;
        }
    }

    iDam = GetMaxHitPoints() - GetCurrentHitPoints();
    if ( iDam == 0 )
    {
        return FALSE;
    }
    //see if wholeness of body is an option
    if ( GetHasFeat( FEAT_WHOLENESS_OF_BODY ) )
    {
        iFeat = FEAT_WHOLENESS_OF_BODY;
        if ( iDam < GetLevelByClass( CLASS_TYPE_MONK ) * 2 )
        {
            iFeat = 0;
        }
    }
    
    //iCR = 20;
    iCR = 10;
    iHealTalent = 0;
    if ( GetRacialType( OBJECT_SELF ) != RACIAL_TYPE_UNDEAD ) //undead do not use healing potions
    {
    	//while ( iCR && GetIsTalentValid( tP = GetCreatureTalentBest( TALENT_CATEGORY_BENEFICIAL_HEALING_POTION, iCR-- ) ) )
    	while ( iCR-- && GetIsTalentValid( tP = GetCreatureTalentRandom( TALENT_CATEGORY_BENEFICIAL_HEALING_POTION ) ) )
    	{
	        if ( iP = GetTalentPotionHealAmount( tP ) )
        	{
	            if ( iP > iHealTalent && iP < iDam )
            	{
	                tHeal = tP;
                	iHealTalent = iP;
            	}
        	}
    	}
    }
    if ( iFeat )
    {
        ActionUseFeat( iFeat, OBJECT_SELF );
        SetLocalObject( OBJECT_SELF, "#HEALER", OBJECT_SELF );
        DelayCommand( 4.0, DeleteLocalObject( OBJECT_SELF, "#HEALER" ) );
        return TRUE;
    }
    if ( iHealTalent && GetIsTalentValid( tHeal ) )
    {
        ActionUseTalentOnObject( tHeal, OBJECT_SELF );
        SetLocalObject( OBJECT_SELF, "#HEALER", OBJECT_SELF );
        DelayCommand( 4.0, DeleteLocalObject( OBJECT_SELF, "#HEALER" ) );
        return TRUE;
    }
    return FALSE;
}

int DoSpellGroupEnhance()
{
    int iSpell, iMinLvl;
    float fRad = 0.0;
    vector vT, vU, vX;
    location lLoc, lMove;
    object oT;

    if ( GetIsObjectValid( oT = GetTarget() ) )
    {
        iMinLvl = GetAverageEnemyLevel() / 3;
    }

	//try feats first
	if ( iSpell = GetGroupEnhanceFeat() )
	{
		fRad = GetGroupEnhanceFeatRadius( iSpell );
		vU = GetPosition( OBJECT_SELF );
		vT = GetFriendlyAreaTarget( fRad, iSpell, 1 );
		if ( VectorMagnitude( vT ) > 0.0 )
		{
			lLoc = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
			DoMoveToLocation( lLoc, TRUE );
			ActionUseFeat( iSpell, OBJECT_SELF );
			return TRUE;
		}
	}
	//then spells
    if ( iSpell = GetGroupEnhanceSpell( iMinLvl ) )
    {
        fRad = GetGroupEnhanceSpellRadius( iSpell );
        vU = GetPosition( OBJECT_SELF );
        vT = GetFriendlyAreaTarget( fRad, iSpell );
        if ( VectorMagnitude( vT ) > 0.0 )
        {
            lLoc = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
            //ActionCastSpellAtLocation( iSpell, lLoc );
            NO_ActionCastSpellAtLocation( iSpell, lLoc );
            return TRUE;
        }
    }
    return FALSE;
}

int DoSpellGroupHeal()
{
    int iSpell, iMinLvl, iHeal;
    float fRad = 0.0;
    vector vT, vU, vX;
    location lLoc, lMove;
    float fDam;
    object oT;

    if ( GetIsObjectValid( oT = GetTarget() ) )
    {
        iMinLvl = GetAverageEnemyLevel() / 3;
    }

    if ( iSpell = GetGroupHealSpell( iMinLvl ) )
    {
        iHeal = GetGroupHealSpellAmount( iSpell );
        fRad = GetGroupHealSpellRadius( iSpell );
        vU = GetPosition( OBJECT_SELF );
        vT = GetAreaHealTarget( fRad, iHeal );
        lLoc = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
        fDam = GetAllyDamageStats( lLoc, fRad );
        if ( VectorMagnitude( vT ) > 0.0 && IntToFloat( iHeal ) < fDam )
        {
            //ActionCastSpellAtLocation( iSpell, lLoc );
            NO_ActionCastSpellAtLocation( iSpell, lLoc );
            return TRUE;
        }
    }
    return FALSE;
}

int DoDispelPersAOE()
{
    vector vT, vU;
    location lT;
    int iD;

    if ( ( iD = GetDispelSpell() ) == -1 )
    {
        //no dispel capability available
        return FALSE;
    }

    if ( GetHostileAOECount( 30.0 ) )
    {
        vU = GetPosition( OBJECT_SELF );
        vT = GetHostileAOEVector( 30.0 );
        lT = Location( GetArea( OBJECT_SELF ), vU + vT, VectorToAngle( vT ) );
        //ActionCastSpellAtLocation( iD, lT );
        NO_ActionCastSpellAtLocation( iD, lT );
        return TRUE;
    }
    return FALSE;
}

int DoDispelSingle()
{
    object oTarget;
    int iAverage = 0;
    int iSpell = 0;
    int iCL = GetMaxDispelCasterLevel();

    if ( GetIsObjectValid( oTarget = GetMostBuffedEnemy( 30.0 ) ) )
    {
        iAverage = GetAverageEffectCasterLevel( oTarget );
        if ( ( iSpell = GetBestDispel( iAverage, iCL ) ) )
        {
        	//PrintString( "DISPEL: " + GetName( OBJECT_SELF ) + " " + IntToString( iSpell ) + " " + GetName( oTarget ) );
            //ActionCastSpellAtObject( iSpell, oTarget );
            NO_ActionCastSpellAtObject( iSpell, oTarget );
            return TRUE;
        }
    }
    return FALSE;
}

int DoDismissal()
{
	int iSpell;
	float fRad = RADIUS_SIZE_COLOSSAL;
	object oT;
	vector vT;
	location lT;
		
	//banishment and dismissal is next optimal choices for enemy summons
	iSpell = GetHasSpell( SPELL_BANISHMENT, OBJECT_SELF ) ? SPELL_BANISHMENT : SPELL_DISMISSAL;
	if ( GetHasSpell( iSpell, OBJECT_SELF ) )
	{
		vT = GetEnemySummonedAssociatesVector( fRad );
		if ( VectorMagnitude( vT ) > 0.0 )
		{
			lT = Location( GetArea( OBJECT_SELF ), GetPosition( OBJECT_SELF ) + vT, VectorToAngle( vT ) );
			//ActionCastSpellAtLocation( iSpell, lT );
			NO_ActionCastSpellAtLocation( iSpell, lT );
			return TRUE;
		}
	}
	//next check is to look for Outsiders and Elementals and clear them out with Word of Faith
	//this will pick up against summoned and non-summoned planars
	iSpell = SPELL_WORD_OF_FAITH;
	if ( GetHasSpell( iSpell, OBJECT_SELF ) )
	{
		vT = GetEnemyPlanarVector( fRad );
		if ( VectorMagnitude( vT ) > 0.0 )
		{
			lT = Location( GetArea( OBJECT_SELF ), GetPosition( OBJECT_SELF ) + vT, VectorToAngle( vT ) );
			//ActionCastSpellAtLocation( iSpell, lT );
			NO_ActionCastSpellAtLocation( iSpell, lT );
			return TRUE;
		}
	}
	//no dismissal, look for a summoned associate owner to use dispel magic on if we have it
	if ( iSpell = GetDispelSpell() )
	{
		//we have a dispel
		if ( GetIsObjectValid( oT = GetStrongestEnemySummonedAssociateOwner( 30.0 ) ) )
		{
			//ActionCastSpellAtObject( iSpell, oT );
			NO_ActionCastSpellAtObject( iSpell, oT );
			return TRUE;
		}
	}
	return FALSE;
}

int DoFastBuffs()
{
	int iSpell = 0;
	int iCnt;
	
	if ( !GetLocalInt( OBJECT_SELF, "#FASTBUFFER" ) || GetLocalInt( OBJECT_SELF, "#FASTBUFFED" ) )
	{
		return FALSE;
	}
	
	iCnt = GenerateFastBuffList();
	
	while ( iCnt )
	{
		iSpell = GetLocalInt( OBJECT_SELF, "#SPL_FB" + IntToString( iCnt ) );
		ActionCastSpellAtObject( iSpell, OBJECT_SELF, METAMAGIC_ANY, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE );
		DeleteLocalInt( OBJECT_SELF, "#SPL_FB" + IntToString( iCnt-- ) );
	}
	
	if ( iSpell )
	{
		return TRUE;
	}
	return FALSE;
}

/*
int DoFlee()
{
    vector vT;
    location lLoc;

    //if we have time stop running use the time to flee

    //if ( GetAttackerCount( 5.0 ) )
    if ( GetHostileCount( 5.0 ) )
    {
        vT = GetHostileEvacVector( GetHostileVector( 15.0 ) );
        if ( VectorMagnitude( vT ) > 5.0 )
        {
        	SetLocalLocation( OBJECT_SELF, "#LASTHOTSPOT", GetLocation( OBJECT_SELF ) );
            SetLocalFloat( OBJECT_SELF, "#LASTAMANGLE", VectorToAngle( vT ) );
            vT = GetPosition( OBJECT_SELF ) - vT;
            lLoc = Location( GetArea( OBJECT_SELF ), vT, VectorToAngle( vT ) );
            DoMoveToLocation( lLoc, TRUE );
            return TRUE;
        }
    }
    return FALSE;
}
*/

int DoMeleeAssist()
{
	object oF, oE, oT, oTarget;
	int iCnt1, iCnt2, iF, iE, iChat, iSpecial;
	float fAssistRange = 30.0;
	float fMeleeRange = 3.0;
	float fRatio, fT, fDist, fDistMin;
	
	oT = OBJECT_INVALID;
	fT = 1.0;
	//look for a friend who needs assistance in melee
	iCnt1 = 0;
	oF = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, ++iCnt1, CREATURE_TYPE_IS_ALIVE, TRUE );
	while ( GetIsObjectValid( oF ) && GetDistanceBetween( OBJECT_SELF, oF ) < fAssistRange )
	{
		if ( GetObjectSeen( oF ) || GetObjectHeard( oF ) )
		{
			iF = GetHitDice( oF ) + GetAllyHD( fMeleeRange, oF );
			iE = GetHostileHD( fMeleeRange, oF );
			fRatio = IntToFloat( iE ) / IntToFloat( iF );
			if ( fRatio > fT )
			{
				fT = fRatio;
				oT = oF;
			}
		}
		oF = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, ++iCnt1, CREATURE_TYPE_IS_ALIVE, TRUE );
	}
	
	if ( GetIsObjectValid( oT ) )
	{
		//found a friend in trouble, stored highest enemy concentration to aid friend
		//should be closest friend in a given melee
		//get nearest enemy fighting friend
		fDistMin = 100.0;
		iCnt1 = 0;
		oTarget = OBJECT_INVALID;
		oE = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oT, ++iCnt1, CREATURE_TYPE_IS_ALIVE, TRUE );
		while ( GetIsObjectValid( oE ) )
		{
			if ( GetObjectSeen( oE ) || GetObjectHeard( oE ) )
			{
				if ( ( fDist = GetDistanceBetween( OBJECT_SELF, oE ) ) < fDistMin )
				{
					oTarget = oE;
					fDistMin = fDist;
				}
			}
			oE = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oT, ++iCnt1, CREATURE_TYPE_IS_ALIVE, TRUE );
		}
		//should now have a valid enemy to target, closest to me in target group
		if ( GetIsObjectValid( oTarget ) )
		{
			//SpeakString( "Assisting " + GetName( oT ) );
			DoEquipMelee( oTarget );
        	DoVoiceChat( NO_VC_MELEEASSIST );
        	if ( iSpecial = GetBestMeleeSpecial( oTarget ) )
        	{
	            //use special attack type
            	ActionUseFeat( iSpecial, oTarget );
        	}
        	else
        	{
	            ActionAttack( oTarget );
        	}
        	return TRUE;
        }
	}
	
	//either nobody needs help or I couldn't find a target
	return FALSE;
}

int DoFlank( object oE=OBJECT_INVALID )
{
	object oF = OBJECT_INVALID;
	location lL = GetLocation( OBJECT_SELF );
	int iCnt = 0;
	
	if ( GetIsObjectValid( oE ) )
	{
		oF = oE;
	}
	else
	{
		oE = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, ++iCnt, CREATURE_TYPE_IS_ALIVE, TRUE );
		while ( GetIsObjectValid( oE ) && !GetIsObjectValid( oF ) )
		{
			if ( GetObjectSeen( oE, OBJECT_SELF ) || GetObjectHeard( oE, OBJECT_SELF ) )
			{
				if ( GetDistanceBetween( oE, OBJECT_SELF ) > 10.0 )
				{
					//enemy is seen or heard and more than 10.0 away, flank it
					oF = oE;
				}
				else
				{
					//creature is within minimum range, abort flank
					break;
				}
			}
			oE = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, ++iCnt, CREATURE_TYPE_IS_ALIVE, TRUE );
		}
	}
	if ( GetIsObjectValid( oF ) )
	{
		lL = GetFlankLoc( oF, lL );
		DoMoveToLocation( lL, TRUE );
		/*XXX
		ActionDoCommand( DelayCommand( 1.0, DoCombat() ) );
		*/
		return TRUE;
	}
	return FALSE;
}

int DoMindBlast()
{
	vector vT;
	object oT;
	int iSpell = -69;
	int iEnemies = GetHostileCount( 40.0 );
	int iEff;
	float fRad = 15.0;
	float fMinSrch = 0.0;
	float fMaxSrch = 30.0;
	location lMark;
	
	if ( iEnemies == 1 )
	{
		oT = GetTarget();
		if ( GetIsObjectValid( oT ) )
		{
			iEff = GetEffectsOnObject( oT );
			if ( ( iEff & NO_EFFECT_STUNNED ) + ( iEff & NO_EFFECT_PARALYZE ) + ( iEff & NO_EFFECT_DAZED ) == 0 )
			{
				//target not stunned, paralyzed or dazed
				//should switch to direct targetting once function is available
				//ActionMindBlast( oT, GetLocation( oT ) );
				//ActionCastSpellAtObject( 1522, oT, METAMAGIC_ANY, TRUE );
				ActionCastSpellAtObject( CODI_MIND_BLAST, oT, METAMAGIC_ANY, TRUE );
				return TRUE;
			}
		}
	}
	else if ( iEnemies > 1 )
	{
		vT = GetAreaTarget( iSpell, fRad, fMinSrch, fMaxSrch );
		if ( VectorMagnitude( vT ) > 0.0 )
		{
			lMark = Location( GetArea( OBJECT_SELF ), GetPosition( OBJECT_SELF ) + vT, VectorToAngle( vT ) );
			//ActionMindBlast( OBJECT_INVALID, lMark );
			//ActionCastSpellAtLocation( 1522, lMark, METAMAGIC_ANY, TRUE );
			ActionCastSpellAtLocation( CODI_MIND_BLAST, lMark, METAMAGIC_ANY, TRUE );
			return TRUE;
		}
	}
	return FALSE;
}

int DoBrainExtraction()
{
	float fScanRange = 30.0;
	float fImmRange = 10.0;
	object oT = GetNearestAddledEnemyNoExtractor( fScanRange );
	int iCnt = 0;
	int iC = 0;
	int iH = 0;	
	int iEff;
	int iDo = TRUE;

	if ( !GetIsObjectValid( oT ) )
	{
		//look for non-petrified enemy close by
		oT = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, ++iCnt, CREATURE_TYPE_IS_ALIVE, TRUE, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN );
		while ( GetIsObjectValid( oT ) && GetDistanceBetween( OBJECT_SELF, oT ) < fImmRange )
		{
			if ( IsBrainExtractable( oT ) )
			{
				break;
			}
			oT = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, ++iCnt, CREATURE_TYPE_IS_ALIVE, TRUE, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN );
		}
		//double check we aren't looking at a petrified enemy
		if ( GetIsObjectValid( oT ) )
		{
			if ( !IsBrainExtractable( oT ) )
			{
				oT = OBJECT_INVALID;
			}
		}
		//see if we have decent chances of extracting
		if ( GetIsObjectValid( oT ) )
		{
			iDo = FALSE;
			int iAttNorm = GetCreatureAttackBonus( OBJECT_SELF );
			int iDefAC = GetAC( oT ) - 10;
			if ( iAttNorm > iDefAC )
			{
				int iAttGrap = GetGrappleBonus( OBJECT_SELF );
				int iDefGrap = GetGrappleBonus( oT );
				if ( iAttGrap > iDefGrap )
				{
					//we will probably hit and probably win a grapple
					iDo = TRUE;
				}
			}
		}
	}
	if ( GetIsObjectValid( oT ) && iDo )
	{
		//got a target, go for the brains
		SetLocalInt( oT, "#EXTRACTING", 1 );
		DelayCommand( 12.0, DeleteLocalInt( oT, "#EXTRACTING" ) ); //death redundancy
		if ( GetDistanceBetween( OBJECT_SELF, oT ) > 10.0 )
		{
			DoMoveToObject( oT, TRUE, 2.1 );
		}
		//ActionHeadSuck( oT );
		//ActionCastSpellAtObject( 1523, oT, METAMAGIC_ANY, TRUE );
		ActionCastSpellAtObject( CODI_BRAIN_EXTRACT, oT, METAMAGIC_ANY, TRUE );
		ActionDoCommand( DeleteLocalInt( oT, "#EXTRACTING" ) );
		return TRUE;
	}
	return FALSE;
}

int DoAvoidEnemies()
{
	object oE = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_IS_ALIVE, TRUE, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN );
	vector vE;
	vector vT;
	location lL;
	
	if ( GetIsObjectValid( oE ) && GetDistanceBetween( OBJECT_SELF, oE ) < 15.0 )
	{
		vE = GetHostileVector( 20.0 );
		vT = -10.0 * VectorNormalize( vE );
		lL = Location( GetArea( OBJECT_SELF ), GetPosition( OBJECT_SELF ) + vT, VectorToAngle( vT ) );
		DoMoveToLocation( lL, TRUE );
		/*XXX
		ActionDoCommand( DelayCommand( 1.0, DoCombat() ) );
		*/
		return TRUE;
	}
	else
	{
		vE = GetHostileVector( 20.0 );
		if ( VectorMagnitude( vE ) > 0.0 )
		{
			vT = -10.0 * VectorNormalize( vE );
		}
		else
		{
			vT = 5.0 * VectorNormalize( AngleToVector( IntToFloat( Random( 360 ) ) ) );
		}
		lL = Location( GetArea( OBJECT_SELF ), GetPosition( OBJECT_SELF ) + vT, VectorToAngle( vT ) );
		DoMoveToLocation( lL, TRUE );
		/*XXX
		ActionDoCommand( DelayCommand( 1.0, DoCombat() ) );
		*/
		return TRUE;
	}
	//should never get down here
	return FALSE;
}

int DoGrenade()
{
	return FALSE;
}

int DoBeholderRays()
{
	float fM = GetFacing( OBJECT_SELF );
	float fH;
	float fS = 5.5 / BEHOLDER_MAX_RAYS;
	int iRep = 95; //chance to use multiple rays on one target if enemies < rays
	int iH = GetHostileCount( 40.0 );
	int iC = 0;
	int iQ1 = 0;
	int iQ2 = 0;
	int iQ3 = 0;
	int iQ4 = 0;
	int iR = 0;
	int iS;
	int iM;
	object oT;
	
	InitializeBeholderRaySelection();
	
	if ( GetHasSpellEffect( SPELL_HASTE, OBJECT_SELF ) || GetHasSpellEffect( SPELL_MASS_HASTE, OBJECT_SELF ) && GetHasFeatEffect( FEAT_EPIC_BLINDING_SPEED, OBJECT_SELF ) )
	{
		fS *= 0.5;
	}
	
	oT = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, ++iC, CREATURE_TYPE_IS_ALIVE, TRUE );
	while ( iC <= iH && iR < BEHOLDER_MAX_RAYS )
	{
		iM = TRUE; //default is that we will not target multiple rays at individual targets
		if ( GetIsObjectValid( oT ) == FALSE )
		{
			//run out of targets unexpectedly
			break;
		}
		else if ( GetIsPerceived( oT, NO_PERCEPTION_SEEN ) == FALSE && GetIsPerceived( oT, NO_PERCEPTION_HEARD ) == FALSE )
		{
			//can't see or hear this creature, ignore it
			iC++;
		}
		else
		{
			SetLocalInt( OBJECT_SELF, "#EYERAYS", 1 ); //flag the beholder as being busy shooting eye rays
			//oT is valid, we can see or hear it
			fH = VectorToAngle( GetPosition( oT ) - GetPosition( OBJECT_SELF ) );
			fH = fH < fM ? fH + 360.0 : fH;
			fH -= fM;
			if ( fH > 315.0 || fH < 45.0 )
			{
				if ( iQ1 < 3 && GetIsCentralEyeOpen() == FALSE )
				{
					//first quadrant target, still have rays available in this quadrant
					iS = MatchRayToTarget( oT );
					if ( iS != 0 )
					{
						iQ1++;
						iR++;
						RemoveBeholderRayFromSelection( iS );
						DelayCommand( fS * iR, DoFireBeholderRay( iS, oT ) );
						if ( iH - iC < BEHOLDER_MAX_RAYS - iR ) //we have more rays left to fire than enemies remaining to fire them at
						{
							if ( iQ1 < 3 && Random( 100 ) < iRep ) //we have rays left in this quadrant, random chance to shoot another at this target
							{
								iM = FALSE; //we will shoot more rays at this target, not moving on
							}
						}
					}
				}
			}
			else if ( fH < 135.0 )
			{
				if ( iQ2 < 3 )
				{
					//second quadrant target, still have rays available in this quadrant
					iS = MatchRayToTarget( oT );
					if ( iS != 0 )
					{
						iQ2++;
						iR++;
						RemoveBeholderRayFromSelection( iS );
						DelayCommand( fS * iR, DoFireBeholderRay( iS, oT ) );
						if ( iH - iC < BEHOLDER_MAX_RAYS - iR ) //we have more rays left to fire than enemies remaining to fire them at
						{
							if ( iQ2 < 3 && Random( 100 ) < iRep ) //we have rays left in this quadrant, random chance to shoot another at this target
							{
								iM = FALSE; //we will shoot more rays at this target, not moving on
							}
						}
					}
				}
			}
			else if ( fH < 225.0 )
			{
				if ( iQ3 < 3 )
				{
					//third quadrant target, still have rays available in this quadrant
					iS = MatchRayToTarget( oT );
					if ( iS != 0 )
					{
						iQ3++;
						iR++;
						RemoveBeholderRayFromSelection( iS );
						DelayCommand( fS * iR, DoFireBeholderRay( iS, oT ) );
						if ( iH - iC < BEHOLDER_MAX_RAYS - iR ) //we have more rays left to fire than enemies remaining to fire them at
						{
							if ( iQ3 < 3 && Random( 100 ) < iRep ) //we have rays left in this quadrant, random chance to shoot another at this target
							{
								iM = FALSE; //we will shoot more rays at this target, not moving on
							}
						}
					}
				}
			}
			else if ( iQ4 < 3 )
			{
				//fourth quadrant target, still have rays available in this quadrant
				iS = MatchRayToTarget( oT );
				if ( iS != 0 )
				{
					iQ4++;
					iR++;
					RemoveBeholderRayFromSelection( iS );
					DelayCommand( fS * iR, DoFireBeholderRay( iS, oT ) );
					if ( iH - iC < BEHOLDER_MAX_RAYS - iR ) //we have more rays left to fire than enemies remaining to fire them at
					{
						if ( iQ4 < 3 && Random( 100 ) < iRep ) //we have rays left in this quadrant, random chance to shoot another at this target
						{
							iM = FALSE; //we will shoot more rays at this target, not moving on
						}
					}
				}
			}
		}
		//we're either getting the same target again or moving onto the next target
		iC += iM; //if iM is true we are moving onto next target, iC is incremented, otherwise it stays where it is
		oT = GetNearestCreature( CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, iC, CREATURE_TYPE_IS_ALIVE, TRUE );
	}
	DelayCommand( fS * ( iR + 1 ), DeleteLocalInt( OBJECT_SELF, "#EYERAYS" ) );
	
	ClearBeholderRaySelection();
	
	if ( iR > 0 )
	{
		//we have fired some rays
		return TRUE;
	}
	//we have not fired any rays
	return FALSE;
}

int DoBeholderCentralEye()
{
    int iSpell = SPELL_BEH_ANTIMAGIC_CONE;
    int iHostiles;
    float fRad = RADIUS_SIZE_MEDIUM;
    float fMinSearch = 2.5;
    float fMaxSearch = fRad;
    vector vT;
    object oT;
    location lMark;
    int iMeta= METAMAGIC_ANY;
    int iCheat = TRUE;
	
	if ( iSpell && GetLastAction() != "+CENTRALEYE" )
	{
		iHostiles = GetHostileCount( 50.0 );
		if ( iHostiles == 1 )
		{
			//we have a breath weapon ready to go, single target
			oT = GetTarget();
			if ( oT != OBJECT_INVALID )
			{
				//ActionCastSpellAtObject( iSpell, oT, iMeta, iCheat );
				NO_ActionCastSpellAtObject( iSpell, oT, iMeta, iCheat, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE, FALSE );
				SetIsCentralEyeOpen( TRUE );
				DoBeholderRays();
				SetIsCentralEyeOpen( FALSE );
				return TRUE;
			}
		}
		else if ( iHostiles > 1 )
    	{
    		//we have a breath weapon ready to go, multiple targets
        	vT = GetAreaTarget( iSpell, fRad, fMinSearch, fMaxSearch );
        	if ( VectorMagnitude( vT ) > 0.0 )
        	{
	            lMark = Location( GetArea( OBJECT_SELF ), GetPosition( OBJECT_SELF ) + vT, GetFacing( OBJECT_SELF ) );
        		//ActionCastSpellAtLocation( iSpell, lMark );
        		NO_ActionCastSpellAtLocation( iSpell, lMark, iMeta, iCheat, PROJECTILE_PATH_TYPE_DEFAULT, TRUE, FALSE );
        		SetIsCentralEyeOpen( TRUE );
        		DoBeholderRays();
        		SetIsCentralEyeOpen( FALSE );
        		return TRUE;
        	}
        }
    }
    return FALSE;
}