Author Topic: [Script for free] Custom XP award -- yet another  (Read 424 times)

Legacy_the.gray.fox

  • Full Member
  • ***
  • Posts: 214
  • Karma: +0/-0
[Script for free] Custom XP award -- yet another
« on: May 20, 2011, 03:43:24 pm »


               Hello.

I did write this some time ago, for a friend.
He never used it -- We could not come to an agreement over how to perform some things.
Now it is sitting on my hard drive unused. Which is a pity.
So I share. If you have a use for this, help yourself.

In a nutshell, it is yet another way to award custom XP.
This one takes into account the difference in HD, and not the difference in CR.
It also does a couple other clever things. The comments should explain all.


[edit]

This script is long. I must split it in 2 parts.
Both parts are to be merged back into one script.

REMEMBER: Syntax check for wrong cAsE of the word class.
This forum has a habit to force it lowercase.



PART 1 of 2:


// +----------------+
// | FOX_INC_XP.nss |
// +----------------+
//
// Fox's Custom Experience Award.
//
// [fox - 01 Sep 2010]
// This is an alternate system to use in place of the BioWare standard one.
// Unlike other custom systems, this one takes into account the Hit Dice of the
// dead creature, instead of its Challenge Rating (CR).



// +-----------+
// | CONSTANTS |
// +-----------+
// Default amount of XP awarded per any kill.
// When no other setting is found, this very amount is picked as a last resort.
// But if you want to configure the system to award no XP at all (at some point,
// and for any reason you may have) then this amount is to be zeroed.
// But then it will be your responsibility to ensure that the system will return
// to award XP. Remember it.
// Look in Step #2 to see in which case this amount is queried.
const int nFOX_XP_BASEXP = 40;

// During the very first levels of a character, some classes are objectively
// much weaker than others. So as long as a weak character is of a level lesser
// than or equal to this amount, all the XP he earns is augmented by some %.
// This is an aid for such weak classes so they gain their skills in less time.
// Past this amount of character levels, no more aid is given.
const int nFOX_XP_EARLYLEVELS = 6;

// Name of the Local Variable to set on Creatures, Areas, or the Module, to
// override the default amount of XP awarded per any kill.
// Local Variable type is: int
// Local Variable amount: any positive non-zero.
const string sFox_XP_AWARD = "FOX_XP_AWARD";

// Do not mind this. For internal use only. Do not touch.
const string sFox_XP_PARTYMEMBERn = "FOX_XP_00";



// +------------+
// | PROTOTYPES |
// +------------+
void  fox_XP_OnDeath              ();
int   fox_XP_XpToHd               (int nXpAmount);
int   fox_XP_HdToXp               (int nHitDice);
float fox_XP_PenaltyForInbalance  (object oPlayer, float fPerMemberXP, int nPartyAvgHD);
float fox_XP_EarlyLevelsHelp      (object oPlayer);
float fox_XP_PenaltyForAssociates (object oPlayer);



