//::///////////////////////////////////////////////
//:: Scrying include
//:: prc_inc_scry
//::///////////////////////////////////////////////
/** @file
    This include contains all of the code that is used
    to scry. At the moment, this is the basic scrying
    and will be expanded with the additional material
    as coding goes along. This is based on the code
    of the Baelnorn Project written by Ornedan.

    All the operations work only on PCs, as there is no
    AI that could have NPCs take any advantage of the
    system.
*/
//:://////////////////////////////////////////////
//:: Created By: Stratovarius
//:: Created On: 30.04.2007
//:://////////////////////////////////////////////

#include "psi_inc_psifunc"
#include "inc_utility"
#include "true_utter_const"
#include "shd_myst_const"

///////////////////////
/* Public Constants  */
///////////////////////

const string COPY_LOCAL_NAME             = "Scry_Copy";
const string ALREADY_IMMORTAL_LOCAL_NAME = "Scry_ImmortalAlready";
const float  SCRY_HB_DELAY         = 1.0f;

//////////////////////////////////////////////////
/* Function prototypes                          */
//////////////////////////////////////////////////

/**
 * ONLY CALL THIS FUNCTION
 * Does all of the necessary set up for scrying
 * Also checks for all of the anti-scrying features
 * if oPC == oTarget, will use PRCGetSpellLocation 
 * as the location for the effects to begin
 *
 * @param oPC     The PC on whom to operate.
 * @param oTarget The object to target
 */
void ScryMain(object oPC, object oTarget);

/**
 * Moves all of the PC's items to the copy creature
 * Switches locations of the two creatures
 *
 * @param oPC     The PC on whom to operate.
 * @param oCopy   The copy of the PC
 */
void DoScryBegin(object oPC, object oCopy);

/**
 * Undoes all the effects of DoScryBegin
 *
 * @param oPC   The PC on whom to operate.
 * @param oCopy The copy of the PC
 */
void DoScryEnd(object oPC, object oCopy);

/**
 * Runs tests to see if the Scry effect can still continue.
 * If the copy is dead, end Scry and kill the PC.
 *
 * @param oPC   The PC on whom to operate.
 * @param oCopy The copy of the PC
 */
void ScryMonitor(object oPC, object oCopy);

/**
 * Reverses ApplyScryEffects
 *
 * @param oPC   The PC on whom to operate.
 */
void RemoveScryEffects(object oPC);

/**
 * Checks whether the PC is scrying or not
 *
 * @param oPC   The PC on whom to operate.
 */
int GetIsScrying(object oPC);

/**
 * Does the Discern Location content
 *
 * @param oPC     The PC on whom to operate.
 * @param oTarget The spell Target
 */
void DiscernLocation(object oPC, object oTarget);

/**
 * Does the Locate Creature and Object content
 *
 * @param oPC     The PC on whom to operate.
 * @param oTarget The spell Target
 */
void LocateCreatureOrObject(object oPC, object oTarget);

/**
 * Prevents the copy from dropping goods when dead.
 *
 * @param oImage  The PC's copy.
 */
void CleanCopy(object oImage);

//////////////////////////////////////////////////
/* Function definitions                         */
//////////////////////////////////////////////////

