/*

    Henchman Inventory And Battle AI

    This file contains miscellaneous functions used by
    many scripts.

*/

// void main() {    }

#include "nw_i0_generic"
#include "x0_i0_voice"
#include "hench_i0_strings"


// As MyPrintString, but to screen instead of log
void Jug_Debug(string sString);

// returns TRUE if any one of the two effect types are present
int GetAnyEffect2(int nEffectType1, int nEffectType2, object oTarget = OBJECT_SELF);

// returns TRUE if any one of the three effect types are present
int GetHasAnyEffect3(int nEffectType1, int nEffectType2, int nEffectType3, object oTarget = OBJECT_SELF);

// returns TRUE if racial type is a humanoid
int GetIsHumanoid(int nRacial);

// returns TRUE if creature can use items from their inventory
int GetCreatureUseItems(int nRace);

// returns TRUE if the item property is present in any one of the equipped items
int GetCreatureHasItemProperty(int nItemProperty, object oCreature = OBJECT_SELF);

// set array access for objects
void SetObjectArray(object oSource, string sName, int iElem, object oElem);

// get array access for objects
object GetObjectArray(object oSource, string sName, int iElem);

// set array access for ints
void SetIntArray(object oSource, string sName, int iElem, int iState);

// get array access for ints
int GetIntArray(object oSource, string sName, int iElem);

// set array access for floats
void SetFloatArray(object oSource, string sName, int iElem, float fVal);

// get array access for floats
float GetFloatArray(object oSource, string sName, int iElem);

// returns TRUE if object1 and object are on opposite sides of door
int IsOnOppositeSideOfDoor(object oDoor, object obj1, object obj2);

// stores primary ability for given class for later use (checks if drained)
void CheckClassAbility(int iClass);

// stores primary abilities for later use (checks if drained)
void InitializeAbilityCheck();

// stores the last spell cast (no timeout)
void HenchSetLastGenericSpellCast(int nSpell);

// returns the highest level master of oAssociate
// returns OBJECT_INVALID if no master
object GetRealMaster(object oAssociate = OBJECT_SELF);

// returns the highest level master of oAssociate
// returns OBJECT_SELF if no master
object GetTopMaster(object oAssociate = OBJECT_SELF);

// converts a prestige class into the best matching primary class
int HenchConvertClass(int nClass1/*, int class2, int class3*/);

// returns the class type that best represents oCharacter
int HenchDetermineClassToUse(object oCharacter = OBJECT_SELF);



// This section is for non SoU compatibility
// uncomment the section for 1.30

