//::///////////////////////////////////////////////
//:: Psionics include: Augmentation
//:: psi_inc_augment
//::///////////////////////////////////////////////
/** @file
    Defines structs and functions for handling
    psionic power augmentation.

    @author Ornedan
    @date   Created - 2005.11.04
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////


//////////////////////////////////////////////////
/*                 Constants                    */
//////////////////////////////////////////////////

// Constants are provided via psi_inc_core

/// Prefix of the local variable names used for storing an user's profiles
const string PRC_AUGMENT_PROFILE         = "PRC_Augment_Profile_";
/// Index of the currently used profile.
const string PRC_CURRENT_AUGMENT_PROFILE = "PRC_Current_Augment_Profile_Index";
/// Name of local variable where override is stored
const string PRC_AUGMENT_OVERRIDE        = "PRC_Augment_Override";
/// Name of local variable where the value of maximal augmentation switch is stored
const string PRC_AUGMENT_MAXAUGMENT      = "PRC_Augment_MaxAugment";

/// The lowest valid value of PRC_CURRENT_AUGMENT_PROFILE
const int PRC_AUGMENT_PROFILE_INDEX_MIN  = 1;
/// The highest valid value of PRC_CURRENT_AUGMENT_PROFILE
const int PRC_AUGMENT_PROFILE_INDEX_MAX  = 49;
/// Prefix of the local variable names used for storing quickselections
const string PRC_AUGMENT_QUICKSELECTION  = "PRC_Augment_Quickselection_";
/// The lowest value the quickslot index can have
const int PRC_AUGMENT_QUICKSELECTION_MIN = 1;
/// The highest value the quickslot index can have
const int PRC_AUGMENT_QUICKSELECTION_MAX = 7;
/// An index that should never contain a profile
const int PRC_AUGMENT_PROFILE_NONE = 0;

/// The value of an empty profile. Also known as zero
const int PRC_AUGMENT_NULL_PROFILE = 0x00000000;

/// The special value for nGenericAugCost in power augmentation profile that means the power has no generic augmentation.
const int PRC_NO_GENERIC_AUGMENTS    = -1;
/// The special value for nMaxAugs_* that means there is no limit to the times that option may be used.
const int PRC_UNLIMITED_AUGMENTATION = -1;

//////////////////////////////////////////////////
/*                 Structures                   */
//////////////////////////////////////////////////

/**
 * A structure used for defining how a particular power may be augmented.
 * Use PowerAugmentationProfile() to create.
 */
struct power_augment_profile{
    /**
     * Many powers specify several augmentation options and in addition a
     * "for each N PP spent augmenting this power, something happens".
     * This value is that N.
     */
    int nGenericAugCost;

    /**
     * How many PP the first augmentation option of the power will cost per
     * times used.
     */
    int nAugCost_1;
    /**
     * How many times, at most, can the first augmentation option be used.
     */
    int nMaxAugs_1;

    /**
     * How many PP the second augmentation option of the power will cost per
     * times used.
     */
    int nAugCost_2;
    /**
     * How many times, at most, can the second augmentation option be used.
     */
    int nMaxAugs_2;

    /**
     * How many PP the third augmentation option of the power will cost per
     * times used.
     */
    int nAugCost_3;
    /**
     * How many times, at most, can the third augmentation option be used.
     */
    int nMaxAugs_3;

    /**
     * How many PP the fourth augmentation option of the power will cost per
     * times used.
     */
    int nAugCost_4;
    /**
     * How many times, at most, can the fourth augmentation option be used.
     */
    int nMaxAugs_4;

    /**
     * How many PP the fifth augmentation option of the power will cost per
     * times used.
     */
    int nAugCost_5;
    /**
     * How many times, at most, can the fifth augmentation option be used.
     */
    int nMaxAugs_5;
};

/**
 * Users define how much PP they want to use for each augmentation option or
 * how many times they want to use each option. This structure is for transferring
 * that data.
 */
struct user_augment_profile{
    int nOption_1;
    int nOption_2;
    int nOption_3;
    int nOption_4;
    int nOption_5;

    /// Whether the values in this structure are to be interpreted as augmentation levels
    /// or as amounts of PP.
    int bValueIsPP;
};


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

