2025/10/26 Update

Swapped out crappy existing XP system for PWFXP.
This commit is contained in:
Jaysyn904
2025-10-26 13:14:12 -04:00
parent 909e70fe28
commit 74ede2e7dd
30 changed files with 1314 additions and 131 deletions

View File

@@ -4,8 +4,6 @@
//:://////////////////////////////////////////////
/** @file
@author Primogenitor
@date 2005.09.23 - Rebuilt the system - Ornedan
*/
@@ -21,29 +19,29 @@ const int DYNCONV_EXITED = -2;
const int DYNCONV_ABORTED = -3;
const int DYNCONV_SETUP_STAGE = -1;
const int DYNCONV_TOKEN_HEADER = 99;
const int DYNCONV_TOKEN_REPLY_0 = 100;
const int DYNCONV_TOKEN_REPLY_1 = 101;
const int DYNCONV_TOKEN_REPLY_2 = 102;
const int DYNCONV_TOKEN_REPLY_3 = 103;
const int DYNCONV_TOKEN_REPLY_4 = 104;
const int DYNCONV_TOKEN_REPLY_5 = 105;
const int DYNCONV_TOKEN_REPLY_6 = 106;
const int DYNCONV_TOKEN_REPLY_7 = 107;
const int DYNCONV_TOKEN_REPLY_8 = 108;
const int DYNCONV_TOKEN_REPLY_9 = 109;
const int DYNCONV_TOKEN_EXIT = 110;
const int DYNCONV_TOKEN_WAIT = 111;
const int DYNCONV_TOKEN_NEXT = 112;
const int DYNCONV_TOKEN_PREV = 113;
const int DYNCONV_MIN_TOKEN = 99;
const int DYNCONV_MAX_TOKEN = 113;
const int DYNCONV_TOKEN_HEADER = 16183899;
const int DYNCONV_TOKEN_REPLY_0 = 161838100;
const int DYNCONV_TOKEN_REPLY_1 = 161838101;
const int DYNCONV_TOKEN_REPLY_2 = 161838102;
const int DYNCONV_TOKEN_REPLY_3 = 161838103;
const int DYNCONV_TOKEN_REPLY_4 = 161838104;
const int DYNCONV_TOKEN_REPLY_5 = 161838105;
const int DYNCONV_TOKEN_REPLY_6 = 161838106;
const int DYNCONV_TOKEN_REPLY_7 = 161838107;
const int DYNCONV_TOKEN_REPLY_8 = 161838108;
const int DYNCONV_TOKEN_REPLY_9 = 161838109;
const int DYNCONV_TOKEN_EXIT = 161838110;
const int DYNCONV_TOKEN_WAIT = 161838111;
const int DYNCONV_TOKEN_NEXT = 161838112;
const int DYNCONV_TOKEN_PREV = 161838113;
const int DYNCONV_MIN_TOKEN = 16183899;
const int DYNCONV_MAX_TOKEN = 161838113;
const int DYNCONV_STRREF_PLEASE_WAIT = 16824202; // "Please wait"
const int DYNCONV_STRREF_PREVIOUS = 16824203; // "Previous"
const int DYNCONV_STRREF_NEXT = 16824204; // "Next"
const int DYNCONV_STRREF_ABORT_CONVO = 16824212; // "Abort"
const int DYNCONV_STRREF_EXIT_CONVO = 78; // "Exit"
const int DYNCONV_STRREF_EXIT_CONVO = 16183878; // "Exit"
const string DYNCONV_SCRIPT = "DynConv_Script";
const string DYNCONV_VARIABLE = "DynConv_Var";

View File

