//::///////////////////////////////////////////////
//:: Include file for Detect Alignment spells
//:: prc_inc_s_det
//:://////////////////////////////////////////////
/** @file

This file handles the Detect Evil/Law/Chaos/Good series of spells
If there are other detect spells, they can probably be put in here too.

    @author Primogenitor
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////


#include "prc_inc_spells"
#include "prc_add_spell_dc"
//#include "inc_draw"    Provided by inc_utility
//#include "inc_utility" Provided by prc_alterations

//this is the main detection function
void DetectAlignmentRound(int nRound, location lLoc, int nGoodEvil, int nLawChaos, string sAura, int nBeamVFX);

// Detect Magic
void DetectMagicAura(int nRound, location lLoc, int nBeamVFX, float fDist);

const int AURA_STRENGTH_DIM          = 1;
const int AURA_STRENGTH_FAINT        = 2;
const int AURA_STRENGTH_MODERATE     = 3;
const int AURA_STRENGTH_STRONG       = 4;
const int AURA_STRENGTH_OVERWHELMING = 5;

int GetIsUndetectable(object oTarget, object oCaster)//fix this
{
    int nUndetectable = FALSE;
    if(GetHasSpellEffect(SPELL_UNDETECTABLE_ALINGMENT, oTarget) ||
    GetHasSpellEffect(SPELL_NONDETECTION, oTarget) ||
    GetLevelByClass(CLASS_TYPE_WILD_MAGE, oTarget) >= 6 ||
    GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oTarget) >= 5 ||
    GetHasSpellEffect(MELD_ENIGMA_HELM, oTarget) ||
    GetRacialType(oTarget) == RACIAL_TYPE_SKULK) // MELD_ENIGMA_HELM
    {
        int nDC = PRCGetSaveDC(OBJECT_SELF, oTarget, SPELL_UNDETECTABLE_ALINGMENT);
        //if you dont beat the saving throw of the spell, you cant detect them
        if(!PRCMySavingThrow(SAVING_THROW_WILL, oCaster, nDC, SAVING_THROW_TYPE_NONE))
            nUndetectable = TRUE;
    }
    return nUndetectable;
}

string AlignmentAuraLocal(int nGoodEvil, int nLawChaos)
{
    if      (nGoodEvil == ALIGNMENT_GOOD)    return "PRC_AURA_GOOD";
    else if(nGoodEvil == ALIGNMENT_EVIL)    return "PRC_AURA_EVIL";
    else if(nLawChaos == ALIGNMENT_LAWFUL)  return "PRC_AURA_LAW";
    else if(nLawChaos == ALIGNMENT_CHAOTIC) return "PRC_AURA_CHAOS";

    return "";
}

int GetAuraStrength(object oTarget, int nGoodEvil, int nLawChaos)//add new classes
{
    int nPower = GetLocalInt(oTarget, AlignmentAuraLocal(nGoodEvil, nLawChaos));

    // Only creatures proceed past this point.
    if (GetObjectType(oTarget) != OBJECT_TYPE_CREATURE)
        return nPower;

    int nRace = MyPRCGetRacialType(oTarget);
    if(nRace == RACIAL_TYPE_OUTSIDER)
        nPower = GetHitDice(oTarget);
    else if((nRace == RACIAL_TYPE_UNDEAD && nGoodEvil==ALIGNMENT_EVIL)//undead only for evils
        || nRace == RACIAL_TYPE_ELEMENTAL)
        nPower = GetHitDice(oTarget)/2;
    else
        nPower = GetHitDice(oTarget)/5;
    if(GetPrCAdjustedCasterLevel(CLASS_TYPE_CLERIC, oTarget) > nPower)
        nPower = GetPrCAdjustedCasterLevel(CLASS_TYPE_CLERIC, oTarget);
    if(GetPrCAdjustedCasterLevel(CLASS_TYPE_ANTI_PALADIN, oTarget) > nPower)
        nPower = GetPrCAdjustedCasterLevel(CLASS_TYPE_ANTI_PALADIN, oTarget);

/*CLASS_TYPE_CLERIC
CLASS_TYPE_ANTI_PALADIN
Dragon shaman
blackguard*/
    return nPower;
}

