#include "inc_mod_constant"
#include "inc_prc_ecl"
//#include "inc_ecl"

// Returns the ID code attached to the end of a Tag.
// Used in item functions.
string GetID(object oObject = OBJECT_SELF);
string GetID(object oObject) // Confirmed Working
    {
    string sTag = GetTag(oObject);
    int iLen = GetStringLength(sTag)-iLen;
    return GetStringRight(sTag,3);
    }

// Converts a positive integer 0 to 999 to a 3 digit string
string Convert(int iNumber);
string Convert(int iNumber)  // Confirmed Working
    {
    string sNum = IntToString(iNumber);
    if (iNumber < 1 || iNumber > 999)
        return "000";
    if (iNumber < 10)
        sNum = "00" + sNum;
    else if (iNumber < 100)
        sNum = "0" + sNum;
    return sNum;
    }

// Checks to see if all PCs are in town.
// A variable "Town" must be set on all areas you wish to distinguish as town.
int AllPCsInTown();
int AllPCsInTown()
    {
    object oPC = GetFirstPC();
    while(GetIsObjectValid(oPC))
        {
        if (!GetLocalInt(GetArea(oPC),"Town"))
            return FALSE;
        oPC = GetNextPC();
        }
    return TRUE;
    }

// Returns oPC or its master if is an associate.
object GetIsPartyMemberOrAssociate(object oPC);
object GetIsPartyMemberOrAssociate(object oPC)
    {
    if (!GetIsPC(oPC))
        {
        if (GetIsObjectValid(GetMaster(oPC)))
            oPC = GetMaster(oPC);
        else
            return OBJECT_INVALID; // Returns this if a non PC or PC Associate
        }
    return oPC;
    }

// Determines the number of people in the party oPC belongs too.
int NumberInParty(object oPC);
int NumberInParty(object oPC)
    {
    int i;
    oPC = GetFirstFactionMember(oPC,TRUE);
    while (GetIsObjectValid(oPC))
        {
        i+=1;
        oPC = GetNextFactionMember(oPC,TRUE);
        }
    return i;
    }

// Returns the average level of the party oPC belongs too
int PartyLevel(object oPC);
int PartyLevel(object oPC)
    {
    object oParty = GetFirstFactionMember(oPC,TRUE);
    int iLevel,iNumber;
    while (GetIsObjectValid(oParty))
        {
//        iLevel += GetHitDice(oParty);
        iLevel += GetECL(oParty);
        iNumber+= 1;
        oParty = GetNextFactionMember(oPC,TRUE);
        }
    if (iNumber)
        return iLevel/iNumber;
    else
        return 1; // This should never happen!
    }
// Returns a DC scaled to iPartyLevel.
int ScaledDC(int iPartyLevel);
int ScaledDC(int iPartyLevel)
    {
    int iScaler = iPartyLevel/2;
    if (iScaler < 1)
        iScaler = 1;
    int i = (Random(iScaler*2) + 1) - iScaler;
    return 20 + iPartyLevel + i;
    }

// Returns TRUE if oPC is in the same area as the caller.
// Areas must be tagged Area### in order to call this function.
// No tagging convention required for objects within an area.
int SameArea(object oPC);
int SameArea(object oPC)
    {
    object oArea;
    if (GetStringLeft(GetTag(OBJECT_SELF),4) == "Area")
        oArea = OBJECT_SELF;
    else
        oArea = GetArea(OBJECT_SELF);
    if (GetArea(oPC) == oArea)
        return TRUE;
    return FALSE;
    }
// Does a standard skill check vs iDC returning TRUE/FALSE.
// If iDC = 0 returns the skill check total.
int SkillCheck(int iSkill, int iDC, object oPC = OBJECT_SELF, int iFeedBack = FALSE, int iTake20 = FALSE);
int SkillCheck(int iSkill, int iDC, object oPC, int iFeedBack, int iTake20)
    {
    if (GetHasSkill(iSkill))
        {
        if (iDC == 0)
            return d20() + GetSkillRank(iSkill);
        else if (iTake20)
            {
            if (20 + GetSkillRank(iSkill) >= iDC)
                return TRUE;
            }
        else if (iFeedBack)
            {
            return GetIsSkillSuccessful(OBJECT_SELF,iSkill,iDC);
            }
        else if (d20() + GetSkillRank(iSkill) >= iDC)
            return TRUE;
        }
    return FALSE;
    }
// Generates a bag at the Dying/Dead PCs location.
// If iDead is FALSE the bag is only created if the PC has a healing kit the
//      party may need and then moves the kit.
// iDead TRUE forces the bag to be created.
object GeneratePCLootBag(object oPC,int iDead = FALSE);
object GeneratePCLootBag(object oPC, int iDead)
    {
    object oBag;
    object oItem = GetItemPossessedBy(oPC,"healerskit");
    if (GetIsObjectValid(oItem) || iDead)
        oBag = CreateObject(OBJECT_TYPE_PLACEABLE,"pclootbag", GetLocation(oPC));
    else
        return OBJECT_INVALID;
    if (GetIsObjectValid(oItem))
        {
        CopyItem(oItem,oBag);
        DestroyObject(oItem);
        }
    SetLocalObject(oPC,"LootBag",oBag);
    return oBag;
    }

// Moves every item in a PC's inventory to the oTarget.
void LootPC(object oPC, object oTarget);
void LootPC(object oPC, object oTarget)
    {
    int i;
    object oItem;
    for (i = 0; i <= 13; i ++)
        {
        oItem = GetItemInSlot(i,oPC);
        AssignCommand(GetModule(),ActionUnequipItem(oItem));
        CopyItem(oItem,oTarget,TRUE);
        DestroyObject(oItem);
        }
    oItem = GetFirstItemInInventory(oPC);
    while (GetIsObjectValid(oItem))
        {
        if (GetTag(oItem) != "DeathToken")
            {
            CopyItem(oItem,oTarget,TRUE);
            DestroyObject(oItem);
            }
        oItem = GetNextItemInInventory(oPC);
        }
    AssignCommand(oTarget,TakeGoldFromCreature(GetGold(oPC),oPC,FALSE));
    }

// Gives 3E Starting Gold.
void StartingGold(object oPC);
void StartingGold(object oPC)
    {
    int i;
    object oNPC, oItem;
    if (!GetXP(oPC))
        {
        for (i = 0; i <= 14; i++)
            {
            oItem = GetItemInSlot(i);
            DestroyObject(oItem);
            }
        oItem = GetFirstItemInInventory(oPC);
        while (GetIsObjectValid(oItem))
            {
                if(GetBaseItemType(oItem) != BASE_ITEM_CBLUDGWEAPON
                    && GetBaseItemType(oItem) != BASE_ITEM_CPIERCWEAPON
                    && GetBaseItemType(oItem) != BASE_ITEM_CREATUREITEM
                    && GetBaseItemType(oItem) != BASE_ITEM_CSLASHWEAPON
                    && GetBaseItemType(oItem) != BASE_ITEM_CSLSHPRCWEAP)
                    DestroyObject(oItem);
                oItem = GetNextItemInInventory(oPC);
            }
        i = GetClassByPosition(1,oPC);
        switch (i)
            {
            case CLASS_TYPE_BARBARIAN:  {i = d4(4)*10;break;}
            case CLASS_TYPE_BARD:       {i = d4(4)*10;break;}
            case CLASS_TYPE_CLERIC:     {i = d4(5)*10;break;}
            case CLASS_TYPE_DRUID:      {i = d4(2)*10;break;}
            case CLASS_TYPE_FIGHTER:    {i = d4(6)*10;break;}
            case CLASS_TYPE_MONK:       {i = d4(5);break;}
            case CLASS_TYPE_PALADIN:    {i = d4(6)*10;break;}
            case CLASS_TYPE_RANGER:     {i = d4(6)*10;break;}
            case CLASS_TYPE_ROGUE:      {i = d4(5)*10;break;}
            case CLASS_TYPE_SORCERER:   {i = d4(3)*10;break;}
            case CLASS_TYPE_WIZARD:     {i = d4(3)*10;break;}
            default:                    {i = d4(4)*10;break;}
            };
        oNPC = GetObjectByTag("Merchant");
//        AssignCommand(oPC,ActionEquipItem(CreateItemOnObject("pcskin",oPC),INVENTORY_SLOT_CARMOUR));
        AssignCommand(oNPC,TakeGoldFromCreature(GetGold(oPC),oPC,TRUE));
        AssignCommand(oNPC,GiveGoldToCreature(oPC,i));
        AssignCommand(oNPC,GiveXPToCreature(oPC,1));
        }
    }