/**
 * Constructs an augmentation profile for a power.
 * The default values for each parameter specify that the power in question
 * does not have that augmentation feature.
 *
 * @param nGenericAugCost Many powers have an augmentation clause saying "for
 *                        each N power points used to augment this power,
 *                        X happens". This parameter is used to define the
 *                        value N.
 *                        Valid values: {x = -1 OR x > 0}
 *                        Default: -1, which means that there is no generic
 *                        augmentation for this power.
 *
 * @param nAugCost_1      Cost to use the first augmentation option of this
 *                        power.
 *                        Valid values: {x >= 0}
 *                        Default: 0
 * @param nMaxAugs_1      Number of times the first augmentation option may at
 *                        most be used. Value of -1 means the option may be used
 *                        an unlimited number of times.
 *                        Valid values: {x >= -1}
 *                        Default: 0
 *
 * @param nAugCost_2      Cost to use the second augmentation option of this
 *                        power.
 *                        Valid values: {x >= 0}
 *                        Default: 0
 * @param nMaxAugs_2      Number of times the second augmentation option may at
 *                        most be used. Value of -1 means the option may be used
 *                        an unlimited number of times.
 *                        Valid values: {x >= -1}
 *                        Default: 0 *
 *
 * @param nAugCost_3      Cost to use the third augmentation option of this
 *                        power.
 *                        Valid values: {x >= 0}
 *                        Default: 0
 * @param nMaxAugs_3      Number of times the third augmentation option may at
 *                        most be used. Value of -1 means the option may be used
 *                        an unlimited number of times.
 *                        Valid values: {x >= -1}
 *                        Default: 0
 *
 * @param nAugCost_4      Cost to use the fourth augmentation option of this
 *                        power.
 *                        Valid values: {x >= 0}
 *                        Default: 0
 * @param nMaxAugs_4      Number of times the fourth augmentation option may at
 *                        most be used. Value of -1 means the option may be used
 *                        an unlimited number of times.
 *                        Valid values: {x >= -1}
 *                        Default: 0
 *
 * @param nAugCost_5      Cost to use the fifth augmentation option of this
 *                        power.
 *                        Valid values: {x >= 0}
 *                        Default: 0
 * @param nMaxAugs_5      Number of times the fifth augmentation option may at
 *                        most be used. Value of -1 means the option may be used
 *                        an unlimited number of times.
 *                        Valid values: {x >= -1}
 *                        Default: 0
 *
 * @return                The parameters compiled into a power_augment_profile
 *                        structure.
 */
struct power_augment_profile PowerAugmentationProfile(int nGenericAugCost = PRC_NO_GENERIC_AUGMENTS,
                                                      int nAugCost_1 = 0, int nMaxAugs_1 = 0,
                                                      int nAugCost_2 = 0, int nMaxAugs_2 = 0,
                                                      int nAugCost_3 = 0, int nMaxAugs_3 = 0,
                                                      int nAugCost_4 = 0, int nMaxAugs_4 = 0,
                                                      int nAugCost_5 = 0, int nMaxAugs_5 = 0
                                                      );

/**
 * Reads an augmentation profile from a user and compiles it into
 * a structure.
 *
 * @param oUser           A creature that has power augmentation profiles set up.
 * @param nIndex          The number of the profile to retrieve.
 * @param bQuickSelection Whether the index is a quickselection or a normal profile.
 * @return                The retrieved profile, compiled into a structure
 */
struct user_augment_profile GetUserAugmentationProfile(object oUser, int nIndex, int bQuickSelection = FALSE);

/**
 * Gets the user's current augmentation profile.
 *
 * @param oUser A creature that has power augmentation profiles set up.
 * @return      The retrieved profile, compiled into a structure
 */
struct user_augment_profile GetCurrentUserAugmentationProfile(object oUser);

/**
 * Stores a user-specified augmentation profile.
 *
 * @param oUser           The user for whose use to store the profile for.
 * @param nIndex          The index number to store the profile under.
 * @param bQuickSelection Whether the index is a quickselection or a normal profile.
 * @param uap             A structure containing the profile to store.
 */
void StoreUserAugmentationProfile(object oUser, int nIndex, struct user_augment_profile uap, int bQuickSelection = FALSE);