/*
const int X0_COMBAT_FLAG_RANGED            = 0x00000001;
const int X0_COMBAT_FLAG_DEFENSIVE         = 0x00000002;
const int X0_COMBAT_FLAG_COWARDLY          = 0x00000004;
const int X0_COMBAT_FLAG_AMBUSHER          = 0x00000008;
const int NW_ASC_MODE_DYING                = 0x00010000;
const int NW_WALK_FLAG_CONSTANT            = 0x00000002;

int GetCombatCondition(int val) { return FALSE; }
int SpecialTacticsCowardly(object target) { return FALSE; }


int MatchSingleHandedWeapon(object oItem)
{
    switch (GetBaseItemType(oItem)) {
        case BASE_ITEM_BATTLEAXE:
        case BASE_ITEM_CLUB:
        case BASE_ITEM_DAGGER:
        case BASE_ITEM_HANDAXE:
        case BASE_ITEM_KAMA:
        case BASE_ITEM_KATANA:
        case BASE_ITEM_KUKRI:
        case BASE_ITEM_LIGHTFLAIL:
        case BASE_ITEM_HEAVYFLAIL:
        case BASE_ITEM_LIGHTHAMMER:
        case BASE_ITEM_LIGHTMACE:
        case BASE_ITEM_LONGSWORD:
        case BASE_ITEM_MORNINGSTAR:
        case BASE_ITEM_BASTARDSWORD:
        case BASE_ITEM_RAPIER:
        case BASE_ITEM_SICKLE:
        case BASE_ITEM_SCIMITAR:
        case BASE_ITEM_SHORTSWORD:
        case BASE_ITEM_WARHAMMER: return TRUE;
        break;

        default: break;
     }
     return FALSE;
}

// TRUE if the item is a double-handed weapon
int MatchDoubleHandedWeapon(object oItem)
{
    switch (GetBaseItemType(oItem)) {
    case BASE_ITEM_DIREMACE:
    case BASE_ITEM_DOUBLEAXE:
    case BASE_ITEM_GREATAXE:
    case BASE_ITEM_GREATSWORD:
    case BASE_ITEM_HALBERD:
    case BASE_ITEM_MAGICSTAFF:
    case BASE_ITEM_QUARTERSTAFF:
    case BASE_ITEM_TWOBLADEDSWORD:
        return TRUE;
    }
    return FALSE;
}

// TRUE if the item is a melee weapon
int MatchMeleeWeapon(object oItem)
{
    return (MatchSingleHandedWeapon(oItem) || MatchDoubleHandedWeapon(oItem));
}

const float MELEE_DISTANCE = 3.0;

// Returns TRUE if the given opponent is a melee
// attacker, meaning they are in melee range and 
// equipped with a melee weapon.
int GetIsMeleeAttacker(object oAttacker)
{
    if (GetDistanceToObject(oAttacker) > MELEE_DISTANCE)
        return FALSE;

    object oRight = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oAttacker);
    if (GetIsObjectValid(oRight) && MatchMeleeWeapon(oRight))
        return TRUE;

    object oLeft = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oAttacker);
    if (GetIsObjectValid(oLeft) && MatchMeleeWeapon(oLeft))
        return TRUE;

    return FALSE;
}

// Get whether the specified WalkWayPoints condition is set
int GetWalkCondition(int nCondition, object oCreature=OBJECT_SELF)
{
    return (GetLocalInt(oCreature, "NW_WALK_CONDITION") & nCondition);
}
*/

// end non SoU compatibility



// As MyPrintString, but to screen instead of log
void Jug_Debug(string sString)
{
    SendMessageToPC(GetFirstPC(), sString);
}


int GetAnyEffect2(int nEffectType1, int nEffectType2, object oTarget = OBJECT_SELF)
{
    effect eCheck = GetFirstEffect(oTarget);
    while(GetIsEffectValid(eCheck))
    {
        int nTestEffect = GetEffectType(eCheck);
        if(nTestEffect == nEffectType1)
        {
             return TRUE;
        }
        if(nTestEffect == nEffectType2)
        {
             return TRUE;
        }
        eCheck = GetNextEffect(oTarget);
    }
    return FALSE;
}


int GetHasAnyEffect3(int nEffectType1, int nEffectType2, int nEffectType3, object oTarget = OBJECT_SELF)
{
    effect eCheck = GetFirstEffect(oTarget);
    while(GetIsEffectValid(eCheck))
    {
        int nTestEffect = GetEffectType(eCheck);
        if(nTestEffect == nEffectType1)
        {
             return TRUE;
        }
        if(nTestEffect == nEffectType2)
        {
             return TRUE;
        }
        if(nTestEffect == nEffectType3)
        {
             return TRUE;
        }
        eCheck = GetNextEffect(oTarget);
    }
    return FALSE;
}


int GetIsHumanoid(int nRacial)
{
    return
        (nRacial == RACIAL_TYPE_DWARF) ||
        (nRacial == RACIAL_TYPE_ELF) ||
        (nRacial == RACIAL_TYPE_GNOME) ||
        (nRacial == RACIAL_TYPE_HUMANOID_GOBLINOID) ||
        (nRacial == RACIAL_TYPE_HALFLING) ||
        (nRacial == RACIAL_TYPE_HUMAN) ||
        (nRacial == RACIAL_TYPE_HALFELF) ||
        (nRacial == RACIAL_TYPE_HALFORC) ||
        (nRacial == RACIAL_TYPE_HUMANOID_MONSTROUS) ||
        (nRacial == RACIAL_TYPE_HUMANOID_ORC) ||
        (nRacial == RACIAL_TYPE_HUMANOID_REPTILIAN);
}