// Returns TRUE if oTarget has an iEffectType
// If oCreator is true then the effector will be checked for match.
// If iRemove is TRUE the effect will be removed.
// If iEffectType = EFFECT_TYPE_INVALID all effect types will be checked. Useful
// for finding any effect created by oCreator on oTarget.
int FindEffect(object oTarget, int iEffectType, object oCreator= OBJECT_INVALID, int iRemove = FALSE);
int FindEffect(object oTarget, int iEffectType, object oCreator, int iRemove)
    {
    effect eEffect = GetFirstEffect(oTarget);
    while(GetIsEffectValid(eEffect))
        {
        if (GetEffectType(eEffect) == iEffectType
         || iEffectType == EFFECT_TYPE_INVALIDEFFECT)
            {
            if ((GetIsObjectValid(oCreator)
             && oCreator == GetEffectCreator(eEffect))
             || !GetIsObjectValid(oCreator))
                {
                if (iRemove)
                    RemoveEffect(oTarget,eEffect);
                return TRUE;
                }
            }
        eEffect = GetNextEffect(oTarget);
        }
    return FALSE;
    }
// Generates a unique tag for the secret door.
string GetSecretDoorTag();
string GetSecretDoorTag()
    {
    int i = GetLocalInt(GetModule(),"SecretDoor");
    SetLocalInt(GetModule(),"SecretDoor",i+=1);
    return "SecretDoor"+IntToString (i);
    }
// Modified version of CreateObject that can be used as a command.
void CreateObject2(int iObjectType,string sResRef,location lLoc, int iAnim = FALSE, string sTag = "");
void CreateObject2(int iObjectType,string sResRef,location lLoc, int iAnim = FALSE, string sTag = "")
    {
    CreateObject(iObjectType,sResRef,lLoc,iAnim,sTag);
    }
// Creates a hidden treasure.
void CreateHiddenTreasure(location lLoc);
void CreateHiddenTreasure(location lLoc)
    {
    int iLocked,iTrapped;
    object oObject = CreateObject(OBJECT_TYPE_PLACEABLE,"rdg_chest",lLoc,FALSE);
    if (d100() <= HIDDEN_TREASURE_LOCKED)
        SetLocked(oObject,TRUE);
    if (d100() > HIDDEN_TREASURE_TRAPPED)
        SetTrapDisabled(oObject);
    }

// Determines if oPC has an effect of iEffectType.
int HasEffect(object oPC, int iEffectType);
int HasEffect(object oPC, int iEffectType)
    {
    effect eEffect = GetFirstEffect(oPC);
    while(GetIsEffectValid(eEffect))
        {
        if (GetEffectType(eEffect) == iEffectType)
            return TRUE;
        eEffect = GetNextEffect(oPC);
        }
    return FALSE;
    }

// Does a standard ability check vs iDC returning TRUE/FALSE.
// If iDC = 0 returns the skill check total.
int AbilityCheck(object oPC,int iAbility,int iDC);
int AbilityCheck(object oPC,int iAbility,int iDC)
    {
    int iRoll;
    // GetAbility
    int iRank = GetAbilityModifier(iAbility,oPC);
    if (GetIsObjectValid(GetItemPossessedBy(oPC,"miscb031")))
        iRank+=1;
    // Determine Roll
        iRoll = d20()+iRank;
    // Return Results
    if (iDC == 0)
        return iRoll;
    else if (iDC != 0 && iRoll >= iDC)
        return TRUE;
    return FALSE;
    }
// Returns an effect based Effect Type and Creator
effect GetEffect(object oTarget, int iEffectType, object oCreator = OBJECT_INVALID);
effect GetEffect(object oTarget, int iEffectType, object oCreator)
    {
    effect eEffect = GetFirstEffect(oTarget);
    while(GetIsEffectValid(eEffect))
        {
        if (GetEffectType(eEffect) == iEffectType)
            {
            if (!GetIsObjectValid(oCreator)
            || GetEffectCreator(eEffect) == oCreator)
                break;
            }
        eEffect = GetNextEffect(oTarget);
        }
    return eEffect;
    }
// This function is used to add damage for a curse it should be used in a delayed loop.
void CurseDamage (object oCreator,effect eEffect, object oTarget);
void CurseDamage (object oCreator,effect eEffect, object oTarget)
    {
    effect eTemp = GetEffect(oTarget,EFFECT_TYPE_CURSE,oCreator);
    if (GetIsEffectValid(eTemp))
        {
        ApplyEffectToObject(DURATION_TYPE_INSTANT,eEffect,oTarget);
        }
    }
// This command will return true if the pc has a gem of the appropriate type or
// better. It will then decrement the stack of gems by one then return to the
// calling function/script.
int GetHasGem(int iValue, object oPC);
int GetHasGem(int iValue, object oPC)
    {
    int iPassed;
    int i;
    string sTag,sResRef;
    object oGem;
    int iStack;
    for (i = iValue; i<= 5; i+=1)
        {
        sResRef = "rdg_gem"+IntToString(i)+"01";
        sTag = "rdg_gem"+IntToString(i)+"01";
        oGem = GetItemPossessedBy(oPC,sTag);
        if (GetIsObjectValid(oGem))
            {
            iStack = GetNumStackedItems(oGem);
            if (iStack > 1)
                CreateItemOnObject(sResRef,oPC,iStack-1);
            DestroyObject(oGem);
            iPassed = TRUE;
            break;
            }
        }
    return iPassed;
    }
