///////////////////////////////////////////////////////////////////////////////
// no_c2_default7

///////////////////////////////////////////////////////////////////////////////
//
// DMG experience rewards modification
/*
ScrewTape's script mimicking the XP Rewards from pp 36-39, DMG v3.5
 - base awards replicate tables on pp 37 of the DMG
*/
//:://////////////////////////////////////////////////
//:: Modifier: ScrewTape
//:: Original Modification: 02/19/04
//:: Latest Revision: 05/19/04
//:://////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// *****INSTRUCTIONS*****

// This script encapsulates all of the DMG XP table functionality in a
//  script that can be called from a custom OnDeath script by adding a call,
//  ExecuteScript("no_c2_default7", OBJECT_SELF); If your script already has a
//  return for oKiller == OBJECT_SELF, then
//   ExecuteScript("no_c2_default7", OBJECT_SELF);
//  is all you need to add (like Jassper's AI).
// For any other custom OnDeath scripts that DO NOT have the return on
//  OBJECT_SELF, after object oKiller = GetLastKiller();,
//  add (without the /* */)
/*
// ============================================================================
// Start of modified behavior
   // Per discussions with Sotae, we don't want to run this script the second
   //  time we get here (since we killed ourself a second time to bypass
   //  Bioware's XP system).
   if (oKiller == OBJECT_SELF)
   {
      return;
   }
// End of modified behavior
// ============================================================================
*/

// There is also a trap exploit fix in OnDeath that you may want.
/*
// ============================================================================
// Start of modified behavior
      // penalize trap creators for killing commoners
      //  Thanks to *** arQon *** for this idea
      // Update - modified so any associate of the trap creator will get the
      //  alignment shift
      if (GetIsObjectValid(GetTrapCreator(oKiller)))
         AdjustAlignment(GetTrapCreator(oKiller), ALIGNMENT_EVIL, 5);
// End of modified behavior
// ============================================================================
*/

// And towards the end (any where after early returns is ok, but your custom
//  OnDeath may recommend the proper place) add
/*
// ============================================================================
// Start of modified behavior
   ExecuteScript("no_c2_default7", OBJECT_SELF);
// End of modified behavior
// ============================================================================
*/

// All three modifications are preceded by

// ============================================================================
// Start of modified behavior
//  [modified code]

// and followed by
// End of modified behavior
// ============================================================================

//  just to make it obvious what is Bioware code and what is my (ScrewTape's)
//   modifications

// below is an example of Bioware's OnDeath using
//  ExecuteScript("no_c2_default7", OBJECT_SELF); to award XP

/*
// ============================================================================
// NW_C2_DEFAULT7 default behavior

#include "x2_inc_compon"
#include "x0_i0_spawncond"

void main()
{
   int nClass = GetLevelByClass(CLASS_TYPE_COMMONER);
   int nAlign = GetAlignmentGoodEvil(OBJECT_SELF);
   object oKiller = GetLastKiller();
// ============================================================================
// Start of modified behavior
   // Per discussions with Sotae, we don't want to run this script the second
   //  time we get here (since we killed ourself a second time to bypass
   //  Bioware's XP system).
   if (oKiller == OBJECT_SELF)
   {
      return;
   }
// End of modified behavior
// ============================================================================

   // If we're a good/neutral commoner,
   // adjust the killer's alignment evil
   if(nClass > 0 && (nAlign == ALIGNMENT_GOOD || nAlign == ALIGNMENT_NEUTRAL))
   {
      AdjustAlignment(oKiller, ALIGNMENT_EVIL, 5);
// ============================================================================
// Start of modified behavior
      // penalize trap creators for killing commoners
      //  Thanks to *** arQon *** for this idea
      // Update - modified so any associate of the trap creator will get the
      //  alignment shift
      if (GetIsObjectValid(GetTrapCreator(oKiller)))
         AdjustAlignment(GetTrapCreator(oKiller), ALIGNMENT_EVIL, 5);
// End of modified behavior
// ============================================================================
   }

   // Call to allies to let them know we're dead
   SpeakString("NW_I_AM_DEAD", TALKVOLUME_SILENT_TALK);

   //Shout Attack my target, only works with the On Spawn In setup
   SpeakString("NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK);

   // NOTE: the OnDeath user-defined event does not
   // trigger reliably and should probably be removed
   if(GetSpawnInCondition(NW_FLAG_DEATH_EVENT))
   {
      SignalEvent(OBJECT_SELF, EventUserDefined(1007));
   }
   craft_drop_items(oKiller);

// ============================================================================
// Start of modified behavior
   ExecuteScript("no_c2_default7", OBJECT_SELF);
// End of modified behavior
// ============================================================================
}
// end of NW_C2_DEFAULT7 default behavior
*/