void ScryMain(object oPC, object oTarget)
{
    // Get the main variables used.
    object oCopy     = GetLocalObject(oPC, COPY_LOCAL_NAME);
    effect eLight    = EffectVisualEffect(VFX_IMP_HEALING_X , FALSE);
    effect eGlow     = EffectVisualEffect(VFX_DUR_ETHEREAL_VISAGE, FALSE);
    // Use prestored variables because of time delay
    int nCasterLevel = GetLocalInt(oPC, "ScryCasterLevel");
    int nSpell       = PRCGetSpellId(); //GetLocalInt(oPC, "ScrySpellId");
    int nDC          = GetLocalInt(oPC, "ScrySpellDC");
    float fDur       = GetLocalFloat(oPC, "ScryDuration");
    if(DEBUG) DoDebug("prc_inc_scry: Beginning ScryMain. nSpell: " + IntToString(nSpell));

    if (oPC != oTarget) // No hitting yourself with defenses
    {
        if(GetHasSpellEffect(POWER_REMOTE_VIEW_TRAP, oTarget))
        {
            effect eVis  = EffectVisualEffect(VFX_IMP_LIGHTNING_S);
            // Failed Save
            if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_NONE)) 
            {
                FloatingTextStringOnCreature("You have been caught in a Remote View Trap", oPC, FALSE);
                eVis = EffectLinkEffects(eVis, EffectDamage(d6(8), DAMAGE_TYPE_ELECTRICAL));
                ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
                return;
            }
            else
            {
                FloatingTextStringOnCreature("You have saved against a Remote View Trap", oPC, FALSE);
                eVis = EffectLinkEffects(eVis, EffectDamage(d6(4), DAMAGE_TYPE_ELECTRICAL));
                ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
            }
        }
        if(GetHasSpellEffect(SPELL_SEQUESTER, oTarget) || GetHasSpellEffect(POWER_SEQUESTER, oTarget))
        {
            // Spell auto-fails if this is the case
            FloatingTextStringOnCreature(GetName(oTarget) + " has been Sequestered.", oPC, FALSE);
            return;
        }
        if(GetHasSpellEffect(SPELL_OBSCURE_OBJECT, oTarget))
        {
            // Spell auto-fails if this is the case
            FloatingTextStringOnCreature(GetName(oTarget) + " has been Obscured.", oPC, FALSE);
            return;
        }
        if(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) 
        {
            // Caster level check or the Divination fails.
            int nTarget = PRCGetCasterLevel(oTarget) + 11;
        	if (GetRacialType(oTarget) == RACIAL_TYPE_SKULK && 20 > nTarget) nTarget = 20;
            if(nTarget > nCasterLevel + d20())
            {
                FloatingTextStringOnCreature(GetName(oTarget) + " has Nondetection active.", oPC, FALSE);
                return;
            }
        }
        if(GetHasSpellEffect(POWER_ESCAPE_DETECTION, oTarget))
        {
            // Caster level check or the Divination fails.
            // Max of 10
            if(max(10, GetManifesterLevel(oTarget)) + 13 > nCasterLevel + d20())
            {
                FloatingTextStringOnCreature(GetName(oTarget) + " has Escape Detection active.", oPC, FALSE);
                return;
            }
        }
        if(GetHasSpellEffect(POWER_DETECT_REMOTE_VIEWING, oTarget))
        {
            // Caster level check for the target to learn where the caster is
            if(GetManifesterLevel(oTarget) + d20() >= nCasterLevel + d20())
            {
                FloatingTextStringOnCreature(GetName(oPC) + " is viewing you from " + GetName(GetArea(oPC)), oTarget, FALSE);
            }
        }
    }
    // Discern Location skips all of this
    if(nSpell == SPELL_DISCERN_LOCATION)
    {
        DiscernLocation(oPC, oTarget);
        return;
    }
    // So do the Locate Twins
    if(nSpell == SPELL_LOCATE_CREATURE || nSpell == SPELL_LOCATE_OBJECT || nSpell == MYST_TRAIL_OF_HAZE)
    {
        LocateCreatureOrObject(oPC, oTarget);
        return;
    }

    if(nSpell != SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE && nSpell != POWER_CLAIRTANGENT_HAND && nSpell != SPELL_PNP_SCRY_FAMILIAR
    && nSpell != POWER_CLAIRVOYANT_SENSE && nSpell != TRUE_SEE_THE_NAMED && oPC != oTarget) // No save if you target yourself.
    {
        //Make SR Check
        if(PRCDoResistSpell(oPC, oTarget, nCasterLevel))
        {
            FloatingTextStringOnCreature(GetName(oTarget) + " made Spell Resistance check vs Scrying", oPC, FALSE);
            DoScryEnd(oPC, oCopy);
            return;
        }
        //Save
        if(PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_MIND_SPELLS))
        {
            FloatingTextStringOnCreature(GetName(oTarget) + " saved vs Scrying", oPC, FALSE);
            DoScryEnd(oPC, oCopy);
            return;
        }
    }

    if(DEBUG) DoDebug("prc_inc_scry running.\n"
                    + "oPC = '" + GetName(oPC) + "' - '" + GetTag(oPC) + "' - " + ObjectToString(oPC)
                    + "Copy exists: " + DebugBool2String(GetIsObjectValid(oCopy))
                      );
    
    location lTarget = GetLocation(oTarget);
    if (oTarget == oPC || !GetIsObjectValid(oTarget)) // Some spells just target ground for this
        lTarget = PRCGetSpellTargetLocation();
    // Create the copy
    oCopy = CopyObject(oPC, lTarget, OBJECT_INVALID, GetName(oPC) + "_" + COPY_LOCAL_NAME);
    CleanCopy(oCopy);
    // Attempted Fix to the Copy get's murdered problem.
    int nMaxHenchmen = GetMaxHenchmen();
    SetMaxHenchmen(99);
    AddHenchman(oPC, oCopy);
    SetMaxHenchmen(nMaxHenchmen);
    SetIsTemporaryFriend(oPC, oCopy, FALSE);
    // Set the copy to be undestroyable, so that it won't vanish to the ether
    // along with the PC's items.
    AssignCommand(oCopy, SetIsDestroyable(FALSE, FALSE, FALSE));
    // Make the copy immobile and minimize the AI on it
    ApplyEffectToObject(DURATION_TYPE_PERMANENT, ExtraordinaryEffect(EffectCutsceneImmobilize()), oCopy);
    SetAILevel(oCopy, AI_LEVEL_VERY_LOW);

    // Store a referece to the copy on the PC
    SetLocalObject(oPC, COPY_LOCAL_NAME, oCopy);

    //Set Immortal flag on the PC or if they were already immortal,
    //leave a note about it on them.
    if(GetImmortal(oPC))
    {
        if(DEBUG) DoDebug("prc_inc_scry: The PC was already immortal");
        SetLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME, TRUE);
    }
    else
    {
        if(DEBUG) DoDebug("prc_inc_scry: Setting PC immortal");
        SetImmortal(oPC, TRUE);
        DeleteLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME); // Paranoia
    }

    // Do VFX on PC and copy
    SPApplyEffectToObject(DURATION_TYPE_INSTANT, eLight, oCopy);
    SPApplyEffectToObject(DURATION_TYPE_INSTANT, eLight, oPC);

    // Do the switching around
    DoScryBegin(oPC, oCopy);

    //Set up duration marker for ending effect
    DelayCommand(fDur, SetLocalInt(oPC, "SCRY_EXPIRED", 1));
}