// -----------------------------------------------------------------------------
// This is to be called from within the OnDeath event script handler of NPC.
// -----------------------------------------------------------------------------
void fox_XP_OnDeath ()
{
    // Get a reference to the dead creature (that is, us).
    object oDead = OBJECT_SELF;


    //-------------------------------------------------------------------
    // Step #1 : Check if this kill should award any XP at all.
    //           We like to honor the standard BioWare rules here:
    //           * Local fauna (animals) are worth no XP.
    //           * Commoners (paesants) are worth no XP.
    //           * Dead associates (summoned, et cetera) are worth no XP.
    //-------------------------------------------------------------------

    // If our primary class is Animal -> No XP. Quit.
    if (GetclassByPosition (1, oDead) == class_TYPE_ANIMAL) return;

    // If our primary class is Commoner -> No XP. Quit.
    if (GetclassByPosition (1, oDead) == class_TYPE_COMMONER) return;

    // If we are an Associate of any type -> No XP. Quit.
    if (GetAssociateType (oDead) != ASSOCIATE_TYPE_NONE) return;

    // <Insert more No_XP_Conditions here>
    // (empty)


    //-------------------------------------------------------------------------
    // Step #2 : Retrieve the total XP to award for the kill of this creature.
    //           Our system allows for hyerarchycal overrides. The XP amount to
    //           award for this kill may be found in multiple places.
    //           It is a matter of priorities. The search scans all places in
    //           search of the first valid value with highest priority.
    //           Top priority is 1. Bottom priority is 4.
    //
    //           Priority 1 = search on the very dead Creature.
    //           Priority 2 = search on the Area the Creature died in.
    //           Priority 3 = search on the Module.
    //           Priority 4 = Use the base default amount.
    //
    //           We could go wild with this, and prepare a system with a ton of
    //           little caveats and rules to regulate with surgical precision
    //           the XP for killing who, when, where, why and how.
    //           But this script is run for _every_ creature that dies.
    //           Many creatures may die in a very short timeframe, know it.
    //           We do not want our system to weight on the CPU by repeatedly
    //           calling a large amount of instructions for each death.
    //
    //                            << Simplicity is Bliss >>
    //
    //           -so sayeth the fox
    //-------------------------------------------------------------------------

    // Priority 1. Search the XP on the Creature.
    int nXP = GetLocalInt (oDead, sFox_XP_AWARD);
    if (nXP < 1)
    {
        // Priority 2. Search the XP on the Area the Creature died in.
        nXP = GetLocalInt (GetArea (oDead), sFox_XP_AWARD);
        if (nXP < 1)
        {
            // Priority 3. Search the XP on the Module.
            nXP = GetLocalInt (GetModule (), sFox_XP_AWARD);
            if (nXP < 1)
            {
                // Priority 4. Use the base default amount.
                nXP = nFOX_XP_BASEXP;

                // Chance for early exit. No XP to award? Quit.
                // This may not be an error, after all. If the BaseXP is zeroed,
                // then it is possible to set the system so that no kill will
                // award any XP (for any reason a scripter may have).
                if (nXP < 1) return;
            }
        }
    }


    //------------------------------------------------------------------------
    // Step #3 : Make sure the killer is a valid Player or any Associate of a
    //           valid Player. Playership is checked with GetIsPC().
    //           GetIsPC() also incorporates a check for validity. So there is
    //           no need to explicit a call to GetIsObjectValid().
    //------------------------------------------------------------------------

    // Get a reference to the one who killed us (we = the dead creature).
    object oKiller = GetLastKiller ();

    // Is our killer a Trap object?
    if (GetTrapBaseType (oKiller) != -1)
    {
        // Yes it is. Get the Trap Creator. Presume that to be our killer.
        oKiller = GetTrapCreator (oKiller);
    }

    // Is our killer a Player, or any Associate of a Player?
    if (   !GetIsPC (oKiller)
        && !GetIsPC (GetMaster (oKiller)))
    {
        // No and no. This creature has been killed by something which has
        // nothing to do with a Player, not even remotely. Just quit.
        return;
    }


    //----------------------------------------------------------------
    // Step #4 : Find the Player most closely connected to the killer.
    //           Yes, the killer itself may be the Player we seek.
    //----------------------------------------------------------------

    // Assume that the Player we seek is the very killer.
    object oPlayer = oKiller;

    // Now make sure. If the Player we seek is not directly the killer...
    if (!GetIsPC (oKiller))
    {
        // ... then it has to be his Master.
        oPlayer = GetMaster (oKiller); // <-- (*)

        ///////////////////////////////////////////////////////////////////////
        // (*) = If this was not the case, we would not be here in the first
        //       place. By virtue of the initial check for playership, down at
        //       this point the killer can only be a Player OR the Associate of
        //       a Player. There is no chance to mistake.
        ///////////////////////////////////////////////////////////////////////
    }


    //--------------------------------------------------------------------------
    // Step #5 : Collect all Player Party Members of the killer. We build a list
    //           out of them. The list will only contain the killer (of course)
    //           and those Player party members that are found in the same Area
    //           of the killer and within 25 meters of him.
    //           Any Player excluded from this list shall not receive XP for
    //           this kill. Makes sense. You have to be reasonably close to the
    //           action to earn the right to claim your share of the XP.
    //
    //           Note that every Player in game rapresents a Faction of his own.
    //           If multiple Players join into the same Party, then all those
    //           Players temporarily acquire the same Faction ID of the current
    //           Party Leader. No matter. What matters is that it is possible to
    //           cycle through all members of a specific Faction. We do not need
    //           to know the Faction ID. We only need a reference to any one
    //           member of said Faction.
    //
    //           So yes: here "Party" and "Faction" have the same meaning.
    //           Cycling through the Faction Members of a Player is like cycling
    //           through the Party Members of that Player.
    //
    //           NOTE:
    //           While at it, we also calculate some stuff about these Players.
    //           We consider the Party as a whole. We are interested in the
    //           average Character Level of the Party, calculated from the XP
    //           amount of the whole Party.
    //--------------------------------------------------------------------------

    int nPartyCount = 0;
    int nPartyXP    = 0;
    object oArea = GetArea (oPlayer);

    // Cycle through all Player Party Members of oPlayer.
    object oMember = GetFirstFactionMember (oPlayer, TRUE);
    while (GetIsObjectValid (oMember))
    {
        // Is this not a DM...?
        // ... AND, is he Alive and Conscious (1 HP at least)?
        // ... AND, is he in the same Area of oPlayer (the killer)?
        // ... AND, is he within 25 meters of oPlayer?
        if (   !GetIsDM (oMember)
            && (GetCurrentHitPoints (oMember) > 0)
            && (GetArea (oMember) == oArea)
            && (GetDistanceBetween (oMember, oPlayer) < 25.0f))
        {
            // Yes, and yes and yes and yes. Append him to the list.
            // (the list is saved onto oPlayer)
            SetLocalObject (oPlayer,
                            sFox_XP_PARTYMEMBERn + IntToString (nPartyCount++),
                            oMember);

            // Get the XP amount of this Member, convert it to the closest HD
            // level, then convert the HD back to XP. Doing so we have an exact
            // "trimmed" amount. Finally, add this XP to the total of the Party.
            nPartyXP += fox_XP_HdToXp (fox_XP_XpToHd (GetXP (oMember)));
        }

        oMember = GetNextFactionMember (oPlayer, TRUE);
    }

    // Calculate the average Character Level of the Party.
    int nAvgHD = fox_XP_XpToHd (nPartyXP / nPartyCount);


    //--------------------------------------------------------------------------
    // Step #6 : Now we have all we need. Time to award XP to the killer. If the
    //           killer is in a Party, the XP is shared among the Party Members
    //           we saved in the Step #5 list.
    //
    //           Not everyone may receive the same amount of XP. There is a nice
    //           reward for those able to slay that which is so much stronger
    //           than them. However, courage and insanity go hand in hand, and
    //           the line between them is thin.
    //           If you fight and slay that which is way too much stronger than
    //           you, do not expect an adeguate reward. Maybe the next time you
    //           will lower your aim and avoid to needlessly risk your neck.
    //
    //           Although we ignore the CR of the characters involved, we want
    //           to base our rule on something derived from it.
    //           According to the NWN online manual, 7 difficulty levels exist:
    //
    //           Impossible    = foe is 5 or more levels above your.
    //           Overpowering  = foe is 3 or 4 levels above your.
    //           Very Dificult = foe is 1 or 2 levels above your.
    //           Challenging   = foe is your same level, or 1 level below your.
    //           Moderate      = foe is 2 or 3 levels below your.
    //           Easy          = foe is 4 or 5 levels below your.
    //           Effortless    = foe is 6 or more levels below your.
    //
    //           We want the XP to quickly scale down (or up) on a squared log10
    //           scale. Like this:
    //
    //            200% xp = foe is 6, or more, levels above your.
    //           ~184% xp = foe is 5 levels above your.
    //           ~163% xp = foe is 4 levels above your.
    //           ~145% xp = foe is 3 levels above your.
    //           ~128% xp = foe is 2 levels above your.
    //           ~113% xp = foe is 1 level above your.
    //            100% xp = foe is your same level.
    //            ~86% xp = foe is 1 level below your.
    //            ~71% xp = foe is 2 levels below your.
    //            ~54% xp = foe is 3 levels below your.
    //            ~36% xp = foe is 4 levels below your.
    //            ~15% xp = foe is 5 levels below your.
    //              0% xp = foe is 6, or more, levels below your.
    //
    //           For example, if the base XP to award for a kill is 40 points,
    //           the total to award to a single Player goes like this:
    //
    //           80 xp = if the foe is 6, or more, levels above your.
    //           73 xp = if the foe is 5 levels above your.
    //           65 xp = if the foe is 4 levels above your.
    //           58 xp = if the foe is 3 levels above your.
    //           51 xp = if the foe is 2 levels above your.
    //           45 xp = if the foe is 1 level above your.
    //           40 xp = if the foe is your same level.
    //           34 xp = if the foe is 1 level below your.
    //           28 xp = if the foe is 2 levels below your.
    //           21 xp = if the foe is 3 levels below your.
    //           14 xp = if the foe is 4 levels below your.
    //            6 xp = if the foe is 5 levels below your.
    //            0 xp = if the foe is 6, or more, levels below your.
    //--------------------------------------------------------------------------

    ///////////////////////////////////////////////////////////////////////////
    // NOTE : To help the performance, we prefer to work using the decimal
    //        datatype: float. In so doing we cut on a number of conversions to
    //        and from the integer datatype all over the script.
    //        We shall convert to integer once we have the final XP to award.
    ///////////////////////////////////////////////////////////////////////////

    // Get the HD of the dead creature, sanity capped in range [1 -- 40].
    int nDeadHD = GetHitDice (oDead);
    if (nDeadHD > 40) nDeadHD = 40;

    // Now pick the correct XP scalar, based on the difference in HD between the
    // dead creature and the average HD of the Party.
    float fScalar;
    int nDeltaHD = nAvgHD - nDeadHD;
         if (nDeltaHD <= -6) fScalar = 2.0f; // 200% XP cap limit.
    else if (nDeltaHD >=  6) fScalar = 0.0f; //   0% XP cap limit.
    else
    {
        //////////////////////////////////////////////////////////////////////
        // We here?
        // The HD delta between the dead creature and the Party Average varies
        // from +5 to -5, passing through 0.
        // Both ends of the scale (+positive and -negative) are calculated in
        // the same way. Only, the negative end is the mirror of the positive
        // end. A delta of 0 is the pivot of the scale, wherein the XP scalar
        // becomes 1 (for 100%).
        // It takes more to explain than to calculate.
        //
        // We start by calculating the scalar assuming that the HD delta is on
        // the +positive side of the scale.
        // Why? Because the most likely case is: the Party killed a weakling.
        // In fact the scalar we obtain with this is in range [1.0 -- 0.0].
        // It will *decrease* the XP to award.
        //////////////////////////////////////////////////////////////////////

        // Calculate the log10 for the positive HD delta, then square it.
        float fLog  = log (10.0f - 1.5f * IntToFloat (abs (nDeltaHD)));
              fLog *= fLog;

        ////////////////////////////////////////////////////////////////////////
        // BUT...!
        // If the dead creature is stronger than the Party Average, the delta is
        // negative. And for negative deltas we mirror the log value onto the
        // -negative side of the scale.
        // That is: the scalar begins to *increase* on a squared log10 scale.
        ////////////////////////////////////////////////////////////////////////

        // As simple as this:
        if (nDeltaHD < 0) fLog = 1.0f - fLog + 1.0f;

        // Whatever value we have in fLog is the scalar we are after.
        fScalar = fLog;

        ////////////////////////////////////////////////////////////////////////
        // Confused over what we have done?
        // Look here. This pseudo-switch is the closest translation of the above
        // procedure:
        //
        // switch (nAvgHD - nDeadHD)
        // {
        //    [OVER] -6: fScalar is capped at 2.0
        //     case  -6: fScalar = 2.0f;     break; // 1 - ((log  1.0) ^ 2) + 1
        //     case  -5: fScalar = 1.84164f; break; // 1 - ((log  2.5) ^ 2) + 1
        //     case  -4: fScalar = 1.63752f; break; // 1 - ((log  4.0) ^ 2) + 1
        //     case  -3: fScalar = 1.45186f; break; // 1 - ((log  5.5) ^ 2) + 1
        //     case  -2: fScalar = 1.28581f; break; // 1 - ((log  7.0) ^ 2) + 1
        //     case  -1: fScalar = 1.13618f; break; // 1 - ((log  8.5) ^ 2) + 1
        //     case   0: fScalar = 1.0f;     break; //      (log 10.0) ^ 2
        //     case   1: fScalar = 0.86382f; break; //      (log  8.5) ^ 2
        //     case   2: fScalar = 0.71419f; break; //      (log  7.0) ^ 2
        //     case   3: fScalar = 0.54814f; break; //      (log  5.5) ^ 2
        //     case   4: fScalar = 0.36248f; break; //      (log  4.0) ^ 2
        //     case   5: fScalar = 0.15836f; break; //      (log  2.5) ^ 2
        //     case   6: fScalar = 0.0f;     break; //      (log  1.0) ^ 2
        //    [OVER] +6: fScalar is capped at 0.0
        // }
        //
        // -fox
        ////////////////////////////////////////////////////////////////////////
    }

    // Chance for early exit. If the picked scalar is 0.0f, the final XP can
    // only be 0, right? In which case we just quit because there is no XP to
    // award to anybody.
    if (fScalar == 0.0f) return;

    // Calculate the XP normally awarded to each Player in the Party.
    float fPerMemberXP = IntToFloat (nXP);
    for (i = 1; i < nPartyCount; ++i)
    {
        // Each Player Party Member past the 1st, causes a 10% XP loss.
        // This is a "diminishing return" formula. The PerMemberXP shall reduce
        // and approach to 0, but will never reach it.
        fPerMemberXP *= 0.9f;
    }

    // Scale down (or up) the XP for each Party Member.
    fPerMemberXP *= fScalar;

    // Cycle through all Party Members.
    for (i = 0; i < nPartyCount; ++i)
    {
        // Get (then delete) a reference to the Party Member in exam.
        string sKey    = sFox_XP_PARTYMEMBERn + IntToString (i);
        object oMember = GetLocalObject    (oPlayer, sKey);
                         DeleteLocalObject (oPlayer, sKey);

        ////////////////////////////////////////////////////////////////////
        // At this point each Party Member may see *his own* share of the XP
        // reduced or augmented for a number of reasons.
        ////////////////////////////////////////////////////////////////////

        // PARTY MEMBER PENALTY:
        // The farther is your HD from the Party Average, the less XP you earn.
        // The closer is your HD to the Party Average, the more XP you earn.
        // If your HD matches the Party Average, you retain 100% XP.
        float fMemberXP = fox_XP_PenaltyForInbalance (oMember, fPerMemberXP, nAvgHD);

        // PARTY MEMBER BONUS:
        // Account for Early Levels Help. Not all Player classes can handle
        // combat with the same efficiency during their first levels.
        fMemberXP *= fox_XP_EarlyLevelsHelp (oMember);

        // PARTY MEMBER PENALTY:
        // Account for penalty for having any Associate with you.
        // The more help you get, the less XP you earn.
        fMemberXP *= fox_XP_PenaltyForAssociates (oMember);

        // Now convert the final XP to integer.
        int nMemberXP = FloatToInt (fMemberXP);

        // Sanity check. Do nothing if the final XP turns out to be 0.
        if (nMemberXP)
        {
            ////////////////////////////////////
            // Here. XP for you. Use it well.
            // --------------------------------------------------------
            // NOTE : We use SetXP() instead of GiveXPToCreature().
            //        The latter function automatically accounts for XP
            //        penalties due to "improper" multiclassing.
            //        We always thought that penalty to be _retarded_.
            // --------------------------------------------------------

            SetXP (oMember, GetXP (oMember) + nMemberXP);
        }
    }
}