// MUST be called by a PC. Handles the detection of hidden objects.
void DetectMode();
void DetectMode()
    {
    int iDC,iSecretDoors;
    int iCntr = 1;
    object oDoor,oChest;
    object oHidden = GetNearestObjectToLocation(OBJECT_TYPE_DOOR|OBJECT_TYPE_PLACEABLE|OBJECT_TYPE_TRIGGER|OBJECT_TYPE_WAYPOINT,GetLocation(OBJECT_SELF),iCntr);
    while(GetIsObjectValid(oHidden))
        {
        if (GetDistanceToObject(oHidden) > 10.0)
            break; // Search has gone beyond allowed distance shut down loop.
        if (LineOfSightObject(OBJECT_SELF,oHidden))
            {
            string sTag = GetTag(oHidden);
            //--------------------------DETECT TRAPS ------------------------------
            if (GetIsTrapped(oHidden))
                {
                if (!GetTrapDetectedBy(oHidden,OBJECT_SELF)) // If trap not detected
                    {
                    iDC = GetLocalInt(oHidden,"DC");
                    if (!iDC) // If no DC stored on trap make one.
                        iDC = ScaledDC(PartyLevel(OBJECT_SELF));
                    if (!GetLevelByClass(CLASS_TYPE_ROGUE)
                     && GetLocalInt(oHidden,"TrapType") > 0
                     && iDC >= 25)
                        iDC = 10000; // Only rogues can find non mechanical traps DC 25+
                    if (SkillCheck(SKILL_SEARCH,iDC))
                        {
                        SetTrapDetectedBy(oHidden,OBJECT_SELF);
                        PlayVoiceChat(VOICE_CHAT_SEARCH);
                        }
                    }
                }
            //---------------------------SECRET DOORS -------------------------
            else if (sTag == "SecretDoor")
                {
                if (GetLocalInt(oHidden,"Active"))
                    {
                    iDC = GetLocalInt(oHidden,"DC");
                    if (!iDC)
                        iDC = ScaledDC(PartyLevel(OBJECT_SELF));
                    if (SkillCheck(SKILL_SEARCH,iDC))
                        {
                        oDoor = GetNearestObjectByTag("rdg_secret_door");
                        if (GetIsObjectValid(oDoor))
                            {
                            if (GetLocation(oDoor) != GetLocation(oHidden))//Attempt to stop double secret door spawning.
                                {
                                // CREATE SECRET DOOR AND UPDATE MODULE REGISTRY
                                oDoor = CreateObject(OBJECT_TYPE_PLACEABLE,"rdg_secret_door",GetLocation(oHidden),FALSE);
                                SetLocalInt(oHidden,"Active",FALSE);//Turn off checks
                                SetLocalObject(oHidden,"SecretDoor",oDoor);
                                PlayVoiceChat(VOICE_CHAT_LOOKHERE);
                                }
                            }
                        else
                            {
                            // CREATE SECRET DOOR AND UPDATE MODULE REGISTRY
                            oDoor = CreateObject(OBJECT_TYPE_PLACEABLE,"rdg_secret_door",GetLocation(oHidden),FALSE);
                            SetLocalInt(oHidden,"Active",FALSE);//Turn off checks
                            SetLocalObject(oHidden,"SecretDoor",oDoor);
                            PlayVoiceChat(VOICE_CHAT_LOOKHERE);
                            }
                        }
                    }
                }
            // ------------------------ HIDDEN TREASURE -----------------------
            else if (sTag == "HiddenChest")
                {
                if (GetLocalInt(oHidden,"Active"))
                    {
                    iDC = GetLocalInt(oHidden,"DC");
                    if (!iDC)
                        iDC = ScaledDC(PartyLevel(OBJECT_SELF));
                    if (SkillCheck(SKILL_SEARCH,iDC))
                        {
                        oChest = GetNearestObjectByTag("RDG_CHEST");//Check this tag
                        if (GetIsObjectValid(oChest))
                            {
                            if (GetLocation(oChest) != GetLocation(oHidden))//Attempt to stop double spawning.
                                {
                                // CREATE SECRET DOOR AND UPDATE MODULE REGISTRY
                                oChest = CreateObject(OBJECT_TYPE_PLACEABLE,"rdg_chest",GetLocation(oHidden),FALSE);
                                SetLocalInt(oHidden,"Active",FALSE);//Turn off checks
                                SetLocalObject(oHidden,"HiddenChest",oChest);
                                PlayVoiceChat(VOICE_CHAT_LOOKHERE);
                                }
                            }
                        else
                            {
                            // CREATE SECRET DOOR AND UPDATE MODULE REGISTRY
                            oChest = CreateObject(OBJECT_TYPE_PLACEABLE,"rdg_chest",GetLocation(oHidden),FALSE);
                            SetLocalInt(oHidden,"Active",FALSE);//Turn off checks
                            SetLocalObject(oHidden,"HiddenChest",oChest);
                            PlayVoiceChat(VOICE_CHAT_LOOKHERE);
                            }
                        }
                    }
                }
            }
        oHidden = GetNearestObjectToLocation(OBJECT_TYPE_DOOR|OBJECT_TYPE_PLACEABLE|OBJECT_TYPE_TRIGGER|OBJECT_TYPE_WAYPOINT,GetLocation(OBJECT_SELF),iCntr+=1);
        }
    }
// Returns the effects of a poison trap.
effect GetPoisonType(int iDangerLevel,int iAOE);
effect GetPoisonType(int iDamage,int iAOE)
    {
    effect eDam;
    if (iAOE)
        {
        switch (iDamage)
            {
            case TRAP_WEAK:     {eDam = EffectPoison(POISON_UNGOL_DUST);break;}
            case TRAP_AVERAGE:  {eDam = EffectPoison(POISON_CHAOS_MIST);break;}
            case TRAP_STRONG:   {eDam = EffectPoison(POISON_BURNT_OTHUR_FUMES);break;}
            case TRAP_DEADLY:   {eDam = EffectPoison(POISON_DRAGON_BILE);break;}
            case TRAP_FATAL:    {eDam = EffectPoison(POISON_BLACK_LOTUS_EXTRACT);break;}
            };
        }
    else
        {
        switch (iDamage)
            {
            case TRAP_WEAK:     {eDam = EffectPoison(POISON_GREENBLOOD_OIL);break;}
            case TRAP_AVERAGE:  {eDam = EffectPoison(POISON_PURPLE_WORM_POISON);break;}
            case TRAP_STRONG:   {eDam = EffectPoison(POISON_WYVERN_POISON);break;}
            case TRAP_DEADLY:   {eDam = EffectPoison(POISON_DRAGON_BILE);break;}
            case TRAP_FATAL:    {eDam = EffectPoison(POISON_BLACK_LOTUS_EXTRACT);break;}
            };
        }
    return eDam;
    }

// Returns the amount of damage a trap will do.
int GetTrapDamage(int iLevel,int iDangerLevel);
int GetTrapDamage(int iLevel,int iDangerLevel)
    {
    int iDam,i;
    if (iLevel > 0)
        {
        for (i = 1; i <= iLevel; i++)
            {
            switch (iDangerLevel)
                {
                case TRAP_WEAK:     {iDam += d6();break;}
                case TRAP_AVERAGE:  {iDam += d8();break;}
                case TRAP_STRONG:   {iDam += d10();break;}
                case TRAP_DEADLY:   {iDam += d12();break;}
                case TRAP_FATAL:    {iDam += d20();break;}
                };
            }
        }
    return iDam;
    }

