PWE_PRC8/_module/nss/give_custom_exp.nss
Jaysyn904 ee1dc35889 Initial Commit
Initial Commit
2025-04-03 10:29:41 -04:00

414 lines
15 KiB
Plaintext

#include "nw_i0_generic"
int iDebug = FALSE; // Set to TRUE enables several debug lines.
// Debug info includes party shares, etc.
int iDouble_EXP = FALSE; // Set to TRUE enables seeing first the PW
// experience followed by Bioware experience.
// This is useful to compare values.
float Experience_Slider = 0.06;// This is the percentage that can be adjusted.
// This value matches the Bioware slider.
float Exp_Level_Cap = 10.0; // This is the basic CR cap.
int iBoss_Cap_Method = 2; // These are mode sets for how bosses are handled
// Mode 1: The CR of the boss is modified.
// The exp cap is left at +8 CR.
// Mode 2: The CR of the boss is left alone.
// The exp cap is raised by the mod.
// Mode 3: The CR of the boss is modified.
// The exp cap is raised by the mod.
float Lower_Exponent = 1.15; // This is the exponent that defines how exp
// drops off when the CR is lower than player.
float Higher_Exponent = 1.15; // This is the exponent that defines how exp
// increases whent he CR is higher than player.
float Party_Proximity = 15.0; // The distance a party member must be to get a
// share of the experience.
float Leveling_Exponent = 1.08;// This exponent defines how fast the exponent is
// when determining party shares.
// The following is the level difference and
// how it relates to shares:
// 1.10 at 10 levels difference = 1.63 shares
// 1.20 at 10 levels difference = 6.19 shares
// 1.30 at 10 levels difference = 13.79 shares
float Familiar_Split = 0.10; // The ratio of shares that a familiar or animal
// companion receives.
float Henchman_Split = 0.25; // The ratio of shares that a henchman or
// summoned creature receives.
/*
The party experience split is calculated based upon an exponential equation
that is referenced to the level of the party members. In general, two players
of equal level each receive equal shares. If there is a 5 level difference,
then the higher level character gets 3.71 shares, while the lower gets 1.00
shares. The intent here is to give more experience to the high level characters
and less to the lower. After about 10 levels of difference, there is basically
no point in grouping, because there is very little experience gained for the
low level member.
All NPCs that are grouped with the party get a fraction of the shares that a
normal player would. This means that NPC party members subtract from the
experience pool like a normal player would, but to a lesser extent. A familiar
and summoned animal friend both recieve 1/3 of the experience pool. A summoned
creature, or henchman receives 2/3 of the experience pool. This means that
using a familiar should be viable for a spell caster. A chart is given at the
end of this script for the exact values of the shares based upon the level
differences in the party.
A major bug in the Bioware code is using the average level of the party to
determine how much experience should be given to the party. This is typically
abused on PWs by creating a 1st level summoned creature to reduce the party
average level, and thereby inflate the experience gained dramatically. This
problem is simply resolved by making the experience given to the party always
based off the highest level member.
The split of the experience pool is finally modified by the size of the party.
The intent here is to increase the experience for groups, and to promote their
use for leveling. The basic concept is two players of equal level would each
recieve 75% of the experience for the kill. This inflates the total experience
for the kill by 50%, but results in a more realistic gain for the party. A
chart is given at the end of this script for how the party experience is
enhanced.
All of these changes should promote grouping with like levels and allow some
of the lesser used game features, such as henchmen and familiars, to be more
common. The base experience gain is roughly equivelent to that of setting the
module experience slider to 4%. The majority of the equations used in this
system are second order polynomials. To change the 4% scale, simply adjust the
value float value at the start of this file. All attempts have been made to
directly match the Bioware scales for basic experience calculations.
When using custom death scripts, the last parameter in give_custom_exp is the
CR modifier that can be used to boost experience for creatures. The method of
the boost is defined by the Boss_Cap_Method variable.
*/
// Absolutely, Positively do not call this function inside another loop
// that uses GetNextPC. This can be used inside GetNextFactionMember().
void SendMessageToParty( object oMember, string sText )
{
object oPartyMember = GetFirstPC();
while( GetIsObjectValid(oPartyMember) )
{
if( GetFactionEqual( oPartyMember, oMember ) )
SendMessageToPC( oPartyMember, sText );
oPartyMember = GetNextPC();
}
}
// This routine comes from and idea from Tone Eternal.
// It limits a player from gaining more experience after reaching enough to
// continue to the next level. This is not really a bug, but a short-coming
// of the Bioware code.
// This function is not used in version 1.4 or higher, but can be installed.
int IsPlayerLevelingUp( object oPlayer )
{
int nNeeded;
switch( GetHitDice(oPlayer) )
{
case 1: nNeeded = 1000; break;
case 2: nNeeded = 3000; break;
case 3: nNeeded = 6000; break;
case 4: nNeeded = 10000; break;
case 5: nNeeded = 15000; break;
case 6: nNeeded = 21000; break;
case 7: nNeeded = 28000; break;
case 8: nNeeded = 36000; break;
case 9: nNeeded = 45000; break;
case 10: nNeeded = 55000; break;
case 11: nNeeded = 66000; break;
case 12: nNeeded = 78000; break;
case 13: nNeeded = 91000; break;
case 14: nNeeded = 105000; break;
case 15: nNeeded = 120000; break;
case 16: nNeeded = 136000; break;
case 17: nNeeded = 153000; break;
case 18: nNeeded = 171000; break;
case 19: nNeeded = 200000; break;
default: nNeeded = 1000000; break;
}
if( nNeeded < GetXP( oPlayer ) )
return( TRUE );
return( FALSE );
}
// The main routine for giving experience to the player.
// Created by David Bills.
void give_custom_exp( object oKiller, object oDead, int nCRMod )
{
// Custom DMG EXP Script (varies slightly on bonuses and penalties)
// Concept and Original Code By Helznicht
// Syntax Clean-Up and Area Check Award by Mmealman
// Major bug rennovation by David Bills. 1-9-03
// Basic experience for creatures is around Bioware 3%.
// To adjust this, modify the polynomial equation below.
if( GetLocalInt( oDead, "AlreadyDyingEXP" ) == 1 )
return;
// First get all the members of the party
float LowestLevelMember = 1000.0;
float HighestLevelMember = -10.0;
float PartyShares = 0.0;
float fLevel = 0.0;
float fCRMod = IntToFloat( nCRMod );
object oMaster, oHighest, oLowest;
int bOld;
int nTotalMembers = 0;
object oPartyMember = GetFirstFactionMember(oKiller, FALSE );
while( GetIsObjectValid(oPartyMember) )
{
if( GetArea(oKiller) == GetArea(oPartyMember) &&
GetCurrentHitPoints( oPartyMember ) > 1 &&
GetDistanceBetween( oKiller, oPartyMember ) < Party_Proximity )
{
// Debug line
//if(iDebug== TRUE)
//{
SendMessageToAllDMs("Member: "+ GetName(oKiller));
//}
//
nTotalMembers ++;
if( GetIsPC( oPartyMember ) )
fLevel = IntToFloat( GetHitDice( oPartyMember ) );
else
fLevel = GetChallengeRating( oPartyMember );
if( LowestLevelMember > fLevel )
{
LowestLevelMember = fLevel;
oLowest = oPartyMember;
}
if( HighestLevelMember < fLevel )
{
HighestLevelMember = fLevel;
oHighest = oPartyMember;
}
}
oPartyMember = GetNextFactionMember(oKiller, FALSE);
}
// Now we need to test for the Really low level creatures.
// They need to be scaled as negative level to function properly.
float fChallenge =GetChallengeRating( oDead );
if( fChallenge < 1.0 )
fChallenge = 1.0 - (( 1.0 - fChallenge ) * 5.0);
// This is a debug routine.
if(iDebug== TRUE)
{
SendMessageToParty( oKiller, "Enemy CR: "+FloatToString( fChallenge ) );
SendMessageToParty( oKiller, "Highest Member: "+GetName( oHighest ) );
}
// This is the level difference of the dead to the highest
fLevel = fChallenge - HighestLevelMember;
if( iBoss_Cap_Method == 1 )
{
fLevel += fCRMod;
if( fLevel >= Exp_Level_Cap )
fLevel = Exp_Level_Cap;
}
else
if( iBoss_Cap_Method == 2 )
{
if( fLevel >= Exp_Level_Cap+fCRMod )
fLevel = Exp_Level_Cap+fCRMod;
}
else
{
if( fLevel >= Exp_Level_Cap )
fLevel = Exp_Level_Cap;
fLevel += fCRMod;
}
//Calculate basic value of kill
float FinalMonValue = 84.0 * HighestLevelMember + 750;
float fSharePercent;
// This is the exponential function to match the 3e rules.
// There is some adjustment here to allow for smooth transitions.
if( fLevel < 0.0 )
FinalMonValue = FinalMonValue * pow( Lower_Exponent, fLevel ) ;
else
FinalMonValue = FinalMonValue * pow( Higher_Exponent, fLevel ) ;
FinalMonValue = FinalMonValue * Experience_Slider;
if( FinalMonValue < 0.0 )
FinalMonValue = 0.0 ;
//if(iDebug == TRUE)//////////////////////////////////////////
//{
SendMessageToAllDMs("Base Xp: "+FloatToString( FinalMonValue ) );
//}
/* Now determine shares based upon the lowest party member. */
oPartyMember = GetFirstFactionMember(oKiller, FALSE);
while( GetIsObjectValid(oPartyMember) )
{
if( GetArea(oKiller) == GetArea(oPartyMember) &&
GetCurrentHitPoints( oPartyMember ) > 0 &&
GetDistanceBetween( oKiller, oPartyMember ) < Party_Proximity )
{
if( GetIsPC( oPartyMember ) )
fLevel = IntToFloat( GetHitDice( oPartyMember ) );
else
fLevel = GetChallengeRating( oPartyMember );
fLevel = fLevel - LowestLevelMember;
fSharePercent = pow( Leveling_Exponent , fLevel );
// This check is for summoned monsters, and familiars.
oMaster = GetMaster( oPartyMember );
if( GetIsObjectValid(oMaster) )
{
if( oPartyMember == GetAssociate( ASSOCIATE_TYPE_FAMILIAR, oMaster ) ||
oPartyMember == GetAssociate( ASSOCIATE_TYPE_ANIMALCOMPANION, oMaster ) )
fSharePercent = fSharePercent * Familiar_Split ;
else
fSharePercent = fSharePercent * Henchman_Split ;
}
// This is a debug line
if(iDebug == TRUE)
{
SendMessageToParty( oKiller, GetName(oPartyMember)+" Share="+FloatToString(fSharePercent) );
}
/* Adjusting the differential for level variance here */
PartyShares += fSharePercent;
}
oPartyMember = GetNextFactionMember(oKiller, FALSE);
}
// This is a debug line
if(iDebug== TRUE)
SendMessageToParty( oKiller, "Total Shares="+FloatToString(PartyShares) );
int nPenalty;
if( PartyShares > 0.0 )
{
//Determine the value of the Split EXP
// This is based on shares compared to the lowest member to the heighest.
int SFEint;
//Distribute EXP to all PCs in the Party based upon shares.
oPartyMember = GetFirstFactionMember(oKiller, TRUE);
while ( GetIsObjectValid(oPartyMember) )
{
if( GetArea(oKiller) == GetArea(oPartyMember) &&
GetCurrentHitPoints( oPartyMember ) > 0 &&
GetDistanceBetween( oKiller, oPartyMember ) < Party_Proximity )
{
fLevel = IntToFloat( GetHitDice( oPartyMember ) );
fLevel = fLevel - LowestLevelMember;
// The share system here is used because a 20th level character
// is MANY times more powerful than a 10th level character. And
// the experience shares should not simply be based upon straight
// level values.
// Use the formula: your share = (your level - lowest level) ^ 1.10
fSharePercent = pow( Leveling_Exponent , fLevel ) / PartyShares;
// The following modification improves experience for groups.
// It is not needed for a flat experience system, but then most
// people would not group. What it normally does is similar to
// two people of equal levels in a group each get 0.75 of the total
// experience, instead of an equal split of 0.50.
// Use the formula: Your fraction = 2 * share - share ^ 2
fSharePercent = 2*fSharePercent - (fSharePercent * fSharePercent );
if(iDebug== TRUE)
SendMessageToParty( oKiller, "Party Percent " +
GetName( oPartyMember ) +": "+
FloatToString(fSharePercent*100.0)+"%" );
SFEint = FloatToInt( fSharePercent * FinalMonValue );
// This code will give experience regardless of level.
//GiveXPToCreature(oPartyMember, SFEint);
// Use this code instead of the above line if you want to stop your
// players from gaining experience when they reach the next level.
if(IsPlayerLevelingUp(oPartyMember) == FALSE)
GiveXPToCreature(oPartyMember, SFEint);
else
SendMessageToPC( oKiller,
"You cannot gain experience until you level your character." );
//
}
oPartyMember = GetNextFactionMember(oKiller, TRUE);
}
}
// This line is needed to stop looping on death.
SetLocalInt( oDead, "AlreadyDyingEXP", 1 );
}
//void main() {}