string GetNounForStrength(int nStrength)
{
    switch(nStrength)
    {
        case AURA_STRENGTH_DIM:          return GetStringByStrRef(16832039); //"dimly"
        case AURA_STRENGTH_FAINT:        return GetStringByStrRef(16832040); //"faintly"
        case AURA_STRENGTH_MODERATE:     return GetStringByStrRef(16832041); //"moderately"
        case AURA_STRENGTH_STRONG:       return GetStringByStrRef(16832042); //"strongly"
        case AURA_STRENGTH_OVERWHELMING: return GetStringByStrRef(16832043); //"overwhelmingly"
    }
    return "";
}

void ApplyEffectDetectAuraOnObject(int nStrength, object oTarget, int nVFX)
{
    location lLoc = GetLocation(oTarget);
    //float fRadius = (IntToFloat(GetCreatureSize(oTarget))*0.5)+0.5; //this is graphics related, not rules
    float fRadius = StringToFloat(Get2DACache("appearance", "CREPERSPACE", GetAppearanceType(oTarget)));
    location lCenter;
    vector vCenter = GetPositionFromLocation(lLoc);
    switch(nStrength)
    {
        //yes fallthroughs here
        case AURA_STRENGTH_OVERWHELMING:
            vCenter.z += (fRadius/2.0);
            lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0);
            BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f);
        case AURA_STRENGTH_STRONG:
            vCenter.z += (fRadius/2.0);
            lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0);
            BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f);
        case AURA_STRENGTH_MODERATE:
            vCenter.z += (fRadius/2.0);
            lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0);
            BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f);
        case AURA_STRENGTH_FAINT:
            vCenter.z += (fRadius/2.0);
            lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0);
            BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f);
        case AURA_STRENGTH_DIM:
            vCenter.z += (fRadius/2.0);
            lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0);
            BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f);
    }
}