///////////////////////////////////////////////////////////////////////////////
// no_c2_default7

///////////////////////////////////////////////////////////////////////////////
//
// DMG experience rewards modification
/*
ScrewTape's script mimicking the XP Rewards from pp 36-39, DMG v3.5
 - base awards replicate tables on pp 37 of the DMG
*/
//:://////////////////////////////////////////////////
//:: Modifier: ScrewTape
//:: Original Modification: 02/19/04
//:: Latest Revision: 05/19/04
//:://////////////////////////////////////////////////

// Below are the tweaking modifiers, used to adjust the amount of XP awarded.
//  Recommended N_MODULE_SLIDER settings are 17 for 1st level modules, 10-15
//  for 2nd-5th, and 7-10 for higher level modules. That should keep you in the
//  ballpark of the Bioware awards. NOTE: the actual module slider has
//  NO EFFECT on these awards and consequently does NOT need to be set to zero.
//  (Setting is irrelevant - see below).

// B_BYPASS_BIOWARE_XP -
//  It is not neccessary to set the module's XP slider to zero. I incorporated
//  an idea by TethyrDarknight to bypass both the slider and the double
//  message for xp. Note: you won't be able to compare this system with the
//  default Bioware awards with this implemented. If you want to do that,
//  set both module sliders the same (the variable below and the actual module
//  slider) and comment out the next line of code and uncomment the one
//  following it.
const int B_BYPASS_BIOWARE_XP = TRUE; // recommended setting
// const int B_BYPASS_BIOWARE_XP = FALSE; // for comparison

// N_MODULE_SLIDER -
//  Use this just as you would use a module's xp slider
//  set to 10 for normal xp - when using 2da's this will be exactly the
//  same, barring other considerations like henchmen, multiplayer, multiclass,
//  etc. If your using the DMG tables, 10 equates to 10%, which will be
//  slightly higher than the Bioware awards. 100 will match the DMG tables.
const int N_MODULE_SLIDER = 10;

// B_USE_MULTICLASS_PENALTY -
//  This is a boolean (set to TRUE (1) or FALSE (O))
//  to decide whether or not to enforce Bioware's multi class penalty.
//  Idea taken from Bioware scripting forum post by Ima Dufus
//  FALSE would be like the DMG
const int B_USE_MULTICLASS_PENALTY = FALSE;   // DMG accurate setting
// const int B_USE_MULTICLASS_PENALTY = TRUE; // alternate setting

// B_COUNT_ASSOCIATES -
//  Per Trickster's request, I included an option to count the players' other
//  associates, summoned/familiar/companion/dominated. In doing so, I realized
//  I wasn't adhering to the DMG's notes regarding "creatures that enemies
//  summon or otherwise add to their forces with magic powers. An ememy's
//  ability to summon or add these creatures is a part of the enemy's CR
//  already." pp 37, just below the steps to determine the XP award. So I
//  included that in the option. These two options seemed to be tied together,
//  that is, if I count the party's associate types when determining total
//  party members to divide experience by, I should also award XP for enemies'
//  associates and on the other hand, if I don't count the party's associates
//  (except henchmen), I shouldn't award XP for enemies' associates.
const int B_COUNT_ASSOCIATES = FALSE; // DMG Accurate
// const int B_COUNT_ASSOCIATES = TRUE; // alternate setting

// B_COUNT_HENCHMEN -
//  And why not allow the same option for henchmen
const int B_COUNT_HENCHMEN = TRUE; // recommended setting
// const int B_COUNT_HENCHMEN = FALSE; // alternate setting

// B_AWARD_DEAD_PLAYERS
//  Allow dead/dying players to receive awards (per DMG, pp 41 'DEATH AND
//  EXPERIENCE POINTS') but leave the option to disable.
 const int B_AWARD_DEAD_PLAYERS = TRUE; // DMG accurate setting
