#include "g_common"
#include "prc_inc_spells"

////////////////////////////////////////////////////////////////////////////////
// Functions List //////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void    EventHandler_Animal_Predator_UD(int iEventID);
void    EventHandler_Animal_Prey_UD(int iEventID);
void    EventHandler_Beast_SoulRender_UD(int iEventID);
void    EventHandler_Default_UD(int iEventID);
void    EventHandler_Giant_Troll_UD(int iEventID);
void    EventHandler_Undead_Zombie_UD(int iEventID);
void    DoZombieSpeak();

////////////////////////////////////////////////////////////////////////////////
// Function Definitions ////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void main() {
    int iEventID = GetUserDefinedEventNumber();

    ////////////////////////////////////////////////////////////////////////////
    // Switch to the 'OnUserDefined' code for the correct creature /////////////
    ////////////////////////////////////////////////////////////////////////////

    // Animals
    if (Object_IsClassAndRace(CLASS_TYPE_ANIMAL, OBJECT_SELF, TRUE)) {
        if (Object_IsPrey()) EventHandler_Animal_Prey_UD(iEventID);     // Prey Items
        else EventHandler_Animal_Predator_UD(iEventID);                 // Predators
    }

    // Soul Renders
    if (Object_IsClassAndRace(CLASS_TYPE_BEAST, OBJECT_SELF, FALSE, "soul render")) {
        EventHandler_Beast_SoulRender_UD(iEventID);
    }

    // Trolls
    if (Object_IsClassAndRace(CLASS_TYPE_GIANT, OBJECT_SELF, FALSE, "troll")) {
        EventHandler_Giant_Troll_UD(iEventID);
    }

    // Zombies
    if (Object_IsClassAndRace(CLASS_TYPE_UNDEAD, OBJECT_SELF, FALSE, "zombie")) {
        EventHandler_Undead_Zombie_UD(iEventID);
    }

    // Default Handler
    EventHandler_Default_UD(iEventID);
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Animal - Predator Event Handler function
////////////////////////////////////////////////////////////////////////////////
void EventHandler_Animal_Predator_UD(int iEventID) {
    int iCurrentHP = GetCurrentHitPoints();
    int iMaximumHP = GetMaxHitPoints();
    int iThreshold = FloatToInt(0.45 * IntToFloat(iMaximumHP));
    int iFlockRoll = d8(3);

    if(iEventID == 1001) { //HEARTBEAT
        // React to PC's
        object oNoticed = GetLocalObject(OBJECT_SELF, "oNoticed");
        if (GetIsObjectValid(oNoticed)) {
            float fDist = GetDistanceToObject(oNoticed);
            // Flee from PC if not strong enough to fight
            if ((fDist <= 30.0) && (iCurrentHP < iThreshold) && GetObjectSeen(oNoticed)) {
                ClearAllActions();
                AssignCommand(OBJECT_SELF, ActionMoveAwayFromObject(oNoticed, TRUE));
                return;
            }
            // Walk randomly if PC is far enough away not to be a bother
            if (fDist >= 27.0) {
                SetLocalObject(OBJECT_SELF, "oNoticed", OBJECT_INVALID);
                AssignCommand(OBJECT_SELF, ClearAllActions());
                AssignCommand(OBJECT_SELF, ActionRandomWalk());
                return;
            }
            // Attack if PC gets too close and we're healthy enough!
            if ((fDist <= 12.0) && (iCurrentHP > iThreshold)) {
                if (!GetIsInCombat()) {
                    AssignCommand(OBJECT_SELF, ClearAllActions());
                    AssignCommand(OBJECT_SELF, ActionAttack(oNoticed));
                    return;
                }
            }
        }
    }
    else if(iEventID == 1002) { // PERCEIVE
        object oNoticed = GetLastPerceived();
        if(GetIsObjectValid(oNoticed)) {
            // Did animal notice a PC?
            if ((GetLastPerceptionSeen() || GetLastPerceptionHeard()) && GetIsPC(oNoticed)) {
                // Do not take aggression against Rangers, Druids, or Elves
                if ( !(GetLevelByClass(CLASS_TYPE_RANGER, oNoticed) > 1) &&
                     !(GetLevelByClass(CLASS_TYPE_DRUID, oNoticed) > 1) &&
                      (GetRacialType(oNoticed) != RACIAL_TYPE_ELF)) {
                        SetLocalObject(OBJECT_SELF, "oNoticed", oNoticed);
                }
            }
        }
    }
    else if(iEventID == 1003) { // END OF COMBAT

    }
    else if(iEventID == 1004) { // ON DIALOGUE

    }
    else if(iEventID == 1005) { // ATTACKED
        if (!GetIsInCombat()) {
            ClearAllActions();
            AssignCommand(OBJECT_SELF, ActionAttack(GetLastAttacker()));
        }
    }
    else if(iEventID == 1006) { // DAMAGED
        if (iCurrentHP <= iThreshold) {  // Run away if too much damage was done
            object oDamager = GetLastDamager();
            SetLocalObject(OBJECT_SELF, "oNoticed", oDamager);
            ClearAllActions();
            SurrenderToEnemies();
            ClearPersonalReputation(oDamager);
            AssignCommand(OBJECT_SELF, ActionMoveAwayFromObject(oDamager, TRUE));
            DelayCommand(8.0, SetLocalObject(OBJECT_SELF, "oNoticed", OBJECT_INVALID));
        }
    }
    else if(iEventID == 1007) { // DEATH
            Treasure_Death_PlaceBodyParts();
            // Respawn_ByResrefWithDelay(OBJECT_SELF, 1200.0 + IntToFloat(Random(450)));
    }
    else if(iEventID == 1008) { // DISTURBED

    }
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Animal - Prey Event Handler function
////////////////////////////////////////////////////////////////////////////////
void EventHandler_Animal_Prey_UD(int iEventID) {
    int iCurrentHP = GetCurrentHitPoints();
    int iMaximumHP = GetMaxHitPoints();
    int iThreshold = FloatToInt(0.45 * IntToFloat(iMaximumHP));

    if(iEventID == 1001) { //HEARTBEAT
        object oNoticed = GetLocalObject(OBJECT_SELF, "oNoticed");
        if (GetIsObjectValid(oNoticed)) {
            float fDist = GetDistanceToObject(oNoticed);
            if (fDist < 20.0 && GetObjectSeen(oNoticed)) { ActionMoveAwayFromObject(oNoticed, TRUE, 30.0); return; }
            if (fDist >= 20.0 && fDist <= 40.0) { if (Random(100) >= 15) ActionMoveAwayFromObject(oNoticed, FALSE, 30.0); return; }
            if (fDist > 40.0) { SetLocalObject(OBJECT_SELF, "oNoticed", OBJECT_INVALID); return; }
        }
        else {
            SetLocalObject(OBJECT_SELF, "oNoticed", OBJECT_INVALID);

            // Do some flocking behaviour
            int iHowMany = Object_CountSameAtLocation(OBJECT_SELF, GetLocation(OBJECT_SELF), 2.0);
            int iTooMany;
            int iSize = GetCreatureSize(OBJECT_SELF);
            if (iSize == CREATURE_SIZE_HUGE) iTooMany = 2;
            if (iSize == CREATURE_SIZE_LARGE) iTooMany = 3;
            if (iSize == CREATURE_SIZE_MEDIUM) iTooMany = 4;
            if (iSize == CREATURE_SIZE_SMALL) iTooMany = 6;
            if (iSize == CREATURE_SIZE_TINY) iTooMany = 9;
            if (iHowMany >= iTooMany) {
                ActionForceMoveToRandomLocation(OBJECT_SELF, 20.0, 20.0, Random(3));
            }
            else AI_Movement_FlockTypeB();
        }
    }
    else if(iEventID == 1002) { // PERCEIVE
        object oNoticed = GetLastPerceived();
        if(GetIsObjectValid(oNoticed)) {
            // Did animal notice another animal?
            string strNoticed = GetTag(oNoticed);
            if (GetTag(OBJECT_SELF) != strNoticed) {
                SetLocalObject(OBJECT_SELF, "oNoticed", oNoticed);
                ActionMoveAwayFromObject(oNoticed, FALSE); // Move away from it
            }
        }
    }
    else if(iEventID == 1003) { // END OF COMBAT

    }
    else if(iEventID == 1004) { // ON DIALOGUE

    }
    else if(iEventID == 1005) { // ATTACKED
        if (!GetIsInCombat()) {
            ClearAllActions();
            if (Random(100) >=60) AssignCommand(OBJECT_SELF, ActionAttack(GetLastAttacker()));
            else ActionMoveAwayFromObject(GetLastAttacker(), TRUE);
        }
    }
    else if(iEventID == 1006) { // DAMAGED
        if (iCurrentHP <= iThreshold) {  // Run away if too much damage was done
            object oDamager = GetLastDamager();
            SetLocalObject(OBJECT_SELF, "oNoticed", oDamager);
            ClearAllActions();
            SurrenderToEnemies();
            ClearPersonalReputation(oDamager);
            AssignCommand(OBJECT_SELF, ActionMoveAwayFromObject(oDamager, TRUE));
            DelayCommand(8.0, SetLocalObject(OBJECT_SELF, "oNoticed", OBJECT_INVALID));
        }
    }
    else if(iEventID == 1007) { // DEATH
            Treasure_Death_PlaceBodyParts();
            // Respawn_ByResrefWithDelay(OBJECT_SELF, 1200.0 + IntToFloat(Random(450)));
    }
    else if(iEventID == 1008) { // DISTURBED

    }
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Default event handler for those creatures who did not get one previously
////////////////////////////////////////////////////////////////////////////////
void EventHandler_Default_UD(int iEventID) {

    if(iEventID == 1001) { //HEARTBEAT

    }
    else if(iEventID == 1002) {// PERCEIVE

    }
    else if(iEventID == 1003) { // END OF COMBAT

    }
    else if(iEventID == 1004) { // ON DIALOGUE

    }
    else if(iEventID == 1005) { // ATTACKED

    }
    else if(iEventID == 1006) { // DAMAGED

    }
    else if(iEventID == 1007) { // DEATH
           Treasure_Death_PlaceBodyParts();
    }
    else if(iEventID == 1008) { // DISTURBED

    }
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Soul Render Event Handler function  /////////////////////////////////////////
// v1.00 ///////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// The Event Handler for the Soul Render is fairly intensive as it utilizes an object search
// inside the heartbeat - keep this creature by itself!
void    EventHandler_Beast_SoulRender_UD(int iEventID) {
    // If in combat, examine nearby enemies for Negative Level Effects.
    // If found, then remove the drain, and select a Skill or Ability
    // Drain and apply it to the PC while applying the opposite effect
    // to the beast.
    if(iEventID == 1001) { //HEARTBEAT
        // Play horrifying sounds, just cause a soul-sucker isn't creepy enough without them...
        if (d100() < 20) {
            int iRand = d4();
            if (iRand == 1) { AssignCommand(OBJECT_SELF, PlaySound("c_beastbrth")); }
            if (iRand == 2) { AssignCommand(OBJECT_SELF, PlaySound("c_beastmoan")); }
            if (iRand == 3) { AssignCommand(OBJECT_SELF, PlaySound("c_beastroar")); }
            if (iRand == 4) { AssignCommand(OBJECT_SELF, PlaySound("c_beastsigh")); }
        }

        // SUCK SOULS!
        if (GetIsInCombat()) {
            // Search for nearby PC's with level drains
            string strResult;
            object oPC = GetFirstObjectInShape(SHAPE_SPHERE, 5.0, GetLocation(OBJECT_SELF), FALSE, OBJECT_TYPE_CREATURE);

            // Determine if the Render has sucked spell memories... chuck them back!
            string strAbsorbedSpells = GetLocalString(OBJECT_SELF, "strAbsorbedSpells");
            if (Array_GetTotalElements(strAbsorbedSpells) && (d20() < GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE) )) {
                Effect_DischargeAbsorbedSpell(OBJECT_SELF);
            }

            // Suck some souls!!!
            while (GetIsObjectValid(oPC)) {
                if (PRCGetHasEffect(EFFECT_TYPE_NEGATIVELEVEL, oPC)) {
                    effect eFX = GetFirstEffect(oPC);
                    while (GetIsEffectValid(eFX)) {
                        if (GetEffectType(eFX) == EFFECT_TYPE_NEGATIVELEVEL) {
                            if (GetEffectCreator(eFX) == OBJECT_SELF) {
                                RemoveEffect(oPC, eFX);
                                strResult = "";
                                int iRoll = d20(2);
                                // If the PC is a spell caster, then allow the Render to absorb spells as well
                                if (GetLevelByClass(CLASS_TYPE_CLERIC, oPC) ||
                                    GetLevelByClass(CLASS_TYPE_DRUID, oPC)  ||
                                    GetLevelByClass(CLASS_TYPE_WIZARD, oPC) ||
                                    GetLevelByClass(CLASS_TYPE_SORCERER, oPC)) iRoll += d10();

                                // Should I suck hitpoints?!
                                if (GetCurrentHitPoints() < 200) {
                                    Effect_AbsorbHitPoints(OBJECT_SELF, oPC);
                                }

                                // If there was some indecision, randomly decide what to SUCK!
                                if      (iRoll < 10) { strResult = Effect_AbsorbRandomAbility(OBJECT_SELF, oPC); } // Absorb an Ability
                                else if (iRoll < 18) { strResult = Effect_AbsorbRandomSavingThrow(OBJECT_SELF, oPC); }  // Absorb a saving throw
                                else if (iRoll < 24) { strResult = Effect_AbsorbHitPoints(OBJECT_SELF, oPC);  }   // Absorb some of the PC's HP's
                                else if (iRoll < 30) { strResult = Effect_AbsorbAbility(OBJECT_SELF, oPC, ABILITY_STRENGTH);  }
                                else if (iRoll < 33) { strResult = Effect_AbsorbAbility(OBJECT_SELF, oPC, ABILITY_DEXTERITY);  }
                                else if (iRoll < 40) { strResult = Effect_AbsorbAbility(OBJECT_SELF, oPC, ABILITY_CONSTITUTION); }
                                else if (iRoll < 50) { strResult = Effect_AbsorbSpellMemory(OBJECT_SELF, oPC);  }
                                if (GetStringLength(strResult) > 0) SendMessageToPC(oPC, "Soul Render " + strResult);
                            }
                        }
                        eFX = GetNextEffect(oPC);
                    } // end while (GetIsEffectValid(eFX))
                }
                oPC = GetNextObjectInShape(SHAPE_SPHERE, 5.0, GetLocation(OBJECT_SELF), FALSE, OBJECT_TYPE_CREATURE);
            } // end while (GetIsObjectValid(oPC))
        }
    } // END of HEARTBEAT
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Troll Event Handler function  ///////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void    EventHandler_Giant_Troll_UD(int iEventID) {
    object oAttacker = GetLastHostileActor();
    object oSeen;

    if(iEventID == 1001) { //HEARTBEAT

    }
    else if(iEventID == 1002) {// PERCEIVE

    }
    else if(iEventID == 1003) { // END OF COMBAT

    }
    else if(iEventID == 1004) { // ON DIALOGUE

    }
    else if(iEventID == 1005) { // ATTACKED

    }
    else if(iEventID == 1006) { // DAMAGED
        Object_CheckDamageVsResistances(20, 10, 25, (4.0 + IntToFloat(d6())), 33);
    }
    else if(iEventID == 1007) { // DEATH
        Treasure_Death_PlaceBodyParts();
    }
    else if(iEventID == 1008) { // DISTURBED

    }

}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Zombie Event Handler function    ////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void EventHandler_Undead_Zombie_UD(int iEventID) {
    object oAttacker = GetLastHostileActor();
    object oSeen;

    if(iEventID == 1001) { //HEARTBEAT

    }
    else if(iEventID == 1002) {// PERCEIVE
        // If a player is perceived with sight say something freaky
        oSeen = GetLastPerceived();
        if (GetIsPC(oSeen)) DoZombieSpeak();
    }
    else if(iEventID == 1003) { // END OF COMBAT

    }
    else if(iEventID == 1004) { // ON DIALOGUE

    }
    else if(iEventID == 1005) { // ATTACKED

    }
    else if(iEventID == 1006) { // DAMAGED
        Object_CheckDamageVsResistances(20, 10, 25, (4.0 + IntToFloat(d6())), 41);
    }
    else if(iEventID == 1007) { // DEATH
           Treasure_Death_PlaceBodyParts();
    }
    else if(iEventID == 1008) { // DISTURBED

    }
}

void DoZombieSpeak() {
    int nRandom = Random(6);
    ClearAllActions();
    string strSpeach;
    if (nRandom == 0) strSpeach = "dead by dawn... dead by dawn...";
    if (nRandom == 1) strSpeach = "I'll swallow your soul!";
    if (nRandom == 2) strSpeach = "brrraaains...";
    if (nRandom == 3) strSpeach = "dead by dawn... dead by dawn...";
    if (nRandom == 4) strSpeach = "Dead by dawn!!";
    if (nRandom == 5) strSpeach = "We want what you have... life...";
    AssignCommand(OBJECT_SELF, ActionSpeakString(strSpeach));
    AssignCommand(OBJECT_SELF, ActionAttack(GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC)));
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////