void DetectAlignmentRound(int nRound, location lLoc, int nGoodEvil, int nLawChaos, string sAura, int nBeamVFX)
{
    object oCaster = OBJECT_SELF;
    int nSpellID = GetSpellId();

    if(!GetHasSpellEffect(nSpellID, oCaster))
        return;

    //if concentration is broken, abort
    if(GetBreakConcentrationCheck(oCaster) && nRound != 0)
    {
        FloatingTextStringOnCreature(GetStringByStrRef(16832000)/*"Concentration broken."*/, oCaster, FALSE);
        PRCRemoveEffectsFromSpell(oCaster, nSpellID);
        return;
    }

    location lNew = GetLocation(oCaster);

    //if you move/turn, restart the process / abort if moved more than 2 m
    if(lLoc != lNew)
    {
        if(GetDistanceBetweenLocations(lLoc, lNew) > 2.0)
        {
            PRCRemoveEffectsFromSpell(oCaster, nSpellID);
            FloatingTextStringOnCreature("You have moved too far, ending Detection", oCaster, FALSE);
            return;
        }
        else
            nRound = 1;
    }
    if(nRound < 0)
        nRound = 1;
    //sanity check
    if(nRound > 4)
        nRound = 4;

    // Generate a target location in front of us.
    float fFacing = GetFacing(oCaster);
    vector vPos = GetPosition(oCaster);
    location lTarget = Location(GetArea(oCaster),
                       Vector(vPos.x + cos(fFacing), vPos.y + sin(fFacing), vPos.z),
                       fFacing);

    object oTest = GetFirstObjectInShape(SHAPE_SPELLCONE, 20.0, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_AREA_OF_EFFECT);
    int nStrongestAura;
    int nAuraCount;
    while(GetIsObjectValid(oTest))
    {
        if(DEBUG) DoDebug("DetectAlignmentRound() : Round = "+IntToString(nRound)+"from "+GetName(oCaster)+" of "+GetName(oTest));

        int nUndetectable = GetIsUndetectable(oTest, oCaster); //you cannot be detected if this is true
        if((GetAlignmentGoodEvil(oTest) == nGoodEvil || nGoodEvil == -1)
            && (GetAlignmentLawChaos(oTest) == nLawChaos || nLawChaos == -1)
            && ((sAura == GetStringByStrRef(5018)) ? MyPRCGetRacialType(oTest) == RACIAL_TYPE_UNDEAD : TRUE)
            && oTest != oCaster
            && !nUndetectable)
        {
            if(nRound == 1)
            {
                //presence/absence
                //ApplyEffectDetectAuraOnObject(AURA_STRENGTH_MODERATE, oCaster, nBeamVFX);
                FloatingTextStringOnCreature(PRCGetRGB(15,5,5) + GetStringByStrRef(16832001)// "You detect the presense of"
                                             + " " + (nLawChaos != -1 ? // "good" and "evil" work as both substantives and adjectives, but not so for "lawful" and "chaotic"
                                                      (nLawChaos == ALIGNMENT_LAWFUL ?
                                                       GetStringByStrRef(4957)   // "law"
                                                       : GetStringByStrRef(4958) // "chaos"
                                                       )
                                                      : sAura
                                                      )
                                              + ".",
                                             oCaster, FALSE);
                break;//end while loop
            }

            int nStrength;
            int nRawStrength = GetAuraStrength(oTest, nGoodEvil, nLawChaos);

            if(nRawStrength == 0)
                nStrength = AURA_STRENGTH_DIM;
            else if(nRawStrength == 1)
                nStrength = AURA_STRENGTH_FAINT;
            else if(nRawStrength >= 2 && nRawStrength <= 4)
                nStrength = AURA_STRENGTH_MODERATE;
            else if(nRawStrength >= 5 && nRawStrength <= 10)
                nStrength = AURA_STRENGTH_STRONG;
            else if(nRawStrength >= 11)
                nStrength = AURA_STRENGTH_OVERWHELMING;

            if(nRound == 2)
            {
                //number & strongest
                nAuraCount++;
                if(nStrength > nStrongestAura)
                    nStrongestAura = nStrength;
                //if overwhelming then can stun
                int nOpposingGoodEvil;
                if(nGoodEvil == ALIGNMENT_EVIL)
                    nOpposingGoodEvil = ALIGNMENT_GOOD;
                else if(nGoodEvil == ALIGNMENT_GOOD)
                    nOpposingGoodEvil = ALIGNMENT_EVIL;
                else
                    nOpposingGoodEvil = -1;
                int nOpposingLawChaos;
                if(nLawChaos == ALIGNMENT_CHAOTIC)
                    nOpposingLawChaos = ALIGNMENT_LAWFUL;
                else if(nLawChaos == ALIGNMENT_LAWFUL)
                    nOpposingLawChaos = ALIGNMENT_CHAOTIC;
                else
                    nOpposingLawChaos = -1;

                if(nStrength == AURA_STRENGTH_OVERWHELMING
                    && (GetAlignmentGoodEvil(oCaster)==nOpposingGoodEvil || nOpposingGoodEvil == -1)
                    && (GetAlignmentLawChaos(oCaster)==nOpposingLawChaos || nOpposingLawChaos == -1)
                    && nRawStrength > GetHitDice(oCaster)*2)
                {
                    ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectStunned(), oCaster, 6.0);
                }
            }
            else if(nRound >= 3)
            {
                if(nRound == 3)
                    ActionDoCommand(FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrength*3),16-(nStrength*3)) + GetName(oTest) + " " + GetStringByStrRef(16832044)/*"feels"*/ + " "+GetNounForStrength(nStrength)+" "+sAura+".", oCaster, FALSE));
                //strength & location
                ActionDoCommand(ApplyEffectDetectAuraOnObject(nStrength, oTest, nBeamVFX));
            }
        }
        oTest = GetNextObjectInShape(SHAPE_SPELLCONE, 20.0, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_AREA_OF_EFFECT);
    }
    if(nRound == 2)
    {
        //reporting
        //ApplyEffectDetectAuraOnObject(nStrongestAura, oCaster, nBeamVFX);
        FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrongestAura*3),16-(nStrongestAura*3)) + GetStringByStrRef(16832045)/*"You detected"*/ + " " + IntToString(nAuraCount) + " " + GetNounForStrength(nStrongestAura) + " " + sAura + " " + GetStringByStrRef(16832046)/*"auras"*/ + ".", oCaster, FALSE);
    }

    //ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0);
    nRound++;

    DelayCommand(6.0f, ActionDoCommand(DetectAlignmentRound(nRound, lNew, nGoodEvil, nLawChaos, sAura, nBeamVFX)));
}

int GetMagicAuraStrength(object oTarget)
{
	int nPower;
    effect eLook = GetFirstEffect(oTarget);
    while (GetIsEffectValid(eLook))
    {
    	int nSpellLvl = PRCGetSpellLevelForClass(GetEffectSpellId(eLook), CLASS_TYPE_INVALID);
		if (nSpellLvl > nPower) nPower = nSpellLvl;

      	eLook = GetNextEffect(oTarget);
    }	
	
    return nPower;
}