// Moves the PC's items to the copy and switches their locations around
void DoScryBegin(object oPC, object oCopy)
{
    if(DEBUG) DoDebug("prc_inc_scry: DoScryBegin():\n"
                    + "oPC = '" + GetName(oPC) + "'\n"
                    + "oCopy = '" + GetName(oCopy) + "'"
                      );
    // Make sure both objects are valid
    if(!GetIsObjectValid(oCopy) || !GetIsObjectValid(oPC)){
        if(DEBUG) DoDebug("DoScryBegin called, but one of the parameters wasn't a valid object. Object status:" +
                          "\nPC - " + (GetIsObjectValid(oPC) ? "valid":"invalid") +
                          "\nCopy - " + (GetIsObjectValid(oCopy) ? "valid":"invalid")
                          );

        // Some cleanup before aborting
        if(!GetLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME))
        {
            SetImmortal(oPC, FALSE);
            DeleteLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME);
        }
        MyDestroyObject(oCopy);
        DeleteLocalInt(oPC, "Scry_Active");
        RemoveScryEffects(oPC);

        return;
    }

    // Set a local on the PC telling that it's a scry. This is used
    // to keep the PC from picking up or losing objects.
    // Also stops it from casting spells
    SetLocalInt(oPC, "Scry_Active", TRUE);

    // Start a pseudo-hb to monitor the status of both PC and copy
    DelayCommand(SCRY_HB_DELAY, ScryMonitor(oPC, oCopy));

    // Add eventhooks
    
    if(DEBUG) DoDebug("AddEventScripts");
    
    AddEventScript(oPC, EVENT_ONPLAYEREQUIPITEM,    "prc_scry_event", TRUE, FALSE); // OnEquip
    AddEventScript(oPC, EVENT_ONACQUIREITEM,        "prc_scry_event", TRUE, FALSE); // OnAcquire
    AddEventScript(oPC, EVENT_ONPLAYERREST_STARTED, "prc_scry_event", FALSE, FALSE); // OnRest
    AddEventScript(oPC, EVENT_ONCLIENTENTER,        "prc_scry_event", TRUE, FALSE); //OnClientEnter
    
    // Adjust reputation so the target monster doesn't attack you
    //ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectCharmed()), oTarget, GetLocalFloat(oPC, "ScryDuration"));
    // Swap the copy and PC
    location lPC   = GetLocation(oPC);
    location lCopy = GetLocation(oCopy);
    DelayCommand(1.5f,AssignCommand(oPC, JumpToLocation(lCopy)));
    DelayCommand(1.5f,AssignCommand(oCopy, JumpToLocation(lPC)));
}