// Gets the visual effect for a trap.
effect GetVisualEffect(int iAOE, int iDamageType);
effect GetVisualEffect(int iAOE,int iType)
    {
    effect eVis;
    if (iAOE)
        {
        switch(iType)
            {
            case DAMAGE_TYPE_BLUDGEONING:   {eVis = EffectAreaOfEffect(AOE_PER_ENTANGLE," ", " ", " ");break;}
            case DAMAGE_TYPE_PIERCING:      {eVis = EffectVisualEffect(VFX_DUR_CALTROPS);break;}
            case DAMAGE_TYPE_SLASHING:      {eVis = EffectVisualEffect(VFX_DUR_CALTROPS);break;}

            case DAMAGE_TYPE_ACID:          {eVis = EffectAreaOfEffect(AOE_PER_FOGACID," ", " ", " ");break;}
            case DAMAGE_TYPE_COLD:          {eVis = EffectAreaOfEffect(AOE_PER_FOGKILL," ", " ", " ");break;}
            case DAMAGE_TYPE_ELECTRICAL:    {eVis = EffectVisualEffect(VFX_BEAM_LIGHTNING);break;}
            case DAMAGE_TYPE_FIRE:          {eVis = EffectVisualEffect(VFX_FNF_FIREBALL);break;}
            case DAMAGE_TYPE_SONIC:         {eVis = EffectVisualEffect(VFX_FNF_SOUND_BURST);break;}

            case DAMAGE_TYPE_MAGICAL:       {eVis = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_MIND);break;}
            case DAMAGE_TYPE_NEGATIVE:      {eVis = EffectAreaOfEffect(AOE_PER_GREASE," ", " ", " ");break;}
            case DAMAGE_TYPE_POSITIVE:      {eVis = EffectVisualEffect(VFX_FNF_STRIKE_HOLY);break;}

            case DAMAGE_TYPE_POISON:        {eVis = EffectAreaOfEffect(AOE_PER_FOGSTINK," ", " ", " ");break;}
            };
        }
    else
        {
        switch(iType)
            {
            case DAMAGE_TYPE_BLUDGEONING:   {eVis = EffectVisualEffect(VFX_IMP_WALLSPIKE);break;}
            case DAMAGE_TYPE_PIERCING:      {eVis = EffectVisualEffect(VFX_IMP_SPIKE_TRAP);break;}
            case DAMAGE_TYPE_SLASHING:      {eVis = EffectVisualEffect(VFX_FNF_SWINGING_BLADE);break;}

            case DAMAGE_TYPE_ACID:          {eVis = EffectVisualEffect(VFX_COM_HIT_ACID);break;}
            case DAMAGE_TYPE_COLD:          {eVis = EffectVisualEffect(VFX_IMP_HEAD_COLD);break;}
            case DAMAGE_TYPE_ELECTRICAL:    {eVis = EffectVisualEffect(VFX_COM_HIT_ELECTRICAL);break;}
            case DAMAGE_TYPE_FIRE:          {eVis = EffectVisualEffect(VFX_COM_HIT_FIRE);break;}
            case DAMAGE_TYPE_SONIC:         {eVis = EffectVisualEffect(VFX_COM_HIT_SONIC);break;}

            case DAMAGE_TYPE_MAGICAL:       {eVis = EffectVisualEffect(VFX_DUR_SMOKE);break;}
            case DAMAGE_TYPE_NEGATIVE:      {eVis = EffectVisualEffect(VFX_COM_HIT_NEGATIVE);break;}
            case DAMAGE_TYPE_POSITIVE:      {eVis = EffectVisualEffect(VFX_COM_HIT_DIVINE);break;}

            case DAMAGE_TYPE_POISON:        {eVis = EffectVisualEffect(VFX_IMP_POISON_L);break;}
            };
        }
    return eVis;
    }
// Returns the Reflex save needed to avoid a trap.
// Not used by poison traps.
int GetReflexSaveDC(int iLevel,int iDangerLevel);
int GetReflexSaveDC(int iLevel,int iDangerLevel)
    {
    int iDC = iLevel;
    if      (iDangerLevel == TRAP_WEAK)    iDC += 5;
    else if (iDangerLevel == TRAP_AVERAGE) iDC += 10;
    else if (iDangerLevel == TRAP_STRONG)  iDC += 15;
    else if (iDangerLevel == TRAP_DEADLY)  iDC += 20;
    else if (iDangerLevel == TRAP_FATAL)   iDC += 25;
    if (iDC < 10)
        iDC = 10;
    return iDC;
    }

// This function will randomly return an unused door.
// If iPit is TRUE will return a "Fall" waypoint instead.
// This function attempts 10 times to find a destination.
// Failure returns OBJECT_INVALID.
object DetermineDestination(int iPit = FALSE, int iSecret = FALSE);
object DetermineDestination(int iPit, int iSecret = FALSE)
    {
    object oDest;
    int iCntr;
    int iDoors = GetLocalInt(GetModule(),"Doors");
    int iFalls = GetLocalInt(GetModule(),"Falls");
    int iSecrets = GetLocalInt(GetModule(),"SecretDoorWPs");
    int iType = GetObjectType(OBJECT_SELF);
    for (iCntr = 1; iCntr <= 10; iCntr++)// Try 10 times to find a link
        {
        if (GetLocalInt(OBJECT_SELF,"Pit") || iPit)
            oDest = GetLocalObject(GetModule(),"Fall"+IntToString(Random(iFalls)+1));
        else if (iSecret)
            oDest = GetLocalObject(GetModule(),"SecretDoor"+IntToString(Random(iSecrets)+1));
        else if (iType == OBJECT_TYPE_DOOR)
            oDest = GetLocalObject(GetModule(),"Door"+IntToString(Random(iDoors)+1));
        if (GetArea(OBJECT_SELF) == GetArea(oDest))
            oDest = OBJECT_INVALID;
        if (GetIsObjectValid(GetLocalObject(oDest,"Dest")))
            oDest = OBJECT_INVALID;
        if (GetIsObjectValid(oDest))
            break;
        }
    return oDest;
    }

// This function should be used in an AssignCommand to a PC.
// This function will transport the PC and his associates to a place determined
// by the DetermineDestination function.
void Transition(object oDest);
void Transition(object oDest)
    {
    location lLoc;
    if (GetIsTrapped(oDest))
        SetTrapDisabled(oDest);
    if (GetLocked(oDest))
        SetLocked(oDest,FALSE);
    if (!GetIsOpen(oDest))
        AssignCommand(oDest,ActionOpenDoor(oDest));
    object oTrans = GetNearestObjectByTag("Transition",oDest);
    if (GetIsObjectValid(oTrans)
     && GetDistanceBetween(oDest,oTrans) < 5.0)
        oDest = oTrans;
    lLoc = GetLocation(oDest);
    ClearAllActions();
    ActionJumpToLocation(lLoc);
    // Transition the PCs familiars, henchmen, etc.
    object oAssociate = GetFirstFactionMember(OBJECT_SELF,FALSE);
    while (GetIsObjectValid(oAssociate))
        {
        if (GetMaster(oAssociate) == OBJECT_SELF)
            {
            AssignCommand(oAssociate,ClearAllActions(TRUE));
            AssignCommand(oAssociate,ActionJumpToLocation(lLoc));
            }
        oAssociate = GetNextFactionMember(OBJECT_SELF,FALSE);
        }
    DelayCommand(2.0,SetFacing(GetFacing(oDest)));
    DelayCommand(2.0,SetCameraFacing(GetFacing(oDest)));
    }

// Used to link any 2 transtions.
// Is sometimes used to mark a Fall or Secret Door waypoint as used.
void LinkTransitions(object oDest, object oSelf =OBJECT_SELF);
void LinkTransitions(object oDest,object oSelf)
    {
    SetLocalObject(oDest,"Dest",oSelf);
    SetLocalObject(oSelf,"Dest",oDest);
    }

// Used to create a randomly placed entrance to undermountain.
// iPit determiens if this should be a "Fall" waypoint.
// If set to FALSE this function will link and return a random door inside the
// dungeon.
// sTag is the Tag of the object you want the entrance to be.
// If sTag returns an invalid object the caller will be used.
object CreateDungeonEntrance(int iPit = TRUE, string sTag = "YawningPortalWell");
object CreateDungeonEntrance(int iPit, string sTag)
    {
    object oTarget = GetObjectByTag(sTag);
    if (!GetIsObjectValid(oTarget))
        oTarget = OBJECT_SELF;
    object oDest = DetermineDestination(iPit);
    if (iPit)
        {
        object oRope = CreateObject(OBJECT_TYPE_PLACEABLE,"rdg_rope_used",GetLocation(oDest),TRUE);
        SetPlotFlag(oRope,TRUE);
        LinkTransitions(oDest,oTarget);//Activates the waypoint to prevent reuse
        LinkTransitions(oRope,oTarget);//Makes the actual link.
        oDest = oRope;
        }
    else
        LinkTransitions(oDest,oTarget);
    return oDest;
    }