void DetectMagicAura(int nRound, location lLoc, int nBeamVFX, float fDist)
{
    object oCaster = OBJECT_SELF;
    int nSpellID = GetSpellId();

    if(!GetHasSpellEffect(nSpellID, oCaster))
        return;

    //if concentration is broken, abort
    if(GetBreakConcentrationCheck(oCaster) && nRound != 0)
    {
        FloatingTextStringOnCreature(GetStringByStrRef(16832000)/*"Concentration broken."*/, oCaster, FALSE);
        PRCRemoveEffectsFromSpell(oCaster, nSpellID);
        return;
    }

    location lNew = GetLocation(oCaster);

    //if you move/turn, restart the process / abort if moved more than 2 m
    if(lLoc != lNew)
    {
        if(GetDistanceBetweenLocations(lLoc, lNew) > 2.0)
        {
            PRCRemoveEffectsFromSpell(oCaster, nSpellID);
            FloatingTextStringOnCreature("You have moved too far, ending Detection", oCaster, FALSE);
            return;
        }
        else
            nRound = 1;
    }
    if(nRound < 0)
        nRound = 1;
    //sanity check
    if(nRound > 4)
        nRound = 4;

    // Generate a target location in front of us.
    float fFacing = GetFacing(oCaster);
    vector vPos = GetPosition(oCaster);
    location lTarget = Location(GetArea(oCaster),
                       Vector(vPos.x + cos(fFacing), vPos.y + sin(fFacing), vPos.z),
                       fFacing);

    object oTest = GetFirstObjectInShape(SHAPE_SPELLCONE, fDist, lTarget, TRUE, OBJECT_TYPE_CREATURE);
    int nStrongestAura;
    int nAuraCount;
    while(GetIsObjectValid(oTest))
    {
        if(DEBUG) DoDebug("DetectMagicAura() : Round = "+IntToString(nRound)+"from "+GetName(oCaster)+" of "+GetName(oTest));

		// You cannot be detected if this is true
        if(!GetIsUndetectable(oTest, oCaster))
        {
            if(nRound == 1)
            {
                FloatingTextStringOnCreature("You detect the presence of magic.", oCaster, FALSE);
                break;//end while loop
            }

            int nStrength;
            int nRawStrength = GetMagicAuraStrength(oTest);

            if(nRawStrength == 0)
                nStrength = AURA_STRENGTH_DIM;
            else if(nRawStrength >= 1 && nRawStrength <= 3)
                nStrength = AURA_STRENGTH_FAINT;
            else if(nRawStrength >= 4 && nRawStrength <= 6)
                nStrength = AURA_STRENGTH_MODERATE;
            else if(nRawStrength >= 7 && nRawStrength <= 9)
                nStrength = AURA_STRENGTH_STRONG;
            else if(nRawStrength >= 10)
                nStrength = AURA_STRENGTH_OVERWHELMING;

            if(nRound == 2)
            {
                //number & strongest
                nAuraCount++;
                if(nStrength > nStrongestAura)
                    nStrongestAura = nStrength;
            }
            else if(nRound >= 3)
            {
                if(nRound == 3)
                    ActionDoCommand(FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrength*3),16-(nStrength*3)) + GetName(oTest) + " feels affected by "+GetNounForStrength(nStrength)+" magic.", oCaster, FALSE));
                //strength & location
                ActionDoCommand(ApplyEffectDetectAuraOnObject(nStrength, oTest, nBeamVFX));
            }
        }
        oTest = GetNextObjectInShape(SHAPE_SPELLCONE, fDist, lTarget, TRUE, OBJECT_TYPE_CREATURE);
    }
    if(nRound == 2)
    {
        //reporting
        //ApplyEffectDetectAuraOnObject(nStrongestAura, oCaster, nBeamVFX);
        FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrongestAura*3),16-(nStrongestAura*3)) + GetStringByStrRef(16832045)/*"You detected"*/ + " " + IntToString(nAuraCount) + " " + GetNounForStrength(nStrongestAura) + " magical " + GetStringByStrRef(16832046)/*"auras"*/ + ".", oCaster, FALSE);
    }

    //ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0);
    nRound++;

    DelayCommand(6.0f, ActionDoCommand(DetectMagicAura(nRound, lNew, nBeamVFX, fDist)));
}

