From 74ede2e7dd232f240f5792459c73b985a24df212 Mon Sep 17 00:00:00 2001 From: Jaysyn904 <68194417+Jaysyn904@users.noreply.github.com> Date: Sun, 26 Oct 2025 13:14:12 -0400 Subject: [PATCH] 2025/10/26 Update Swapped out crappy existing XP system for PWFXP. --- src/include/inc_dynconv.nss | 46 ++- src/include/inc_item_props.nss | 55 ++- src/include/prc_getbest_inc.nss | 3 +- src/include/prc_inc_nwscript.nss | 5 +- src/include/prc_inc_onhit.nss | 21 +- src/include/prc_inc_unarmed.nss | 4 +- src/include/prc_inc_wpnrest.nss | 66 +++- src/include/prc_nui_consts.nss | 47 +-- src/include/prc_nui_sb_inc.nss | 357 ++++++++++++++++-- src/include/prc_sp_func.nss | 18 + src/include/psi_inc_core.nss | 4 +- src/include/x3_inc_horse.nss | 5 +- src/module/itp/creaturepalcus.itp.json | 2 +- src/module/nss/0_tab_ondeath1.nss | 2 +- src/module/nss/0_tab_ondeath2.nss | 2 +- src/module/nss/0_tab_ondeath3.nss | 2 +- src/module/nss/at_asscpt_die.nss | 2 +- src/module/nss/at_lucephdie.nss | 2 +- src/module/nss/aw_boogiedeath.nss | 2 +- src/module/nss/aw_spawnvera.nss | 2 +- src/module/nss/cb_minddeath.nss | 2 +- src/module/nss/clan_death_02.nss | 2 +- src/module/nss/darth_death.nss | 2 +- src/module/nss/nui_c_storage.nss | 12 +- src/module/nss/phw_death.nss | 2 +- src/module/nss/prc_pwondeath.nss | 7 +- src/module/nss/pwfxp.nss | 479 +++++++++++++++++++++++++ src/module/nss/pwfxp_def.nss | 274 ++++++++++++++ src/module/nss/pwfxp_prc_race.nss | 16 + src/module/nss/x2_def_ondeath.nss | 2 +- 30 files changed, 1314 insertions(+), 131 deletions(-) create mode 100644 src/module/nss/pwfxp.nss create mode 100644 src/module/nss/pwfxp_def.nss create mode 100644 src/module/nss/pwfxp_prc_race.nss diff --git a/src/include/inc_dynconv.nss b/src/include/inc_dynconv.nss index 4603fc4..a90a69c 100644 --- a/src/include/inc_dynconv.nss +++ b/src/include/inc_dynconv.nss @@ -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_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 = 16183878; // "Exit" const string DYNCONV_SCRIPT = "DynConv_Script"; const string DYNCONV_VARIABLE = "DynConv_Var"; diff --git a/src/include/inc_item_props.nss b/src/include/inc_item_props.nss index b1edfc2..b912fad 100644 --- a/src/include/inc_item_props.nss +++ b/src/include/inc_item_props.nss @@ -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; diff --git a/src/include/prc_getbest_inc.nss b/src/include/prc_getbest_inc.nss index a17eb0a..a0efeca 100644 --- a/src/include/prc_getbest_inc.nss +++ b/src/include/prc_getbest_inc.nss @@ -400,5 +400,4 @@ int GetBestAvailableSpell(object oTarget) if(nBestSpell == 99999) nBestSpell = GetBestL1Spell(oTarget, nBestSpell); if(nBestSpell == 99999) nBestSpell = GetBestL0Spell(oTarget, nBestSpell); return nBestSpell; -} - +} \ No newline at end of file diff --git a/src/include/prc_inc_nwscript.nss b/src/include/prc_inc_nwscript.nss index 8ebc821..4671756 100644 --- a/src/include/prc_inc_nwscript.nss +++ b/src/include/prc_inc_nwscript.nss @@ -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; diff --git a/src/include/prc_inc_onhit.nss b/src/include/prc_inc_onhit.nss index 3f0da67..ab68887 100644 --- a/src/include/prc_inc_onhit.nss +++ b/src/include/prc_inc_onhit.nss @@ -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) diff --git a/src/include/prc_inc_unarmed.nss b/src/include/prc_inc_unarmed.nss index a08f912..096b334 100644 --- a/src/include/prc_inc_unarmed.nss +++ b/src/include/prc_inc_unarmed.nss @@ -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; diff --git a/src/include/prc_inc_wpnrest.nss b/src/include/prc_inc_wpnrest.nss index 9dcf65a..1734b3b 100644 --- a/src/include/prc_inc_wpnrest.nss +++ b/src/include/prc_inc_wpnrest.nss @@ -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 diff --git a/src/include/prc_nui_consts.nss b/src/include/prc_nui_consts.nss index e1e9c5c..0cb0efa 100644 --- a/src/include/prc_nui_consts.nss +++ b/src/include/prc_nui_consts.nss @@ -110,49 +110,4 @@ const string NUI_PRC_PA_TEXT_BIND = "nui_prc_pa_text_bind"; // Left Button Enabled Bind for Power Attack NUI 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"; - +const string NUI_PRC_PA_RIGHT_BUTTON_ENABLED_BIND = "rightButtonEnabled"; \ No newline at end of file diff --git a/src/include/prc_nui_sb_inc.nss b/src/include/prc_nui_sb_inc.nss index 7148413..a1a768e 100644 --- a/src/include/prc_nui_sb_inc.nss +++ b/src/include/prc_nui_sb_inc.nss @@ -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 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 + 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 @@ -830,16 +1144,5 @@ int JsonArrayContainsInt(json list, int item) return TRUE; } - return FALSE; -} - -int IsSpellbookNUIOpen(object oPC) -{ - int nPreviousToken = NuiFindWindow(oPC, PRC_SPELLBOOK_NUI_WINDOW_ID); - if (nPreviousToken != 0) - { - return TRUE; - } - return FALSE; } \ No newline at end of file diff --git a/src/include/prc_sp_func.nss b/src/include/prc_sp_func.nss index 233090f..c249c5f 100644 --- a/src/include/prc_sp_func.nss +++ b/src/include/prc_sp_func.nss @@ -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) { diff --git a/src/include/psi_inc_core.nss b/src/include/psi_inc_core.nss index 9083bee..a7acfb4 100644 --- a/src/include/psi_inc_core.nss +++ b/src/include/psi_inc_core.nss @@ -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)); diff --git a/src/include/x3_inc_horse.nss b/src/include/x3_inc_horse.nss index 65548a0..422e5c2 100644 --- a/src/include/x3_inc_horse.nss +++ b/src/include/x3_inc_horse.nss @@ -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() diff --git a/src/module/itp/creaturepalcus.itp.json b/src/module/itp/creaturepalcus.itp.json index 3a47624..29e272b 100644 --- a/src/module/itp/creaturepalcus.itp.json +++ b/src/module/itp/creaturepalcus.itp.json @@ -13852,7 +13852,7 @@ "__struct_id": 0, "CR": { "type": "float", - "value": 0.5 + "value": 0.3333 }, "FACTION": { "type": "cexostring", diff --git a/src/module/nss/0_tab_ondeath1.nss b/src/module/nss/0_tab_ondeath1.nss index f64745f..0c0682e 100644 --- a/src/module/nss/0_tab_ondeath1.nss +++ b/src/module/nss/0_tab_ondeath1.nss @@ -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(); diff --git a/src/module/nss/0_tab_ondeath2.nss b/src/module/nss/0_tab_ondeath2.nss index 364054d..cabfb0b 100644 --- a/src/module/nss/0_tab_ondeath2.nss +++ b/src/module/nss/0_tab_ondeath2.nss @@ -13,5 +13,5 @@ void main() { ExecuteScript("nw_c2_default7", OBJECT_SELF); - ExecuteScript("tab_xp_multi",OBJECT_SELF); + //ExecuteScript("tab_xp_multi",OBJECT_SELF); } diff --git a/src/module/nss/0_tab_ondeath3.nss b/src/module/nss/0_tab_ondeath3.nss index 364054d..cabfb0b 100644 --- a/src/module/nss/0_tab_ondeath3.nss +++ b/src/module/nss/0_tab_ondeath3.nss @@ -13,5 +13,5 @@ void main() { ExecuteScript("nw_c2_default7", OBJECT_SELF); - ExecuteScript("tab_xp_multi",OBJECT_SELF); + //ExecuteScript("tab_xp_multi",OBJECT_SELF); } diff --git a/src/module/nss/at_asscpt_die.nss b/src/module/nss/at_asscpt_die.nss index d9fbf39..03aa1e3 100644 --- a/src/module/nss/at_asscpt_die.nss +++ b/src/module/nss/at_asscpt_die.nss @@ -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))); } diff --git a/src/module/nss/at_lucephdie.nss b/src/module/nss/at_lucephdie.nss index 49263aa..f975330 100644 --- a/src/module/nss/at_lucephdie.nss +++ b/src/module/nss/at_lucephdie.nss @@ -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; diff --git a/src/module/nss/aw_boogiedeath.nss b/src/module/nss/aw_boogiedeath.nss index a689653..2acf8df 100644 --- a/src/module/nss/aw_boogiedeath.nss +++ b/src/module/nss/aw_boogiedeath.nss @@ -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; diff --git a/src/module/nss/aw_spawnvera.nss b/src/module/nss/aw_spawnvera.nss index c6a15f0..11b0ce8 100644 --- a/src/module/nss/aw_spawnvera.nss +++ b/src/module/nss/aw_spawnvera.nss @@ -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; diff --git a/src/module/nss/cb_minddeath.nss b/src/module/nss/cb_minddeath.nss index 049c44c..6127e90 100644 --- a/src/module/nss/cb_minddeath.nss +++ b/src/module/nss/cb_minddeath.nss @@ -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))) diff --git a/src/module/nss/clan_death_02.nss b/src/module/nss/clan_death_02.nss index a54df78..672fc5d 100644 --- a/src/module/nss/clan_death_02.nss +++ b/src/module/nss/clan_death_02.nss @@ -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(); diff --git a/src/module/nss/darth_death.nss b/src/module/nss/darth_death.nss index 379fd5f..52a3f22 100644 --- a/src/module/nss/darth_death.nss +++ b/src/module/nss/darth_death.nss @@ -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; diff --git a/src/module/nss/nui_c_storage.nss b/src/module/nss/nui_c_storage.nss index 903e0aa..8bb31ad 100644 --- a/src/module/nss/nui_c_storage.nss +++ b/src/module/nss/nui_c_storage.nss @@ -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 diff --git a/src/module/nss/phw_death.nss b/src/module/nss/phw_death.nss index fd3f037..834c625 100644 --- a/src/module/nss/phw_death.nss +++ b/src/module/nss/phw_death.nss @@ -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; diff --git a/src/module/nss/prc_pwondeath.nss b/src/module/nss/prc_pwondeath.nss index c2d7a85..8166e95 100644 --- a/src/module/nss/prc_pwondeath.nss +++ b/src/module/nss/prc_pwondeath.nss @@ -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); } - } + } */ } \ No newline at end of file diff --git a/src/module/nss/pwfxp.nss b/src/module/nss/pwfxp.nss new file mode 100644 index 0000000..c177696 --- /dev/null +++ b/src/module/nss/pwfxp.nss @@ -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); + } +} diff --git a/src/module/nss/pwfxp_def.nss b/src/module/nss/pwfxp_def.nss new file mode 100644 index 0000000..57d9055 --- /dev/null +++ b/src/module/nss/pwfxp_def.nss @@ -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); + diff --git a/src/module/nss/pwfxp_prc_race.nss b/src/module/nss/pwfxp_prc_race.nss new file mode 100644 index 0000000..05e241f --- /dev/null +++ b/src/module/nss/pwfxp_prc_race.nss @@ -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; +} diff --git a/src/module/nss/x2_def_ondeath.nss b/src/module/nss/x2_def_ondeath.nss index a97fe77..21277aa 100644 --- a/src/module/nss/x2_def_ondeath.nss +++ b/src/module/nss/x2_def_ondeath.nss @@ -13,5 +13,5 @@ void main() { ExecuteScript("nw_c2_default7", OBJECT_SELF); - ExecuteScript("tab_xpscript",OBJECT_SELF); + //ExecuteScript("tab_xpscript",OBJECT_SELF); }