// This command will clear ALL transition links in the dungeon.
void ClearAllTransitions();
void ClearAllTransitions()
    {
    int iDoors = GetLocalInt(GetModule(),"Doors");
    int iSecretDoors = GetLocalInt(GetModule(),"SecretDoorWPs");
    int iFalls = GetLocalInt(GetModule(),"Falls");
    int iDynamic = GetLocalInt(GetModule(),"SecretDoor");
    int i,j;
    object oArea,oDoor,oWP,oTrap,oRope;
    // Clear all door transitions.
    for (i = 1; i <= iDoors; i+=1)
        {
        oDoor = GetLocalObject(GetModule(),"Door"+IntToString(i));
        DeleteLocalObject(oDoor,"Dest");
        }
    // Reset all secret door waypoints to new values.
    for (i = 1; i <= iSecretDoors; i += 1)
        {
        oWP = GetLocalObject(GetModule(),"SecretDoor"+IntToString(i));
        oDoor = GetLocalObject(oWP,"SecretDoor");
        if (d100() <= ROOM_SECRET_DOOR)
            SetLocalInt(oWP,"Active",TRUE);
        else
            DeleteLocalInt(oWP,"Active");
        DeleteLocalObject(oWP,"Dest");
        if (GetIsObjectValid(oDoor))
            {
            SetPlotFlag(oDoor,FALSE);
            DestroyObject(oDoor);
            }
        }
    // Destroy any player made doors.
    for (i = 1; i <= iDynamic; i+=1)
        {
        oDoor = GetObjectByTag("SecretDoor"+IntToString (i));
        SetPlotFlag(oDoor,FALSE);
        oWP = GetNearestObjectByTag("Transition",oDoor);
        if (GetLocalString(oWP,"Tag") != "")
            DestroyObject (oWP);
        DestroyObject (oDoor);
        }
    // Reset all FALL Waypoints and destroy Ropes.
    for (i = 1; i <= iFalls; i+=1)
        {
        oWP = GetLocalObject(GetModule(),"Fall"+IntToString(i));
        oDoor = GetLocalObject(oWP,"Dest");
        if (GetIsObjectValid(oDoor))
            {
            // Find and destroy possible rope attached to trap door.
            if (GetObjectType(oDoor) == OBJECT_TYPE_PLACEABLE)
                {
                oTrap = GetLocalObject(oDoor,"Dest");
                if (GetTag(oTrap) != "YawningPortalWell")// DONT KILL THE WELL!
                    {
                    SetPlotFlag(oTrap,FALSE);
                    DestroyObject(oTrap);
                    }
                }
            if (GetTag(oDoor) != "YawningPortalWell")// DONT KILL THE WELL!
                {
                //Destroy the trap door.
                SetPlotFlag(oDoor,FALSE);
                DestroyObject(oDoor);
                }
            }
        DeleteLocalObject(oWP,"Dest");
        }
    DeleteLocalInt(GetModule(),"AreasExplored");
    CreateDungeonEntrance();
    }

// This function returns the danger level of a trap.
int GetTrapDangerLevel();
int GetTrapDangerLevel()
    {
    int iRoll = d100();
    if (iRoll <= TRAP_WEAK)         {return TRAP_WEAK;}
    else if (iRoll <= TRAP_AVERAGE) {return TRAP_AVERAGE;}
    else if (iRoll <= TRAP_STRONG)  {return TRAP_STRONG;}
    else if (iRoll <= TRAP_DEADLY)  {return TRAP_DEADLY;}
    else if (iRoll <= TRAP_FATAL)   {return TRAP_FATAL;}
    return TRAP_WEAK;//This should never happen. Added for compile reasons only.
    }

// Determines the type of trap.
// Only a rogue can detect traps over DC25 if not mechanical.
int SetTrapType();
int SetTrapType()
    {
    int iRoll = d100();
    if (iRoll <= TRAP_MECHANICAL)
        return TRAP_MECHANICAL;
    else if (iRoll <= TRAP_ELEMENTAL)
        return TRAP_ELEMENTAL;
    else if (iRoll <= TRAP_SPECIAL)
        return TRAP_SPECIAL;
    return 1; //This should never happen included for compile reasons
    }

// Determines the damage type of a trap.
int GetDamageType(int iType);
int GetDamageType(int iType)
    {
    int iRoll,iSave;
    if (iType == TRAP_MECHANICAL) // Mechanical Trap
        iRoll = d4();
    else if (iType == TRAP_ELEMENTAL) // Elemental Magic Trap
        iRoll = Random(5)+5;
    else if (iType == TRAP_SPECIAL) // Special Magic Trap
        iRoll = Random(3)+10;
    // Determine Savign Throw Type
    switch (iRoll)
        {
        //Mechanical
        case 1: {iType = DAMAGE_TYPE_BLUDGEONING;   iSave = SAVING_THROW_TYPE_TRAP;break;}
        case 2: {iType = DAMAGE_TYPE_PIERCING;      iSave = SAVING_THROW_TYPE_TRAP;break;}
        case 3: {iType = DAMAGE_TYPE_SLASHING;      iSave = SAVING_THROW_TYPE_TRAP;break;}
        case 4: {iType = DAMAGE_TYPE_POISON;        iSave = SAVING_THROW_TYPE_POISON;break;}
        //Elemental
        case 5: {iType = DAMAGE_TYPE_ACID;          iSave = SAVING_THROW_TYPE_ACID;break;}
        case 6: {iType = DAMAGE_TYPE_COLD;          iSave = SAVING_THROW_TYPE_COLD;break;}
        case 7: {iType = DAMAGE_TYPE_ELECTRICAL;    iSave = SAVING_THROW_TYPE_ELECTRICITY;break;}
        case 8: {iType = DAMAGE_TYPE_FIRE;          iSave = SAVING_THROW_TYPE_FIRE;break;}
        case 9: {iType = DAMAGE_TYPE_SONIC;         iSave = SAVING_THROW_TYPE_SONIC;break;}
        //Special
        case 10: {iType = DAMAGE_TYPE_NEGATIVE;      iSave = SAVING_THROW_TYPE_NEGATIVE;break;}
        case 11: {iType = DAMAGE_TYPE_MAGICAL;       iSave = SAVING_THROW_TYPE_SPELL;break;}
        case 12: {iType = DAMAGE_TYPE_POSITIVE;      iSave = SAVING_THROW_TYPE_POSITIVE;break;}
        }
    SetLocalInt(OBJECT_SELF,"SaveThrow",iSave);
    return iType;
    }
// This function is used to determine the basic properties of a trap.
void SetUpTrap(object oTrap);
void SetUpTrap(object oTrap)
    {
    // Determine weak to fatal trap type
    int iDangerLevel = GetTrapDangerLevel();
    //Determine if Area of Effect Trap.
    int iAOE;
    if (d100() <= TRAP_AOE)
        iAOE = TRUE;
    // Determine if Trap is Mechanical or Magic
    int iType = SetTrapType();
    if (iType == TRAP_MECHANICAL
     && d100() <= TRAP_PIT)
        SetLocalInt(oTrap,"Pit",TRUE);// All Pits are bludgeoning and use special rules.
    else
        SetLocalInt(oTrap,"DamageType",GetDamageType(iType));
    SetLocalInt(oTrap,"TrapType",iType);
    SetLocalInt(oTrap,"AOE",iAOE);
    SetLocalInt(oTrap,"Danger",iDangerLevel);
    }