void DetectRaceAura(int nRound, int nRace, location lLoc, int nBeamVFX, float fDist)
{
    object oCaster = OBJECT_SELF;
    int nSpellID = GetSpellId();

    if(!GetHasSpellEffect(nSpellID, oCaster))
        return;

    //if concentration is broken, abort
    if(GetBreakConcentrationCheck(oCaster) && nRound != 0)
    {
        FloatingTextStringOnCreature(GetStringByStrRef(16832000)/*"Concentration broken."*/, oCaster, FALSE);
        PRCRemoveEffectsFromSpell(oCaster, nSpellID);
        return;
    }

    location lNew = GetLocation(oCaster);

    //if you move/turn, restart the process / abort if moved more than 2 m
    if(lLoc != lNew)
    {
        if(GetDistanceBetweenLocations(lLoc, lNew) > 2.0)
        {
            PRCRemoveEffectsFromSpell(oCaster, nSpellID);
            FloatingTextStringOnCreature("You have moved too far, ending Detection", oCaster, FALSE);
            return;
        }
        else
            nRound = 1;
    }
    if(nRound < 0)
        nRound = 1;
    //sanity check
    if(nRound > 4)
        nRound = 4;

    // Generate a target location in front of us.
    float fFacing = GetFacing(oCaster);
    vector vPos = GetPosition(oCaster);
    location lTarget = Location(GetArea(oCaster),
                       Vector(vPos.x + cos(fFacing), vPos.y + sin(fFacing), vPos.z),
                       fFacing);

    object oTest = GetFirstObjectInShape(SHAPE_SPELLCONE, fDist, lTarget, TRUE, OBJECT_TYPE_CREATURE);
    int nStrongestAura;
    int nAuraCount;
    while(GetIsObjectValid(oTest))
    {
        if(DEBUG) DoDebug("DetectMagicAura() : Round = "+IntToString(nRound)+"from "+GetName(oCaster)+" of "+GetName(oTest));

		// You cannot be detected if this is true
        if(!GetIsUndetectable(oTest, oCaster) && GetObjectType(oTest) == OBJECT_TYPE_CREATURE)
        {
            if(nRound == 1)
            {
                FloatingTextStringOnCreature("You detect the presence of the chosen race.", oCaster, FALSE);
                break;//end while loop
            }

            int nStrength, nRawStrength;
            int nCheck = MyPRCGetRacialType(oTest);
            // Detect Living is -1
    		if(nRace == nCheck || (nRace == -1 && PRCGetIsAliveCreature(oTest)))
        		nRawStrength = GetHitDice(oTest);            

            if(nRawStrength >= 0 && nRawStrength <= 1)
                nStrength = AURA_STRENGTH_FAINT;
            else if(nRawStrength >= 2 && nRawStrength <= 4)
                nStrength = AURA_STRENGTH_MODERATE;
            else if(nRawStrength >= 5 && nRawStrength <= 10)
                nStrength = AURA_STRENGTH_STRONG;
            else if(nRawStrength >= 11)
                nStrength = AURA_STRENGTH_OVERWHELMING;

            if(nRound == 2)
            {
                //number & strongest
                nAuraCount++;
                if(nStrength > nStrongestAura)
                    nStrongestAura = nStrength;
            }
            else if(nRound >= 3)
            {
                if(nRound == 3)
                    ActionDoCommand(FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrength*3),16-(nStrength*3)) + GetName(oTest) + " finds the greatest aura is "+GetNounForStrength(nStrength)+".", oCaster, FALSE));
                //strength & location
                ActionDoCommand(ApplyEffectDetectAuraOnObject(nStrength, oTest, nBeamVFX));
            }
        }
        oTest = GetNextObjectInShape(SHAPE_SPELLCONE, fDist, lTarget, TRUE, OBJECT_TYPE_CREATURE);
    }
    if(nRound == 2)
    {
        //reporting
        //ApplyEffectDetectAuraOnObject(nStrongestAura, oCaster, nBeamVFX);
        FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrongestAura*3),16-(nStrongestAura*3)) + GetStringByStrRef(16832045)/*"You detected"*/ + " " + IntToString(nAuraCount) + " " + GetNounForStrength(nStrongestAura) + " racial " + GetStringByStrRef(16832046)/*"auras"*/ + ".", oCaster, FALSE);
    }

    //ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0);
    nRound++;

    DelayCommand(6.0f, ActionDoCommand(DetectRaceAura(nRound, nRace, lNew, nBeamVFX, fDist)));
}