// Switches the PC's inventory back from the copy and returns the PC to the copy's location.
void DoScryEnd(object oPC, object oCopy)
{
    if(DEBUG) DoDebug("prc_inc_scry: DoScryEnd():\n"
                    + "oPC = '" + GetName(oPC) + "'\n"
                    + "oCopy = '" + GetName(oCopy) + "'"
                      );

    //effect eGlow     = EffectVisualEffect(VFX_DUR_ETHEREAL_VISAGE, FALSE);
    effect eLight = EffectVisualEffect(VFX_IMP_HEALING_X , FALSE);
    
    // Remove Immortality from the PC if necessary
    if(!GetLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME))
        SetImmortal(oPC, FALSE);

    // Remove the VFX and the attack penalty from all spells.
    PRCRemoveSpellEffects(SPELL_SCRY             , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_GREATER_SCRYING  , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_DISCERN_LOCATION , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_LOCATE_CREATURE  , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_LOCATE_OBJECT    , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_ARCANE_EYE       , oPC, oPC);
    PRCRemoveSpellEffects(MYST_BEND_PERSPECTIVE  , oPC, oPC);
    PRCRemoveSpellEffects(MYST_FAR_SIGHT         , oPC, oPC);
    PRCRemoveSpellEffects(MYST_EPHEMERAL_IMAGE   , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_SHADOWDOUBLE     , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_OBSCURE_OBJECT   , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_SEQUESTER        , oPC, oPC);
    PRCRemoveSpellEffects(SPELL_PNP_SCRY_FAMILIAR, oPC, oPC);
    PRCRemoveSpellEffects(SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE, oPC, oPC);

    // Remove the local signifying that the PC is a projection
    DeleteLocalInt(oPC, "Scry_Active");

    // Remove the local signifying projection being terminated by an external cause
    DeleteLocalInt(oPC, "Scry_Abort");

    // Remove the heartbeat HP tracking local
    DeleteLocalInt(oPC, "Scry_HB_HP");
    // Delete all of the marker ints for the spell
    DeleteLocalInt(oPC, "ScryCasterLevel");
    DeleteLocalInt(oPC, "ScrySpellId");
    DeleteLocalInt(oPC, "ScrySpellDC");
    DeleteLocalFloat(oPC, "ScryDuration");

    // Remove weapons nerfing
    RemoveScryEffects(oPC);

    // Remove eventhooks
    RemoveEventScript(oPC, EVENT_ONPLAYEREQUIPITEM,    "prc_scry_event", TRUE, FALSE); // OnEquip
    RemoveEventScript(oPC, EVENT_ONACQUIREITEM,        "prc_scry_event", TRUE, FALSE); // OnAcquire
    RemoveEventScript(oPC, EVENT_ONPLAYERREST_STARTED, "prc_scry_event", FALSE, FALSE); // OnRest
    RemoveEventScript(oPC, EVENT_ONCLIENTENTER,        "prc_scry_event", TRUE, FALSE); //OnClientEnter

    // Move PC and inventory
    location lCopy = GetLocation(oCopy);
    DelayCommand(1.5f, AssignCommand(oPC, JumpToLocation(lCopy)));

    // Set the PC's hitpoints to be whatever the copy has
    int nOffset = GetCurrentHitPoints(oCopy) - GetCurrentHitPoints(oPC);
    if(nOffset > 0)
        ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nOffset), oPC);
    else if (nOffset < 0)
        ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(-nOffset, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY), oPC);

    // Schedule deletion of the copy
    DelayCommand(0.3f, MyDestroyObject(oCopy));

    //Delete the object reference
    DeleteLocalObject(oPC, COPY_LOCAL_NAME);

    // VFX
    ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eLight, lCopy, 3.0);
    DestroyObject(oCopy);
    
    //Remove duration marker
    DeleteLocalInt(oPC, "SCRY_EXPIRED");
}