// Sets up the traps in an area.
void SetUpTraps(object oArea);
void SetUpTraps(object oArea)
    {
    int iCntr,iRoll;
    object oObject = GetFirstObjectInArea(oArea);
    while (GetIsObjectValid(oObject))
        {
        // IF A TRAP
        if (GetIsTrapped(oObject))
            {
            if (GetObjectType(oObject) == OBJECT_TYPE_TRIGGER)
                iRoll = ROOM_TRAP_PERCENTAGE;
            else if (GetObjectType(oObject) == OBJECT_TYPE_DOOR)
                iRoll = DOOR_TRAPED_PERCENTAGE;
            else if (GetObjectType(oObject) == OBJECT_TYPE_PLACEABLE)
                iRoll = PLACEABLE_TRAP_PERCENTAGE;
            if (d100() <= iRoll)
                SetUpTrap(oObject);
            else
                SetTrapDisabled(oObject);
            }
        iRoll = 0; //Reset Variable for next object.
        oObject = GetNextObjectInArea(oArea);
        }
    }


// This function awards the CR value of XPs to the party of oPC.
void AwardXPs(object oPC);
void AwardXPs(object oPC)
    {
    if (GetObjectType(oPC) == OBJECT_TYPE_AREA_OF_EFFECT)
        {
        oPC = GetAreaOfEffectCreator(oPC);
        if (!GetIsPC(oPC))
            oPC = GetItemPossessor(oPC);
        }
    if (!GetIsPC(oPC) && !GetIsPC(GetMaster(oPC)))
        return;
    int iCR,iXP,iBase;
    float fCR;
    object oParty;
    int iParty = PartyLevel(oPC);
    if (!iParty)
        return;
    if (GetObjectType(OBJECT_SELF) == OBJECT_TYPE_CREATURE)
        {
        if (GetIsObjectValid(GetMaster())) // Prevent XPs for summoned monsters.
            return;
        else if (!GetIsObjectValid(oPC))
            return;
        else
            fCR = GetChallengeRating(OBJECT_SELF);
        }
    else
        fCR = GetLocalFloat(OBJECT_SELF,"CR");
    if (fCR < 1.0)
        iCR = 1;
    else
        iCR = FloatToInt(fCR);
    int iStep = iParty - iCR;
    if (iStep >= 8)
        {
        SendMessageToPC(oPC,"CR too low to be awarded XPs.");
        return;
        }
    else if (iStep <= -8)
        {
        iStep = -7;
        }
    else if (iParty <=6 && iCR <=1)
        iXP = 300;
    else if (iParty <=4 && iCR ==2)
        iXP = 600;
    else
        {
        iBase = 300 * iParty;
        switch(iStep)
            {
            case -7 : {iXP = iBase*12;break;}
            case -6 : {iXP = iBase*8;break;}
            case -5 : {iXP = iBase*6;break;}
            case -4 : {iXP = iBase*4;break;}
            case -3 : {iXP = iBase*3;break;}
            case -2 : {iXP = iBase*2;break;}
            case -1 : {iXP = iBase/2*3;break;}
            case 0  : {iXP = iBase;break;}
            case 1  : {iXP = iBase/3*2; break;}
            case 2  : {iXP = iBase/2;break;}
            case 3  : {iXP = iBase/3;break;}
            case 4  : {iXP = iBase/4;break;}
            case 5  : {iXP = iBase/6;break;}
            case 6  : {iXP = iBase/8;break;}
            case 7  : {iXP = iBase/12; break;}
            };
        }
    if (fCR < 1.0)
        iXP = FloatToInt(IntToFloat(iXP)*fCR);
    iXP = iXP/NumberInParty(oPC);
    oParty = GetFirstFactionMember(oPC);
    while(GetIsObjectValid(oParty))
        {
        if (GetIsPC(oParty))
            GiveXPToCreature(oParty,iXP);
        oParty = GetNextFactionMember(oPC);
        }
    }
//This function is what does the Rest Resets for the RDG.
// This function will reset the rests for the entire party belonging to oPC.
void CheckSafeRest(object oPC);
void CheckSafeRest(object oPC)
    {
    if (d100() <= SAFE_AREA_PERCENTAGE)
        {
        oPC = GetFirstFactionMember(oPC);
        while (GetIsObjectValid(oPC))
            {
            SetLocalInt(oPC,"Rest",TRUE);
            DelayCommand(2.0,FloatingTextStringOnCreature("Rest Reset.",oPC,FALSE));
            SetPanelButtonFlash(oPC,PANEL_BUTTON_REST,FALSE);
            oPC = GetNextFactionMember(oPC);
            }
        }
    }
// Will return true if oPC has an item that grants feather fall effects.
int CheckFeatherFall(object oPC);
int CheckFeatherFall(object oPC)
    {
    object oSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC);
    if (GetLocalInt(oSkin,"miscb036")
     || GetLocalInt(oSkin,"miscb080")
     || GetLocalInt(oSkin,"ring035")
     || GetLocalInt(oSkin,"ring030"))
        return TRUE;
    return FALSE;
    }

//This function handles pit type transitions.
// The optional parameters can be used to change how a pit works.
void TransitionPit(object oPC,int iFallIn = TRUE,int iJump = FALSE,int iDC = 0,int iDepth = 0, object oPit = OBJECT_SELF, object oDest = OBJECT_INVALID);
void TransitionPit(object oPC,int iFallIn,int iJump,int iDC,int iDepth, object oPit, object oDest)
    {
    if (!iDC)
        iDC = GetLocalInt(oPit,"Reflex");
    if (!iDepth)
        iDepth = GetLocalInt(oPit,"Dice");
    if (!GetIsObjectValid(oDest))
        oDest = GetLocalObject(oPit,"Dest");
    if ((iFallIn && !ReflexSave(oPC,iDC))
      || iJump)
        {
        AssignCommand(oPC,ClearAllActions(TRUE));
        AssignCommand(oPC,Transition(oDest));
        if (!CheckFeatherFall(oPC))// If has feather fall they will never take damage.
            {
            int iMonk = GetLevelByClass(CLASS_TYPE_MONK,oPC);// Monks dont take as much damage from falls
            if (iMonk >= 18)    iDepth = 0;
            else if (iMonk >= 8)iDepth -= 5;
            else if (iMonk >= 6)iDepth -=3;
            else if (iMonk >= 4)iDepth -=2;
            if (iDepth > 0)
                {
                int iDam = d6(iDepth);
                AssignCommand(oPC,ActionDoCommand(ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectDamage(iDam,DAMAGE_TYPE_BLUDGEONING,DAMAGE_POWER_ENERGY),oPC)));
                }
            }
        }
    else if (!iFallIn && !iJump)
        {
        AssignCommand(oPC,ClearAllActions(TRUE));
        AssignCommand(oPC,Transition(oDest));
        }
    }