//const int B_AWARD_DEAD_PLAYERS = FALSE; // alternate setting

#include "inc_prc_ecl"

///////////////////////////////////////////////////////////////////////////////
// GetXPFromTable, inputs: level and CR, output: experience points
int GetXPFromDMGTable(int nLevel, float fCR);

///////////////////////////////////////////////////////////////////////////////
// IsPlayerEligible, input: player object, output: TRUE or FALSE
int IsPlayerEligible(object oPlayer)
{
   if (B_AWARD_DEAD_PLAYERS)
      return GetArea(GetObjectByTag("NW_DEATH_TEMPLE")) == GetArea(oPlayer) ||
         GetDistanceBetween(OBJECT_SELF, oPlayer) < 50.0f;

   return GetArea(OBJECT_SELF) == GetArea(oPlayer) &&
      GetIsInCombat(oPlayer) && GetCurrentHitPoints(oPlayer) > 0;
}

///////////////////////////////////////////////////////////////////////////////
// GetHitDiceByXP
//  Solve for hd from xp = hd * (hd - 1) * 500
//   hd = 1/50 * (sqrt(5) * sqrt(xp + 125) + 25)
int GetHitDiceByXP(float fXP)
{
   return FloatToInt(0.02 * (sqrt(5.0f) * sqrt(fXP + 125.0f) + 25.0f));
}