/**
 * Converts the given augmentation profile into a string.
 *
 * @param uap   The augmentation profile to convert to a string.
 * @return      A string of format:
 *              Option 1: N [times|PP], Option 2: N [times|PP], Option 2: N [times|PP], Option 4: N [times|PP], Option 5: N [times|PP]
 */
string UserAugmentationProfileToString(struct user_augment_profile uap);

/**
 * Calculates how many times each augmentation option of a power is used and
 * the increased PP cost caused by augmentation.
 * In addition to basic augmentation, currently accounts for:
 * - Wild Surge
 *
 *
 * @param manif The manifestation data related to an ongoing manifestation attempt.
 * @param pap   The power's augmentation profile.
 * @return      The manifestation data with augmentation's effects added in.
 */
struct manifestation EvaluateAugmentation(struct manifestation manif, struct power_augment_profile pap);

/**
 * Overrides the given creature's augmentation settings during it's next
 * manifestation with the given settings.
 *
 * NOTE: These values are assumed to be augmentation levels.
 *
 * @param oCreature Creature whose augmentation to override
 * @param uap       The profile to use as override
 */
void SetAugmentationOverride(object oCreature, struct user_augment_profile uap);


//////////////////////////////////////////////////
/*                  Includes                    */
//////////////////////////////////////////////////

#include "psi_inc_core"


//////////////////////////////////////////////////
/*             Internal functions               */
//////////////////////////////////////////////////

/** Internal function.
 * @param nToDecode Integer from which to extract an user augmentation profile
 * @return          An user augmentation profile created based on nToDecode
 */
struct user_augment_profile _DecodeProfile(int nToDecode)
{
    struct user_augment_profile uap;

    // The augmentation profile is stored in one integer, with 6 bits per option.
    // MSB -> [xx555555444444333333222222111111] <- LSB
    int nMask = 0x3F; // 6 LSB
    uap.nOption_1 = nToDecode          & nMask;
    uap.nOption_2 = (nToDecode >>> 6)  & nMask;
    uap.nOption_3 = (nToDecode >>> 12) & nMask;
    uap.nOption_4 = (nToDecode >>> 18) & nMask;
    uap.nOption_5 = (nToDecode >>> 24) & nMask;

    return uap;
}

/** Internal function.
 * @param uapToEncode An user augmentation profile to encode into a single integer
 * @return            Integer built from values in uapToEncode
 */
int _EncodeProfile(struct user_augment_profile uapToEncode)
{
    // The augmentation profile is stored in one integer, with 6 bits per option.
    // MSB -> [xx555555444444333333222222111111] <- LSB
    int nProfile = PRC_AUGMENT_NULL_PROFILE;
    int nMask = 0x3F; // 6 LSB

    nProfile |= (uapToEncode.nOption_1 & nMask);
    nProfile |= (uapToEncode.nOption_2 & nMask) << 6;
    nProfile |= (uapToEncode.nOption_3 & nMask) << 12;
    nProfile |= (uapToEncode.nOption_4 & nMask) << 18;
    nProfile |= (uapToEncode.nOption_5 & nMask) << 24;

    return nProfile;
}


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


struct power_augment_profile PowerAugmentationProfile(int nGenericAugCost = PRC_NO_GENERIC_AUGMENTS,
                                                      int nAugCost_1 = 0, int nMaxAugs_1 = 0,
                                                      int nAugCost_2 = 0, int nMaxAugs_2 = 0,
                                                      int nAugCost_3 = 0, int nMaxAugs_3 = 0,
                                                      int nAugCost_4 = 0, int nMaxAugs_4 = 0,
                                                      int nAugCost_5 = 0, int nMaxAugs_5 = 0
                                                      )
{
    struct power_augment_profile pap;

    pap.nGenericAugCost = nGenericAugCost;
    pap.nAugCost_1 = nAugCost_1;
    pap.nMaxAugs_1 = nMaxAugs_1;
    pap.nAugCost_2 = nAugCost_2;
    pap.nMaxAugs_2 = nMaxAugs_2;
    pap.nAugCost_3 = nAugCost_3;
    pap.nMaxAugs_3 = nMaxAugs_3;
    pap.nAugCost_4 = nAugCost_4;
    pap.nMaxAugs_4 = nMaxAugs_4;
    pap.nAugCost_5 = nAugCost_5;
    pap.nMaxAugs_5 = nMaxAugs_5;

    return pap;
}