(continues on Part 2...)
               
               

               


                     Modifié par the.gray.fox, 20 mai 2011 - 04:39 .
                     
                  


            

Legacy_the.gray.fox

  • Full Member
  • ***
  • Posts: 214
  • Karma: +0/-0
[Script for free] Custom XP award -- yet another
« Reply #1 on: May 20, 2011, 03:48:51 pm »


               (... continued from Part 1)

PART 2 of 2:

// -----------------------------------------------------------------------------
// Given an amount of Hit Dice (or Character Levels) returns the corresponding
// amount of XP needed to reach said HD.
// -----------------------------------------------------------------------------
int fox_XP_HdToXp (int nHitDice)
{
    ////////////////////////////////////////////////////////////////////////
    // Here is the well known formula to calculate the XP corresponding to a
    // given HD amount. Where:
    //
    //   "x" is the XP amount we seek.
    //   "y" is the current HD of the character.
    //
    //          2
    //        (y - y)
    //   x = --------- * 1000
    //           2
    //
    // You are probably more familiar with its simplified form:
    //
    //   x = (y - 1) * y * 500
    //
    // -fox
    ////////////////////////////////////////////////////////////////////////

    return ((nHitDice - 1) * nHitDice * 500);
}



// -----------------------------------------------------------------------------
// Given an amount of XP returns the corresponding amount of HD a Player could
// achieve if he had said XP.
//
// A curious function this is, is it not? :-)
// Our system bases the XP award on the amount of HD a player has. Right?
// But this mechanism may be exploit by clever Players. Imagine a Player that
// keeps killing foes that are his same level. He earns 100% XP from them.
// Eventually this Player shall hit a LevelUp. Agree?
// And because he LevelUps, he becomes 1 level above the foes he kept killing.
// And he gets less XP from them. Right?
// But Players are NOT forced to LevelUp. What happens if this Player instead
// stays at his current level? He has done it for so long, he can keep going.
// And if he does... he keeps getting 100% XP from the same foes.
// We do not want that.
// That is why this function reads the actual XP amount of a Player and converts
// it to the nearest Character Level _achievable_ with said XP.
// Whether the clever Player shall LevelUp or not, our code will discover what
// his True Level could be, and correctly adjust the earned XP from the kills.
//
// Bottom line: with our system, players have no reason to delay their LevelUp.
//
// -so sayeth the fox
// -----------------------------------------------------------------------------
int fox_XP_XpToHd (int nXpAmount)
{
    //////////////////////////////////////////////////////////////////////////
    // Here is the (less known) formula to calculate the HD corresponding to a
    // given XP amount. Being a 2nd grade equation we have 2 possible outcomes
    // for each "y" but of course we are only interested in the +positive one.
    // Which is shown below.
    // Where:
    //
    //   "x" is the HD we seek.
    //   "y" is the current XP amount of the character.
    //                __________
    //               /      y
    //        1 +   / 1 + -----
    //            \\/       125
    //   x = --------------------
    //                2
    //
    // If we want the most simplified version of it:
    //                 ____________
    //        1       / 1      y
    //   x = --- +   / --- + -----
    //        2    \\/   4     500
    //
    // Which looks uglier on the paper, but translates to a slightly faster
    // e-version.
    //
    // -fox
    //////////////////////////////////////////////////////////////////////////

    int nHD = FloatToInt (sqrt (IntToFloat (nXpAmount) * 0.002f + 0.25f) + 0.5f);
    return (nHD > 40) ? 40 : nHD; // Cap to 40 HD for cases of 780000+ XP.
}