///////////////////////////////////////////////////////////////////////////////
// no_c2_default7, inputs: the killing object, outputs: none (returned)
void main()
{
   // We'll start by declaring (and sometimes getting) our variables
   //  OBJECT_SELF is initialized by the call to ExecuteScript
   object oKiller = GetLastKiller();
   int    nPartyMembers = 0;
   int    nIndex = 0;
   int    nLevel = 0;
   int    nXP = 0;
   int    nHoldXP = 0;
   int    nAssociateType = 0;
   float  fTotalXP = 0.0f;
   float  fScale = 1.0f;
   float  fCR = GetChallengeRating(OBJECT_SELF);
   object oMaster = GetMaster(oKiller);
   object oMember;

   // Update 5/19/04 - make sure we check the actual master in case we have a
   //  henchman or other associate who also has an associate (for example,
   //  henchman is a wizard and has a familiar or has summoned a creature).
   // Update 07/12/04 thanks to ***RodneyOrpheus*** fixed nasty bug here
   while (GetIsObjectValid(oMaster))
   {
      oKiller = oMaster;
      oMaster = GetMaster(oKiller);
   }

   // This gets rid of the double experience awards (will also override
   //  module's XP slider, so it doesn't need to be set to zero)
   //  Thanks to *** TethyrDarknight *** for this idea
   if (B_BYPASS_BIOWARE_XP)
   {
      if (GetIsPC(oKiller) || GetIsPC(GetTrapCreator(oKiller)))
      {
         ApplyEffectToObject(DURATION_TYPE_INSTANT,
            EffectResurrection(), OBJECT_SELF);
         ApplyEffectToObject(DURATION_TYPE_INSTANT,
            EffectDamage((GetCurrentHitPoints() * 2)), OBJECT_SELF);
      }
   }

   // Sanity check - should only happen if creature is invalid
   //  No point in calculating any more if CR is less than or = to 0
   //  or if the script's module slider is set to 0
   if (fCR <= 0.0 || N_MODULE_SLIDER == 0)
      return;

   // Before we start calculating XP, let's check to see if we are a controlled
   //  type* of someone else. * summoned/familiar/companion/dominated
   // Update 8/5/04 - thanks ***Belial Prime*** - we were hitting this check on
   //  DM controlled enemies, where we should still be awarding xp.
   //  Used GetAssociateType instead of GetMaster.
   if (B_COUNT_ASSOCIATES == FALSE &&
       GetAssociateType(OBJECT_SELF) != ASSOCIATE_TYPE_NONE)
      return;

   // This will also ensure we award XP for traps.
   //  Thanks to *** Sotae *** for this idea
   if (GetIsObjectValid(GetTrapCreator(oKiller)))
      oKiller = GetTrapCreator(oKiller);

   // First let's determine the number of members in the party. The values of
   //  B_COUNT_HENCHMEN and B_COUNT_ASSOCIATES will determine whether or not
   //  we count henchman and other associates when determining the total number
   //  of party members to divide the Total XP award by. Although the DMG
   //  doesn't specify for players, the DMG does specify for that for enemies,
   //  we don't count xp separately for their creatures added to their forces.
   //  Since henchman can play such a big role, I recommend counting them.
   //  Note: I don't award XP to associates, I just use them in determining
   //  the factor to divide the total XP if specified.
   oMember = GetFirstFactionMember(oKiller); // this only returns PC's
   while (GetIsObjectValid(oMember))
   {
      // sanity check - first let's see if the PC member is in the same area as
      //  the creature just killed  and if the member was in combat. This way,
      //  if the member's in the area, but not in combat - we won't include
      //  him. It should be noted, there's a delay after an enemy is killed and
      //  before a PC is no longer in combat. (Try resting immediately after
      //  you've killed an enemy, and also note the music). This delay is
      //  sufficient for us to count XP. Handy! Note: this delay does NOT occur
      //  if the player is dead, so dead members will NOT be awarded XP.
      if (IsPlayerEligible(oMember))
      {
         // add the PC him/herself
         nPartyMembers++;

         // We'll treat henchmen separately from the other associates, as,
         //  since version 1.59, you can have more than one henchman.
         if (B_COUNT_HENCHMEN)
         {
            // Loop through the available henchmen and see if he's got 'em
            //  Update 08/05/04 - fixed off by one error in the comparison
            for (nIndex = 1; nIndex <= GetMaxHenchmen(); nIndex++)
            {
               if (GetIsObjectValid(GetHenchman(oMember, nIndex)))
                  nPartyMembers++; // increment by number of henchmen
            }
         }

         // According to the lexicon, henchman is the only associate type
         //  that the PC's can have more than one of, so we'll make a simple
         //  check once for each type. I looked in the nwscript.nss file, and
         //  the associate types range from 0 - 5 (where 0 is none, 1 is
         //  henchmen and 2 - 5 are the one's we are interested in)
         if (B_COUNT_ASSOCIATES)
         {
            // loop through each associate type, not including henchmen
            for (nAssociateType  = ASSOCIATE_TYPE_ANIMALCOMPANION;
                 nAssociateType <= ASSOCIATE_TYPE_DOMINATED; nAssociateType++)
            {
               if (GetIsObjectValid(GetAssociate(nAssociateType, oMember)))
                  nPartyMembers++; // increment by number of associates
            }
         }

      } // end if (IsPlayerEligible(oMember))

      // get the next guy in the PC's party
      oMember = GetNextFactionMember(oMember);
   } // end while (GetIsObjectValid(oMember))

   // Sanity check - this shouldn't happen, but in case it does...
   //  this will also prevent a divide by zero below
   if (nPartyMembers < 1)
      return;

   // According to the DMG, there are 6 steps. The 6th step is a loop, so
   //  we'll start with that. (why didn't we start yet - because we needed to
   //  know the total party members first)
   oMember = GetFirstFactionMember(oKiller);
   while (GetIsObjectValid(oMember))
   {
      // same check as before
      if (IsPlayerEligible(oMember))
      {
         // Step 1. determine character level
         // Update 08/05/04 - thanks ***Belial Prime*** We want to get level
         //  by xp, to discourage pcs from waiting to level
//         nLevel = GetHitDiceByXP(IntToFloat(GetXP(oMember)));
        nLevel = GetECL(oMember);

         // Step 2. get the monster's fCR
         // fCR = GetChallengeRating(OBJECT_SELF); // we've already done this

         // Step 3. consult the table and adjust using N_MODULE_SLIDER
         // Update 08/05/04 - go back to using rounding instead of truncation
         fScale = IntToFloat(N_MODULE_SLIDER) / 100.0f;
         fTotalXP = IntToFloat(GetXPFromDMGTable(nLevel, fCR)) * fScale;

         // Step 4. divide the XP by the number of party members
         //  we did the sanity check above, so we can't get DIV/0
         nXP = FloatToInt(fTotalXP / IntToFloat(nPartyMembers) + 0.5f);

         // Step 5 is taken care of by calling this script when each
         //  monster's OnDeath script gets called (this one)

         // Award the XP to the PC - always award at least 1 xp so we know the
         //  awards are working
         if (nXP <= 0)
            nXP = 1;

         // use the standard bioware penalty for multi-class characters
         if (B_USE_MULTICLASS_PENALTY)
            GiveXPToCreature(oMember, nXP);

         // or use get and set to bypass the multi-class penalty
         //  Thanks to *** Ima Dufus *** for this idea
         else
         {
            // we'll get the current xp
            nHoldXP = GetXP(oMember);

            // add them to our newly calculated xp
            nXP += nHoldXP;

            // and re-assign the total,
            //  thus bypassing the multi-class penalty
            SetXP(oMember, nXP);
         }

      } // end if (IsPlayerEligible(oMember))

      // Step 6. get the next guy
      oMember = GetNextFactionMember(oMember);
   } // end while (GetIsObjectValid(oMember))
}