//Runs tests to see if the projection effect can still continue.
//If the PC has reached 1 HP, end projection normally.
//If the copy is dead, end projection and kill the PC.
void ScryMonitor(object oPC, object oCopy)
{
    if(DEBUG) DoDebug("prc_inc_scry: ScryMonitor():\n"
                    + "oPC = '" + GetName(oPC) + "'\n"
                    + "oCopy = '" + GetName(oCopy) + "'"
                      );
    // Abort if the projection is no longer marked as being active
    if(!GetLocalInt(oPC, "Scry_Active"))
        return;

    // Some paranoia in case something interfered and either PC or copy has been destroyed
    if(!(GetIsObjectValid(oPC) && GetIsObjectValid(oCopy))){
        WriteTimestampedLogEntry("Baelnorn Projection hearbeat aborting due to an invalid object. Object status:" +
                                 "\nPC - " + (GetIsObjectValid(oPC) ? "valid":"invalid") +
                                 "\nCopy - " + (GetIsObjectValid(oCopy) ? "valid":"invalid"));
        return;
    }

    // Start the actual work by checking the copy's status. The death thing should take priority
    if(GetIsDead(oCopy))
    {
        if (DEBUG) DoDebug("prc_inc_scry: Copy is dead, killing PC");
        DoScryEnd(oPC, oCopy);
        effect eKill = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY);
        DelayCommand(3.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eKill, oPC));
    }
    else
    {
        // Check if the "projection" has been destroyed or if some other event has caused the projection to end
        if(GetLocalInt(oPC, "Scry_Abort"))
        {
            if(DEBUG) DoDebug("prc_inc_scry: ScryMonitor(): The Projection has been terminated, ending projection");
            DoScryEnd(oPC, oCopy);
        }
        else
        {
            // This makes sure you are invisible.
            //AssignCommand(oPC, ClearAllActions(TRUE));
            ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectEthereal()), oPC, SCRY_HB_DELAY + 0.3);
            DelayCommand(SCRY_HB_DELAY, ScryMonitor(oPC, oCopy));
        }
        
        //If duration expired, end effect
        if(GetLocalInt(oPC, "SCRY_EXPIRED"))
        {
            DoScryEnd(oPC, oCopy);
        }
        // End due to being in combat, except when ephemeral image
        if(GetIsInCombat(oPC) && GetLocalInt(oPC, "ScrySpellId") != MYST_EPHEMERAL_IMAGE)
        {
            DoScryEnd(oPC, oCopy);
            if(DEBUG) DoDebug("prc_inc_scry: ScryMonitor(): Scryer in combat, ending projection");
            AssignCommand(oPC, ClearAllActions(TRUE));
        }
        // In PnP, once you lose line of effect, the spell ends
        float fDistance = FeetToMeters(100.0 + GetLocalInt(oPC, "ScryCasterLevel") * 10.0);
        if (GetLocalInt(oPC, "ScrySpellId") == MYST_EPHEMERAL_IMAGE && GetDistanceBetween(oPC, oCopy) > fDistance)
        {
            DoScryEnd(oPC, oCopy);
            if(DEBUG) DoDebug("prc_inc_scry: ScryMonitor(): Ephemeral Image distance exceeded, ending projection");
            AssignCommand(oPC, ClearAllActions(TRUE));            
        }
    }
}

//Undoes changes made in ApplyScryEffects().
void RemoveScryEffects(object oPC)
{
    if(DEBUG) DoDebug("prc_inc_scry: RemoveScryEffects():\n"
                    + "oPC = '" + GetName(oPC) + "'"
                      );
    effect eCheck = GetFirstEffect(oPC);
    while(GetIsEffectValid(eCheck)){
        if(GetEffectSpellId(eCheck) == GetLocalInt(oPC, "ScrySpellId"))
        {
            RemoveEffect(oPC, eCheck);
        }
        eCheck = GetNextEffect(oPC);
    }

    // Remove the no-damage property from all weapons it was added to
    int i;
    object oWeapon;
    for(i = 0; i < array_get_size(oPC, "Scry_Nerfed"); i++)
    {
        oWeapon = array_get_object(oPC, "Scry_Nerfed", i);
        IPRemoveMatchingItemProperties(oWeapon, ITEM_PROPERTY_NO_DAMAGE, DURATION_TYPE_PERMANENT);
    }

    array_delete(oPC, "Scry_Nerfed");
}