struct user_augment_profile GetUserAugmentationProfile(object oUser, int nIndex, int bQuickSelection = FALSE)
{
    int nProfile = GetPersistantLocalInt(oUser, (bQuickSelection ? PRC_AUGMENT_QUICKSELECTION : PRC_AUGMENT_PROFILE) + IntToString(nIndex));
    struct user_augment_profile uap = _DecodeProfile(nProfile);

    // Get the augmentation levels / PP switch
    uap.bValueIsPP = GetPersistantLocalInt(oUser, PRC_PLAYER_SWITCH_AUGMENT_IS_PP);

    // Validity check on the index
    if(bQuickSelection ?
       (nIndex < PRC_AUGMENT_QUICKSELECTION_MIN ||
        nIndex > PRC_AUGMENT_QUICKSELECTION_MAX
        ):
       (nIndex < PRC_AUGMENT_PROFILE_INDEX_MIN ||
        nIndex > PRC_AUGMENT_PROFILE_INDEX_MAX
        )
       )
    {
        // Null the profile, just in case
        uap.nOption_1 = 0;
        uap.nOption_2 = 0;
        uap.nOption_3 = 0;
        uap.nOption_4 = 0;
        uap.nOption_5 = 0;
    }

    return uap;
}

/**
 * Gets the user's current augmentation profile.
 *
 * @param oUser A creature that has power augmentation profiles set up.
 * @return      The retrieved profile, compiled into a structure
 */
struct user_augment_profile GetCurrentUserAugmentationProfile(object oUser)
{
    struct user_augment_profile uap_ret;

    // Is augmentation override in effect?
    if(GetLocalInt(oUser, PRC_AUGMENT_OVERRIDE))
    {
        uap_ret = _DecodeProfile(GetLocalInt(oUser, PRC_AUGMENT_OVERRIDE) - 1);
        uap_ret.bValueIsPP = FALSE; // Override is always considered to be augmentation levels
    }
    // It wasn't, so get normally
    else
    {
        int nIndex = GetLocalInt(oUser, PRC_CURRENT_AUGMENT_PROFILE);
        int bQuick = nIndex < 0 ? TRUE : FALSE;

        if(bQuick) nIndex = -nIndex;

        uap_ret = GetUserAugmentationProfile(oUser, nIndex, bQuick);
    }

    return uap_ret;
}

void StoreUserAugmentationProfile(object oUser, int nIndex, struct user_augment_profile uap, int bQuickSelection = FALSE)
{
    // Validity check on the index
    if(bQuickSelection ?
       (nIndex < PRC_AUGMENT_QUICKSELECTION_MIN ||
        nIndex > PRC_AUGMENT_QUICKSELECTION_MAX
        ):
       (nIndex < PRC_AUGMENT_PROFILE_INDEX_MIN ||
        nIndex > PRC_AUGMENT_PROFILE_INDEX_MAX
        )
       )
    {
        if(DEBUG) DoDebug("StoreUserAugmentationProfile(): Attempt to store outside valid range: " + IntToString(nIndex));
        return;
    }

    SetPersistantLocalInt(oUser, (bQuickSelection ? PRC_AUGMENT_QUICKSELECTION : PRC_AUGMENT_PROFILE) + IntToString(nIndex), _EncodeProfile(uap));
}

string UserAugmentationProfileToString(struct user_augment_profile uap)
{
    string sBegin = GetStringByStrRef(16823498) + " "; // "Option"
    string sEnd   = " " + (uap.bValueIsPP ? "PP" : GetStringByStrRef(16823499)); // "times"

    return sBegin + "1: " + IntToString(uap.nOption_1) + sEnd + "; "
         + sBegin + "2: " + IntToString(uap.nOption_2) + sEnd + "; "
         + sBegin + "3: " + IntToString(uap.nOption_3) + sEnd + "; "
         + sBegin + "4: " + IntToString(uap.nOption_4) + sEnd + "; "
         + sBegin + "5: " + IntToString(uap.nOption_5) + sEnd;
}