// -----------------------------------------------------------------------------
// The farther is the HD of the given Player from the HD of the Party Average,
// the less XP the Player gets. Conversely, the closer the Player HD is to that
// of the Party Average, the more XP the Player gets.
// If his HD matches that of the Party, he retains 100% XP.
//
// This function is meant to penalize those Party Members whose HD causes an
// inbalance in the Party. They will never see their XP reduced to 0, but they
// would surely get more XP if they played in a more homogeneous Party.
//
// Note that this function does not return a scalar.
// The value returned is directly the new XP amount to be given to the Player
// (if not considering further penalties or boons).
// -----------------------------------------------------------------------------
float fox_XP_PenaltyForInbalance (object oPlayer, float fPerMemberXP, int nAvgHD)
{
    // All in one line.
    return (  fPerMemberXP
            - (  fPerMemberXP
               / IntToFloat (nAvgHD)
               * IntToFloat (abs (  fox_XP_XpToHd (GetXP (oPlayer))
                                  - nAvgHD))));

    ////////////////////////////////////////////////////////////////////////////
    // Confused over what we have done?
    // Here are the steps for it:
    //
    //  // Get the True HD of this Player.
    //  int nHD = fox_XP_XpToHd (GetXP (oPlayer));
    //
    //  //////////////////////////////////
    //  // Now calculate the XP per HD.
    //  // ---------------------------------------------------------------------
    //  // NOTE : This operation is Party-wide, and does not change per-Member.
    //  //        It would be best calculated only once, out of here, and then
    //  //        its value passed to this function.
    //  //        However, to calculate it we need two of the parameters that we
    //  //        pass to this function in any case.
    //  //        Normally the cut of a division and a conversion to float would
    //  //        be enough to justify a 4th parameter. But this optimization
    //  //        pays only in presence of a Party. For lone players this code
    //  //        is already as performant as it can get (and actually a 4th
    //  //        parameter would hinder the performance for them).
    //  //        So: it is a difficult choice.
    //  //
    //  //        Unable to gauge what is best, we choose to favor readability.
    //  //        It makes more sense to perform this operation herein, than not
    //  //        outside of here.
    //  //        In any case, however, the use of the one-line instruction to
    //  //        calculate everything (at the beginning of the function) should
    //  //        make up for the added cost to recalculate this constant thing.
    //  //
    //  //        -fox
    //  // ---------------------------------------------------------------------
    //
    //  float fXpPerHd = fPerMemberXP / IntToFloat (nAvgHD);
    //
    //  // How much XP is lost by this Party member?
    //  float fLoss = fXpPerHd * IntToFloat (abs (nHD - nAvgHD));
    //
    //  // Return the new Member XP.
    //  return (fPerMemberXP - fLoss);
    //
    //  -fox
    ////////////////////////////////////////////////////////////////////////////
}