int GetIsScrying(object oPC)
{
    return GetLocalInt(oPC, "Scry_Active");
}

void DiscernLocation(object oPC, object oTarget)
{
    // Tells the name of the area the target is in
    // Yes, this is a little crappy for an 8th level spell.
    FloatingTextStringOnCreature(GetName(oTarget) + " is in " + GetName(GetArea(oTarget)), oPC, FALSE);

    // Delete all of the marker ints for the spell
    DeleteLocalInt(oPC, "ScryCasterLevel");
    DeleteLocalInt(oPC, "ScrySpellId");
    DeleteLocalInt(oPC, "ScrySpellDC");
    DeleteLocalFloat(oPC, "ScryDuration");
}

void LocateCreatureOrObject(object oPC, object oTarget)
{
    location lPC     = GetLocation(oPC);
    location lTarget = GetLocation(oTarget);
    // Cause the caller to face the target
    SetFacingPoint(GetPosition(oTarget));
    // Now spit out the distance
    float fDist = GetDistanceBetweenLocations(lPC, lTarget);
    FloatingTextStringOnCreature(GetName(oTarget) + " is " + FloatToString(MetersToFeet(fDist)) + " feet away from you.", oPC, FALSE);

    // Delete all of the marker ints for the spell 
    DeleteLocalInt(oPC, "ScryCasterLevel");
    DeleteLocalInt(oPC, "ScrySpellId");
    DeleteLocalInt(oPC, "ScrySpellDC");
    DeleteLocalFloat(oPC, "ScryDuration");
}

void CleanCopy(object oImage)
{
    SetLootable(oImage, FALSE);
    // remove inventory contents
    object oItem = GetFirstItemInInventory(oImage);
    while(GetIsObjectValid(oItem))
    {
        SetPlotFlag(oItem,FALSE);
        if(GetHasInventory(oItem))
        {
            object oItem2 = GetFirstItemInInventory(oItem);
            while(GetIsObjectValid(oItem2))
            {
                object oItem3 = GetFirstItemInInventory(oItem2);
                while(GetIsObjectValid(oItem3))
                {
                    SetPlotFlag(oItem3,FALSE);
                    DestroyObject(oItem3);
                    oItem3 = GetNextItemInInventory(oItem2);
                }
                SetPlotFlag(oItem2,FALSE);
                DestroyObject(oItem2);
                oItem2 = GetNextItemInInventory(oItem);
            }
        }
        DestroyObject(oItem);
        oItem = GetNextItemInInventory(oImage);
    }
    // remove non-visible equipped items
    int i;
    for(i=0;i<NUM_INVENTORY_SLOTS;i++)//equipment
    {
        oItem = GetItemInSlot(i, oImage);
        if(GetIsObjectValid(oItem))
        {
            if(i == INVENTORY_SLOT_HEAD || i == INVENTORY_SLOT_CHEST ||
                i == INVENTORY_SLOT_RIGHTHAND || i == INVENTORY_SLOT_LEFTHAND ||
                i == INVENTORY_SLOT_CLOAK) // visible equipped items
            {
                SetDroppableFlag(oItem, FALSE);
                SetItemCursedFlag(oItem, TRUE);
                // remove all item properties
                itemproperty ipLoop=GetFirstItemProperty(oItem);
                while (GetIsItemPropertyValid(ipLoop))
                {
                    RemoveItemProperty(oItem, ipLoop);
                    ipLoop=GetNextItemProperty(oItem);
                }
            }
            else // can't see it so destroy
            {                
                SetPlotFlag(oItem,FALSE);
                DestroyObject(oItem);
            }
        }
        
    }
    TakeGoldFromCreature(GetGold(oImage), oImage, TRUE);
}