int GetCreatureUseItems(int nRace)
{
    if(nRace != RACIAL_TYPE_ABERRATION && nRace != RACIAL_TYPE_ANIMAL && nRace != RACIAL_TYPE_BEAST &&
       nRace != RACIAL_TYPE_CONSTRUCT && nRace != RACIAL_TYPE_DRAGON && nRace != RACIAL_TYPE_ELEMENTAL &&
       nRace != RACIAL_TYPE_MAGICAL_BEAST && nRace != RACIAL_TYPE_OUTSIDER && nRace != RACIAL_TYPE_SHAPECHANGER &&
       nRace != RACIAL_TYPE_VERMIN && nRace != RACIAL_TYPE_INVALID)
    {
        return TRUE;
    }
    return FALSE;
}


int GetCreatureHasItemProperty(int nItemProperty, object oCreature = OBJECT_SELF)
{
    int i;
    for (i = 0; i < NUM_INVENTORY_SLOTS; i++)
    {
        object oItem = GetItemInSlot(i, oCreature);
        if(GetItemHasItemProperty(oItem, nItemProperty))
        {
            return TRUE;
        }
    }
    return FALSE;
}


void SetObjectArray(object oSource, string sName, int iElem, object oElem)
{
    string sFull = sName+IntToString(iElem);
    SetLocalObject(oSource,sFull,oElem);
}

object GetObjectArray(object oSource, string sName, int iElem)
{
    string sFull = sName+IntToString(iElem);
    return GetLocalObject(oSource,sFull);
}

void SetIntArray(object oSource, string sName, int iElem, int iState)
{
    string sFull = sName+IntToString(iElem);
    SetLocalInt(oSource,sFull,iState);
}

int GetIntArray(object oSource, string sName, int iElem)
{
    string sFull = sName+IntToString(iElem);
    return GetLocalInt(oSource,sFull);
}

void SetFloatArray(object oSource, string sName, int iElem, float fVal)
{
    string sFull = sName+IntToString(iElem);
    SetLocalFloat(oSource,sFull,fVal);
}

float GetFloatArray(object oSource, string sName, int iElem)
{
    string sFull = sName+IntToString(iElem);
    return GetLocalFloat(oSource,sFull);
}

int IsOnOppositeSideOfDoor(object oDoor, object obj1, object obj2)
{
    float fDoorAngle = GetFacing(oDoor);
    
    vector vDoor = GetPositionFromLocation(GetLocation(oDoor));
    vector v1 = GetPositionFromLocation(GetLocation(obj1));
    vector v2 = GetPositionFromLocation(GetLocation(obj2));
    
    float fAngle1 = VectorToAngle(v1 - vDoor);
    float fAngle2 = VectorToAngle(v2 - vDoor);

    fAngle1 -= fDoorAngle;
    fAngle2 -= fDoorAngle;
    if (fAngle1 < 0.0)
    {
        fAngle1 += 360.0;
    }
    if (fAngle2 < 0.0)
    {
        fAngle2 += 360.0;
    }
    
    int bSide1 = fAngle1 < 90.0 || fAngle1 > 270.0;
    int bSide2 = fAngle2 < 90.0 || fAngle2 > 270.0;
    
    return bSide1 != bSide2;    
}


void CheckClassAbility(int iClass)
{
    switch (iClass)
    {
        case CLASS_TYPE_CLERIC:
        case CLASS_TYPE_DRUID:
        case CLASS_TYPE_PALADIN:
        case CLASS_TYPE_RANGER:
            SetLocalInt(OBJECT_SELF, "tkInitialWisdom", GetAbilityScore(OBJECT_SELF, ABILITY_WISDOM));
            break;
        case CLASS_TYPE_SORCERER:
        case CLASS_TYPE_BARD:
            SetLocalInt(OBJECT_SELF, "tkInitialCharisma", GetAbilityScore(OBJECT_SELF, ABILITY_CHARISMA));
            break;
        case CLASS_TYPE_WIZARD:
            SetLocalInt(OBJECT_SELF, "tkInitialIntelligence", GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE));
            break;
    }
}


void InitializeAbilityCheck()
{
    int nHitDice = GetHitDice(OBJECT_SELF);
    if (GetLocalInt(OBJECT_SELF, "tkInitialAblitiyCheck") != nHitDice)
    {
        CheckClassAbility(GetClassByPosition(1));
        CheckClassAbility(GetClassByPosition(2));
        CheckClassAbility(GetClassByPosition(3));
        SetLocalInt(OBJECT_SELF, "tkInitialAblitiyCheck", nHitDice);
    }
}