// -----------------------------------------------------------------------------
// During the early levels of a Player Character, not all classes have the same
// chances to win a fight. This function analyzes a given character and returns
// a scalar to augment the XP to award to said character.
// -----------------------------------------------------------------------------
float fox_XP_EarlyLevelsHelp (object oPlayer)
{
    ////////////////////////////////////////////////////////////////////////////
    // No point denying it. The quicker the BAB growth, the easier for a
    // character is to hit his foe in melee combat. It so happens that the best
    // BAB growths go hand in hand with the higher HP growths.
    // And it so happens that fighting with melee weapons is all too often more
    // convenient than fighting with magic. Melee weapons can not run out of
    // "bullets", for starters, whereas spells can quickly deplete -- forcing a
    // mage to take a full Resting to recharge his spells and sustain another
    // fight. A melee character, instead, may just drink a potion and keep
    // going. His weapons need no recharge.
    //
    // True is that all these differences tend to wash-out as a character gains
    // more and more levels, regardless of the chosen classes. However, so long
    // a character is in his early levels, his own skills are definitely lacking
    // and the BAB+HP difference is felt. Heavily.
    //
    // For these reasons here we prepare a scalar to increase the earned XP by
    // characters with low BAB (and therefore low HP). This is meant to speed up
    // their character growth, so they can begin to enjoy the battles sooner.
    //
    // The starting assumption is that a character with a BAB matching his own
    // Level receives a scalar of 1.0f... which produces no change in the earned
    // XP.
    // But the more the actual BAB of a character is "left behind" his Level,
    // the greater the scalar becomes -> the more the XP earned -> the quicker
    // the character grows.
    //
    // -fox
    ////////////////////////////////////////////////////////////////////////////

    // We do *not* get the True HD of this Player.
    // We get the actual HD even if the Player could LevelUp. The BAB value is
    // bound to the classes effectively taken. Meaning that if we account for
    // any "pending" LevelUp, we always calculate a scalar greater than it
    // should be, for the BAB is inevitably left behind due to lack of classes.
    // Which would lead our code to provide more "help" than needed.
    // Makes sense?

    int nHD = GetHitDice (oPlayer);
    if (nHD > nFOX_XP_EARLYLEVELS)
    {
        // This Player has grown up. No more help from us.
        return 1.0f;
    }
    else
    {
        // This Player is young. Help him some (if appropriate).
        float fBAB = IntToFloat (GetBaseAttackBonus (oPlayer));
        if (fBAB < 1.0f) fBAB = 0.5f; // Prevent division by 0 at HD 1.

        return (IntToFloat (nHD) / fBAB);
    }
}