struct manifestation EvaluateAugmentation(struct manifestation manif, struct power_augment_profile pap)
{
    // Get the user's augmentation profile - will be all zeroes if no profile is active
    struct user_augment_profile uap = GetCurrentUserAugmentationProfile(manif.oManifester);
    int nSurge               = GetWildSurge(manif.oManifester);
    int nAugPPCost           = 0;
    int nAugPPCostReductions = 0;
    int bMaxAugment          = GetLocalInt(manif.oManifester, PRC_AUGMENT_MAXAUGMENT) &&
                               !GetLocalInt(manif.oManifester, PRC_AUGMENT_OVERRIDE); // Override profile also overrides max augment

    // Initialise the augmentation data in the manifestation structure to zero
    /* Probably unnecessary due to auto-init
    manif.nTimesAugOptUsed_1   = 0;
    manif.nTimesAugOptUsed_2   = 0;
    manif.nTimesAugOptUsed_3   = 0;
    manif.nTimesAugOptUsed_4   = 0;
    manif.nTimesAugOptUsed_5   = 0;
    manif.nTimesGenericAugUsed = 0;
    */

    /* Bloody duplication follows. Real arrays would be nice */
    // Make sure the option is available for use and the user wants to do something with it
    if(pap.nMaxAugs_1 && uap.nOption_1)
    {
        // Determine how many times the augmentation option has been used
        manif.nTimesAugOptUsed_1 = uap.bValueIsPP ?                  // The user can determine whether their settings are interpreted
                                    uap.nOption_1 / pap.nAugCost_1 : // as static PP costs
                                    uap.nOption_1;                   // or as number of times to use
        // Make sure the number of times the option is used does not exceed the maximum
        if(pap.nMaxAugs_1 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_1 > pap.nMaxAugs_1)
            manif.nTimesAugOptUsed_1 = pap.nMaxAugs_1;
        // Calculate the amount of PP the augmentation will cost
        nAugPPCost += manif.nTimesAugOptUsed_1 * pap.nAugCost_1;
    }
    // Make sure the option is available for use and the user wants to do something with it
    if(pap.nMaxAugs_2 && uap.nOption_2)
    {
        // Determine how many times the augmentation option has been used
        manif.nTimesAugOptUsed_2 = uap.bValueIsPP ?                  // The user can determine whether their settings are interpreted
                                    uap.nOption_2 / pap.nAugCost_2 : // as static PP costs
                                    uap.nOption_2;                   // or as number of times to use
        // Make sure the number of times the option is used does not exceed the maximum
        if(pap.nMaxAugs_2 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_2 > pap.nMaxAugs_2)
            manif.nTimesAugOptUsed_2 = pap.nMaxAugs_2;
        // Calculate the amount of PP the augmentation will cost
        nAugPPCost += manif.nTimesAugOptUsed_2 * pap.nAugCost_2;
    }
    // Make sure the option is available for use and the user wants to do something with it
    if(pap.nMaxAugs_3 && uap.nOption_3)
    {
        // Determine how many times the augmentation option has been used
        manif.nTimesAugOptUsed_3 = uap.bValueIsPP ?                  // The user can determine whether their settings are interpreted
                                    uap.nOption_3 / pap.nAugCost_3 : // as static PP costs
                                    uap.nOption_3;                   // or as number of times to use
        // Make sure the number of times the option is used does not exceed the maximum
        if(pap.nMaxAugs_3 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_3 > pap.nMaxAugs_3)
            manif.nTimesAugOptUsed_3 = pap.nMaxAugs_3;
        // Calculate the amount of PP the augmentation will cost
        nAugPPCost += manif.nTimesAugOptUsed_3 * pap.nAugCost_3;
    }
    // Make sure the option is available for use and the user wants to do something with it
    if(pap.nMaxAugs_4 && uap.nOption_4)
    {
        // Determine how many times the augmentation option has been used
        manif.nTimesAugOptUsed_4 = uap.bValueIsPP ?                  // The user can determine whether their settings are interpreted
                                    uap.nOption_4 / pap.nAugCost_4 : // as static PP costs
                                    uap.nOption_4;                   // or as number of times to use
        // Make sure the number of times the option is used does not exceed the maximum
        if(pap.nMaxAugs_4 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_4 > pap.nMaxAugs_4)
            manif.nTimesAugOptUsed_4 = pap.nMaxAugs_4;
        // Calculate the amount of PP the augmentation will cost
        nAugPPCost += manif.nTimesAugOptUsed_4 * pap.nAugCost_4;
    }
    // Make sure the option is available for use and the user wants to do something with it
    if(pap.nMaxAugs_5 && uap.nOption_5)
    {
        // Determine how many times the augmentation option has been used
        manif.nTimesAugOptUsed_5 = uap.bValueIsPP ?                  // The user can determine whether their settings are interpreted
                                    uap.nOption_5 / pap.nAugCost_5 : // as static PP costs
                                    uap.nOption_5;                   // or as number of times to use
        // Make sure the number of times the option is used does not exceed the maximum
        if(pap.nMaxAugs_5 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_5 > pap.nMaxAugs_5)
            manif.nTimesAugOptUsed_5 = pap.nMaxAugs_5;
        // Calculate the amount of PP the augmentation will cost
        nAugPPCost += manif.nTimesAugOptUsed_5 * pap.nAugCost_5;
    }