@@ -1643,7 +1643,60 @@ int GetIsMagicItem(object oItem)
int FeatToIprop(int nFeat)
{
switch(nFeat)
{//: Weapon Focus
{
//:: Weapon Proficiencies
case FEAT_WEAPON_PROFICIENCY_SHORTSWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTSWORD;
case FEAT_WEAPON_PROFICIENCY_LONGSWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LONGSWORD;
case FEAT_WEAPON_PROFICIENCY_BATTLEAXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_BATTLEAXE;
case FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD;
case FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL;
case FEAT_WEAPON_PROFICIENCY_WARHAMMER: return IP_CONST_FEAT_WEAPON_PROFICIENCY_WARHAMMER;
case FEAT_WEAPON_PROFICIENCY_LONGBOW: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LONGBOW;
case FEAT_WEAPON_PROFICIENCY_LIGHT_MACE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_MACE;
case FEAT_WEAPON_PROFICIENCY_HALBERD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HALBERD;
case FEAT_WEAPON_PROFICIENCY_SHORTBOW: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTBOW;
case FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD;
case FEAT_WEAPON_PROFICIENCY_GREATSWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_GREATSWORD;
case FEAT_WEAPON_PROFICIENCY_GREATAXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_GREATAXE;
case FEAT_WEAPON_PROFICIENCY_DART: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DART;
case FEAT_WEAPON_PROFICIENCY_DIRE_MACE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DIRE_MACE;
case FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE;
case FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL;
case FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER;
case FEAT_WEAPON_PROFICIENCY_HANDAXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HANDAXE;
case FEAT_WEAPON_PROFICIENCY_KAMA: return IP_CONST_FEAT_WEAPON_PROFICIENCY_KAMA;
case FEAT_WEAPON_PROFICIENCY_KATANA: return IP_CONST_FEAT_WEAPON_PROFICIENCY_KATANA;
case FEAT_WEAPON_PROFICIENCY_KUKRI: return IP_CONST_FEAT_WEAPON_PROFICIENCY_KUKRI;
case FEAT_WEAPON_PROFICIENCY_MORNINGSTAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_MORNINGSTAR;
case FEAT_WEAPON_PROFICIENCY_RAPIER: return IP_CONST_FEAT_WEAPON_PROFICIENCY_RAPIER;
case FEAT_WEAPON_PROFICIENCY_SCIMITAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SCIMITAR;
case FEAT_WEAPON_PROFICIENCY_SCYTHE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SCYTHE;
case FEAT_WEAPON_PROFICIENCY_SHORTSPEAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTSPEAR;
case FEAT_WEAPON_PROFICIENCY_SHURIKEN: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SHURIKEN;
case FEAT_WEAPON_PROFICIENCY_SICKLE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SICKLE;
case FEAT_WEAPON_PROFICIENCY_SLING: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SLING;
case FEAT_WEAPON_PROFICIENCY_THROWING_AXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_THROWING_AXE;
case FEAT_WEAPON_PROFICIENCY_TRIDENT: return IP_CONST_FEAT_WEAPON_PROFICIENCY_TRIDENT;
case FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE;
case FEAT_WEAPON_PROFICIENCY_WHIP: return IP_CONST_FEAT_WEAPON_PROFICIENCY_WHIP;
case FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE;
case FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE;
case FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE;
case FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE;
case FEAT_WEAPON_PROFICIENCY_HEAVY_PICK: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_PICK;
case FEAT_WEAPON_PROFICIENCY_LIGHT_PICK: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_PICK;
case FEAT_WEAPON_PROFICIENCY_SAI: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SAI;
case FEAT_WEAPON_PROFICIENCY_NUNCHAKU: return IP_CONST_FEAT_WEAPON_PROFICIENCY_NUNCHAKU;
case FEAT_WEAPON_PROFICIENCY_FALCHION: return IP_CONST_FEAT_WEAPON_PROFICIENCY_FALCHION;
case FEAT_WEAPON_PROFICIENCY_SAP: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SAP;
case FEAT_WEAPON_PROFICIENCY_KATAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_KATAR;
case FEAT_WEAPON_PROFICIENCY_HEAVY_MACE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_MACE;
case FEAT_WEAPON_PROFICIENCY_MAUL: return IP_CONST_FEAT_WEAPON_PROFICIENCY_MAUL;
case FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR;
case FEAT_WEAPON_PROFICIENCY_GOAD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_GOAD;
case FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW: return IP_CONST_FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW;
//: Weapon Focus
case FEAT_WEAPON_FOCUS_BASTARD_SWORD: return IP_CONST_FEAT_WEAPON_FOCUS_BASTARD_SWORD;
case FEAT_WEAPON_FOCUS_BATTLE_AXE: return IP_CONST_FEAT_WEAPON_FOCUS_BATTLE_AXE;
case FEAT_WEAPON_FOCUS_CLUB: return IP_CONST_FEAT_WEAPON_FOCUS_CLUB;

View File

@@ -401,4 +401,3 @@ int GetBestAvailableSpell(object oTarget)
if(nBestSpell == 99999) nBestSpell = GetBestL0Spell(oTarget, nBestSpell);
return nBestSpell;
}

View File

@@ -572,7 +572,10 @@ int GetMaxEssentiaCapacity(object oMeldshaper, int nClass, int nMeld)
{
int nMax = 1; // Always can invest one
int nHD = GetHitDice(oMeldshaper);
if (nHD >= 31) nMax = 5;
if (nHD >= 61) nMax = 8;
else if (nHD >= 51) nMax = 7;
else if (nHD >= 41) nMax = 6;
else if (nHD >= 31) nMax = 5;
else if (nHD >= 18) nMax = 4;
else if (nHD >= 12) nMax = 3;
else if (nHD >= 6) nMax = 2;

View File

@@ -808,7 +808,24 @@ int GetIsOnHitCastSpell(object oSpellTarget = OBJECT_INVALID, object oSpellCastI
if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is armor; attacker = "+GetName(oAttacker)+", defender = "+GetName(oDefender));
}
// is the spell type item a weapon?
else if (iWeaponType == StringToInt(Get2DACache("baseitems", "WeaponType", iBaseType)))
int nWT = StringToInt(Get2DACache("baseitems", "WeaponType", iBaseType));
if (nWT > 0)
{
if (oSpellTarget == OBJECT_INVALID)
oSpellTarget = PRCGetSpellTargetObject(oSpellOrigin);
oAttacker = oSpellOrigin;
oDefender = oSpellTarget;
if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is weapon [WT="+IntToString(nWT)+"]; attacker="+GetName(oAttacker)+", defender="+GetName(oDefender));
}
else
{
if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is neither weapon nor armor; returning FALSE");
return FALSE;
}
/* else if (iWeaponType == StringToInt(Get2DACache("baseitems", "WeaponType", iBaseType)))
{
// determine the target, if not already given
if (oSpellTarget == OBJECT_INVALID)
@@ -823,7 +840,7 @@ int GetIsOnHitCastSpell(object oSpellTarget = OBJECT_INVALID, object oSpellCastI
{
if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is neither weapon nor armor; returning FALSE");
return FALSE;
}
} */
// the spell origin must possess the item that cast the spell (at least for the aurora engine, in prc_inc_combat that may differ)

View File

@@ -308,7 +308,7 @@ int FindUnarmedDamage(object oCreature)
if (GetHasFeat(FEAT_INCREASE_DAMAGE2, oCreature)) iDieIncrease = 2;
else if (GetHasFeat(FEAT_INCREASE_DAMAGE1, oCreature)) iDieIncrease = 1;
//:: Expansion / Compression powers
/* //:: Expansion / Compression powers (Double dipping?)
int nExpansion = GetLocalInt(oCreature, "PRC_Power_Expansion_SizeIncrease");
int nCompression = GetLocalInt(oCreature, "PRC_Power_Compression_SizeReduction");
@@ -320,7 +320,7 @@ int FindUnarmedDamage(object oCreature)
if (nCompression)
{
iSize -= nCompression;
}
} */
iMonkDamage += iDieIncrease;
iShouDamage += iDieIncrease;

View File

@@ -1,6 +1,6 @@
//::///////////////////////////////////////////////
//:: Weapon Restriction System Include
//:: prc_inc_restwpn.nss
//:: prc_inc_wpnrest.nss
//::///////////////////////////////////////////////
/*
Functions to support PnP Weapon Proficiency and
@@ -23,6 +23,70 @@
* @param nHand The hand the weapon is wielded in. In the form of
* ATTACK_BONUS_ONHAND or ATTACK_BONUS_OFFHAND.
*/
//:: returns TRUE if the wielded weapon works with the Swashbuckler's class abilities.
int GetHasSwashbucklerWeapon(object oPC)
{
object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
if (!GetIsObjectValid(oWeap)) return FALSE;
int nType = GetBaseItemType(oWeap);
switch (nType)
{
case BASE_ITEM_DAGGER:
case BASE_ITEM_KATAR:
case BASE_ITEM_HANDAXE:
case BASE_ITEM_KAMA:
case BASE_ITEM_KUKRI:
case BASE_ITEM_LIGHTHAMMER:
case BASE_ITEM_LIGHTMACE:
case BASE_ITEM_LIGHT_PICK:
case BASE_ITEM_RAPIER:
case BASE_ITEM_SHORTSWORD:
case BASE_ITEM_SICKLE:
case BASE_ITEM_WHIP:
case BASE_ITEM_SAI:
case BASE_ITEM_SAP:
case BASE_ITEM_NUNCHAKU:
case BASE_ITEM_GOAD:
case BASE_ITEM_ELVEN_LIGHTBLADE:
case BASE_ITEM_ELVEN_THINBLADE:
case BASE_ITEM_EAGLE_CLAW:
return TRUE;
}
// Iaijutsu Master allows katana
if (GetLevelByClass(CLASS_TYPE_IAIJUTSU_MASTER, oPC) > 0)
{
if (nType == BASE_ITEM_KATANA) return TRUE;
}
return FALSE;
}
//:: returns TRUE if the wielded weapon works with the Champion of Corellon's Elegant Strike.
int GetHasCorellonWeapon(object oPC)
{
object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
if (!GetIsObjectValid(oWeap)) return FALSE;
int nType = GetBaseItemType(oWeap);
switch (nType)
{
case BASE_ITEM_SCIMITAR:
case BASE_ITEM_LONGSWORD:
case BASE_ITEM_RAPIER:
case BASE_ITEM_ELVEN_COURTBLADE:
case BASE_ITEM_ELVEN_LIGHTBLADE:
case BASE_ITEM_ELVEN_THINBLADE:
return TRUE;
}
return FALSE;
}
void DoRacialEquip(object oPC, int nBaseType);
//return if PC has proficiency in an item

View File

@@ -111,48 +111,3 @@ const string NUI_PRC_PA_TEXT_BIND = "nui_prc_pa_text_bind";
const string NUI_PRC_PA_LEFT_BUTTON_ENABLED_BIND = "leftButtonEnabled";
// Right Button Enabled Bind for Power Attack NUI
const string NUI_PRC_PA_RIGHT_BUTTON_ENABLED_BIND = "rightButtonEnabled";
//////////////////////////////////////////////////
// //
// NUI Level Up //
// //
//////////////////////////////////////////////////
const string NUI_LEVEL_UP_WINDOW_ID = "prcLevelUpNui";
const string NUI_LEVEL_UP_SPELL_CIRCLE_BUTTON_BASEID = "NuiLevelUpCircleButton_";
const string NUI_LEVEL_UP_SPELL_BUTTON_BASEID = "NuiLevelUpSpellButton_";
const string NUI_LEVEL_UP_SPELL_DISABLED_BUTTON_BASEID = "NuiLevelUpDisabledSpellButton_";
const string NUI_LEVEL_UP_SPELL_CHOSEN_BUTTON_BASEID = "NuiLevelUpChosenSpellButton_";
const string NUI_LEVEL_UP_SPELL_CHOSEN_DISABLED_BUTTON_BASEID = "NuiLevelUpDisabledChosenSpellButton_";
const string NUI_LEVEL_UP_DONE_BUTTON = "NuiLevelUpDoneButton";
const string NUI_LEVEL_UP_RESET_BUTTON = "NuiLevelUpResetButton";
const string NUI_LEVEL_UP_SELECTED_CLASS_VAR = "NUILevelUpSelectedClass";
const string NUI_LEVEL_UP_SELECTED_CIRCLE_VAR = "NUILevelUpSelectedCircle";
const string NUI_LEVEL_UP_KNOWN_SPELLS_VAR = "NUILevelUpKnownSpells";
const string NUI_LEVEL_UP_CHOSEN_SPELLS_VAR = "NUILevelUpChosenSpells";
const string NUI_LEVEL_UP_EXPANDED_KNOW_LIST_VAR = "NUILevelUpExpKnowList";
const string NUI_LEVEL_UP_POWER_LIST_VAR = "NUILevelUpPowerList";
const string NUI_LEVEL_UP_DISCIPLINE_INFO_VAR = "GetDisciplineInfoObjectCache_";
const string NUI_LEVEL_UP_SPELLID_LIST_VAR = "NUILevelUpSpellIDList_";
const string NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR = "NUIRemainingChoicesCache";
const string NUI_LEVEL_UP_RELEARN_LIST_VAR = "NUILevelUpRelearnList";
const string NUI_LEVEL_UP_ARCHIVIST_NEW_SPELLS_LIST_VAR = "NUILevelUpArchivistNewSpellsList";
const string NUI_LEVEL_UP_EXPANDED_CHOICES_VAR = "NUIExpandedChoices";
const string NUI_LEVEL_UP_EPIC_EXPANDED_CHOICES_VAR = "NUIEpicExpandedChoices";
const int NUI_LEVEL_UP_MANEUVER_PREREQ_LIMIT = 6;
const string NUI_LEVEL_UP_MANEUVER_TOTAL = "ManeuverTotal";
const string NUI_LEVEL_UP_STANCE_TOTAL = "StanceTotal";
const string NUI_LEVEL_UP_SPELLBOOK_OBJECT_CACHE_VAR = "GetSpellListObjectCache_";
const string NUI_LEVEL_UP_KNOWN_INVOCATIONS_CACHE_VAR = "GetInvokerKnownListObjectCache_";
const string NUI_SPELL_DESCRIPTION_FEATID_VAR = "NUISpellDescriptionFeatID";
const string NUI_SPELL_DESCRIPTION_CLASSID_VAR = "NUISpellDescriptionClassID";
const string NUI_SPELL_DESCRIPTION_SPELLID_VAR = "NUISpellDescriptionSpellID";
const string NUI_SPELL_DESCRIPTION_REAL_SPELLID_VAR = "NUISpellDescriptionRealSpellID";

View File

@@ -10,8 +10,10 @@
//:: Created By: Rakiov
//:: Created On: 24.05.2005
//:://////////////////////////////////////////////
#include "prc_nui_com_inc"
#include "inc_newspellbook"
#include "psi_inc_psifunc"
#include "inc_lookups"
#include "prc_nui_consts"
//
// GetSpellListForCircle
@@ -41,6 +43,69 @@ json GetSpellListForCircle(object oPlayer, int nClass, int circle);
//
json GetSupportedNUISpellbookClasses(object oPlayer);
//
// GetCurrentSpellLevel
// Gets the current spell level the class can achieve at the current
// caster level (ranging from 0-9)
//
// Arguments:
// nClass:int the ClassID
// nLevel:int the caster level
//
// Returns:
// int the circle the class can achieve currently
//
int GetCurrentSpellLevel(int nClass, int nLevel);
//
// GetMaxSpellLevel
// Gets the highest possible circle the class can achieve (from 0-9)
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the highest circle that can be achieved
//
int GetMaxSpellLevel(int nClass);
//
// GetMinSpellLevel
// Gets the lowest possible circle the class can achieve (from 0-9)
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the lowest circle that can be achieved
//
int GetMinSpellLevel(int nClass);
//
// GetHighestLevelPossibleInClass
// Given a class Id this will determine what the max level of a class can be
// achieved
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the highest possible level the class can achieve
//
int GetHighestLevelPossibleInClass(int nClass);
//
// GetClassSpellbookFile
// Gets the class 2da spellbook/ability for the given class Id
//
// Arguments:
// nClass:int the classID
//
// Returns:
// string the 2da file name for the spell/abilities of the ClassID
//
string GetClassSpellbookFile(int nClass);
//
// IsSpellKnown
// Returns whether the player with the given class, spell file, and spellbook id
@@ -169,6 +234,23 @@ json GetMetaMysteryFeatList();
//
int GetTrueClassIfRHD(object oPlayer, int nClass);
//
// GetBinderSpellToFeatDictionary
// Sets up the Binder Spell Dictionary that is used to match a binder's vestige
// to their feat. This is constructed based off the binder's known location of
// their feat and spell ranges in the base 2das respectivly. After constructing
// this it will be saved to the player locally as a cached result since we do
// not need to call this again.
//
// Argument:
// oPlayer:object the player
//
// Returns:
// json:Dictionary<String,Int> a dictionary of mapping between the SpellID
// and the FeatID of a vestige ability
//
json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF);
//
// ShouldAddSpell
// Given a spellId and a class, determines if the spell should be added to the
@@ -236,18 +318,6 @@ json GetInvokerEssenceSpellList(int nClass, object oPlayer=OBJECT_SELF);
//
int JsonArrayContainsInt(json list, int item);
//
// IsSpellbookNUIOpen
// Checks to see if the Spellbook NUI is open on a given player.
//
// Arguments:
// oPC:object the player
//
// Returns:
// int:Boolean TRUE if window is open, FALSE otherwise
//
int IsSpellbookNUIOpen(object oPC);
json GetSpellListForCircle(object oPlayer, int nClass, int circle)
{
json retValue = JsonArray();
@@ -327,6 +397,86 @@ int ShouldAddSpell(int nClass, int spellId, object oPlayer=OBJECT_SELF)
return TRUE;
}
json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF)
{
// a dictionary of <SpellID, FeatID>
json binderDict = GetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR);
// if this hasn't been created, create it now.
if (binderDict == JsonNull())
binderDict = JsonObject();
else
return binderDict;
// the starting row for binder spells
int spellIndex = 19070;
// the starting row for binder feats
int featIndex = 9030;
//the end of the binder spells/feats
while (spellIndex <= 19156 && featIndex <= 9104)
{
// get the SpellID tied to the feat
int spellID = StringToInt(Get2DACache("feat", "SPELLID", featIndex));
// if the spellID matches the current index, then this is the spell
// attached to the feat
if (spellID == spellIndex)
{
binderDict = JsonObjectSet(binderDict, IntToString(spellID), JsonInt(featIndex));
// move to next spell/feat
featIndex++;
spellIndex++;
}
// else we have reached a subdial spell
else
{
// loop through until we reach back at spellID
while (spellIndex < spellID)
{
int masterSpell = StringToInt(Get2DACache("spells", "Master", spellIndex));
// add the sub radial to the dict, tied to the master's FeatID
int featId = JsonGetInt(JsonObjectGet(binderDict, IntToString(masterSpell)));
binderDict = JsonObjectSet(binderDict, IntToString(spellIndex), JsonInt(featId));
spellIndex++;
}
// some feats overlap the same FeatID, can cause this to get stuck.
// if it happens then move on
if (spellIndex > spellID)
featIndex++;
}
}
// cache the result
SetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR, binderDict);
return binderDict;
}
string GetClassSpellbookFile(int nClass)
{
string sFile;
// Spontaneous casters use a specific file name structure
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
sFile = GetFileForClass(nClass);
}
// everyone else uses this structure
else
{
sFile = GetAMSDefinitionFileName(nClass);
if (nClass == CLASS_TYPE_BINDER)
{
sFile = "vestiges";
}
}
return sFile;
}
json GetSupportedNUISpellbookClasses(object oPlayer)
{
json retValue = JsonArray();
@@ -376,6 +526,167 @@ int IsSpellKnown(object oPlayer, int nClass, int spellId)
return FALSE;
}
int GetCurrentSpellLevel(int nClass, int nLevel)
{
int currentLevel = nLevel;
// ToB doesn't have a concept of spell levels, but still match up to it
if(nClass == CLASS_TYPE_WARBLADE
|| nClass == CLASS_TYPE_SWORDSAGE
|| nClass == CLASS_TYPE_CRUSADER
|| nClass == CLASS_TYPE_SHADOWCASTER)
{
return 9;
}
// Binders don't really have a concept of spell level
if (nClass == CLASS_TYPE_BINDER
|| nClass == CLASS_TYPE_DRAGON_SHAMAN) // they can only reach 1st circle
return 1;
//Shadowsmith has no concept of spell levels
if (nClass == CLASS_TYPE_SHADOWSMITH)
return 2;
if (nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
return 4;
// Spont casters have their own function
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
int maxLevel = GetMaxSpellLevelForCasterLevel(nClass, currentLevel);
return maxLevel;
}
else
{
// everyone else uses this
string spellLevel2da = GetAMSKnownFileName(nClass);
currentLevel = nLevel - 1; // Level is 1 off of the row in the 2da
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_PSION
|| nClass == CLASS_TYPE_PSYWAR
|| nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_WARMIND)
currentLevel = GetManifesterLevel(OBJECT_SELF, nClass, TRUE) - 1;
int totalLevel = Get2DARowCount(spellLevel2da);
// in case we somehow go over bounds just don't :)
if (currentLevel >= totalLevel)
currentLevel = totalLevel - 1;
//Psionics have MaxPowerLevel as their column name
string columnName = "MaxPowerLevel";
//Invokers have MaxInvocationLevel
if (nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
columnName = "MaxInvocationLevel";
// Truenamers have 3 sets of utterances, ranging from 1-6, EvolvingMind covers the entire range
if (nClass == CLASS_TYPE_TRUENAMER)
{
columnName = "EvolvingMind";
spellLevel2da = "cls_true_maxlvl"; //has a different 2da we want to look at
}
if (nClass == CLASS_TYPE_BINDER)
{
columnName = "VestigeLvl";
spellLevel2da = "cls_bind_binder";
}
// ToB doesn't have a concept of this, but we don't care.
int maxLevel = StringToInt(Get2DACache(spellLevel2da, columnName, currentLevel));
return maxLevel;
}
}
int GetMinSpellLevel(int nClass)
{
// again sponts have their own function
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
return GetMinSpellLevelForCasterLevel(nClass, GetHighestLevelPossibleInClass(nClass));
}
else
{
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_PSION
|| nClass == CLASS_TYPE_PSYWAR
|| nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_WARMIND
|| nClass == CLASS_TYPE_WARBLADE
|| nClass == CLASS_TYPE_SWORDSAGE
|| nClass == CLASS_TYPE_CRUSADER
|| nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|| nClass == CLASS_TYPE_SHADOWCASTER
|| nClass == CLASS_TYPE_SHADOWSMITH
|| nClass == CLASS_TYPE_BINDER)
return 1;
return GetCurrentSpellLevel(nClass, 1);
}
}
int GetMaxSpellLevel(int nClass)
{
if (nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSION)
return 9;
if (nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_WARMIND)
return 5;
if (nClass == CLASS_TYPE_PSYWAR)
return 6;
return GetCurrentSpellLevel(nClass, GetHighestLevelPossibleInClass(nClass));
}
int GetHighestLevelPossibleInClass(int nClass)
{
string sFile;
//sponts have their spells in the classes.2da
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
sFile = Get2DACache("classes", "SpellGainTable", nClass);
}
else
{
// everyone else uses this
sFile = GetAMSKnownFileName(nClass);
if (nClass == CLASS_TYPE_TRUENAMER)
{
sFile = "cls_true_maxlvl"; //has a different 2da we want to look at
}
if (nClass == CLASS_TYPE_BINDER)
{
sFile = "cls_bind_binder";
}
}
return Get2DARowCount(sFile);
}
int IsClassAllowedToUseNUISpellbook(object oPlayer, int nClass)
{
// This controls who can use the Spellbook NUI, if for some reason you don't
@@ -387,7 +698,8 @@ int IsClassAllowedToUseNUISpellbook(object oPlayer, int nClass)
return TRUE;
// Arcane Spont
if (nClass == CLASS_TYPE_BEGUILER
if (nClass == CLASS_TYPE_ASSASSIN
|| nClass == CLASS_TYPE_BEGUILER
|| nClass == CLASS_TYPE_CELEBRANT_SHARESS
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
|| nClass == CLASS_TYPE_DUSKBLADE
@@ -505,7 +817,8 @@ int CanClassUseMetamagicFeats(int nClass)
// I don't want to spend the time looping through each class's
// feat 2da so this is the list of all classes that are allowed to use the
// Spellbook NUI and can use Metamagic
return (nClass == CLASS_TYPE_BARD
return (nClass == CLASS_TYPE_ASSASSIN
|| nClass == CLASS_TYPE_BARD
|| nClass == CLASS_TYPE_SORCERER
|| nClass == CLASS_TYPE_BEGUILER
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
@@ -525,6 +838,7 @@ int CanClassUseSuddenMetamagicFeats(int nClass)
// Spellbook NUI and can use Sudden Metamagic
return (nClass == CLASS_TYPE_SHADOWLORD
|| nClass == CLASS_TYPE_ARCHIVIST
|| nClass == CLASS_TYPE_ASSASSIN
|| nClass == CLASS_TYPE_BARD
|| nClass == CLASS_TYPE_BEGUILER
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
@@ -832,14 +1146,3 @@ int JsonArrayContainsInt(json list, int item)
return FALSE;
}
int IsSpellbookNUIOpen(object oPC)
{
int nPreviousToken = NuiFindWindow(oPC, PRC_SPELLBOOK_NUI_WINDOW_ID);
if (nPreviousToken != 0)
{
return TRUE;
}
return FALSE;
}

View File

@@ -180,6 +180,24 @@ void RunImpactScript(object oPC, int nSpellID, int nEventType)
DeleteLocalInt(oPC, PRC_SPELLID_OVERRIDE);
}
//Returns true if the spell is one of the repair spells
int IsRepair(int nSpellID)
{
return ((nSpellID >= SPELL_REPAIR_MINOR_DAMAGE) && (nSpellID <= SPELL_REPAIR_CRITICAL_DAMAGE));
}
//Returns true if the spell is one of the mass repair spells
int IsMassRepair(int nSpellID)
{
return ((nSpellID >= SPELL_MASS_REPAIR_LIGHT_DAMAGE) && (nSpellID <= SPELL_MASS_REPAIR_CRITICAL_DAMAGE));
}
//Returns true if the spell is one of the mass inflict damage spells
int IsMassInflictDamage(int nSpellID)
{
return ((nSpellID >= SPELL_MASS_INFLICT_LIGHT_DAMAGE) && (nSpellID <= SPELL_MASS_INFLICT_CRITICAL_DAMAGE));
}
//Returns true if the spell is one of the cure spells
int IsCure(int nSpellID)
{

View File

@@ -520,9 +520,9 @@ void GainPsionicFocus(object oGainee = OBJECT_SELF)
{
int nPsySneak = 1;
if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_2d6, oGainee))
nPsySneak += 2;
nPsySneak += 1;
if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_3d6, oGainee))
nPsySneak += 3;
nPsySneak += 1;
SetLocalInt(oGainee, "PsyRogueSneak",nPsySneak);
DelayCommand(0.1, ExecuteScript("prc_sneak_att", oGainee));

View File

@@ -24,6 +24,7 @@
#include "x0_i0_position"
#include "X0_INC_HENAI"
#include "x3_inc_skin"
#include "prc_racial_const"
/*
@@ -638,7 +639,7 @@ int HorseGetMountTail(object oHorse);
// FILE: x3_inc_horse FUNCTION: HorseGetMountFailureMessage()
// This is a companion function to HorseGetCanBeMounted. If you need a text
// message that explains why the horse cannot be mounted.
string HorseGetMountFailureMessage(object oTarget,object oRider=OBJECT_INVALID);
string HorseGetMountFailureMessage(object oHorse,object oRider=OBJECT_INVALID);
// FILE: x3_inc_horse FUNCTION: HorseAddHorseMenu()
@@ -1050,6 +1051,8 @@ void HORSE_SupportOriginalSpeed(object oRider)
} // check to see if matches conditions
eSearch=GetNextEffect(oRider);
} // cycle through effects
} // HORSE_SupportOriginalSpeed()

View File

@@ -13852,7 +13852,7 @@
"__struct_id": 0,
"CR": {
"type": "float",
"value": 0.5
"value": 0.3333
},
"FACTION": {
"type": "cexostring",

View File

@@ -12,7 +12,7 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xp_multi", OBJECT_SELF);
//ExecuteScript("tab_xp_multi", OBJECT_SELF);
string sResref = GetResRef(OBJECT_SELF);
object oKiller = GetLastKiller();

View File

@@ -13,5 +13,5 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xp_multi",OBJECT_SELF);
//ExecuteScript("tab_xp_multi",OBJECT_SELF);
}

View File

@@ -13,5 +13,5 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xp_multi",OBJECT_SELF);
//ExecuteScript("tab_xp_multi",OBJECT_SELF);
}

View File

@@ -14,7 +14,7 @@ void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xpscript",OBJECT_SELF);
//ExecuteScript("tab_xpscript",OBJECT_SELF);
location lSource = GetLocation(OBJECT_SELF);
DelayCommand(1., ExplodeAtLocation(lSource, d12(55)));
}

View File

@@ -2,7 +2,7 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xp_multi",OBJECT_SELF);
//ExecuteScript("tab_xp_multi",OBJECT_SELF);
object oPC = GetLastKiller();
if (!GetIsPC(oPC)) return;

View File

@@ -1,7 +1,7 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xpscript",OBJECT_SELF);
//ExecuteScript("tab_xpscript",OBJECT_SELF);
object oPC = GetLastKiller();
object oTarget;

View File

@@ -2,7 +2,7 @@ void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xpscript",OBJECT_SELF);
//ExecuteScript("tab_xpscript",OBJECT_SELF);
SpeakString("Dont think you have won just yet!", TALKVOLUME_TALK);
object oSpawn;

View File

@@ -3,7 +3,7 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xp_multi",OBJECT_SELF);
//ExecuteScript("tab_xp_multi",OBJECT_SELF);
object oPC = GetLastKiller();
while (GetIsObjectValid(GetMaster(oPC)))

View File

@@ -13,7 +13,7 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xpscript",OBJECT_SELF);
//ExecuteScript("tab_xpscript",OBJECT_SELF);
object oPC = GetLastKiller();

View File

@@ -1,7 +1,7 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xpscript",OBJECT_SELF);
//ExecuteScript("tab_xpscript",OBJECT_SELF);
object oPC = GetLastKiller();
object oTarget;

View File

@@ -32,7 +32,7 @@
/// Local Override (int): PS_FORCE_SEARCH_BUTTON
/// 1 = PS_TRUE
/// -1 = PS_FALSE
const int PS_FORCE_SEARCH_BUTTON_DEFAULT = PS_FALSE;
const int PS_FORCE_SEARCH_BUTTON_DEFAULT = -1;
/// @brief Determines whether item object state is saved to the database. The
/// object state includes variables and effects.
@@ -42,7 +42,7 @@ const int PS_FORCE_SEARCH_BUTTON_DEFAULT = PS_FALSE;
/// Local Override (int): PS_FORCE_OBJECT_STATE
/// 1 = PS_TRUE
/// -1 = PS_FALSE
const int PS_FORCE_OBJECT_STATE_DEFAULT = PS_TRUE;
const int PS_FORCE_OBJECT_STATE_DEFAULT = 1;
/// @brief Sets the item storage limit.
/// Configuration File:
@@ -79,7 +79,7 @@ const float PS_DISTANCE_DEFAULT = 2.0;
/// Local Override (int): PS_ACCESS_TYPE
/// 1 = PS_ACCESS_EXCLUSIVE
/// 2 = PS_ACCESS_CONTENTIOUS
const int PS_ACCESS_TYPE_DEFAULT = PS_ACCESS_EXCLUSIVE;
const int PS_ACCESS_TYPE_DEFAULT = 1;
/// @brief Set the container type. Containers can be of multiple types:
/// - Public: Any player can open, deposit and withdraw items from this
@@ -101,7 +101,7 @@ const int PS_ACCESS_TYPE_DEFAULT = PS_ACCESS_EXCLUSIVE;
/// 1 = PS_CONTAINER_PUBLIC
/// 2 = PS_CONTAINER_CHARACTER
/// 3 = PS_CONTAINER_CDKEY
const int PS_CONTAINER_TYPE_DEFAULT = PS_CONTAINER_PUBLIC;
const int PS_CONTAINER_TYPE_DEFAULT = 1;
/// @brief Set the default container type, if the container is an item. Containers
/// can be of multiple types:
@@ -124,7 +124,7 @@ const int PS_CONTAINER_TYPE_DEFAULT = PS_CONTAINER_PUBLIC;
/// 1 = PS_CONTAINER_PUBLIC
/// 2 = PS_CONTAINER_CHARACTER
/// 3 = PS_CONTAINER_CDKEY
const int PS_CONTAINER_ITEM_TYPE_DEFAULT = PS_CONTAINER_CHARACTER;
const int PS_CONTAINER_ITEM_TYPE_DEFAULT = 2;
/// @brief Determines whether the player's inventory window will be opened
/// when a container is opened.
@@ -135,7 +135,7 @@ const int PS_CONTAINER_ITEM_TYPE_DEFAULT = PS_CONTAINER_CHARACTER;
/// Local Override (int): PS_OPEN_INVENTORY
/// 1 = PS_TRUE
/// -1 = PS_FALSE
const int PS_OPEN_INVENTORY_DEFAULT = PS_TRUE;
const int PS_OPEN_INVENTORY_DEFAULT = 1;
/// @brief Determines the maximum amount of gold a container can store.
/// If the container is set to store no gold, the form controls that

View File

@@ -1,7 +1,7 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xpscript",OBJECT_SELF);
//ExecuteScript("tab_xpscript",OBJECT_SELF);
object oPC = GetLastKiller();
object oTarget;

View File

@@ -24,13 +24,14 @@ void main()
return;
}
ExecuteScript("tab_xpscript",OBJECT_SELF);
//ExecuteScript("tab_xpscript",OBJECT_SELF);
ExecuteScript("pwfxp",OBJECT_SELF);
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);
if(NPC_XP == 1)
/* if(NPC_XP == 1)
{
float fCR = GetChallengeRating(OBJECT_SELF);
int nMonsterXP;
@@ -167,5 +168,5 @@ void main()
GiveXPToCreature(oPC, nCharXP);
oPC = GetNextFactionMember(oKiller, TRUE);
}
}
} */
}

479
src/module/nss/pwfxp.nss Normal file
View File

@@ -0,0 +1,479 @@
//PRC racial pack compatible PWFXP script
//
//PRC Version: 3.1e
//Written by: Silvercloud (scl.vcs-online.com) & Ornedan
// Modified by fluffyamoeba for 3.x versions of the PRC
/*
changes: 2007-11-14
- pwfxp_prc_race now reads the 2da cache directly.
- the race LA is done entirely through this script. DO NOT set PRC_XP_USE_SIMPLE_LA
or the XP penalty will be applied twice
- if using this with the supplied prc_pwondeath script, you don't need to make any
changes to nw_c2_default7 (the OnDeath script)
- prc_racial_const no longer used
=================
Include added for prc races: pwfxp_prc_race, modify this file if you want to
adjust ECLs (the subrace constants in pwfxp_def is removed).
Main function for determining ECL rewritten to hook into getECLMod.
package includes prc_racial_const for completeness, but you do not have to over-
write the one in your world per se (for example if you have more races).
have fun,
Silvercloud
//::///////////////////////////////////////////////
//:: XP Distribution Script by Knat
//:: pwfxp v1.70
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
IMPORTANT: see pwfxp_def modifier definition script...
check this link in case you want to discuss this script:
http://www.thefold.org/nuke/modules.php?name=Forums&file=viewforum&f=69
This is a more sophisticated XP distribution script geared towards PWs.
It comes with two dozen XP modifiers to fine tune XP output and
is losely based on the old but solid TTV XP script.
here is a small example of features, all modifiers are scalable:
- independent XP modifier for every single level. this breaks nwns linear
XP progression and enables you to define your own progression function.
you can give out fast xp early and let players slow down at any rate you want.
or model your own progression function with the manipulation of two constants
- PCs suffer XP reduction if their level is not close enough to the average
party level. level 1 grouping with level 10 is probably not a good idea...
- PCs suffer XP reduction if their level is not close enough to the CR of the killed MOB
(both directions independent now)
- Adjustable & cached ECL modifiers, easy to sync with any subrace scripts
- Group bonus. groups receive a small XP bonus (or big, if you wish) to encourage teamplay
- Groupmembers need to be within minimum distance to the killed MOB if they want to receive XP
- associates get a share of the xp, but you can set a different divisor for each associate type
e.g.: henchman soak up more XP then animal companions
- several counter exploit mechanisms included
- many more, see the constants...
- easy to add new modifiers..
all in all, this is pushing the nwn XP system more close to what you get in a MMORPG. You can
make it very hard to level or easy as hell, with good control of group impact and flexible
boundaries.
system went through extensive beta tests at www.thefold.org - thanks to all the great
players and staff there...
------------------------------------------------------------------------------------------
--- USAGE --- --- USAGE --- --- USAGE --- --- USAGE --- --- USAGE ------------------------
------------------------------------------------------------------------------------------
just add the following line to the onDeath script of your creatures (default: nw_c2_default7):
ExecuteScript("pwfxp",OBJECT_SELF);
Don't forget to set the XP-Scale slider to 0 (module properties)
*note* if using your own prc_pwondeath script add this to that script instead.
ATTENTION: HOW TO REMOVE THE DOUBLE XP MESSAGE !
put this code above the pwfxp execution in your onDeath script
// safety mechanism in case creature kills itself
if(GetLastKiller() == OBJECT_SELF) return;
put this code near the bottom of your onDeath script to remove the double-xp message
thanks to spider661/Sotae for this catch...
// resurrect & self kill to bypass bioware xp message
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectResurrection(), OBJECT_SELF);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(10000, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_PLUS_TWENTY), OBJECT_SELF);
------------------------------------------------------------------------------------------
changelog:
v1.7 update (3/2004)
- added PWFXP_MAXIMUM_XP constant due to user request...
v1.62 update (2/2004)
- fixed documentation error. using the wrong info could lead to divide by zero error
if you want to eliminate mob CR < PC-Level reduction:
set PWFXP_CR_LESSTHAN_PCLEVEL_REDUCTION to PWFXP_CR_MAX and
PWFXP_CR_LESSTHAN_PCLEVEL_NOXP to PWFXP_CR_MAX + 1
if you want to eliminate mob CR > PC-Level reduction:
set PWFXP_CR_GREATERTHAN_PCLEVEL_REDUCTION to PWFXP_CR_MAX and
PWFXP_CR_GREATERTHAN_PCLEVEL_NOXP to PWFXP_CR_MAX + 1
if you want to eliminate Average Party Level reduction:
set PWFXP_APL_REDUCTION to 40 and
PWFXP_APL_NOXP to 41
thanx to tribble for the catch
v1.61 update(1/2004)
- fixed minor naming convention error
v1.6 update(1/2004)
- improved XP divisor. you can now distinct between animal companion,
familiar, dominated, summoned and henchman. you can set a different
xp divisor for each of them. (default: henchman has max reduction impact followed
by dominated, summoned, familiars, animal companion)
see PWFXP_XP_DIVISOR_* constants
- added PWFXP_USE_TOTAL_XP_TO_COMPUTE_PCLEVEL constant
pc level gets computed based on the total XP instead of
using GetLevelBy functions if set to TRUE. this way players ready to levelup
can't bunker XP to gain better XP bonuses/modifiers.
- removed dumb debug fragments, no more svirfneblin invasions...
thanks to Beowulf for the catch...
v1.5 update(12/2003)
- improved ECL modifier: added caching to decrease cpu use
improved parser
v1.4 update(12/2003)
- removed constant PWFXP_CR_REDUCTION and PWFXP_CR_NOXP
- added 4 new constants instead to distinct between..
PC-Level > CR
PC-Level < CR
..cases:
PWFXP_CR_LESSTHAN_PCLEVEL_REDUCTION
PWFXP_CR_LESSTHAN_PCLEVEL_NOXP
PWFXP_CR_GREATERTHAN_PCLEVEL_REDUCTION
PWFXP_CR_GREATERTHAN_PCLEVEL_NOXP
- added PWFXP_USE_SETXP constant
- split the script up. now all constants are isolated in their own
definiton file (pwfxp_def). easier to update that way...
v1.3 update (12/2003)
- added PWFXP_LEVEL_MODIFIERS. this removes the linear xp output... read more below
v1.2 update (10/2003)
- killer gets excluded from distance check now if he *is* a PC.
he gets XP even if his spell kills something far away (e.g. long range spells,
damage over time spells. maybe traps, not tested so far. this does not include NPCs)
every other groupmember gets still checked for distance...
[thanks to telstar for the report/request...]
v1.1 initial full release (10/2003)
- fine tuned and slightly optimized code
- added debug toggle
v1.0 beta (8/2003):
- distance check should now work correctly
- minimum XP award (see new PWFXP_MINIMUM_XP constant)
- henchman, familiars, animal companions, summoned creatures and other NPCs in a player
group now take away XP. see PWFXP_XP_DIVISOR_PC and PWFXP_XP_DIVISOR_NPC constants
- made it easier to manage ECL modifiers. see PWFXP_ECL_MODIFIERS string constant
*/
//:://////////////////////////////////////////////
//:: Created By: LasCivious & Knat
//:: Created On: 7/2003
//:://////////////////////////////////////////////
#include "pwfxp_def"
#include "pwfxp_prc_race"
int PWFXP_GetLevel(object oPC)
{
// we need to use a derivation of the base xp formular to compute the
// pc level based on total XP.
//
// base XP formula (x = pc level, t = total xp):
//
// t = x * (x-1) * 500
//
// need to use some base math..
// transform for pq formula use (remove brackets with x inside and zero right side)
//
// x^2 - x - (t / 500) = 0
//
// use pq formula to solve it [ x^2 + px + q = 0, p = -1, q = -(t/500) ]...
//
// that's our new formular to get the level based on total xp:
// level = 0.5 + sqrt(0.25 + (t/500))
//
if(PWFXP_USE_TOTAL_XP_TO_COMPUTE_PCLEVEL) // use total XP to compute PC level
return FloatToInt(0.5 + sqrt(0.25 + ( IntToFloat(GetXP(oPC)) / 500 )));
else // use total class level to compute PC level
return GetLevelByPosition(1,oPC) + GetLevelByPosition(2,oPC) + GetLevelByPosition(3,oPC);
}
// see PWFXP_ECL_MODIFIERS constant description
float PWFXP_GetECLModifier(object oPC)
{
// get current
int nHD = GetHitDice(oPC);
// get last PC HD from cache
int nECLHitDice = GetLocalInt(oPC,"PWFXP_ECL_HITDICE");
// last PC HD = current PC HD ? get ECL modifier from cache and return...
if(nECLHitDice == nHD) return GetLocalFloat(oPC,"PWFXP_ECL_MODIFIER");
// recompute ECL modifier and cache it
// this code section will run only in the case of two circumstances:
//
// 1. first time kill
// 2. pc hitdice change (e.g. levelup)
float fECLMod;
fECLMod = IntToFloat(nHD) / (IntToFloat(nHD) + IntToFloat(GetECLMod(oPC)));
SetLocalFloat(oPC,"PWFXP_ECL_MODIFIER", fECLMod);
SetLocalInt(oPC,"PWFXP_ECL_HITDICE",nHD);
return fECLMod;
}
// see PWFXP_LEVEL_MODIFIER constant description
float PWFXP_GetLevelModifier(int nLevel)
{
return StringToFloat(GetSubString( PWFXP_LEVEL_MODIFIERS, (nLevel - 1) * 7, 6));
}
// see PWFXP_APL_REDUCTION & PWFXP_APL_NOXP constant description
float PWFXP_GetLevelDistanceModifier(float fLevelDistance)
{
if( fLevelDistance >= PWFXP_APL_NOXP )
{
// level distance greater than maximum allowed > no XP award at all
return 0.0; // -100%
}
else if(fLevelDistance >= PWFXP_APL_REDUCTION)
{
// level distance greater than reduction limit ? reduce xp
return 1 - ((fLevelDistance - PWFXP_APL_REDUCTION) * PWFXP_APL_MODIFIER);
}
return 1.0;
}
// see PWFXP_CR_LESSTHAN_PCLEVEL_REDUCTION, PWFXP_CR_LESSTHAN_PCLEVEL_NOXP
// PWFXP_CR_GREATERTHAN_PCLEVEL_REDUCTION, PWFXP_CR_GREATERTHAN_PCLEVEL_NOXP
// constant description
float PWFXP_GetCRDistanceModifier(float fCRDistance)
{
// PC level > creature CR ?
if(fCRDistance < 0.0)
{
fCRDistance = fabs(fCRDistance);
if( fCRDistance >= PWFXP_CR_LESSTHAN_PCLEVEL_NOXP )
{
// level distance greater than maximum allowed > no XP award at all
return 0.0; // -100%
}
else if(fCRDistance >= PWFXP_CR_LESSTHAN_PCLEVEL_REDUCTION)
{
// level distance greater than reduction limit ? reduce xp
return 1 - ((fCRDistance - PWFXP_CR_LESSTHAN_PCLEVEL_REDUCTION) * PWFXP_CR_LESSTHAN_PCLEVEL_MODIFIER);
}
}
else
{
fCRDistance = fabs(fCRDistance);
if( fCRDistance >= PWFXP_CR_GREATERTHAN_PCLEVEL_NOXP )
{
// level distance greater than maximum allowed > no XP award at all
return 0.0; // -100%
}
else if(fCRDistance >= PWFXP_CR_GREATERTHAN_PCLEVEL_REDUCTION)
{
// level distance greater than reduction limit ? reduce xp
return 1 - ((fCRDistance - PWFXP_CR_GREATERTHAN_PCLEVEL_REDUCTION) * PWFXP_CR_GREATERTHAN_PCLEVEL_MODIFIER);
}
}
return 1.0;
}
// see PWFXP_KILLINGBLOW_MODIFIER constant description
float PWFXP_GetMiscModifier(object oPC, object oKiller)
{
if(oPC == oKiller && PWFXP_KILLINGBLOW_MODIFIER != 0.0)
{
return 1 + PWFXP_KILLINGBLOW_MODIFIER;
}
return 1.0;
}
// see PWFXP_GROUPBONUS_MODIFIER constant description
float PWFXP_GetGroupBonusModifier(int nGroupSize)
{
return 1 + ((nGroupSize-1) * PWFXP_GROUPBONUS_MODIFIER);
}
// see PWFXP_XP_DIVISOR_* constants
float PWFXP_GetAssociateDivisor(object oCreature)
{
switch(GetAssociateType(oCreature))
{
case ASSOCIATE_TYPE_ANIMALCOMPANION: return PWFXP_XP_DIVISOR_ANIMALCOMPANION;
case ASSOCIATE_TYPE_DOMINATED: return PWFXP_XP_DIVISOR_DOMINATED;
case ASSOCIATE_TYPE_FAMILIAR: return PWFXP_XP_DIVISOR_FAMILIAR;
case ASSOCIATE_TYPE_HENCHMAN: return PWFXP_XP_DIVISOR_HENCHMAN;
case ASSOCIATE_TYPE_SUMMONED: return PWFXP_XP_DIVISOR_SUMMONED;
default: return PWFXP_XP_DIVISOR_UNKNOWN;
}
return 1.0;
}
// see PWFXP_MAXIMUM_DISTANCE_TO_GROUP constant description
int PWFXP_CheckDistance(object oDead, object oGroupMbr)
{
return ( GetDistanceBetween(oDead, oGroupMbr) <= PWFXP_MAXIMUM_DISTANCE_TO_GROUP ) && ( GetArea(oDead) == GetArea(oGroupMbr) );
}
// see PWFXP_USE_SETXP constant description
void PWFXP_GiveXP(object oPC, int nXP)
{
if(PWFXP_USE_SETXP)
SetXP(oPC, GetXP(oPC) + nXP);
else
GiveXPToCreature(oPC, nXP);
}
void main()
{
object oDead = OBJECT_SELF;
object oKiller = GetLastKiller();
// only continue if killer is valid and not from same faction...
if ((oKiller==OBJECT_INVALID) || (GetFactionEqual(oKiller, oDead))) return;
// average party level, xp divisor
float fAvgLevel, fDivisor;
// groupsize, only PCs count
int nGroupSize;
// get some basic group data like average PC level , PC group size, and XP divisor
object oGroupMbr = GetFirstFactionMember(oKiller, FALSE);
while(oGroupMbr != OBJECT_INVALID)
{
if( PWFXP_CheckDistance(oDead, oGroupMbr) || oGroupMbr == oKiller)
{
if(GetIsPC(oGroupMbr))
{
nGroupSize++;
// add pc divisor
fDivisor += PWFXP_XP_DIVISOR_PC;
fAvgLevel += IntToFloat(PWFXP_GetLevel(oGroupMbr));
}
else
fDivisor += PWFXP_GetAssociateDivisor(oGroupMbr); // add npc divisor
}
oGroupMbr = GetNextFactionMember(oKiller, FALSE);
}
if(nGroupSize == 0)
{
// NPC (Minion) killed something without a PC (Master) near enough to get XP
return;
}
// calculate average partylevel
fAvgLevel /= IntToFloat(nGroupSize);
// modifiers
float fLevelModifier, fDistanceModifier, fCRModifier, fMiscModifier, fFinalModifier, fECLModifier, fGroupBonusModifier;
// groupmember level
float fMbrLevel;
// get creature CR
float fCR = GetChallengeRating(oDead);
// reduce CR if greater then maximum CR cap
if(fCR > PWFXP_CR_MAX) fCR = PWFXP_CR_MAX; // cap CR
// multiply CR with global XP modifier
float fModCR = fCR * PWFXP_GLOBAL_MODIFIER;
// calculate modifiers for each PC individually
oGroupMbr = GetFirstFactionMember(oKiller, TRUE);
while(oGroupMbr != OBJECT_INVALID)
{
fMbrLevel = IntToFloat(PWFXP_GetLevel(oGroupMbr));
if( PWFXP_CheckDistance(oDead, oGroupMbr) || oGroupMbr == oKiller)
{
// get global level modifier
fLevelModifier = PWFXP_GetLevelModifier(FloatToInt(fMbrLevel));
// get PC-level distance to average group-level and compute modifier
fDistanceModifier = PWFXP_GetLevelDistanceModifier(fabs(fAvgLevel - fMbrLevel));
// get PC-level distance to CR of dead creature and compute modifier
fCRModifier = PWFXP_GetCRDistanceModifier(fCR - fMbrLevel);
// get misc modifiers (right now only 10% for killing blow dealer)
fMiscModifier = PWFXP_GetMiscModifier(oGroupMbr, oKiller);
// get group bonus modifier
fGroupBonusModifier = PWFXP_GetGroupBonusModifier(nGroupSize);
// get subrace ECL modifier
fECLModifier = PWFXP_GetECLModifier(oGroupMbr);
// calculate final modifier
fFinalModifier = fLevelModifier * fDistanceModifier * fCRModifier * fMiscModifier * fGroupBonusModifier * fECLModifier;
// debug
if(PWFXP_DEBUG)
SendMessageToPC(oGroupMbr,GetName(oGroupMbr)+"'s XP Base: "+IntToString(FloatToInt(fModCR / fDivisor))+
" / Modifiers: LVL [" + IntToString(FloatToInt((fLevelModifier-1)*100)) +
"%] APD ["+IntToString(FloatToInt((fDistanceModifier-1)*100)) +
"%] CRD ["+IntToString((fCR-fMbrLevel) < 0.0) + "/" + IntToString(FloatToInt((fCRModifier-1)*100))+
"%] MSC ["+IntToString(FloatToInt((fMiscModifier-1)*100))+
"%] GRP ["+IntToString(FloatToInt((fGroupBonusModifier-1)*100))+
"%] ECL ["+IntToString(FloatToInt((fECLModifier-1)*100))+
"%] GRS ["+IntToString(nGroupSize)+
"] DIV ["+GetSubString(FloatToString(fDivisor),6,5) +
"] FIN ["+IntToString(FloatToInt((fFinalModifier-1)*100))+
"%]");
int nXP = FloatToInt((fModCR / fDivisor) * fFinalModifier);
// award minimum/maximum xp if needed
if(nXP < PWFXP_MINIMUM_XP)
nXP = PWFXP_MINIMUM_XP;
else if(nXP > PWFXP_MAXIMUM_XP)
nXP = PWFXP_MAXIMUM_XP;
// misc checks for reasons the party member might not get XP would go here (eg. if they are dead)
if(nXP > 0) PWFXP_GiveXP(oGroupMbr, nXP);
}
oGroupMbr = GetNextFactionMember(oKiller, TRUE);
}
}

View File

@@ -0,0 +1,274 @@
//::///////////////////////////////////////////////
//:: XP Distribution Script by Knat
//:: pwfxp
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
//void main(){}
// note: default values are geared towards a LOW xp setting...
// with easy leveling early (level 1-5) and very hard xp gain later on (lvl 30+)
// all default values should be epic-level compatible
// this will modify global xp output similar to the module xp-slider
// higher value = more xp
// PC needs to kill a combined CR of (PC-LEVEL * 1000) / PWFXP_GLOBAL_MODIFIER
// on average to gain a level. use this as a rule of thumb and base to calculate
// the more advanced modifiers
//
// e.g. a level 10 needs to kill 100 CR-10 mobs to level (aprox.) using default 10.0 global modifier
// he will only need 50 CR-10 mobs if you set it to 20.0
// setting this to 1000.0 = he only needs one CR-10 mob to level
//
// you can further scale the leveling progress more precisely with the PWFXP_LEVEL_MODIFIERS constant
// just continue to read my comments...
const float PWFXP_GLOBAL_MODIFIER = 15.0;
// displays one-line XP status info after each kill
// useful while you fine tune the system.
// and check the readme.txt in case you want to remove
// the double-xp message from bioware...
const int PWFXP_DEBUG = TRUE;
// NEW & experimental:
// system will use the SetXP function instead of GiveXPToCreature if you set this to TRUE
// this should bypass any "possible" bioware xp modification, like multiclass penalties.
// i did not really test this so far, it's just some rumor i picked up from the
// bioboards. choose whatever you feel better with...
const int PWFXP_USE_SETXP = TRUE;
// NEW:
// pc level gets computed based on the total XP instead of
// using GetLevelBy functions if set to TRUE. this way players ready to levelup
// can't bunker XP to gain better XP bonuses. a level 2 player with
// 3500 total XP (thus, ready to levelup) gets considered
// level 3 by the XP script if you set this switch to TRUE
//
// setting this to FALSE will use the old way of GetLevelByPosition.
// i highly recommend the new way to counter XP (and probably more) exploits...
const int PWFXP_USE_TOTAL_XP_TO_COMPUTE_PCLEVEL = TRUE;
// this is where you apply your subrace ECL modifiers
// add them to the constant in this form "(ECL Modifier)-(Subrace)|...next|...etc"
// COMMENTED OUT as it is no longer used in the calculations.
//const string PWFXP_ECL_MODIFIERS = "1-ARCTIC DWARF|1-HALF OGRE|1-GITHYANKI|1-GITZERAI|1-OROG|1-GNOLL|1-LIZARDFOLK|1-AASIMAR|1-TIEFLING|1-HOBGOLBIN|1-GRAY DWARF|2-DROW MALE|2-DROW FEMALE|2-DEEP GNOME SVIRFNEBLIN|2-AVARIEL|2-MINOTAUR|2-BUGBEAR|2-FEY'RI|2-TANARUKK|3-OGRE|3-YUANTI PURE|3-AZER|4-PIXIE|4-ILLITHID|5-TROLL|6-RAKSHASHA";
// NEW:
//
// you can add a modifier to change XP output for every single level (including epic levels)
// this also enables you to break the linear nature of NWNs default XP output.
// you can change it to: logarithmic, exponential or any other non-linear
// mathematical function using the PWFXP_LEVEL_MODIFIERS table
//
// you can make the first few levels an easy catch but make the last a pain to reach.... very flexible now
//
// default setting:
//
// level 1 = 1000% xp bonus
// level 2 = 500% xp bonus
// level 3 = 300% xp bonus
// level 4 = 200% xp bonus
// level 5 = 100% xp bonus
//
// level 6 - 10 = no xp change
// level 11 = -15% xp penalty
// level 12 = -15%
// level 13 = -20%
// level 14 = -20%
// level 15 = -25%
// level 16 = -25%
// level 17 = -30%
// level 18 = -30%
// level 19 = -35%
// level 20 = -35%
//
// level 21 = -40% xp penalty
// level 22 = -45%
// level 23 = -50%
// level 24 = -55%
// level 25 = -60%
// level 26 = -65%
// level 27 = -70%
// level 28 = -80%
// level 29 = -90%
//
// level 30 = -91% xp penalty
// level 31 = -91%
// level 32 = -92%
// level 33 = -92%
// level 34 = -93%
// level 35 = -93%
// level 36 = -94%
// level 37 = -94%
// level 38 = -95%
// level 39 = -96%
//
// these settings make it easy first but very tough at later levels.
// the pc would need to kill 100 level 10 creatures to level from 10 to 11, but
// several thousand CR 40 creatures to level from 39 to 40, with the above settings.
// (not counting group bonus or other advanced modifiers)
//
// modifier explanation:
//
// a value of 1 (01.000) means no xp change.
//
// the actual xp bonus/penalty in % = (modifier - 1) * 100
//
// use value < 1.0 to reduce the xp
// e.g. 00.500 = -50%
// 00.010 = -99%
// 00.001 = -99.9%
//
// attention: syntax !!
// always pad with 0. each number must be 6 chars long
// otherwise the parser will fail and your players get 0xp
// i use very simplistic parsing to optimize cpu use...
//
// the first number modifies level 1, the last number level 40
//
// LEVEL-----01--|--02--|--03--|--04--|--05--|--06--|--07--|--08--|--09--|--10--|--11--|--12--|--13--|--14--|--15--|--16--|--17--|--18--|--19--|--20--|--21--|--22--|--23--|--24--|--25--|--26--|--27--|--28--|--29--|--30--|--31--|--32--|--33--|--34--|--35--|--36--|--37--|--38--|--39--|--40--|
const string PWFXP_LEVEL_MODIFIERS = "11.000|06.000|04.000|01.000|01.000|01.000|01.000|01.000|01.000|01.000|00.850|00.850|00.800|00.800|00.750|00.750|00.700|00.700|00.650|00.650|00.600|00.550|00.500|00.450|00.400|00.350|00.300|00.200|00.100|00.090|00.090|00.080|00.080|00.070|00.070|00.060|00.060|00.050|00.040|00.040";
// small bonus for killing blow dealer
const float PWFXP_KILLINGBLOW_MODIFIER = 1.5; // 0%
// PC level gets compared to the average party level.
// APL = Average Party Level
//
//
// attention: the below example was from version 1.1
// most if not all constants have been changed (scalar for example is 100% now and thus fully linear)
//
// example uses below values
// const float PWFXP_APL_REDUCTION = 2.0;
// const float PWFXP_APL_NOXP = 4.0;
// const float PWFXP_SCALAR = 0.5
//
// XP gets reduced if PC-level > APL + APL_REDUCTION
// XP reduction is based on SCALAR, be careful if you change this
// right now its 0 - 50% (scalar 0.5) for delta 2 (APL_REDUCTION) .. delta 4 (APL_NOXP)
// delta = abs(APL - PC Level)
// this means it will switch from 50% reduction to 100% reduction in one leap in case the PC level
// is greater then APL + APL_NOXP.
// i did this for a better granularity for the given default values but
// you can freely change APL_REDUCTION and/or APL_NOXP. XP reduction gets auto-adjusted to the maximum
// of SCALAR (50% default).
//
// XP gets reduced to zero if PC-level > APL + APL_NOXP
//
// Example (using default values):
// PC A = level 7
// PC B = level 3
// PC C = level 1
//
// average party level (APL) = 3.66
//
// Distance PC A = abs(PClevel - AveragePartyLevel) = abs(7 - 3.66) = 3.34
// PC-A has a final distance of 1.34 (3.34 - APL_REDUCTION)
// XP reduction = (SCALAR / (APL_NOXP - APL_REDUCTION)) * 1.34 = (0.5 / 2) * 1.34 = 33.5% XP reduction
//
// Distance PC B = abs(PClevel - AveragePartyLevel) = abs(3 - 3.66) = 0.66
// PC-A has a final distance of -1.34 (0.66 - APL_REDUCTION)
// no XP reduction
//
// Distance PC C = abs(PClevel - AveragePartyLevel) = abs(1 - 3.66) = 2.66
// PC-A has a final distanceof 0.66 (2.66 - APL_REDUCTION)
// XP reduction = (SCALAR / (APL_NOXP - APL_REDUCTION)) * 0.66 = (0.5 / 2) * 0.66 = 16.5% XP reduction
//
// those PCs with the biggest impact to the average party level receive the biggest XP reduction
// (in the above case PC A)
//
// set _REDUCTION to 40 and _NOXP to 41 if you don't want any APL reduction
//
// changed default to a bit less harsh values
const float PWFXP_APL_REDUCTION = 3.0; // levels
const float PWFXP_APL_NOXP = 6.0;
// NEW:
// these 4 constants works like the APL constants above but it compares
// PC level vs challenge rating of the dead creature
//
// you can distinct two different cases now:
//
// PC level > CR of the creature (CR + PWFXP_CR_LESSTHAN_PCLEVEL_REDUCTION)
// PC level < CR of the creature (CR + PWFXP_CR_GREATERTHAN_PCLEVEL_REDUCTION)
//
// math is the same as the APL example above, just exchange
// AveragePartyLevel with CR of dead creature and use
// PWFXP_CR_*_PCLEVEL_REDUCTION and PWFXP_CR_*_PCLEVEL_NOXP as the constants
//
// set _REDUCTION to CR_MAX and _NOXP to CR_MAX+1 if you don't want any cr reduction
//
// reduction constants for PCs fighting mobs with a CR below their level
const float PWFXP_CR_LESSTHAN_PCLEVEL_REDUCTION = 3.0;
const float PWFXP_CR_LESSTHAN_PCLEVEL_NOXP = 10.0;
// note: default setting only penalize PCs if they try to kill something
// that should be *impossible* for their level.
// a 40 level epic PC will be able to kill CR 60 creatures without
// penalty and a large low-level group of players will be able to
// kill a much higher CR creature without penalty...(a group of lvl5 players killing
// a CR 20 creature won't receive any penalty. penalty will start to kick in if they try
// to kill a creature with a CR > 25
// you can use this to counter low-level XP exploits. e.g. a level 40 player
// could mangle a mob down to 1 HP. then a low level comes in and deals the final
// blow -> classical xp exploit...
// default settings make sure that nothing can get out of hand, but you can make
// this harsher if you want (but keep in mind that creatures can have a higher
// CR than the players maximum level, like CR 60)
//
// set _REDUCTION to CR_MAX and _NOXP to CR_MAX+1 if you don't want any cr reduction
//
// reduction constants for PCs fighting mobs with a CR above their level
const float PWFXP_CR_GREATERTHAN_PCLEVEL_REDUCTION = 20.0;
const float PWFXP_CR_GREATERTHAN_PCLEVEL_NOXP = 30.0;
// described above
const float PWFXP_SCALAR = 1.0;
// maximum CR cap
// this stops creatures with sky-high CRs from giving godly XP
const float PWFXP_CR_MAX = 60.0;
// groups get a small xp bonus
// formular is groupsize-1 * modifier
// with a default value of 0.1 (10%) a party of 4 receives 30% XP bonus
// this should encourage grouping
// set it to 0.0 if you dont like that...
const float PWFXP_GROUPBONUS_MODIFIER = 0.1;
// groub members need to be within this distance to the dead creature
// if they want to get any XP during fights
const float PWFXP_MAXIMUM_DISTANCE_TO_GROUP = 70.0; // meters
// safety mechanism
// minimum XP for a kill
const int PWFXP_MINIMUM_XP = 1;
// safety mechanism
// maximum XP for a kill
const int PWFXP_MAXIMUM_XP = 1000;
// UPDATED:
// these constants determine how XP division works
// you can now distinct between animal companion,
// familiars, dominated, summoned and henchman. you can set a different
// xp divisor for each of them. (default: henchman has max reduction impact followed
// by dominated, summoned, familiars, animal companion)
// e.g.: a group with two PCs + 1 FAMILIAR + 1 SUMMONED CREATURE
// gets a total XP divisor of 2.5 (using default values).
// if they kill a 1000XP mob, both PCs only receive 400 XP
const float PWFXP_XP_DIVISOR_PC = 1.0;
const float PWFXP_XP_DIVISOR_DOMINATED = 0.5;
const float PWFXP_XP_DIVISOR_HENCHMAN = 0.5;
const float PWFXP_XP_DIVISOR_SUMMONED = 0.3;
const float PWFXP_XP_DIVISOR_ANIMALCOMPANION = 0.1;
const float PWFXP_XP_DIVISOR_FAMILIAR = 0.1;
// used in case i can't determine the associate type
const float PWFXP_XP_DIVISOR_UNKNOWN = 0.5;
// don't change these
float PWFXP_APL_MODIFIER = PWFXP_SCALAR / (PWFXP_APL_NOXP - PWFXP_APL_REDUCTION);
float PWFXP_CR_LESSTHAN_PCLEVEL_MODIFIER = PWFXP_SCALAR / (PWFXP_CR_LESSTHAN_PCLEVEL_NOXP - PWFXP_CR_LESSTHAN_PCLEVEL_REDUCTION);
float PWFXP_CR_GREATERTHAN_PCLEVEL_MODIFIER = PWFXP_SCALAR / (PWFXP_CR_GREATERTHAN_PCLEVEL_NOXP - PWFXP_CR_GREATERTHAN_PCLEVEL_REDUCTION);

View File

@@ -0,0 +1,16 @@
// written by fluffyamoeba 09-07-06
// actually gets the LA modifier, not ECL
// gets the LA from ecl.2da (actually the 2da cache)
// used to hand out XP adjusted for LA
int GetECLMod(object oCreature);
#include "inc_utility"
int GetECLMod(object oCreature)
{
int nRace = GetRacialType(oCreature); //note this is not MyPRCGetRacialType becuase we want to include subraces too
int nLA = 0;
nLA = StringToInt(Get2DACache("ECL", "LA", nRace));
return nLA;
}

View File

@@ -13,5 +13,5 @@
void main()
{
ExecuteScript("nw_c2_default7", OBJECT_SELF);
ExecuteScript("tab_xpscript",OBJECT_SELF);
//ExecuteScript("tab_xpscript",OBJECT_SELF);
}