// -----------------------------------------------------------------------------
// The more (NPC) Associates a Player has, the lower the XP awarded.
// Each Associate, regardless of type, applies a -10% relative penalty.
// -----------------------------------------------------------------------------
float fox_XP_PenaltyForAssociates (object oPlayer)
{
    /////////////////////////////////////
    // ASSOCIATE_TYPE_HENCHMAN        : 1
    // ASSOCIATE_TYPE_ANIMALCOMPANION : 2
    // ASSOCIATE_TYPE_FAMILIAR        : 3
    // ASSOCIATE_TYPE_SUMMONED        : 4
    // ASSOCIATE_TYPE_DOMINATED       : 5
    /////////////////////////////////////

    // The scalar starts at 1.0f, meaning 100% xp retained (no loss).
    float fScalar = 1.0f;

    // Cycle through all 5 associate types.
    int i;
    for (i = 1; i <= 5; ++i)
    {
        // Cycle through all associates of the type in exam.
        int j = 0;
        object oAssociate = GetAssociate (i, oPlayer, ++j);
        while (GetIsObjectValid (oAssociate))
        {
            ////////////////////////////////////////
            // For each associate of this type...
            // -----------------------------------------------------------------
            // NOTE : This will cause a recursive loss of 10% from the final XP.
            //        It is a "diminishing return" formula. The scalar will
            //        approach 0.0f but will never reach it.
            //        Nonetheless: the more help you get, the less XP you earn.
            //
            //        The diminishing return approach makes sense. If you keep
            //        adding associates under your command, at some point their
            //        number stops making any sensible difference for the
            //        outcome. Too much crowd may even slow down the thing, for
            //        the associates could get in the way of each other.
            //
            //        If you wish to increase this penalty, do not change the
            //        formula. Rather decrease the multiplier. For example, a
            //        multiplier of 0.85f causes a recursive loss of 15% XP per
            //        associate.
            // -----------------------------------------------------------------

            fScalar *= 0.9f;

            oAssociate = GetAssociate (i, oPlayer, ++j);
        }
    }


    ////////////////////////
    // Return the scalar.

    return fScalar;
}

(end of file)


-fox
               
               

               


                     Modifié par the.gray.fox, 20 mai 2011 - 04:34 .