    // Calculate number of times a generic augmentation happens with this number of PP
    if(pap.nGenericAugCost != PRC_NO_GENERIC_AUGMENTS)
        manif.nTimesGenericAugUsed = nAugPPCost / pap.nGenericAugCost;


    /*/ Various effects modifying the augmentation go below /*/

    // Account for wild surge
    nAugPPCostReductions += nSurge;
    
    // Midnight Augmentation feat from incarnum
    if (GetHasFeat(FEAT_MIDNIGHT_AUGMENTATION, manif.oManifester))
    {
    	// Make sure the power is the correct one, and that you can expend your psionic focus
    	if (manif.nSpellID == GetLocalInt(manif.oManifester, "MidnightAugPower") && UsePsionicFocus(manif.oManifester))
    		nAugPPCostReductions += GetEssentiaInvestedFeat(manif.oManifester, FEAT_MIDNIGHT_AUGMENTATION);
    }

    /*/ Various effects modifying the augmentation go above /*/

    // Auto-distribution, if modifying effects provided more PP than has been used so far or
    // the maximal augmentation switch is active
    if((nAugPPCost - nAugPPCostReductions) < 0 || bMaxAugment)
    {
        int nToAutodistribute = 0, nAutodistributed;

        // Calculate autodistribution amount
        if(bMaxAugment)
        {
            // Maximal augmentation ignores PP cost reductions here. They are instead handled at the end
            if(((manif.nManifesterLevel - manif.nPPCost) - nAugPPCost) > 0)
                nToAutodistribute = manif.nManifesterLevel // Maximum usable
                                  - manif.nPPCost          // Reduced by what's already been used for other things
                                  - nAugPPCost;            // Reduced by what's already been used for augmentation
        }
        // No maximal augmentation, instead more PP cost reduction provided than PP has been used
        else if((nAugPPCost - nAugPPCostReductions) < 0)
            nToAutodistribute = -(nAugPPCost - nAugPPCostReductions); // The amount of PP cost reduction that's in excess of what's already been used for augmentation

        // Store the value for use in cost calculations
        nAutodistributed = nToAutodistribute;

        // Start the distribution
        int nTimesCanAug;
        int nTimesAugd;
        if(nToAutodistribute > 0 && pap.nMaxAugs_1)
        {
            // Determine how many times this option could be used
            if(pap.nMaxAugs_1 == PRC_UNLIMITED_AUGMENTATION)
                nTimesCanAug = PRC_UNLIMITED_AUGMENTATION;
            else
                nTimesCanAug = pap.nMaxAugs_1 - manif.nTimesAugOptUsed_1;

            if(nTimesCanAug)
            {
                // Determine how many times it can be used and how much it costs
                nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ?
                              nToAutodistribute / pap.nAugCost_1 :
                              min(nToAutodistribute / pap.nAugCost_1, nTimesCanAug);
                nToAutodistribute -= nTimesAugd * pap.nAugCost_1;

                manif.nTimesAugOptUsed_1 += nTimesAugd;
            }
        }
        if(nToAutodistribute > 0 && pap.nMaxAugs_2)
        {
            // Determine how many times this option can be used
            if(pap.nMaxAugs_2 == PRC_UNLIMITED_AUGMENTATION)
                nTimesCanAug = PRC_UNLIMITED_AUGMENTATION;
            else
                nTimesCanAug = pap.nMaxAugs_2 - manif.nTimesAugOptUsed_2;

            if(nTimesCanAug)
            {
                // Determine how many times it can be used and how much it costs
                nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ?
                              nToAutodistribute / pap.nAugCost_2 :
                              min(nToAutodistribute / pap.nAugCost_2, nTimesCanAug);
                nToAutodistribute -= nTimesAugd * pap.nAugCost_2;

                manif.nTimesAugOptUsed_2 += nTimesAugd;
            }
        }
        if(nToAutodistribute > 0 && pap.nMaxAugs_3)
        {
            // Determine how many times this option can be used
            if(pap.nMaxAugs_3 == PRC_UNLIMITED_AUGMENTATION)
                nTimesCanAug = PRC_UNLIMITED_AUGMENTATION;
            else
                nTimesCanAug = pap.nMaxAugs_3 - manif.nTimesAugOptUsed_3;

            if(nTimesCanAug)
            {
                // Determine how many times it can be used and how much it costs
                nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ?
                              nToAutodistribute / pap.nAugCost_3 :
                              min(nToAutodistribute / pap.nAugCost_3, nTimesCanAug);
                nToAutodistribute -= nTimesAugd * pap.nAugCost_3;

                manif.nTimesAugOptUsed_3 += nTimesAugd;
            }
        }
        if(nToAutodistribute > 0 && pap.nMaxAugs_4)
        {
            // Determine how many times this option can be used
            if(pap.nMaxAugs_4 == PRC_UNLIMITED_AUGMENTATION)
                nTimesCanAug = PRC_UNLIMITED_AUGMENTATION;
            else
                nTimesCanAug = pap.nMaxAugs_4 - manif.nTimesAugOptUsed_4;

            if(nTimesCanAug)
            {
                // Determine how many times it can be used and how much it costs
                nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ?
                              nToAutodistribute / pap.nAugCost_4 :
                              min(nToAutodistribute / pap.nAugCost_4, nTimesCanAug);
                nToAutodistribute -= nTimesAugd * pap.nAugCost_4;

                manif.nTimesAugOptUsed_4 += nTimesAugd;
            }
        }
        if(nToAutodistribute > 0 && pap.nMaxAugs_5)
        {
            // Determine how many times this option can be used
            if(pap.nMaxAugs_5 == PRC_UNLIMITED_AUGMENTATION)
                nTimesCanAug = PRC_UNLIMITED_AUGMENTATION;
            else
                nTimesCanAug = pap.nMaxAugs_5 - manif.nTimesAugOptUsed_5;

            if(nTimesCanAug)
            {
                // Determine how many times it can be used and how much it costs
                nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ?
                              nToAutodistribute / pap.nAugCost_5 :
                              min(nToAutodistribute / pap.nAugCost_5, nTimesCanAug);
                nToAutodistribute -= nTimesAugd * pap.nAugCost_5;

                manif.nTimesAugOptUsed_5 += nTimesAugd;
            }
        }

        // Calculate amount actually autodistributed. nToAutoDistribute now contains the amount of PP that could not be auto-distributed
        nAutodistributed = (nAutodistributed - nToAutodistribute);

        // Calculate increase to generic augmentation
        if(pap.nGenericAugCost != PRC_NO_GENERIC_AUGMENTS)
            manif.nTimesGenericAugUsed += nAutodistributed / pap.nGenericAugCost;

        // Determine new augmentation PP cost
        nAugPPCost += nAutodistributed;
    }

    // Add in cost reduction
    nAugPPCost = max(0, nAugPPCost - nAugPPCostReductions);

    // Store the PP cost increase
    manif.nPPCost += nAugPPCost;

    return manif;
}

void SetAugmentationOverride(object oCreature, struct user_augment_profile uap)
{
    SetLocalInt(oCreature, PRC_AUGMENT_OVERRIDE, _EncodeProfile(uap) + 1);
}

// Test main
//void main(){}