// Will spawn a pit and transition link it at the PCs feet.
// If iFallIn is TRUE he will fall thru it on a failed Reflex Save.
// If iJump is TRUE he will fall thru and take damage.
void CreatePit(object oPC,int iFallIn = TRUE, int iJump = FALSE, object oTrap = OBJECT_SELF);
void CreatePit(object oPC,int iFallIn, int iJump, object oTrap)
    {
    //--------------------------DETERMINE PIT SPECIFICS------------------------
    // Create top of pit
    object oPit = CreateObject(OBJECT_TYPE_PLACEABLE,"rdg_pit" + IntToString(d4()),GetLocation(oPC));
    // Determine bottom of pit
    object oDest = DetermineDestination(TRUE);
    // Get Danger Level, This is used to determine how hard the pit is to avoid.
    int iDanger = GetLocalInt(oTrap,"Danger");
    // Scale Fall Distance
    int iDepth = 20 - ScaledDC(PartyLevel(oPC));
    if (iDepth < 1) iDepth = 1;   // Pits are always at least 1d6 damage
    if (iDepth > 20) iDepth = 20; //Pits are NEVER more than 20 d6 damage.
    // -------------------------APPLY PIT SPECIFICS---------------------------
    int iDC = GetReflexSaveDC(PartyLevel(oPC),iDanger);
    SetLocalInt(oPit,"Danger",iDanger);
    SetLocalInt(oPit,"Reflex",iDC);
    SetLocalInt(oPit,"Dice",iDepth);
    LinkTransitions(oDest,oPit); // Marks Fall waypoint so not used by RDG again. Establishes a link the rope conversation can use.
    if (iFallIn || iJump)
        TransitionPit(oPC,iFallIn,iJump,iDC,iDepth,oPit,oDest);
    SetTrapDisabled(oTrap);
    }

// Applies trap effects to oTarget.
void ApplyTrap(object oTarget);
void ApplyTrap(object oTarget)
    {
    //---------------------------- PITS ---------------------------------------
    if (GetLocalInt(OBJECT_SELF,"Pit"))
        {
        DeleteLocalInt(OBJECT_SELF,"Pit");// Had to add this to prevent pit trapped doors from having odd transitions
        CreatePit(oTarget,TRUE);
        return;
        }
    //---------------------------- DETERMINE TRAP SPECIFICS -------------------
    object oTrap = OBJECT_SELF;
    int iPartyLevel = PartyLevel(oTarget);
    location lLoc = GetLocation(oTarget);
    //Determine if Area of Effect Trap.
    int iAOE = GetLocalInt(oTrap,"AOE");
    // Determine weak to fatal trap type
    int iDangerLevel = GetLocalInt(oTrap,"Danger");
    // Determine DamageType
    int iType = GetLocalInt(oTrap,"DamageType");
    // Get Saving Throw
    int iSave = GetLocalInt(OBJECT_SELF,"SaveThrow");
    //Determine Trap Damage
    effect eDam;
    int iDam,iDC;
    if (iType == DAMAGE_TYPE_POISON)
        eDam = GetPoisonType(iDangerLevel,iAOE);
    else
        {
        iDam = GetTrapDamage(iPartyLevel,iDangerLevel);
        iDC = GetReflexSaveDC(iPartyLevel,iDangerLevel);
        }
    // Determine Trap Visual Effect
    effect eVis = GetVisualEffect(iAOE,iType);
    //--------------------------- APPLY THE TRAP EFFECTS-----------------------
    object oObject;
    if (iAOE)//----------------------BEGIN AREA EFFECT TRAPS-------------------
        {
        // ----------------------- APPLY TRAP TO PRIMARY TARGET----------------
        if (iType != DAMAGE_TYPE_ELECTRICAL)
            ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY,eVis,lLoc,3.0);
        // Apply Trap effect to Target.
        if (iType == DAMAGE_TYPE_POISON) // Poison Traps
            ApplyEffectToObject(DURATION_TYPE_PERMANENT,eDam,oTarget);
        else //Non Poison Traps
            {
            if (iType == DAMAGE_TYPE_ELECTRICAL)
                ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVis,oTarget,2.0);
            ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectDamage(GetReflexAdjustedDamage(iDam,oTarget,iDC,iSave,OBJECT_SELF),iType),oTarget);
            }
        // --------------------- APPLY TRAP TO SECONDARY TARGETS---------------
        // Added this due to primary target was getting hit twice by AE effects.
        oObject = GetFirstObjectInShape(SHAPE_SPHERE,8.0,lLoc,TRUE);
        if (iType == DAMAGE_TYPE_POISON) // Poison Traps
            {
            while(GetIsObjectValid(oObject))
                {
                if (oObject != oTarget)
                    ApplyEffectToObject(DURATION_TYPE_PERMANENT,eDam,oObject);
                oObject = GetNextObjectInShape(SHAPE_SPHERE,8.0,lLoc,TRUE);
                }
            }
        else //Non Poison Traps
            {
            while(GetIsObjectValid(oObject))
                {
                if (oObject != oTarget)
                    {
                    ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectDamage(GetReflexAdjustedDamage(iDam,oTarget,iDC,iSave,OBJECT_SELF),iType),oTarget);
                    if (iType == DAMAGE_TYPE_ELECTRICAL)
                        {AssignCommand(oTarget, ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVis,oObject,2.0));}
                    }
                oObject = GetNextObjectInShape(SHAPE_SPHERE,8.0,lLoc,FALSE);
                }
            }
        }// ------------------------END AREA OF EFFECT TRAPS-------------------
    else
        {// ------------------------BEGIN PERSONAL TRAPS-----------------------
        if (iType == DAMAGE_TYPE_POISON) // Poison Traps
            {
            ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVis,oTarget,2.0);
            ApplyEffectToObject(DURATION_TYPE_PERMANENT,eDam,oTarget);
            }
        else // Non Poison Traps
            {
            ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVis,oTarget,2.0);
            ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectDamage(GetReflexAdjustedDamage(iDam,oTarget,iDC,iSave,OBJECT_SELF),iType),oTarget);
            }
        }//-------------------------END PERSONAL TRAPS-------------------------
    SetTrapDisabled(OBJECT_SELF);
    }

// This function will reactivate all monster spawns in the dungeon.
void ResetMonsterSpawns();
void ResetMonsterSpawns()
    {
    int iMon = GetLocalInt(GetModule(),"RoomMonsterWPs");
    int i;
    object oWP, oArea, oMonster, oItem;
    for (i = 1; i<= iMon; i+=1)
        {
        oWP = GetLocalObject(GetModule(),"MonsterWP"+IntToString(i));
        oArea = GetArea(oWP);
        oMonster = GetFirstObjectInArea(oArea);
        while(GetIsObjectValid(oMonster))
            {
            if (GetObjectType(oMonster) == OBJECT_TYPE_CREATURE
             && !GetIsPC(oMonster)
             && !GetIsPC(GetMaster(oMonster)))
                {
                AssignCommand(oMonster,SetIsDestroyable(TRUE));
                DestroyObject(oMonster);
                }
            else if (GetTag(oMonster) == "X0_CORPSE")
                {
                oItem = GetFirstItemInInventory(oMonster);
                while(GetIsObjectValid(oItem))
                    {
                    SetPlotFlag(oItem,FALSE);
                    DestroyObject(oItem);
                    oItem = GetNextItemInInventory(oMonster);
                    }
                DestroyObject(oMonster);
                }
            oMonster = GetNextObjectInArea(oArea);
            }
        DeleteLocalInt(oWP,"DoOnce");
        DeleteLocalInt(GetArea(oWP),"DoOnce");
        }
    }