void HenchSetLastGenericSpellCast(int nSpell)
{
    SetLocalInt(OBJECT_SELF, "NW_GENERIC_LAST_SPELL", nSpell);
}


//69MEH69 GetRealMaster Code
object GetRealMaster(object oAssociate = OBJECT_SELF)
{
    object oMaster = GetMaster(oAssociate);
    if (GetIsObjectValid(oMaster))
    {
        while (GetIsObjectValid(GetMaster(oMaster)))
        {
            oMaster = GetMaster(oMaster);
        }
    }
    return oMaster;
}


object GetTopMaster(object oAssociate = OBJECT_SELF)
{
    object oRetVal = OBJECT_SELF;
    object oMaster = GetMaster(oAssociate);
    while (GetIsObjectValid(oMaster))
    {
        oRetVal = oMaster;
        oMaster = GetMaster(oMaster);
    }
    return oRetVal;
}


int HenchConvertClass(int nClass1/*, int class2, int class3*/)
{
    switch(nClass1)
    {
        case CLASS_TYPE_SHADOWDANCER:
        case CLASS_TYPE_ASSASSIN:
            return CLASS_TYPE_ROGUE;
        case CLASS_TYPE_HARPER:
        case CLASS_TYPE_ARCANE_ARCHER:
            return CLASS_TYPE_BARD;
    }    
    return nClass1;
}


int HenchDetermineClassToUse(object oCharacter = OBJECT_SELF)
{
    int nClass;
    int nTotal = GetHitDice(oCharacter);
    if (nTotal < 1)
    {
        nTotal = 1;
    }
    
    int nClassLevel1 = GetLevelByPosition(1, oCharacter);
    int nClass1 =  GetClassByPosition(1, oCharacter);
    // quick exit
    if (nClassLevel1 >= nTotal)
    {
        return nClass1;
    }

    int nClassLevel2 = GetLevelByPosition(2, oCharacter);
    int nClass2 = GetClassByPosition(2, oCharacter);
    int nClassLevel3 = GetLevelByPosition(3, oCharacter);
    int nClass3 = GetClassByPosition(3, oCharacter);
    // blackguard over everthing else
    if (nClass1 == CLASS_TYPE_BLACKGUARD || nClass2 == CLASS_TYPE_BLACKGUARD ||
        nClass3 == CLASS_TYPE_BLACKGUARD)
    {
        return CLASS_TYPE_BLACKGUARD;
    }    
    // adjust classes to remove prestige    
    nClass1 = HenchConvertClass(nClass1);    
    nClass2 = HenchConvertClass(nClass2);
    nClass3 = HenchConvertClass(nClass3);    
    if (nClass1 == nClass2)
    {
        nClassLevel1 += nClassLevel2;
        nClassLevel2 = 0;
    }
    if (nClass1 == nClass3)
    {
        nClassLevel1 += nClassLevel3;
        nClassLevel3 = 0;   
    }
    if (nClass2 == nClass3)
    {
        nClassLevel2 += nClassLevel3;
        nClassLevel3 = 0;   
    }
    // find top class    
    int nMaxClassLevel = nClassLevel1 >= nClassLevel2 ? nClassLevel1 : nClassLevel2;
    nMaxClassLevel = nMaxClassLevel >= nClassLevel3 ? nMaxClassLevel : nClassLevel3;
    // filter out classes less than two levels below the max    
    if (nMaxClassLevel - 1 > nClass1)
    {
        nClassLevel1 = 0;      
    }
    if (nMaxClassLevel - 1 > nClass2)
    {
        nClassLevel2 = 0;      
    }    
    if (nMaxClassLevel - 1 > nClass3)
    {
        nClassLevel3 = 0;      
    }
    nTotal = nClassLevel1 + nClassLevel2 + nClassLevel3;    
    int nPickClass = Random(nTotal);        
    nPickClass -= nClassLevel1;    
    if (nPickClass < 0)
    {
        return nClass1;   
    }    
    nPickClass -= nClassLevel2;    
    if (nPickClass < 0)
    {
        return nClass2;   
    }
    return nClass3;
}