///////////////////////////////////////////////////////////////////////////////
// GetXPFromDMGTable, inputs: level and CR, outputs: experience points
int GetXPFromDMGTable(int nLevel, float fCR)
{
   // I wanted to try and mimic the DMG tables, including how to
   //  calculate XP for less than CR 1 and CR above 20, per the DMG's
   //  notes and to account for character levels above 20, which is NOT in
   //  the DMG.

   // this is what we'll determine, the final result
   int nXP = 0;

   // this is the base XP for defeating an enemy with a CR equal to the
   // character's level
   int nBaseXP = 300;

   // Let's change the CR to an integer (we'll lose precision here, but it
   //  should only matter for CR < 1, which we don't want to calculate unless
   //  we first determine if it's warranted to even award XP
   int nCR = FloatToInt(fCR);

   // according to the DMG - the table doesn't support XP for monsters that are
   //  8 CR ratings lower or higher than the characters level (see below)
   int nAbsDifference = abs(nLevel - nCR);

   // used for CR > 20
   int nMultiplier = 1;

   // used for Level > 20
   float fModifier = 1.0f;

   // if the difference in CR and Level is greater than 7, then we don't reward
   //  XP. The DMG assume's it's not a challenge for Level > CR and must be
   //  divine fortune in the case of CR > Level.
   // Note: this isn't entirely DMG accurate. The difference is determined
   //  in this script BEFORE we start treating 1st and 2nd level characters
   //  as 3rd level characters. According to the DMG, a 1st and 2nd level
   //  character would be awarded XP for up to a CR 10 monster. Making this
   //  check prior to treating 1st and 2nd level characters as 3rd removes
   //  the XP award for both CR 9 and 10 from a 1st level character and
   //  removes the XP award for CR 10 from a 2nd level character. Originally,
   //  this was a mistake, however, I thought it a good mistake, so I left it.
   //  This is an extreme corner case where it is highly unlikely for the
   //  players to survive anyway. Let me know if you don't agree. I also
   //  thought leaving this mistake in would help discourage high level party's
   //  from trying to drag around 1st level characters to exploit the DMG's
   //  benefits to lower level party members.
   if (nAbsDifference > 7)
   {
      return 0;
   }

   // Otherwise, if the CR equals the level, which is the same as the bold
   //  entries in the table, we can award the base XP and we're done.
   else if (nAbsDifference == 0)
   {
      return nBaseXP * nCR;
   }

   // For CR < 1, we'll treat it as 1 and multiply the result by our float CR
   //  at the end, per our DMG notes.
   if (nCR == 0)
   {
      nCR = 1;
   }

   // The table treats 1st - 3rd level characters the same (see notes above)
   if (nLevel < 3)
   {
      nLevel = 3;
   }

   // The DMG does not handle character levels above 20, so, rather than always
   //  returning 0 XP, we'll have a special case to handle player levels > 20.
   //  The DMG tables ratio of XP to CR/Level increases as levels go up. For
   //  example, at CR 11, with a 4th level character, the XP is about 3.9 times
   //  optimum. However, at CR 20, for a 13th level character, XP is nearly 8
   //  times as high. So, I've assumed a fixed ratio, based on the CR 20
   //  column, with the upper XP based off what the ratio would scale down to
   //  for the higher levels from the pattern from CR 11 to CR 20.

   // Note: it's not normally good practice to compact lines of code, but in
   //  this particular case, it's pretty obvious what I'm doing, so I'm going
   //  to use the compacted style case to reduce the number of visual lines.
   if (nLevel > 20)
   {
      // setup our default value
      nXP = nBaseXP * nCR;

      // we've already handled when they are equal and where they are greater
      //  that 7 levels of difference above
      switch (nLevel - nCR)
      {
         case -7:  fModifier = 8.0f;  break;
         case -6:  fModifier = 6.0f;  break;
         case -5:  fModifier = 5.0f;  break;
         case -4:  fModifier = 3.5f;  break;
         case -3:  fModifier = 3.0f;  break;
         case -2:  fModifier = 2.0f;  break;
         case -1:  fModifier = 1.5f;  break;
         case  0:  fModifier = 1.0f;  break;
         case  1:  fModifier = 0.7f;  break;
         case  2:  fModifier = 0.5f;  break;
         case  3:  fModifier = 0.4f;  break;
         case  4:  fModifier = 0.3f;  break;
         case  5:  fModifier = 0.2f;  break;
         case  6:  fModifier = 0.15f; break;
         case  7:  fModifier = 0.10f; break;
         default:  fModifier = 1.0f;  break; // already taken care of
      }

      // return the modified value
      return FloatToInt(IntToFloat(nXP) * fModifier);
   }

   // This next bit is used for > 20 CR. According to the DMG notes just below
   //  the tables on page 38 - we double the reward for a CR 2 levels below the
   //  desired CR, for instance, CR 21 is calculated as CR 19 with a
   //  multiplier of 2, CR 22 is calculated as CR 20 with a multiplier of 4,
   //  and CR 26 is calculated as CR 20 with a multiplier of 16 (basically,
   //  all even CRs will calculate at CR 20 with a multiplier and odd
   //  CRs will calculate at CR 19 with a multiplier).
   if (nCR > 20)
   {
      while (nCR > 20)
      {
         nMultiplier *= 2; // this may get excessive - if so, use '+= 2'
         nCR -= 2;
      }
   }

   // Now, we got this far, and it's ok to award XP, let's "lookup" the XP from
   //  the table - we'll deal with < 1 CR at the end of CR 1 - that's the only
   //  place we'll need it. The first switch is the CR (table columns) and the
   //  nested switches are the character level (table rows). CR 1, CR 19 and CR
   //  20 have optional manipulations and thus use the standard case,
   //  statement, and break. The break is not necessary for the other CR cases
   //  as we can simply return the XP at each case. As I mentioned before, I'm
   //  using the compacted style case to reduce the number of visual lines.

   // start with column 1, CR 1
   switch (nCR)
   {
   case 1: // CR 1 (or < 1)
      switch (nLevel) // character level (the rows)
      {
      case 3: // Level 3-6 are the same
      case 4:
      case 5:
      case 6: nXP = 300; break; // this is the bold entry in the table and has
         // already been handled, but I'm going to leave it in the tables for
         // completeness.
      case 7: nXP = 263; break; // Level 7
      case 8: nXP = 200; break; // Level 8
         // we've already handled the case where the CR rating is 8 levels above
         // or below, but, it's good practice to always have a default case
      default: return 0;
      }

      // handle CR < 1
      if (fCR < 1.0f)
      {
         nXP = FloatToInt(IntToFloat(nXP) * fCR);
      }

      // return the XP
      return nXP;

   case 2: // CR 2
      switch (nLevel) // each case is the character level
      {
      case 3:  // Level 3-4 are the same
      case 4:  return 600; // we can return XP at this point
      case 5:  return 500;
      case 6:  return 450;
      case 7:  return 350;
      case 8:  return 300;
      case 9:  return 225;
      default: return 0;
      }

   case 3: // CR 3
      switch (nLevel) // character level
      {
      case 3:  return 900;
      case 4:  return 800;
      case 5:  return 750;
      case 6:  return 600;
      case 7:  return 525;
      case 8:  return 400;
      case 9:  return 338;
      case 10: return 250;
      default: return 0;
      }

   case 4: // CR 4
      switch (nLevel) // character level
      {
      case 3:  return 1350;
      case 4:  return 1200;
      case 5:  return 1000;
      case 6:  return 900;
      case 7:  return 700;
      case 8:  return 600;
      case 9:  return 450;
      case 10: return 375;
      case 11: return 275;
      default: return 0;
      }

   case 5: // CR 5
      switch (nLevel) // character level
      {
      case 3:  return 1800;
      case 4:  return 1600;
      case 5:  return 1500;
      case 6:  return 1200;
      case 7:  return 1050;
      case 8:  return 800;
      case 9:  return 675;
      case 10: return 500;
      case 11: return 413;
      case 12: return 300;
      default: return 0;
      }

   case 6: // CR 6
      switch (nLevel) // character level
      {
      case 3:  return 2700;
      case 4:  return 2400;
      case 5:  return 2250;
      case 6:  return 1800;
      case 7:  return 1400;
      case 8:  return 1200;
      case 9:  return 900;
      case 10: return 750;
      case 11: return 550;
      case 12: return 450;
      case 13: return 325;
      default: return 0;
      }

   case 7: // CR 7
      switch (nLevel) // character level
      {
      case 3:  return 3600;
      case 4:  return 3200;
      case 5:  return 3000;
      case 6:  return 2700;
      case 7:  return 2100;
      case 8:  return 1600;
      case 9:  return 1350;
      case 10: return 1000;
      case 11: return 825;
      case 12: return 600;
      case 13: return 488;
      case 14: return 350;
      default: return 0;
      }

   case 8: // CR 8
      switch (nLevel) // character level
      {
      case 3:  return 5400;
      case 4:  return 4800;
      case 5:  return 4500;
      case 6:  return 3600;
      case 7:  return 3150;
      case 8:  return 2400;
      case 9:  return 1800;
      case 10: return 1500;
      case 11: return 1100;
      case 12: return 900;
      case 13: return 650;
      case 14: return 525;
      case 15: return 375;
      default: return 0;
      }

   case 9: // CR 9
      switch (nLevel) // character level
      {
      case 3:  return 7200;
      case 4:  return 6400;
      case 5:  return 6000;
      case 6:  return 5400;
      case 7:  return 4200;
      case 8:  return 3600;
      case 9:  return 2700;
      case 10: return 2000;
      case 11: return 1650;
      case 12: return 1200;
      case 13: return 975;
      case 14: return 700;
      case 15: return 563;
      case 16: return 400;
      default: return 0;
      }

   case 10: // CR 10
      switch (nLevel) // character level
      {
      case 3:  return 10800; // lucky player!!
      case 4:  return 9600;
      case 5:  return 9000;
      case 6:  return 7200;
      case 7:  return 6300;
      case 8:  return 4800;
      case 9:  return 4050;
      case 10: return 3000;
      case 11: return 2200;
      case 12: return 1800;
      case 13: return 1300;
      case 14: return 1050;
      case 15: return 750;
      case 16: return 600;
      case 17: return 425;
      default: return 0;
      }

   case 11: // CR 11
      switch (nLevel) // character level
      {
      case 4:  return 12800;
      case 5:  return 12000;
      case 6:  return 10800;
      case 7:  return 8400;
      case 8:  return 7200;
      case 9:  return 5400;
      case 10: return 4500;
      case 11: return 3300;
      case 12: return 2400;
      case 13: return 1950;
      case 14: return 1400;
      case 15: return 1125;
      case 16: return 800;
      case 17: return 638;
      case 18: return 450;
      default: return 0;
      }

   case 12: // CR 12
      switch (nLevel) // character level
      {
      case 5:  return 18000;
      case 6:  return 14400;
      case 7:  return 12600;
      case 8:  return 9600;
      case 9:  return 8100;
      case 10: return 6000;
      case 11: return 4950;
      case 12: return 3600;
      case 13: return 2600;
      case 14: return 2100;
      case 15: return 1500;
      case 16: return 1200;
      case 17: return 850;
      case 18: return 675;
      case 19: return 475; // 12 + 15 = 27 comparisons
      default: return 0;
      }

   case 13: // CR 13
      switch (nLevel) // character level
      {
      case 6:  return 21600;
      case 7:  return 16800;
      case 8:  return 14400;
      case 9:  return 10800;
      case 10: return 9000;
      case 11: return 6600;
      case 12: return 5400;
      case 13: return 3900;
      case 14: return 2800;
      case 15: return 2250;
      case 16: return 1600;
      case 17: return 1275;
      case 18: return 900;
      case 19: return 713;
      case 20: return 500; // worst case, 13 + 15 = 28 comparisons
      default: return 0;
      }

   case 14: // CR 14
      switch (nLevel) // character level
      {
      case 7:  return 25200;
      case 8:  return 19200;
      case 9:  return 16200;
      case 10: return 12000;
      case 11: return 9900;
      case 12: return 7200;
      case 13: return 5850;
      case 14: return 4200;
      case 15: return 3000;
      case 16: return 2400;
      case 17: return 1700;
      case 18: return 1350;
      case 19: return 950;
      case 20: return 750; // worst case, 14 + 14 = 28 comparisons
      default: return 0;
      }

   case 15: // CR 15
      switch (nLevel) // character level
      {
      case 8:  return 28800;
      case 9:  return 21600;
      case 10: return 18000;
      case 11: return 13200;
      case 12: return 10800;
      case 13: return 7800;
      case 14: return 6300;
      case 15: return 4500;
      case 16: return 3200;
      case 17: return 2550;
      case 18: return 1800;
      case 19: return 1425;
      case 20: return 1000; // worst case, 15 + 13 = 28 comparisons
      default: return 0;
      }

   case 16: // CR 16
      switch (nLevel) // character level
      {
      case 9:  return 32400;
      case 10: return 24000;
      case 11: return 19800;
      case 12: return 14400;
      case 13: return 11700;
      case 14: return 8400;
      case 15: return 6750;
      case 16: return 4800;
      case 17: return 3400;
      case 18: return 2700;
      case 19: return 1900;
      case 20: return 1500; // worst case, 16 + 12 = 28 comparisons
      default: return 0;
      }

   case 17: // CR 17
      switch (nLevel) // character level
      {
      case 10: return 36000;
      case 11: return 26400;
      case 12: return 21600;
      case 13: return 15600;
      case 14: return 12600;
      case 15: return 9000;
      case 16: return 7200;
      case 17: return 5100;
      case 18: return 3600;
      case 19: return 2850;
      case 20: return 2000; // worst case, 17 + 11 = 28 comparisons
      default: return 0;
      }

   case 18: // CR 18
      switch (nLevel) // character level
      {
      case 11: return 39600;
      case 12: return 28800;
      case 13: return 23400;
      case 14: return 16800;
      case 15: return 13500;
      case 16: return 9600;
      case 17: return 7650;
      case 18: return 5400;
      case 19: return 3800;
      case 20: return 3000; // worst case, 18 + 10 = 28 comparisons
      default: return 0;
      }

   case 19: // CR 19
      switch (nLevel) // character level
      {
      case 12: nXP = 43200; break;
      case 13: nXP = 31200; break;
      case 14: nXP = 25200; break;
      case 15: nXP = 18000; break;
      case 16: nXP = 14400; break;
      case 17: nXP = 10200; break;
      case 18: nXP = 8100;  break;
      case 19: nXP = 5700;  break;
      case 20: nXP = 4000;  break; // worst case, 19 + 9 = 28 comparisons
      default: return 0;
      }

      // adjust for > CR 20
      nXP = nMultiplier * nXP;

      // return the total
      return nXP;

   case 20: // CR 20
      switch (nLevel) // character level
      {
      case 13: nXP = 46800; break;
      case 14: nXP = 33600; break;
      case 15: nXP = 27000; break;
      case 16: nXP = 19200; break;
      case 17: nXP = 15300; break;
      case 18: nXP = 10800; break;
      case 19: nXP = 8550;  break;
      case 20: nXP = 6000;  break; // worst case, 20 + 8 = 28 comparisons
      default: return 0;
      }

      // adjust for > CR 20
      nXP = nMultiplier * nXP;

      // return the total
      return nXP;

   } // phew!

   // we should never get here, but to keep the compiler happy, (all paths must
   // have a return value), we'll return zero
   return 0;
}

// End of no_c2_default7
// ============================================================================