// This function will reset the hidden treasure waypoints.
void ResetHiddenWaypoints();
void ResetHiddenWaypoints()
    {
    int iCntr;
    int iHidden = GetLocalInt(GetModule(),"HiddenTreasureWPs");
    object oHidden, oChest, oItem;
    for (iCntr = 1; iCntr <= iHidden; iCntr+=1)
        {
        oHidden = GetLocalObject(GetModule(),"HiddenTreasure"+IntToString(iCntr));
        oChest = GetLocalObject(oHidden,"HiddenChest");
        if (GetIsObjectValid(oChest))
            {
            oItem = GetFirstItemInInventory(oChest);
            while(GetIsObjectValid(oItem))
                {
                SetPlotFlag(oItem,FALSE);
                DestroyObject(oItem);
                oItem = GetNextItemInInventory(oChest);
                }
            SetPlotFlag(oChest,FALSE);
            DestroyObject(oChest);
            }
        if (d100() <= ROOM_HIDDEN_TREASURE)
            SetLocalInt(oHidden,"Active",TRUE);
        else
            DeleteLocalInt(oHidden,"Active");
        }
    }
// Returns the highest skill rank in the party.
// This only counts PCs.
// Also only works if teh PC is in the same area as the caller
int GetHighestPartySkill(object oPartyMember,int iSkill);
int GetHighestPartySkill(object oPartyMember,int iSkill)
    {
    object oPC = GetFirstFactionMember(oPartyMember);
    int iRank,iHigh;
    while(GetIsObjectValid(oPC))
        {
        if (GetIsPC(oPC))
            {
            if (GetArea(oPC) == GetArea(OBJECT_SELF))
                {
                iRank = GetSkillRank(iSkill,oPC);
                if (iRank > iHigh)
                    iHigh = iRank;
                }
            oPC = GetNextFactionMember(oPartyMember);
            }
        }
    return iHigh;
    }

// Returns the party member with the highest skill rank.
// This only counts PCs.
// Also only works if teh PC is in the same area as the caller
object GetHighestPartySkillMember(object oPartyMember,int iSkill);
object GetHighestPartySkillMember(object oPartyMember,int iSkill)
    {
    object oPC = GetFirstFactionMember(oPartyMember);
    int iRank,iHigh;
    object oHigh;
    while(GetIsObjectValid(oPC))
        {
        if (GetIsPC(oPC))
            {
            if (GetArea(oPC) == GetArea(OBJECT_SELF))
                {
                iRank = GetSkillRank(iSkill,oPC);
                if (iRank > iHigh)
                    {
                    iHigh = iRank;
                    oHigh = oPC;
                    }
                }
            oPC = GetNextFactionMember(oPartyMember);
            }
        }
    return oHigh;
    }
//Returns true is a PC has food or a magical replacement.
int GetHasFood(object oPC);
int GetHasFood(object oPC)
    {
    object oSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC);
    if (GetIsObjectValid(GetItemPossessedBy(oPC,"food")))
        return TRUE;
    else if (GetLocalInt(oSkin,"misca079"))
        return TRUE;
    else if (GetLocalInt(oSkin,"miscb047"))
        return TRUE;
    else if (GetLocalInt(oSkin,"ring002"))
        return TRUE;
    else if (GetLocalInt(oSkin,"ring036"))
        return TRUE;
    else if (GetLocalInt(oSkin,"ring033"))
        return TRUE;
    else if (GetLocalInt(oPC,"Food"))
        return TRUE;
    return FALSE;
    }
// This script is used for creating LARGE amounts of gold.
// Up to 500,000 gold pieces.
void CreateGold(int iGold, object oBox = OBJECT_SELF);
void CreateGold(int iGold, object oBox)
    {
    while (iGold > 50000)
        {
        CreateItemOnObject("nw_it_gold001",oBox,50000);
        iGold = iGold - 50000;
        }
    CreateItemOnObject("nw_it_gold001",oBox,iGold);
    }
// Returns TRUE if oPC in in an area with the "Town" variable set to TRUE.
int GetIsInTown(object oPC = OBJECT_SELF);
int GetIsInTown(object oPC)
    {
    return GetLocalInt(GetArea(oPC),"Town");
    }
// Returns TRUE is oPC has a item activated that gives them a rest.
int HasRestGivingItem(object oPC);
int HasRestGivingItem(object oPC)
    {
    object oSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR,OBJECT_SELF);
    if (GetLocalInt(oSkin,"miscb038"))      return TRUE;
    else if (GetLocalInt(oSkin,"miscb047")) return TRUE;
    return FALSE;
    }

int HasAvailableRest(object oPC);
int HasAvailableRest(object oPC)
    {
    if (HasRestGivingItem(oPC))             return TRUE;
    else if (GetLocalInt(oPC,"Rest"))       return TRUE;
    return FALSE;
    }

// Returns true if all the party has rested.
int GetHasAllPartyRested(object oPC);
int GetHasAllPartyRested(object oRester)
    {
    object oPC = GetFirstFactionMember(oRester);
    while(GetIsObjectValid(oPC))
        {
        if (!GetIsDead(oPC))  // Dead PC's dont count
            {
            if (!GetIsInTown(oPC)) // PC's in Town don't count
                {
                if (!HasRestGivingItem(oPC))// PCs with Free rest ability dont count.
                    {
                    if (GetLocalInt(oPC,"Rest"))
                        return FALSE;
                    }
                }
            }
        oPC = GetNextFactionMember(oRester);
        }
    return TRUE;
    }

// Destroys food in a PC inventory if they have any.
void EatFood(object oPC);
void EatFood(object oPC)
    {
    if (GetLocalInt(oPC,"Food"))
        DeleteLocalInt(oPC,"Food");
    else if (GetIsObjectValid(GetItemPossessedBy(oPC,"food")))
        DestroyObject(GetItemPossessedBy(oPC,"food"));
    }

// Resets the rest counter for the party.
void ResetPartyRests(object oRester);
void ResetPartyRests(object oRester)
    {
    object oPC = GetFirstFactionMember(oRester);
    while(GetIsObjectValid(oPC))
        {
        SetLocalInt(oPC,"Rest",TRUE);
        SetPanelButtonFlash(oPC,PANEL_BUTTON_REST,FALSE);
        oPC = GetNextFactionMember(oRester);
        }
    }

// Determines if another party member is resting.
int IsAnotherPartyMemberResting(object oRester);
int IsAnotherPartyMemberResting(object oRester)
    {
    object oPC = GetFirstFactionMember(oRester);
    while(GetIsObjectValid(oPC))
        {
        if (GetIsResting(oPC))
            return TRUE;
        oPC = GetNextFactionMember(oRester);
        }
    return FALSE;
    }

int GetRandomAbility();
int GetRandomAbility()
    {
    int iRoll =d6();
    int iAbility;
    switch (iRoll)
        {
        case 1:{iAbility = ABILITY_STRENGTH;break;}
        case 2:{iAbility = ABILITY_DEXTERITY;break;}
        case 3:{iAbility = ABILITY_CONSTITUTION;break;}
        case 4:{iAbility = ABILITY_INTELLIGENCE;break;}
        case 5:{iAbility = ABILITY_WISDOM;break;}
        case 6:{iAbility = ABILITY_CHARISMA;break;}
        }
    return iAbility;
    }

void NotifyPartyAverageLevel(object oPC);
void NotifyPartyAverageLevel(object oPC)
    {
    object oLeader = GetFactionLeader(oPC);
    int iAPL = PartyLevel(oPC);
    SendMessageToPC(oLeader,"Average Party Level is: "+IntToString(iAPL));
    }

int GetMinLevelXP(object oPC,int iLevel = 0);
int GetMinLevelXP(object oPC,int iLevel)
    {
    int nHD = GetHitDice(oPC) + iLevel;
    int nMinXPForLevel = ((nHD * (nHD - 1)) / 2) * 1000;
    return nMinXPForLevel;
    }
