diff --git a/nasher.cfg b/nasher.cfg index d42bfe1..1ea14e7 100644 --- a/nasher.cfg +++ b/nasher.cfg @@ -227,7 +227,20 @@ description = "PRC8 version of The Lord of Terror module." filter = "x2_inc_cutscenep.nss" filter = "x2_inc_spellhook.nss" filter = "x3_inc_horse.nss" - filter = "xchst_inc.nss" + filter = "prc_inc_string.nss" + filter = "prc_nui_sc_inc.nss" + filter = "prc_nui_scd_inc.nss" + filter = "prc_nui_consts.nss" + filter = "nw_inc_nui.nss" + filter = "xchst_inc.nss" + filter = "prc_nui_sbd_inc.nss" + filter = "prc_nui_sb_inc.nss" + filter = "prc_nui_lv_inc.nss" + filter = "prc_nui_com_inc.nss" + filter = "prc_inc_size.nss" + filter = "prc_inc_json.nss" + filter = "prc_inc_gff.nss" + filter = "inc_infusion.nss" [target] name = "tophak" diff --git a/src/include/inc_dynconv.nss b/src/include/inc_dynconv.nss index a90a69c..68aabac 100644 --- a/src/include/inc_dynconv.nss +++ b/src/include/inc_dynconv.nss @@ -4,12 +4,15 @@ //::////////////////////////////////////////////// /** @file + + @author Primogenitor @date 2005.09.23 - Rebuilt the system - Ornedan */ //::////////////////////////////////////////////// //::////////////////////////////////////////////// +//PRC8 Token pre-fix = 161838 ////////////////////////////////////////////////// /* Constant definitions */ @@ -37,11 +40,11 @@ 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 = 16183878; // "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 = 78; // "Exit" const string DYNCONV_SCRIPT = "DynConv_Script"; const string DYNCONV_VARIABLE = "DynConv_Var"; @@ -475,9 +478,28 @@ void _DynConvInternal_ExitedConvo(object oPC, int bAbort) DeleteLocalInt(oPC, DYNCONV_STAGE); DeleteLocalString(oPC, DYNCONV_SCRIPT); DeleteLocalString(oPC, "DynConv_HeaderText"); - int i; - for(i = DYNCONV_MIN_TOKEN; i <= DYNCONV_MAX_TOKEN; i++) - DeleteLocalString(oPC, GetTokenIDString(i)); + + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_HEADER)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_0)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_1)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_2)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_3)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_4)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_5)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_6)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_7)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_8)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_9)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_EXIT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_WAIT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_NEXT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_PREV)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_MIN_TOKEN)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_MAX_TOKEN)); + + //int i; + //for(i = DYNCONV_MIN_TOKEN; i <= DYNCONV_MAX_TOKEN; i++) + //DeleteLocalString(oPC, GetTokenIDString(i)); } } } diff --git a/src/include/inc_epicspellfnc.nss b/src/include/inc_epicspellfnc.nss index 7fb6054..4537d45 100644 --- a/src/include/inc_epicspellfnc.nss +++ b/src/include/inc_epicspellfnc.nss @@ -26,7 +26,7 @@ int GetSpellFromAbrev(string sAbrev); ////////////////////////////////////////////////// #include "inc_utility" -//#include "inc_epicspelldef" +#include "inc_epicspells" // SEED FUNCTIONS diff --git a/src/include/inc_epicspells.nss b/src/include/inc_epicspells.nss index ec3eac9..4682672 100644 --- a/src/include/inc_epicspells.nss +++ b/src/include/inc_epicspells.nss @@ -303,11 +303,18 @@ int GetIsEpicShaman(object oPC) && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; } -int GetIsEpicSorcerer(object oPC) +int GetIsEpicSorcerer(object oPC) +{ + return GetHitDice(oPC) >= 21 + && GetCasterLvl(CLASS_TYPE_SORCERER, oPC) > 17 + && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; +} + +/* int GetIsEpicSorcerer(object oPC) { return GetPrCAdjustedCasterLevel(CLASS_TYPE_SORCERER, oPC, FALSE) > 17 && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; -} +} */ int GetIsEpicSublimeChord(object oPC) { diff --git a/src/include/inc_item_props.nss b/src/include/inc_item_props.nss index 2d30330..b912fad 100644 --- a/src/include/inc_item_props.nss +++ b/src/include/inc_item_props.nss @@ -1686,7 +1686,7 @@ int FeatToIprop(int nFeat) 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_NUNCHUKU: return IP_CONST_FEAT_WEAPON_PROFICIENCY_NUNCHUKU; + 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; diff --git a/src/include/inc_newspellbook.nss b/src/include/inc_newspellbook.nss index 539b842..5a06b4d 100644 --- a/src/include/inc_newspellbook.nss +++ b/src/include/inc_newspellbook.nss @@ -108,11 +108,10 @@ void ProcessPreparedSpellLevel(object oPC, int nClass, int nSpellLevel, int nLev //#include "prc_effect_inc" //access via prc_inc_core //#include "inc_lookups" //access via prc_inc_core #include "prc_inc_core" -#include "inc_sp_gain_mem" //providing child access to prc_inc_core - //Must load in this order. +#include "inc_sp_gain_mem" //#include "prc_inc_castlvl" //access via prc_inc_core //#include "prc_inc_descrptr" //access via prc_inc_core - +#include "inc_item_props" ////////////////////////////////////////////////// /* Function definitions */ @@ -1622,6 +1621,3 @@ void DoCleanUp(int nMetamagic) DeleteLocalInt(OBJECT_SELF, "NSB_SpellLevel"); DeleteLocalInt(OBJECT_SELF, "NSB_SpellbookID"); } - -//:: Test Void -//:: void main (){} \ No newline at end of file diff --git a/src/include/inc_npc.nss b/src/include/inc_npc.nss index 6b99a4a..8385ddc 100644 --- a/src/include/inc_npc.nss +++ b/src/include/inc_npc.nss @@ -7,8 +7,11 @@ //::////////////////////////////////////////////// //::////////////////////////////////////////////// +//:: Levels up an NPC according to variables set on NPC. +void LevelUpSummon(object oSummon, int iTargetLvl); + // Get the master of oAssociate. -object GetMasterNPC(object oAssociate=OBJECT_SELF); +object GetMasterNPC(object oAssociate0 = OBJECT_SELF); // Returns the associate type of the specified creature. // - Returns ASSOCIATE_TYPE_NONE if the creature is not the associate of anyone. @@ -75,7 +78,6 @@ void DestroySummon(object oSummon) DestroyObject(oSummon); } - object CreateLocalNPC(object oMaster,int nAssociateType,string sTemplate,location loc,int Nth=1,string sTag="") { object oSummon=CreateObject(OBJECT_TYPE_CREATURE,sTemplate,loc,FALSE,sTag); @@ -111,7 +113,7 @@ object CreateLocalNextNPC(object oMaster,int nAssociateType,string sTemplate,loc SetLocalObject(oMaster, IntToString(nAssociateType)+"oHench"+IntToString(nCount), oSummon); SetLocalInt(oSummon, "iAssocNth", nCount); - SetAssociateState(NW_ASC_HAVE_MASTER,TRUE,oSummon); + SetAssociateState(NW_ASC_HAVE_MASTER, TRUE, oSummon); SetAssociateState(NW_ASC_DISTANCE_2_METERS); SetAssociateState(NW_ASC_DISTANCE_4_METERS, FALSE); SetAssociateState(NW_ASC_DISTANCE_6_METERS, FALSE); @@ -122,6 +124,7 @@ object CreateLocalNextNPC(object oMaster,int nAssociateType,string sTemplate,loc return oSummon; } + object GetMasterNPC(object oAssociate=OBJECT_SELF) { object oMaster = GetLocalObject(oAssociate, "oMaster"); @@ -220,4 +223,173 @@ int GetAssociateHealMasterNPC() return FALSE; } +/** + * @brief Levels up a summoned creature based on its master's total casting level, + * while respecting configured HD limits and multiclass transition rules. + * Should only be called on the NPC onSpawn event. + * + * This function: + * - Retrieves the master’s total casting level and clamps it to the creature’s + * minimum and maximum HD (iMinHD, iMaxHD). + * - Repeatedly calls LevelUpHenchman() until the creature reaches that level, + * switching classes when the creature's stored "ClassXStart" thresholds are met. + * + * Local variables recognized on the summoned creature: + * + * | Variable Name | Purpose | + * |-----------------|-------------------------------------------------------------| + * | iMinHD | Minimum HD allowed | + * | iMaxHD | Maximum HD allowed | + * | Class2Start | Level to begin second class progression | + * | Class2 | Class type for second progression | + * | Class2Package | Package for second progression | + * | Class3Start | Level to begin third class progression | + * | Class3 | Class type for third progression | + * | Class3Package | Package for third progression | + * | Class4Start | Level to begin fourth class progression | + * | Class4 | Class type for fourth progression | + * | Class4Package | Package for fourth progression | + * + * Behavior notes: + * - Leveling continues until the creature reaches the master’s effective + * casting level (bounded by iMinHD/iMaxHD). + * - If LevelUpHenchman() returns 0, the creature shouts a failure message. + * - CLASS_TYPE_INVALID causes the creature to level in its current class. + * + * @param oCreature The summoned creature being leveled. Defaults to OBJECT_SELF. + * + * @see LevelUpHenchman + * @see GetLocalInt + * @see GetHitDice + */ +void LevelUpSummon(object oSummon, int iTargetLvl) +{ + int nCurrentHD = GetHitDice(oSummon); + int iNewHD = nCurrentHD; + // Read multiclassing info from locals + int iClass2Start = GetLocalInt(oSummon, "Class2Start"); + int iClass2 = GetLocalInt(oSummon, "Class2"); + int iClass2Package = GetLocalInt(oSummon, "Class2Package"); + + int iClass3Start = GetLocalInt(oSummon, "Class3Start"); + int iClass3 = GetLocalInt(oSummon, "Class3"); + int iClass3Package = GetLocalInt(oSummon, "Class3Package"); + + int iClass4Start = GetLocalInt(oSummon, "Class4Start"); + int iClass4 = GetLocalInt(oSummon, "Class4"); + int iClass4Package = GetLocalInt(oSummon, "Class4Package"); + + int iClass; // current class to level + int iPackage; // package to use + + // Main leveling loop + while (nCurrentHD < iTargetLvl && nCurrentHD > 0) + { + // Determine which class and package to use + if (iClass4Start != 0 && nCurrentHD >= iClass4Start) + { + iClass = iClass4; + iPackage = iClass4Package; + } + else if (iClass3Start != 0 && nCurrentHD >= iClass3Start) + { + iClass = iClass3; + iPackage = iClass3Package; + } + else if (iClass2Start != 0 && nCurrentHD >= iClass2Start) + { + iClass = iClass2; + iPackage = iClass2Package; + } + else + { + // Base class (first class in the sheet) + iClass = CLASS_TYPE_INVALID; // keeps current + iPackage = PACKAGE_INVALID; + } + + // Level up one HD + iNewHD = LevelUpHenchman(oSummon, iClass, TRUE, iPackage); + + if (iNewHD == 0) + { + SpeakString(GetName(oSummon) + " failed to level properly!", TALKVOLUME_SHOUT); + break; + } + + nCurrentHD = iNewHD; + } + + // Force the creature to rest to memorize spells + // PRCForceRest(oSummon); + +} + + + + + +/* void LevelUpSummon(object oSummon, int iTargetLvl) +{ + //get the default hit dice of the summon + int nDefaultHD = GetHitDice(oSummon); + + if (DEBUG) DoDebug("inc_npc >> LevelUpSummon: nDefaultHD = " +IntToString(nDefaultHD)+"."); + + if (DEBUG) DoDebug("inc_npc >> LevelUpSummon: iTargetLvl = " +IntToString(iTargetLvl)+"."); + + //get the multiclassing variables to see if we need to change classes from its base class + int iClass2Start = GetLocalInt(oSummon, "Class2Start"); + int iClass2 = GetLocalInt(oSummon, "Class2"); + int iClass2Package = GetLocalInt(oSummon, "Class2Package"); + + int iClass3Start = GetLocalInt(oSummon, "Class3Start"); + int iClass3 = GetLocalInt(oSummon, "Class3"); + int iClass3Package = GetLocalInt(oSummon, "Class3Package"); + + int iClass4Start = GetLocalInt(oSummon, "Class4Start"); + int iClass4 = GetLocalInt(oSummon, "Class4"); + int iClass4Package = GetLocalInt(oSummon, "Class4Package"); + + //check for zero cause thats an error + //if creatures are not leveling then best bet is they are not legal creatures + while( (nDefaultHD < iTargetLvl) && (nDefaultHD > 0) ) + { + //check the multiclassing numbers to change classes + if( (iClass4Start != 0) && (nDefaultHD >= iClass4Start) ) + { + //level up using the new class and Packageage + nDefaultHD = LevelUpHenchman(oSummon, iClass4 ,TRUE, iClass4Package); + + if(nDefaultHD == 0) + SpeakString(GetName(oSummon) + " Failed on fourth class", TALKVOLUME_SHOUT); + } + else if( (iClass3Start != 0) && (nDefaultHD >= iClass3Start) ) + { + //level up using the new class and Packageage + nDefaultHD = LevelUpHenchman(oSummon, iClass3 ,TRUE, iClass3Package); + + if(nDefaultHD == 0) + SpeakString(GetName(oSummon) + " Failed on third class", TALKVOLUME_SHOUT); + } + else if( (iClass2Start != 0) && (nDefaultHD >= iClass2Start) ) + { + //level up using the new class and Packageage + nDefaultHD = LevelUpHenchman(oSummon, iClass2 ,TRUE, iClass2Package); + + if(nDefaultHD == 0) + SpeakString(GetName(oSummon) + " Failed on second class", TALKVOLUME_SHOUT); + } + else + { + //just level up using the class it already has + nDefaultHD = LevelUpHenchman(oSummon, CLASS_TYPE_INVALID ,TRUE); + + if(nDefaultHD == 0) + SpeakString(GetName(oSummon) + " Failed to level properly", TALKVOLUME_SHOUT); + } + } +} + */ +//:: void main() {} \ No newline at end of file diff --git a/src/include/inc_rand_equip.nss b/src/include/inc_rand_equip.nss index 814f533..07f7191 100644 --- a/src/include/inc_rand_equip.nss +++ b/src/include/inc_rand_equip.nss @@ -3433,6 +3433,7 @@ int PrimoGetWeaponSize(object oItem) case BASE_ITEM_LIGHTFLAIL: case BASE_ITEM_KATANA: case BASE_ITEM_MAGICSTAFF: + case BASE_ITEM_CRAFTED_SCEPTER: case BASE_ITEM_LONGSWORD: case BASE_ITEM_TRIDENT: case BASE_ITEM_MORNINGSTAR: diff --git a/src/include/inc_rend.nss b/src/include/inc_rend.nss index 06e2b2f..07ae6b3 100644 --- a/src/include/inc_rend.nss +++ b/src/include/inc_rend.nss @@ -30,6 +30,7 @@ int GetDamageFromConstant(int nIPConst); void DoFrostRend(object oTarget, object oAttacker, object oWeapon); #include "moi_inc_moifunc" +#include "prc_inc_combat" ////////////////////////////////////////////////// /* Function defintions */ diff --git a/src/include/inc_sp_gain_mem.nss b/src/include/inc_sp_gain_mem.nss index d994896..b3981be 100644 --- a/src/include/inc_sp_gain_mem.nss +++ b/src/include/inc_sp_gain_mem.nss @@ -17,9 +17,6 @@ Created: May 1, 2008 //:: Updated for .35 by Jaysyn 2023/03/11 -//:: Test Void -//void main (){} - //::////////////////////////////////////////////// //:: Constants //::////////////////////////////////////////////// @@ -63,6 +60,14 @@ string GetMetaMagicString(int nMetaMagic); int GetMetaMagicFromFeat(int nFeat); int GetMetaMagicOfCaster(object oPC = OBJECT_SELF); +string GetFileForClass(int nClass); +int GetSpellslotLevel(int nClass, object oPC); +int GetSpellKnownMaxCount(int nLevel, int nSpellLevel, int nClass, object oPC); +int GetSpellbookTypeForClass(int nClass); + +#include "inc_pers_array" +#include "inc_2dacache" + // name of the new spellbook file (cls_spell_*) string GetNSBDefinitionFileName(int nClass) { diff --git a/src/include/inc_switch_setup.nss b/src/include/inc_switch_setup.nss index 15182a9..86c526c 100644 --- a/src/include/inc_switch_setup.nss +++ b/src/include/inc_switch_setup.nss @@ -44,6 +44,7 @@ void CreateSwitchNameArray(); #include "prc_inc_array" // Needs direct include instead of inc_utility #include "prc_inc_switch" +#include "inc_2dacache" ////////////////////////////////////////////////// /* Function definitions */ @@ -335,7 +336,7 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_craft_golem", 40); SetPRCSwitch("PRC_FILE_END_craft_ring", 41); SetPRCSwitch("PRC_FILE_END_craft_weapon", 46); - SetPRCSwitch("PRC_FILE_END_craft_wondrous", 115); + SetPRCSwitch("PRC_FILE_END_craft_wondrous", 131); SetPRCSwitch("PRC_FILE_END_creaturesize", 5); SetPRCSwitch("PRC_FILE_END_creaturespeed", 8); SetPRCSwitch("PRC_FILE_END_crtemplates", 10); @@ -408,7 +409,7 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_iprp_ammocost", 15); SetPRCSwitch("PRC_FILE_END_iprp_ammotype", 2); SetPRCSwitch("PRC_FILE_END_iprp_amount", 4); - SetPRCSwitch("PRC_FILE_END_iprp_aoe", 7); + SetPRCSwitch("PRC_FILE_END_iprp_aoe", 8); SetPRCSwitch("PRC_FILE_END_iprp_arcspell", 19); SetPRCSwitch("PRC_FILE_END_iprp_base1", -1); SetPRCSwitch("PRC_FILE_END_iprp_bladecost", 5); @@ -430,8 +431,8 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_iprp_incvalue2", 9); SetPRCSwitch("PRC_FILE_END_iprp_kitcost", 50); SetPRCSwitch("PRC_FILE_END_iprp_lightcost", 4); - SetPRCSwitch("PRC_FILE_END_iprp_matcost", 77); - SetPRCSwitch("PRC_FILE_END_iprp_material", 77); + SetPRCSwitch("PRC_FILE_END_iprp_matcost", 145); + SetPRCSwitch("PRC_FILE_END_iprp_material", 145); SetPRCSwitch("PRC_FILE_END_iprp_maxpp", 8); SetPRCSwitch("PRC_FILE_END_iprp_meleecost", 20); SetPRCSwitch("PRC_FILE_END_iprp_metamagic", 6); @@ -866,6 +867,31 @@ void CreateSwitchNameArray() //if you add more switches, add them to this list array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEBUG); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_COMBAT_DEBUG); + +//craft + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_CRAFT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MULTIPLIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MAX); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MIN); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BREW_POTION_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SCRIBE_SCROLL_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_WAND_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_ROD_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_STAFF_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_BASE_ITEMS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_BREWPOTION_MAXLEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_BREWPOTION_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_SCRIBESCROLL_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CRAFTWAND_MAXLEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CRAFTWAND_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CREATEINFUSION_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_ARBITRARY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_COST_SCALE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_TIME_SCALE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CREATE_INFUSION_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CREATE_INFUSION_OPTIONAL_HERBS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_SCEPTER_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_MATERIAL_COMPONENTS); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_COMPONENTS_SHOP); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_TRUESEEING); @@ -876,6 +902,8 @@ void CreateSwitchNameArray() array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_HARM); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_NEUTRALIZE_POISON); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_REMOVE_DISEASE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ALLOWED_TO_REMOVE_FRIENDLY_SPELLS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ALLOWED_TO_SEE_HOSTILE_SPELLS); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIO_UNLEARN); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_UNLEARN_SPELL_MAXNR); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_BIOWARE_DURATION); @@ -994,7 +1022,7 @@ void CreateSwitchNameArray() array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USES_PER_WEAPON_POISON_COUNT); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USES_PER_WEAPON_POISON_DIE); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_POISON_ALLOW_CLEAN_IN_EQUIP); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_POISON_USE_INGREDIENST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_POISON_USE_INGREDIENTS); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PSI_ASTRAL_CONSTRUCT_USE_2DA); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_RAPID_METABOLISM); @@ -1058,31 +1086,31 @@ void CreateSwitchNameArray() array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LETOSCRIPT_UNICORN_SQL); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LETOSCRIPT_GETNEWESTBIC); -//craft - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_CRAFT); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MULTIPLIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MAX); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MIN); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BREW_POTION_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SCRIBE_SCROLL_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_WAND_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_ROD_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_STAFF_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_BASE_ITEMS); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_BREWPOTION_MAXLEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_BREWPOTION_COSTMODIFIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_SCRIBESCROLL_COSTMODIFIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CRAFTWAND_MAXLEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CRAFTWAND_COSTMODIFIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CREATEINFUSION_COSTMODIFIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_ARBITRARY); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_COST_SCALE); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_TIME_SCALE); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CREATE_INFUSION_CASTER_LEVEL); - //spells //shifter + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPECHANGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_USECR); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_HUGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_LARGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_MEDIUM); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_SMALL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_TINY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_OUTSIDER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ELEMENTAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_CONSTRUCT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_UNDEAD); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_DRAGON); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ABERRATION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_OOZE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_MAGICALBEAST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_GIANT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_VERMIN); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_BEAST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ANIMAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_MONSTROUSHUMANOID); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_HUMANOID); //general diff --git a/src/include/inv_inc_blast.nss b/src/include/inv_inc_blast.nss index 683b9cd..94709f6 100644 --- a/src/include/inv_inc_blast.nss +++ b/src/include/inv_inc_blast.nss @@ -1,4 +1,5 @@ #include "prc_inc_clsfunc" +#include "prc_inc_sp_tch" int GetBlastDamageDices(object oInvoker, int nInvokerLevel) { diff --git a/src/include/inv_inc_invknown.nss b/src/include/inv_inc_invknown.nss index dbd7acb..95800ad 100644 --- a/src/include/inv_inc_invknown.nss +++ b/src/include/inv_inc_invknown.nss @@ -135,6 +135,9 @@ int GetHasInvocation(int nInvocation, object oCreature = OBJECT_SELF); /* Includes */ ////////////////////////////////////////////////// +int GetPrimaryInvocationClass(object oCreature = OBJECT_SELF); +int GetInvocationPRCLevels(object oCaster); + #include "inc_item_props" #include "prc_x2_itemprop" #include "inc_lookups" diff --git a/src/include/inv_inc_invoke.nss b/src/include/inv_inc_invoke.nss index 0abd1e1..da67444 100644 --- a/src/include/inv_inc_invoke.nss +++ b/src/include/inv_inc_invoke.nss @@ -123,7 +123,7 @@ void DeleteLocalInvocation(object oObject, string sName); /* Includes */ ////////////////////////////////////////////////// -//#include "inv_inc_invfunc" //Access in parent +#include "inv_inc_invfunc" //Access in parent #include "prc_spellf_inc" ////////////////////////////////////////////////// diff --git a/src/include/inv_invokehook.nss b/src/include/inv_invokehook.nss index 92b0f1f..53010f3 100644 --- a/src/include/inv_invokehook.nss +++ b/src/include/inv_invokehook.nss @@ -77,6 +77,15 @@ int PreInvocationCastCode() int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oInvoker); + //--------------------------------------------------------------------------- + // Block forsakers from using invocations + //--------------------------------------------------------------------------- + if(GetLevelByClass(CLASS_TYPE_FORSAKER, oInvoker) > 0) + { + SendMessageToPC(oInvoker, "Forsakers cannot use invocations."); + return FALSE; + } + //--------------------------------------------------------------------------- // Break any spell require maintaining concentration //--------------------------------------------------------------------------- @@ -168,3 +177,4 @@ int PreInvocationCastCode() return nContinue; } +//:: void main (){} \ No newline at end of file diff --git a/src/include/pnp_shft_poly.nss b/src/include/pnp_shft_poly.nss index ce6d860..21b6663 100644 --- a/src/include/pnp_shft_poly.nss +++ b/src/include/pnp_shft_poly.nss @@ -10,9 +10,586 @@ void ShifterCheck(object oPC); #include "pnp_shft_main" #include "prc_inc_shifting" +//::////////////////Begin Werewolf////////////////// const string PRC_PNP_SHIFTING = "PRC_Shift"; -////////////////Begin Werewolf////////////////// +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon & Armor merge block + object oMergeWeaponSource = OBJECT_INVALID; + object oMergeArmorSource = OBJECT_INVALID; + + //:: Determine Weapon Merge Source + if (bWeapon) + { + if (bMonkGloves) + { + if (GetIsObjectValid(oGlovesOld)) + oMergeWeaponSource = oGlovesOld; + } + else + { + //:: Always attempt to merge melee weapon to creature weapon + oMergeWeaponSource = oWeaponOld; // even if empty, ensures proper state + } + } + else + { + //:: Weapon not requested, but arms-slot allowed monk gloves can merge via armor branch + if (bMonkGloves && bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + oMergeWeaponSource = oGlovesOld; + } + + //:: Determine Armor Merge Source + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (!bMonkGloves) + { + if (bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + oMergeArmorSource = oGlovesOld; + + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + } + else + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + //:: Apply Weapon Merge + if (GetIsObjectValid(oMergeWeaponSource) || bWeapon) + { + //:: Always attempt to merge weapon properties even if source is OBJECT_INVALID + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewBite, TRUE); + } + + //:: Apply Armor Merge + if (GetIsObjectValid(oMergeArmorSource)) + { + if (GetIsObjectValid(oArmorNew)) IPWildShapeCopyItemProperties(oMergeArmorSource, oArmorNew); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// + + +/* //::////////////////Begin Werewolf////////////////// +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon & Armor merge block + object oMergeWeaponSource = OBJECT_INVALID; + object oMergeArmorSource = OBJECT_INVALID; + + // ---- Determine Weapon Merge Source ---- + if (bWeapon) + { + if (bMonkGloves) + { + if (GetIsObjectValid(oGlovesOld)) + oMergeWeaponSource = oGlovesOld; + } + else + { + if (GetIsObjectValid(oWeaponOld)) + oMergeWeaponSource = oWeaponOld; + } + } + else + { + if (bMonkGloves && bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + oMergeWeaponSource = oGlovesOld; + } + + // ---- Determine Armor Merge Source ---- + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (!bMonkGloves) + { + if (bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + oMergeArmorSource = oGlovesOld; + + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + } + else + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + // ---- Apply Weapon Merge ---- + if (GetIsObjectValid(oMergeWeaponSource)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewBite, TRUE); + } + + // ---- Apply Armor Merge ---- + if (GetIsObjectValid(oMergeArmorSource)) + { + if (GetIsObjectValid(oArmorNew)) IPWildShapeCopyItemProperties(oMergeArmorSource, oArmorNew); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// */ + + +/* //::////////////////Begin Werewolf////////////////// +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon merge block + if (bWeapon) + { + object oMergeSource = OBJECT_INVALID; + + // Priority: monk gloves override if worn and arms-slot not allowed + if (bMonkGloves && !bArmsSlotAllowed) + { + if (GetIsObjectValid(oGlovesOld)) + oMergeSource = oGlovesOld; + } + else + { + // Otherwise use main-hand weapon if it exists + if (GetIsObjectValid(oWeaponOld)) + oMergeSource = oWeaponOld; + } + + // Apply merge to creature weapons if we have a source + if (GetIsObjectValid(oMergeSource)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oMergeSource, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oMergeSource, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oMergeSource, oWeaponNewBite, TRUE); + } + } + + //:: Armor merge block + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + + // Arms-slot allowed -> apply gloves & bracers to creature weapons + if (bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + { + if (DEBUG) DoDebug("LycanthropePoly: Arms-slot allowed -> applying gloves/bracers to creature weapons from armor branch."); + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// + */ + +/* //::////////////////Begin Werewolf////////////////// +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon merge block + //:: Only blocked if monk gloves are equipped AND arms-slot merge is NOT allowed + if (bWeapon && !bMonkGloves) + { + if (GetIsObjectValid(oWeaponOld)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewBite, TRUE); + } + } + else if (bWeapon && bMonkGloves && !bArmsSlotAllowed) + { + if (DEBUG) DoDebug("LycanthropePoly: Monk gloves overriding weapon merge (arms slot NOT allowed)."); + if (GetIsObjectValid(oGlovesOld)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + + + //:: Armor merge block + //:: Apply armor and gloves (if arms-slot allowed) + + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + + if (bArmsSlotAllowed && bMonkGloves && GetIsObjectValid(oGlovesOld)) + { + if (DEBUG) DoDebug("LycanthropePoly: Arms-slot allowed -> applying gloves to creature weapons from armor branch."); + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// */ + + +/* //::////////////////Begin Werewolf////////////////// +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon merge block + //:: Only blocked if monk gloves are equipped AND arms-slot merge is NOT allowed + if (bWeapon && !bMonkGloves) + { + if (GetIsObjectValid(oWeaponOld)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewBite, TRUE); + } + } + else if (bWeapon && bMonkGloves && !bArmsSlotAllowed) + { + if (DEBUG) DoDebug("LycanthropePoly: Monk gloves overriding weapon merge (arms slot NOT allowed)."); + if (GetIsObjectValid(oGlovesOld)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + + + //:: Armor merge block + //:: Apply armor and gloves (if arms-slot allowed) + + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + + if (bArmsSlotAllowed && bMonkGloves && GetIsObjectValid(oGlovesOld)) + { + if (DEBUG) DoDebug("LycanthropePoly: Arms-slot allowed -> applying gloves to creature weapons from armor branch."); + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// */ + + +/* ////////////////Begin Werewolf////////////////// void LycanthropePoly(object oPC, int nPoly) { @@ -84,7 +661,7 @@ void LycanthropePoly(object oPC, int nPoly) } -////////////////End Werewolf////////////////// +////////////////End Werewolf////////////////// */ void ShifterCheck(object oPC) { @@ -246,4 +823,6 @@ void DoTail(object oPC, int nTailType) SetCreatureTailType(nTailType, oPC); //override any stored default appearance SetPersistantLocalInt(oPC, "AppearanceStoredTail", nTailType); -} \ No newline at end of file +} + +//::void main (){} \ No newline at end of file diff --git a/src/include/prc_add_spl_pen.nss b/src/include/prc_add_spl_pen.nss index e339ebd..d7ca3d6 100644 --- a/src/include/prc_add_spl_pen.nss +++ b/src/include/prc_add_spl_pen.nss @@ -54,11 +54,11 @@ int SPGetPenetrAOE(object oCaster = OBJECT_SELF, int nCasterLvl = 0); /* Includes */ ////////////////////////////////////////////////// -//#include "prc_inc_spells" +#include "prc_inc_spells" //#include "prc_alterations" //#include "prcsp_archmaginc" //#include "prc_inc_racial" - +#include "inc_2dacache" ////////////////////////////////////////////////// /* Function definitions */ diff --git a/src/include/prc_craft_inc.nss b/src/include/prc_craft_inc.nss index 8d0c398..ee87ad8 100644 --- a/src/include/prc_craft_inc.nss +++ b/src/include/prc_craft_inc.nss @@ -444,6 +444,7 @@ int Get2DALineFromItemprop(string sFile, itemproperty ip, object oItem) } break; } + case ITEM_PROPERTY_ECHOBLADE: return 46; break; } } return -1; @@ -1529,7 +1530,12 @@ void ApplyItemProps(object oItem, string sFile, int nLine) break; //no more itemprops, no gaps, assuming no errors } if(sFile != "craft_weapon" && sFile != "craft_armour") - SetName(oItem, GetStringByStrRef(StringToInt(Get2DACache(sFile, "Name", nLine)))); + { + SetName(oItem, GetStringByStrRef(StringToInt(Get2DACache(sFile, "Name", nLine)))); + string sDescRef = Get2DACache(sFile, "CraftedDescription", nLine); + if(sDescRef != "") + SetDescription(oItem, GetStringByStrRef(StringToInt(sDescRef))); + } } //Partly ripped off the lexicon :P @@ -1620,7 +1626,8 @@ string GetCrafting2DA(object oItem) (nBase == BASE_ITEM_BOOTS) || (nBase == BASE_ITEM_GLOVES) || (nBase == BASE_ITEM_BRACER) || - (nBase == BASE_ITEM_CLOAK)) + (nBase == BASE_ITEM_CLOAK) || + (nBase == BASE_ITEM_CRAFTED_VIAL)) ) return "craft_wondrous"; @@ -1657,19 +1664,28 @@ int GetCraftingFeat(object oItem) if(nBase == BASE_ITEM_RING) return FEAT_FORGE_RING; //routing bioware feats through this convo + if((nBase == BASE_ITEM_CRAFTED_SCEPTER) || + (nBase == BASE_ITEM_CRAFTED_SCEPTER) + ) + return FEAT_CRAFT_SCEPTER; + if((nBase == BASE_ITEM_MAGICROD) || (nBase == BASE_ITEM_CRAFTED_ROD) ) return FEAT_CRAFT_ROD; + if((nBase == BASE_ITEM_MAGICSTAFF) || (nBase == BASE_ITEM_CRAFTED_STAFF) ) return FEAT_CRAFT_STAFF; + if((nBase == BASE_ITEM_MAGICWAND) || (nBase == BASE_ITEM_BLANK_WAND) ) return FEAT_CRAFT_WAND; + if(nBase == BASE_ITEM_BLANK_POTION) return FEAT_BREW_POTION; + if(nBase == BASE_ITEM_BLANK_SCROLL) return FEAT_SCRIBE_SCROLL; if(((nBase == BASE_ITEM_HELMET) || @@ -1678,7 +1694,8 @@ int GetCraftingFeat(object oItem) (nBase == BASE_ITEM_BOOTS) || (nBase == BASE_ITEM_GLOVES) || (nBase == BASE_ITEM_BRACER) || - (nBase == BASE_ITEM_CLOAK)) + (nBase == BASE_ITEM_CLOAK) || + (nBase == BASE_ITEM_CRAFTED_VIAL)) ) return FEAT_CRAFT_WONDROUS; diff --git a/src/include/prc_effect_inc.nss b/src/include/prc_effect_inc.nss index 8a12d42..a41f7d9 100644 --- a/src/include/prc_effect_inc.nss +++ b/src/include/prc_effect_inc.nss @@ -75,6 +75,7 @@ void DeathlessFrenzyCheck(object oTarget); // * PRC Version of a Bioware function to disable include loops void PRCRemoveSpellEffects(int nSpell_ID, object oCaster, object oTarget); + /** * Target is immune to gaze attacks * @@ -89,6 +90,9 @@ effect EffectGazeImmune(); */ effect EffectDazzle(); +//ebonfowl: adding this function to check if a target is already shaken +int GetIsShaken(object oTarget); + /** * Shaken effect: -2 to attack, all skills and saving throws * @@ -177,13 +181,9 @@ effect EffectAbilityBasedSkillIncrease(int iAbility, int iIncrease = 1); */ effect EffectAbilityBasedSkillDecrease(int iAbility, int iDecrease = 1); -//ebonfowl: adding this function to check if a target is already shaken -int GetIsShaken(object oTarget); - ////////////////////////////////////////////////// /* Include section */ ////////////////////////////////////////////////// - #include "prc_inc_castlvl" // get prc_racial_const, prc_inc_nwscript, prc_inc_newip #include "inc_epicspelldef" @@ -269,6 +269,8 @@ object GetObjectToApplyNewEffect(string sTag, object oPC, int nStripEffects = TR SetCreatureAppearanceType(oWP, APPEARANCE_TYPE_INVISIBLE_HUMAN_MALE); ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), oWP); ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneGhost(), oWP); + AssignCommand(oWP, ActionUseSkill(SKILL_HIDE, oWP)); + } //remove previous effects if(nStripEffects) @@ -802,5 +804,11 @@ int GetIsShaken(object oTarget) return FALSE; } +// Forward declarations for size change effects +// Implementations are in prc_inc_size +effect EffectSizeChange(object oTarget, int nObjectType, int bEnlarge, int nChanges); +void DelayedSetVisualTransform(int nExpectedGeneration, object oTarget, int nTransform, float fValue); +void DelaySetVisualTransform(float fDelay, object oTarget, string sGenerationName, int nTransform, float fValue); + //:: Test void -//:: void main() {} \ No newline at end of file +//::void main() {} \ No newline at end of file diff --git a/src/include/prc_feat_const.nss b/src/include/prc_feat_const.nss index 8035960..6fd8c94 100644 --- a/src/include/prc_feat_const.nss +++ b/src/include/prc_feat_const.nss @@ -4,6 +4,11 @@ //:: PRC Options Conversation const int FEAT_OPTIONS_CONVERSATION = 2285; +//;; Builder Feats +const int FEAT_ARCHETYPAL_FORM = 2918; +const int FEAT_INTRINSIC_ARMOR = 25990; +const int FEAT_INTRINSIC_WEAPON = 25991; + //:: Missing Bioware Feats const int FEAT_EPIC_PLANAR_TURNING = 854; @@ -219,6 +224,9 @@ const int FEAT_MAGICAL_ARTISAN_CREATE_INFUSION = 25961; const int FEAT_PLANT_DEFIANCE = 25992; const int FEAT_PLANT_CONTROL = 25993; + //:: Lost Empires of Faerun feats +const int FEAT_CRAFT_SCEPTER = 25962; +const int FEAT_MAGICAL_ARTISAN_CRAFT_SCEPTER = 25963; //:: Racial Feats const int FEAT_WEMIC_JUMP_8 = 4518; @@ -1314,6 +1322,7 @@ const int FEAT_SOMATIC_WEAPONRY = 5186; // Forgotten Realms Campaign Setting const int FEAT_INSCRIBE_RUNE = 2462; +const int EPIC_FEAT_INSCRIBE_EPIC_RUNES = 2549; // Miniature Handbook const int FEAT_SHIELDMATE = 3258; @@ -3726,6 +3735,9 @@ const int FEAT_EPIC_ARTIFICER = 4072; //////////////// END INFUSIONS ///////////////// //////////////////////////////////////////////////*/ +//:: Monk +const int FEAT_MONK_ABUNDANT_STEP = 2351; + //Justice of Weald and Woe const int FEAT_LUCKY_SHOT = 24021; @@ -6275,6 +6287,7 @@ const int FEAT_DSONG_SPELLCASTING_NONE = 19592; const int FEAT_OLLAM_SPELLCASTING_NONE = 19593; //:: PRC8 Hidden Talent Feats +const int FEAT_HIDDEN_TALENT = 25900; const int FEAT_HIDDEN_TALENT_BIOFEEDBACK = 25901; const int FEAT_HIDDEN_TALENT_BITE_WOLF = 25902; const int FEAT_HIDDEN_TALENT_BOLT = 25903; diff --git a/src/include/prc_inc_breath.nss b/src/include/prc_inc_breath.nss index 0d439f9..094029c 100644 --- a/src/include/prc_inc_breath.nss +++ b/src/include/prc_inc_breath.nss @@ -124,6 +124,8 @@ void ApplyBreath(struct breath BreathUsed, location lTargetArea, int bLinger = F ////////////////////////////////////////////////// #include "prc_alterations" +#include "prcsp_archmaginc" +#include "prc_inc_spells" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/prc_inc_chat_pow.nss b/src/include/prc_inc_chat_pow.nss index aced79a..f1c1b64 100644 --- a/src/include/prc_inc_chat_pow.nss +++ b/src/include/prc_inc_chat_pow.nss @@ -16,6 +16,7 @@ Command summary: */ #include "prc_inc_chat" +#include "inc_persist_loca" const string CMD_POWER_ATTACK = "pow-erattack"; diff --git a/src/include/prc_inc_clsfunc.nss b/src/include/prc_inc_clsfunc.nss index 4d1ffcb..9bd42ed 100644 --- a/src/include/prc_inc_clsfunc.nss +++ b/src/include/prc_inc_clsfunc.nss @@ -380,6 +380,7 @@ int Vile_Feat(int iTypeWeap) case BASE_ITEM_BASTARDSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_BASTARDSWORD); case BASE_ITEM_BATTLEAXE: return GetHasFeat(FEAT_VILE_MARTIAL_BATTLEAXE); case BASE_ITEM_CLUB: return GetHasFeat(FEAT_VILE_MARTIAL_CLUB); + case BASE_ITEM_CRAFTED_SCEPTER: return GetHasFeat(FEAT_VILE_MARTIAL_CLUB); case BASE_ITEM_DAGGER: return GetHasFeat(FEAT_VILE_MARTIAL_DAGGER); case BASE_ITEM_DART: return GetHasFeat(FEAT_VILE_MARTIAL_DART); case BASE_ITEM_DIREMACE: return GetHasFeat(FEAT_VILE_MARTIAL_DIREMACE); @@ -426,7 +427,7 @@ int Vile_Feat(int iTypeWeap) GetHasFeat(FEAT_VILE_MARTIAL_RAPIER) || GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_THINBLADE)); - case BASE_ITEM_ELVEN_COURTBLADE: return GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD || + case BASE_ITEM_ELVEN_COURTBLADE: return (GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD) || GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_COURTBLADE)); case BASE_ITEM_DOUBLE_SCIMITAR: return GetHasFeat(FEAT_VILE_MARTIAL_DBL_SCIMITAR); @@ -461,6 +462,7 @@ int GetSanctifedMartialFeat(int iTypeWeap) case BASE_ITEM_BASTARDSWORD: return FEAT_SANCTIFY_MARTIAL_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_SANCTIFY_MARTIAL_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_SANCTIFY_MARTIAL_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_SANCTIFY_MARTIAL_CLUB; case BASE_ITEM_DAGGER: return FEAT_SANCTIFY_MARTIAL_DAGGER; case BASE_ITEM_DART: return FEAT_SANCTIFY_MARTIAL_DART; case BASE_ITEM_DIREMACE: return FEAT_SANCTIFY_MARTIAL_DIREMACE; @@ -535,6 +537,7 @@ int Sanctify_Feat(int iTypeWeap) case BASE_ITEM_BASTARDSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_BASTARDSWORD); case BASE_ITEM_BATTLEAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_BATTLEAXE); case BASE_ITEM_CLUB: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_CLUB); + case BASE_ITEM_CRAFTED_SCEPTER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_CLUB); case BASE_ITEM_DAGGER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DAGGER); case BASE_ITEM_DART: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DART); case BASE_ITEM_DIREMACE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DIREMACE); diff --git a/src/include/prc_inc_combat.nss b/src/include/prc_inc_combat.nss index df4a5b7..6b143b5 100644 --- a/src/include/prc_inc_combat.nss +++ b/src/include/prc_inc_combat.nss @@ -1082,7 +1082,7 @@ int GetIsTwoHandedMeleeWeaponType(int iWeaponType) case BASE_ITEM_HEAVYFLAIL: return TRUE; case BASE_ITEM_SCYTHE: return TRUE; case BASE_ITEM_QUARTERSTAFF: return TRUE; - case BASE_ITEM_MAGICSTAFF: return TRUE; + //case BASE_ITEM_MAGICSTAFF: return TRUE; case BASE_ITEM_ELVEN_COURTBLADE: return TRUE; case BASE_ITEM_MAUL: return TRUE; case BASE_ITEM_FALCHION: return TRUE; @@ -1141,8 +1141,8 @@ int GetIsSimpleWeaponType(int iWeaponType) case BASE_ITEM_CSLSHPRCWEAP: return 1; case BASE_ITEM_GLOVES: return 1; case BASE_ITEM_BRACER: return 1; - - case BASE_ITEM_CLUB: return 2; + case BASE_ITEM_CRAFTED_SCEPTER: return 1; + case BASE_ITEM_DAGGER: return 2; case BASE_ITEM_LIGHTMACE: return 2; case BASE_ITEM_SICKLE: return 2; @@ -1279,6 +1279,19 @@ struct WeaponFeat GetAllFeatsOfWeaponType(int iWeaponType) sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_CLUB; break; } + case BASE_ITEM_CRAFTED_SCEPTER: { + sFeat.Focus = FEAT_WEAPON_FOCUS_CLUB; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_CLUB; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_CLUB; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_CLUB; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_CLUB; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_CLUB; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_CLUB; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_CLUB; + break; + } case BASE_ITEM_DAGGER: { sFeat.Focus = FEAT_WEAPON_FOCUS_DAGGER; sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_DAGGER; @@ -8090,7 +8103,12 @@ void AttackLoopLogic(object oDefender, object oAttacker, if (DEBUG) DoDebug("entered AttackLoopLogic: bFirstAttack = " + IntToString(bFirstAttack) + ", cleave = " + IntToString(bIsCleaveAttack) + ", current action = " + GetActionName(iAction)); if (DEBUG) DoDebug("AttackLoopLogic: iMainAttacks = " + IntToString(iMainAttacks) + ", iOffHandAttacks = " + IntToString(iOffHandAttacks) + ", iBonusAttacks = " + IntToString(iBonusAttacks)); - int bIsRangedAttack = sAttackVars.bIsRangedWeapon || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED; + //int bIsRangedAttack = sAttackVars.bIsRangedWeapon || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED; + + int bIsRangedAttack = sAttackVars.bIsRangedWeapon || + sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL || + sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED || + GetLocalInt(oAttacker, "WhirlingBlade"); // check for valid target etc., but only if it is not a cleave or circle kick (in this case we checked all of this before) if (!bIsCleaveAttack) diff --git a/src/include/prc_inc_combmove.nss b/src/include/prc_inc_combmove.nss index d280dd1..180925b 100644 --- a/src/include/prc_inc_combmove.nss +++ b/src/include/prc_inc_combmove.nss @@ -273,6 +273,7 @@ void TigerBlooded(object oInitiator, object oTarget); #include "prc_inc_combat" #include "prc_inc_sp_tch" +#include "prc_feat_const" ////////////////////////////////////////////////// /* Internal functions */ @@ -1321,7 +1322,29 @@ int DoTrip(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, 0, 0, 0, "Improved Trip Free Attack Hit", "Improved Trip Free Attack Miss")); } } - else // If you fail, enemy gets a counter trip attempt, using Strength + else // If you fail, enemy gets a counter trip attempt, using Strength + { + if(!nCounterTrip) + { + nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_TRIP, FALSE, TRUE); + FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE); + // Roll counter trip attempt + nTargetCheck = nTargetStat + nTargetBonus + d20(); + nPCCheck = nPCStat + nPCBonus + d20(); + // If counters aren't allowed, don't knock em down + // Its down here to allow the text message to go through + SendMessageToPC(oPC, "Enemy Counter Trip Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + + SetLocalInt(oPC, "TripDifference", nTargetCheck - nPCCheck); + DelayCommand(2.0, DeleteLocalInt(oPC, "TripDifference")); + } + if (nTargetCheck >= nPCCheck && nCounterTrip) + { + // Knock em down + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectKnockdown()), oPC, 6.0); + } + } +/* else // If you fail, enemy gets a counter trip attempt, using Strength { nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_TRIP, FALSE, TRUE); FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE); @@ -1338,7 +1361,7 @@ int DoTrip(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, } SetLocalInt(oPC, "TripDifference", nTargetCheck - nPCCheck); DelayCommand(2.0, DeleteLocalInt(oPC, "TripDifference")); - } + } */ } else FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE); @@ -1941,10 +1964,21 @@ void TigerBlooded(object oInitiator, object oTarget) int DoDisarm(object oPC, object oTarget, int nExtraBonus = 0, int nGenerateAoO = TRUE, int nCounter = TRUE) { object oTargetWep = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + + int bNoDisarm = GetHasFeat(FEAT_INTRINSIC_WEAPON, oTarget); + + string sName = GetName(oTarget); + + if(bNoDisarm) + { + FloatingTextStringOnCreature(sName+" is wielding an intrinsic weapon", oPC, FALSE); + AssignCommand(oPC, ActionAttack(oTarget)); + return FALSE; + } if (!GetIsObjectValid(oTargetWep) || GetPlotFlag(oTargetWep) || (!GetIsCreatureDisarmable(oTarget) && !GetPRCSwitch(PRC_PNP_DISARM)) || GetLocalInt(oTarget, "TigerFangDisarm")) { - FloatingTextStringOnCreature("Target is not a legal target", oPC, FALSE); + FloatingTextStringOnCreature(sName+" is not a legal target", oPC, FALSE); AssignCommand(oPC, ActionAttack(oTarget)); return FALSE; } diff --git a/src/include/prc_inc_core.nss b/src/include/prc_inc_core.nss index 91c5ba1..df73451 100644 --- a/src/include/prc_inc_core.nss +++ b/src/include/prc_inc_core.nss @@ -133,6 +133,7 @@ const int METAMAGIC_QUICKEN_LEVEL = 4; #include "prc_inc_damage" #include "prc_inc_sb_const" // Spell Book Constants #include "x0_i0_position" +#include "inc_newspellbook" /* access to prc_inc_nwscript via prc_inc_damage diff --git a/src/include/prc_inc_descrptr.nss b/src/include/prc_inc_descrptr.nss index bbee255..d814a6c 100644 --- a/src/include/prc_inc_descrptr.nss +++ b/src/include/prc_inc_descrptr.nss @@ -169,8 +169,8 @@ int GetSubschoolFlags(int nSpellID); /* Includes */ ////////////////////////////////////////////////// -#include "inc_2dacache" // already has access via inc_utility -//#include "inc_utility" +#include "inc_2dacache" +#include "inc_utility" ////////////////////////////////////////////////// /* Function definitions */ diff --git a/src/include/prc_inc_factotum.nss b/src/include/prc_inc_factotum.nss index 46be9c4..eee86b1 100644 --- a/src/include/prc_inc_factotum.nss +++ b/src/include/prc_inc_factotum.nss @@ -49,6 +49,8 @@ const int BRILLIANCE_SLOT_3 = 3919; ////////////////////////////////////////////////// /* Function definitions */ ////////////////////////////////////////////////// +void TriggerInspiration(object oPC, int nCombat); + void PrepareArcDilSpell(object oPC, int nSpell) { @@ -188,7 +190,8 @@ void SetInspiration(object oPC) for(i = FEAT_FONT_INSPIRATION_1; i <= FEAT_FONT_INSPIRATION_10; i++) if(GetHasFeat(i, oPC)) nFont++; - nInspiration += nFont * (1 + nFont + 1) / 2; + //nInspiration += nFont * (1 + nFont + 1) / 2; + nInspiration += nFont * (nFont + 1) / 2; SetLocalInt(oPC, "InspirationPool", nInspiration); FloatingTextStringOnCreature("Encounter begins with "+IntToString(nInspiration)+" inspiration", oPC, FALSE); } @@ -201,6 +204,8 @@ void ClearInspiration(object oPC) int ExpendInspiration(object oPC, int nCost) { + if (nCost <= 0) return FALSE; + int nInspiration = GetLocalInt(oPC, "InspirationPool"); if (nInspiration >= nCost) { @@ -261,6 +266,21 @@ void FactotumTriggerAbil(object oPC, int nAbil) IPSafeAddItemProperty(oSkin, ipIP, 60.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); } +void TriggerInspiration(object oPC, int nCombat) +{ + SetLocalInt(oPC, "InspirationHBRunning", TRUE); + DelayCommand(0.249, DeleteLocalInt(oPC, "InspirationHBRunning")); + int nCurrent = GetIsInCombat(oPC); + // We just entered combat + if (nCurrent == TRUE && nCombat == FALSE) + SetInspiration(oPC); + else if (nCurrent == FALSE && nCombat == TRUE) // Just left combat + ClearInspiration(oPC); + + DelayCommand(0.25, TriggerInspiration(oPC, nCurrent)); +} + + /*void AddCunningBrillianceAbility(object oPC, int nAbil) { if (DEBUG) DoDebug("AddCunningBrillianceAbility "+IntToString(nAbil)); diff --git a/src/include/prc_inc_fork.nss b/src/include/prc_inc_fork.nss index 5cefd1d..3b82eeb 100644 --- a/src/include/prc_inc_fork.nss +++ b/src/include/prc_inc_fork.nss @@ -231,6 +231,7 @@ int GetProficiencyFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD; case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_PROFICIENCY_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_WEAPON_PROFICIENCY_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_WEAPON_PROFICIENCY_CLUB; case BASE_ITEM_DAGGER: return FEAT_WEAPON_PROFICIENCY_DAGGER; case BASE_ITEM_DART: return FEAT_WEAPON_PROFICIENCY_DART; case BASE_ITEM_DIREMACE: return FEAT_WEAPON_PROFICIENCY_DIRE_MACE; @@ -304,6 +305,7 @@ int GetFocusFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_FOCUS_BASTARD_SWORD; case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_FOCUS_BATTLE_AXE; case BASE_ITEM_CLUB: return FEAT_WEAPON_FOCUS_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_WEAPON_FOCUS_CLUB; case BASE_ITEM_DAGGER: return FEAT_WEAPON_FOCUS_DAGGER; case BASE_ITEM_DART: return FEAT_WEAPON_FOCUS_DART; case BASE_ITEM_DIREMACE: return FEAT_WEAPON_FOCUS_DIRE_MACE; @@ -375,6 +377,7 @@ int GetSpecializationFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD; case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE; case BASE_ITEM_CLUB: return FEAT_WEAPON_SPECIALIZATION_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_WEAPON_SPECIALIZATION_CLUB; case BASE_ITEM_DAGGER: return FEAT_WEAPON_SPECIALIZATION_DAGGER; case BASE_ITEM_DART: return FEAT_WEAPON_SPECIALIZATION_DART; case BASE_ITEM_DIREMACE: return FEAT_WEAPON_SPECIALIZATION_DIRE_MACE; @@ -446,6 +449,7 @@ int GetEpicFocusFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_WEAPON_FOCUS_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_WEAPON_FOCUS_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_EPIC_WEAPON_FOCUS_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_EPIC_WEAPON_FOCUS_CLUB; case BASE_ITEM_DAGGER: return FEAT_EPIC_WEAPON_FOCUS_DAGGER; case BASE_ITEM_DART: return FEAT_EPIC_WEAPON_FOCUS_DART; case BASE_ITEM_DIREMACE: return FEAT_EPIC_WEAPON_FOCUS_DIREMACE; @@ -517,6 +521,7 @@ int GetEpicSpecializationFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_WEAPON_SPECIALIZATION_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_WEAPON_SPECIALIZATION_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB; case BASE_ITEM_DAGGER: return FEAT_EPIC_WEAPON_SPECIALIZATION_DAGGER; case BASE_ITEM_DART: return FEAT_EPIC_WEAPON_SPECIALIZATION_DART; case BASE_ITEM_DIREMACE: return FEAT_EPIC_WEAPON_SPECIALIZATION_DIREMACE; @@ -588,6 +593,7 @@ int GetImprovedCriticalFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_IMPROVED_CRITICAL_BASTARD_SWORD; case BASE_ITEM_BATTLEAXE: return FEAT_IMPROVED_CRITICAL_BATTLE_AXE; case BASE_ITEM_CLUB: return FEAT_IMPROVED_CRITICAL_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_IMPROVED_CRITICAL_CLUB; case BASE_ITEM_DAGGER: return FEAT_IMPROVED_CRITICAL_DAGGER; case BASE_ITEM_DART: return FEAT_IMPROVED_CRITICAL_DART; case BASE_ITEM_DIREMACE: return FEAT_IMPROVED_CRITICAL_DIRE_MACE; @@ -659,6 +665,7 @@ int GetOverwhelmingCriticalFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_OVERWHELMING_CRITICAL_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_OVERWHELMING_CRITICAL_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB; case BASE_ITEM_DAGGER: return FEAT_EPIC_OVERWHELMING_CRITICAL_DAGGER; case BASE_ITEM_DART: return FEAT_EPIC_OVERWHELMING_CRITICAL_DART; case BASE_ITEM_DIREMACE: return FEAT_EPIC_OVERWHELMING_CRITICAL_DIREMACE; @@ -730,6 +737,7 @@ int GetDevastatingCriticalFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_DEVASTATING_CRITICAL_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_DEVASTATING_CRITICAL_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_EPIC_DEVASTATING_CRITICAL_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_EPIC_DEVASTATING_CRITICAL_CLUB; case BASE_ITEM_DAGGER: return FEAT_EPIC_DEVASTATING_CRITICAL_DAGGER; case BASE_ITEM_DART: return FEAT_EPIC_DEVASTATING_CRITICAL_DART; case BASE_ITEM_DIREMACE: return FEAT_EPIC_DEVASTATING_CRITICAL_DIREMACE; @@ -796,6 +804,7 @@ int GetWeaponOfChoiceFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_OF_CHOICE_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_OF_CHOICE_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_WEAPON_OF_CHOICE_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_WEAPON_OF_CHOICE_CLUB; case BASE_ITEM_DAGGER: return FEAT_WEAPON_OF_CHOICE_DAGGER; case BASE_ITEM_DIREMACE: return FEAT_WEAPON_OF_CHOICE_DIREMACE; case BASE_ITEM_DOUBLEAXE: return FEAT_WEAPON_OF_CHOICE_DOUBLEAXE; diff --git a/src/include/prc_inc_function.nss b/src/include/prc_inc_function.nss index f1a0fea..a3b24e5 100644 --- a/src/include/prc_inc_function.nss +++ b/src/include/prc_inc_function.nss @@ -431,7 +431,7 @@ void EvalPRCFeats(object oPC) ExecuteScript("moi_events", oPC); if (GetIsBinder(oPC)) - ExecuteScript("bnd_events", oPC); + ExecuteScript("bnd_events", oPC); // check if character with crafting feat has appropriate base item in her inventory // x - moved from prc_onhb_indiv.nss diff --git a/src/include/prc_inc_hextor.nss b/src/include/prc_inc_hextor.nss index affe360..531ba9a 100644 --- a/src/include/prc_inc_hextor.nss +++ b/src/include/prc_inc_hextor.nss @@ -1,4 +1,6 @@ #include "prc_feat_const" +#include "inc_item_props" +#include "prc_inc_spells" const string BRUTAL_STRIKE_MODE_VAR = "PRC_BRUTAL_STRIKE_MODE"; diff --git a/src/include/prc_inc_itmrstr.nss b/src/include/prc_inc_itmrstr.nss index 156f530..dd852f0 100644 --- a/src/include/prc_inc_itmrstr.nss +++ b/src/include/prc_inc_itmrstr.nss @@ -36,6 +36,8 @@ void CheckForPnPHolyAvenger(object oItem); #include "inc_utility" #include "prc_inc_newip" +#include "prc_inc_castlvl" +#include "inc_newspellbook" ////////////////////////////////////////////////// diff --git a/src/include/prc_inc_json.nss b/src/include/prc_inc_json.nss index e976ce1..f15c675 100644 --- a/src/include/prc_inc_json.nss +++ b/src/include/prc_inc_json.nss @@ -18,11 +18,56 @@ //::////////////////////////////////////////////// #include "nw_inc_gff" #include "inc_debug" +#include "prc_inc_racial" +#include "prc_inc_nwscript" +#include "prc_inc_spells" +#include "prc_inc_util" +#include "prc_inc_fork" +#include "prc_inc_natweap" +//:: Get a random General feat. +void ApplyParagonBonusFeat(object oCreature, int iFeat); //::---------------------------------------------| //:: Helper functions | //::---------------------------------------------| +int GetHealerCompanionBonus(int nHealerLvl) +{ + // No bonus before 12th level + if (nHealerLvl < 12) + return 0; + + int nBonus = 0; + + // Non-epic improvements: 12, 15, 18, 21 (every 3 levels) + if (nHealerLvl >= 12) + { + int nPreEpicIntervals = ( (nHealerLvl < 21) ? (nHealerLvl - 12) : (21 - 12) ) / 3; + nBonus += 2 + (nPreEpicIntervals * 2); + } + + // Epic improvements: 24, 28, 32, 36... (every 4 levels) + if (nHealerLvl >= 24) + { + int nEpicIntervals = (nHealerLvl - 24) / 4; + // First epic improvement is +2 at 24 + nBonus += 2 + (nEpicIntervals * 2); + } + + return nBonus; +} + +/* int GetHealerCompanionBonus(int nHealerLvl) +{ + if (nHealerLvl < 12) + return 0; + + // Shift so that 12–14 yields interval 0 + int nIntervals = (nHealerLvl - 12) / 3; + + return 2 + (nIntervals * 2); +} */ + //:: Function to calculate the maximum possible hitpoints for oCreature int GetMaxPossibleHP(object oCreature) @@ -30,6 +75,8 @@ int GetMaxPossibleHP(object oCreature) int nMaxHP = 0; // Stores the total maximum hitpoints int i = 1; // Initialize position for class index int nConb = GetAbilityModifier(ABILITY_CONSTITUTION, oCreature); + int nRacial = MyPRCGetRacialType(oCreature); + int nSize = PRCGetCreatureSize(oCreature); // Loop through each class position the creature may have, checking each class in turn while (TRUE) @@ -54,8 +101,25 @@ int GetMaxPossibleHP(object oCreature) i++; } + if(nRacial == RACIAL_TYPE_CONSTRUCT || nRacial == RACIAL_TYPE_UNDEAD) + { + nConb = 0; + } + nMaxHP += nConb * GetHitDice(oCreature); + if(nRacial == RACIAL_TYPE_CONSTRUCT) + { + if(nSize == CREATURE_SIZE_FINE) nMaxHP += 0; + if(nSize == CREATURE_SIZE_DIMINUTIVE) nMaxHP += 0; + if(nSize == CREATURE_SIZE_TINY) nMaxHP += 0; + if(nSize == CREATURE_SIZE_SMALL) nMaxHP += 10; + if(nSize == CREATURE_SIZE_MEDIUM) nMaxHP += 20; + if(nSize == CREATURE_SIZE_LARGE) nMaxHP += 30; + if(nSize == CREATURE_SIZE_HUGE) nMaxHP += 40; + if(nSize == CREATURE_SIZE_GARGANTUAN) nMaxHP += 60; + } + return nMaxHP; } @@ -110,6 +174,515 @@ int GetAbilityModFromValue(int nAbilityValue) return nMod; } +//:: Get a random General feat. +void PickParagonBonusFeat(object oCreature) +{ +//:: Paragon creatures get a +15 to all ability scores, +//:: so can always meet feat pre-reqs. + +//:: Detect spellcasting classes (FOR FUTURE USE) + int i; + for (i = 1; i <= 8; i++) + { + if (GetIsArcaneClass(GetClassByPosition(i, oCreature))) + { + SetLocalInt(oCreature, "ParagonArcaneCaster", 0); + } + if (GetIsDivineClass(GetClassByPosition(i, oCreature))) + { + SetLocalInt(oCreature, "ParagonDivineCaster", 0); + } + } + switch (Random(18)) + { + //:: Dodge -> Mobility -> Spring Attack + case 0: + { + int iDodge = GetHasFeat(FEAT_DODGE, oCreature); + int iMobility = GetHasFeat(FEAT_MOBILITY, oCreature); + int iSpringAttack = GetHasFeat(FEAT_SPRING_ATTACK, oCreature); + + //:: Grant only the first missing feat in the chain + if (iDodge == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_DODGE); + } + else if (iMobility == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_MOBILITY); + } + else if (iSpringAttack == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_SPRING_ATTACK); + } + } + break; + //:: Power Attack -> Cleave -> Imp Power Attack -> Great Cleave + case 1: + { + int iPower = GetHasFeat(FEAT_POWER_ATTACK, oCreature); + int iCleave = GetHasFeat(FEAT_CLEAVE, oCreature); + int iImpPower = GetHasFeat(FEAT_IMPROVED_POWER_ATTACK, oCreature); + int iGrCleave = GetHasFeat(FEAT_GREAT_CLEAVE, oCreature); + + //:: Grant only the first missing feat in the chain + if (iPower == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_POWER_ATTACK); + } + else if (iCleave == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_CLEAVE); + } + else if (iImpPower == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_POWER_ATTACK); + } + else if (iGrCleave == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_GREAT_CLEAVE); + } + } + break; + //:: Expertise -> Imp Expertise -> Whirlwind Attack -> Imp Whirlwind Attack + case 2: + { + int iEx = GetHasFeat(FEAT_EXPERTISE, oCreature); + int iImpEx = GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature); + int iWhirl = GetHasFeat(FEAT_WHIRLWIND_ATTACK, oCreature); + int iImpWhirl = GetHasFeat(FEAT_IMPROVED_WHIRLWIND, oCreature); + + //:: Grant only the first missing feat in the chain + if (iEx == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_EXPERTISE); + } + else if (iImpEx == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_EXPERTISE); + } + else if (iWhirl == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_WHIRLWIND_ATTACK); + } + else if (iImpWhirl == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_WHIRLWIND); + } + } + break; + //:: Disarm -> Expertise -> Improved Disarm -> Imp Expertise + case 3: + { + int iDisarm = GetHasFeat(FEAT_DISARM, oCreature); + int iEx = GetHasFeat(FEAT_EXPERTISE, oCreature); + int iImpDisarm = GetHasFeat(FEAT_IMPROVED_DISARM, oCreature); + int iImpEx = GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature); + + //:: Grant only the first missing feat in the chain + if (iDisarm == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_DISARM); + } + else if (iEx == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_EXPERTISE); + } + else if (iImpDisarm == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_DISARM); + } + else if (iImpEx == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_EXPERTISE); + } + } + break; + //:: Toughness + case 4: + { + ApplyParagonBonusFeat(oCreature, FEAT_TOUGHNESS); + } + break; + //:: Great Fortitude + case 5: + { + ApplyParagonBonusFeat(oCreature, FEAT_GREAT_FORTITUDE); + } + break; + //:: Lightining Reflexes + case 6: + { + ApplyParagonBonusFeat(oCreature, FEAT_LIGHTNING_REFLEXES); + } + break; + //:: Iron Will -> Unnatural Will + case 7: + { + int iIronWill = GetHasFeat(FEAT_IRON_WILL, oCreature); + int iUnnaturalWill = GetHasFeat(FEAT_UNNATURAL_WILL, oCreature); + + //:: Grant only the first missing feat in the chain + if (iIronWill == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IRON_WILL); + } + else if (iUnnaturalWill == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_UNNATURAL_WILL); + } + } + break; + //:: Blind-Fight + case 8: + { + ApplyParagonBonusFeat(oCreature, FEAT_BLIND_FIGHT); + } + break; + //:: Improved Initiative + case 9: + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_INITIATIVE); + } + break; + //:: Alertness + case 10: + { + ApplyParagonBonusFeat(oCreature, FEAT_ALERTNESS); + } + break; + //:: Blooded + case 11: + { + ApplyParagonBonusFeat(oCreature, FEAT_BLOODED); + } + break; + //:: Side-step Charge + case 12: + { + ApplyParagonBonusFeat(oCreature, FEAT_SIDESTEP_CHARGE); + } + break; + //:: Thug + case 13: + { + ApplyParagonBonusFeat(oCreature, FEAT_THUG); + } + break; + //:: Dive for Cover + case 14: + { + ApplyParagonBonusFeat(oCreature, FEAT_DIVE_FOR_COVER); + } + break; + //:: Endurance -> Strong Stomach + case 15: + { + int iEndurance = GetHasFeat(FEAT_ENDURANCE, oCreature); + int iStrStomach = GetHasFeat(FEAT_STRONG_STOMACH, oCreature); + + //:: Grant only the first missing feat in the chain + if (iEndurance == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_ENDURANCE); + } + else if (iStrStomach == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_STRONG_STOMACH); + } + } + break; + //:: Resist Disease + case 16: + { + ApplyParagonBonusFeat(oCreature, FEAT_RESIST_DISEASE); + } + break; + //:: Resist Poison + case 17: + { + ApplyParagonBonusFeat(oCreature, FEAT_RESIST_POISON); + } + break; + } +} + +//:: Check & apply the feat using EffectBonusFeat if it +//:: doesn't exist on the creature already +void ApplyParagonBonusFeat(object oCreature, int iFeat) +{ + // If the creature does not already have the feat, apply it + if (!GetHasFeat(iFeat, oCreature)) + { + effect eFeat = EffectBonusFeat(iFeat); + effect eLink = UnyieldingEffect(eFeat); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oCreature); + } + else + { + DelayCommand(0.0f, PickParagonBonusFeat(oCreature)); + } +} + +//:: Apply Paragon effects to a non-PC creature +void ApplyParagonEffects(object oCreature, int nBaseHD, int nBaseCR) +{ +//:: Declare major variables + int nNewCR; + + effect eParagon; + +//:: Set maximum hit points for each HD + int nParagonHP = (GetMaxPossibleHP(oCreature) + (nBaseHD * GetAbilityModifier(ABILITY_CONSTITUTION, oCreature))); + SetCurrentHitPoints(oCreature, nParagonHP); + +//:: Tripling the speed for all movement types + eParagon = EffectLinkEffects(eParagon, EffectMovementSpeedIncrease(300)); + +//:: +25 luck bonus on all attack rolls + eParagon = EffectLinkEffects(eParagon, EffectAttackIncrease(25)); + +//:: +20 luck bonus on damage rolls for melee and thrown ranged attacks + eParagon = EffectLinkEffects(eParagon, EffectDamageIncrease(20)); + +//:: AC Bonuses: +12 insight, +12 luck + eParagon = EffectLinkEffects(eParagon, EffectACIncrease(12, AC_DODGE_BONUS)); + eParagon = EffectLinkEffects(eParagon, EffectACIncrease(12, AC_DEFLECTION_BONUS)); + +//:: Boost caster & SLA level by 15 + SetLocalInt(oCreature, PRC_CASTERLEVEL_ADJUSTMENT, 15); + +//:: Fire and cold resistance 10, or keep the higher existing resistance if applicable + eParagon = EffectLinkEffects(eParagon, EffectDamageResistance(DAMAGE_TYPE_FIRE, 10)); + eParagon = EffectLinkEffects(eParagon, EffectDamageResistance(DAMAGE_TYPE_COLD, 10)); + +//:: Damage Reduction 20/epic or retain existing DR if higher + eParagon = EffectLinkEffects(eParagon, EffectDamageReduction(20, DAMAGE_POWER_ENERGY)); + +//:: Spell Resistance equal to CR +10, or retain existing SR if higher + int iExSR = GetSpellResistance(oCreature); + int nSpellResistance; + + if (iExSR < nBaseCR + 10) + { + nSpellResistance = nBaseCR + 10; + } + else + { + nSpellResistance = 0; + } + + eParagon = EffectLinkEffects(eParagon, EffectSpellResistanceIncrease(nSpellResistance)); + +//:: Fast Healing 20 + eParagon = EffectLinkEffects(eParagon, EffectRegenerate(20, 6.0f)); + +//:: Saving Throws: +10 insight bonus on all saving throws + eParagon = EffectLinkEffects(eParagon, EffectSavingThrowIncrease(SAVING_THROW_ALL, 10)); + +//:: Skills: +10 competence bonus to all skill checks + int nSkillID = 0; + + while (TRUE) + { + //:: Get & check skill + string sSkillLabel = Get2DACache("skills", "Label", nSkillID); + + //:: Break when out of skills + if (sSkillLabel == "") + break; + + //:: Apply the skill increase effect for the current skill + eParagon = EffectLinkEffects(eParagon, EffectSkillIncrease(nSkillID, 10)); + + + //:: Move to the next skill ID + nSkillID++; + } + +//:: Two free general feats. + PickParagonBonusFeat(oCreature); + PickParagonBonusFeat(oCreature); + + eParagon = UnyieldingEffect(eParagon); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eParagon, oCreature); +} + +// Build and return all effects for the Celestial Template +effect CelestialTemplateEffects(int nHD) +{ + int nResist; + int nDRAmount; + int nDRBypass; + + // ------------------------- + // Elemental Resistances + // ------------------------- + // 1–7 HD = 5 + // 8+ HD = 10 + if (nHD >= 8) + { + nResist = 10; + } + else + { + nResist = 5; + } + + // ------------------------- + // Damage Reduction + // ------------------------- + // 1–3 HD = none + // 4–11 HD = 5/magic + // 12+ HD = 10/magic + if (nHD >= 12) + { + nDRAmount = 10; + nDRBypass = DAMAGE_POWER_PLUS_ONE; // DR 10/magic + } + else if (nHD >= 4) + { + nDRAmount = 5; + nDRBypass = DAMAGE_POWER_PLUS_ONE; // DR 5/magic + } + else + { + nDRAmount = 0; + nDRBypass = 0; // no DR + } + + // ------------------------- + // Build Effects + // ------------------------- + effect eEffects; + effect eRes; + + // Acid + eRes = EffectDamageResistance(DAMAGE_TYPE_ACID, nResist, 0); + eEffects = eRes; + + // Cold + eRes = EffectDamageResistance(DAMAGE_TYPE_COLD, nResist, 0); + eEffects = EffectLinkEffects(eEffects, eRes); + + // Electricity + eRes = EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nResist, 0); + eEffects = EffectLinkEffects(eEffects, eRes); + + // DR if any + if (nDRAmount > 0) + { + effect eDR = EffectDamageReduction(nDRAmount, nDRBypass, 0); + eEffects = EffectLinkEffects(eEffects, eDR); + } + + eEffects = UnyieldingEffect(eEffects); + + return eEffects; +} + + + +void ReallyEquipItemInSlot(object oNPC, object oItem, int nSlot) +{ + if (GetItemInSlot(nSlot) != oItem) + { + //ClearAllActions(); + AssignCommand(oNPC, ActionEquipItem(oItem, nSlot)); + DelayCommand(0.5, ReallyEquipItemInSlot(oNPC, oItem, nSlot)); + } +} + +// Get the size of a JSON array +int GetJsonArraySize(json jArray) +{ + int iSize = 0; + while (JsonArrayGet(jArray, iSize) != JsonNull()) + { + iSize++; + } + return iSize; +} + +int CheckForWeapon(object oCreature) +{ + if (GetIsWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature)) == 1 || GetIsWeapon(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature)) == 1) + { + // oCreature has a weapon in at least one hand + return TRUE; + } + else + { + // oCreature doesn't have a weapon in either hand + return FALSE; + } +} + +//:: Adds Psuedonatural resistances & DR. +void ApplyPseudonaturalEffects(object oCreature) +{ + if(!GetIsObjectValid(oCreature)) return; + + int nHD = GetHitDice(oCreature); + if(DEBUG) DoDebug("prc_inc_json >> ApplyPseudonaturalEffects: nHD is: "+IntToString(nHD)+"."); + // ------------------------- + // Spell Resistance + // SR = 10 + HD (max 25) + // ------------------------- + int nSR = 10 + nHD; + if(nSR > 25) nSR = 25; + + effect eSR = EffectSpellResistanceIncrease(nSR); + eSR = TagEffect(eSR, "PSEUDO_SR"); + eSR = EffectLinkEffects(eSR, UnyieldingEffect(eSR)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSR, oCreature); + + // ------------------------- + // Acid/Electricity Resistance + // Reference Table: + // HD 1–3 : Resist 5 + // HD 4–7 : Resist 5 + // HD 8–11 : Resist 10 + // HD >=12 : Resist 15 + // ------------------------- + int nResist; + + if(nHD <= 7) nResist = 5; + else if(nHD <=11) nResist = 10; + else nResist = 15; + + effect eResAcid = EffectDamageResistance(DAMAGE_TYPE_ACID, nResist); + eResAcid = TagEffect(eResAcid, "PSEUDO_RES_ACID"); + eResAcid = EffectLinkEffects(eResAcid, UnyieldingEffect(eResAcid)); + + effect eResElec = EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nResist); + eResElec = TagEffect(eResElec, "PSEUDO_RES_ELEC"); + eResElec = EffectLinkEffects(eResElec, UnyieldingEffect(eResElec)); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eResAcid, oCreature); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eResElec, oCreature); + + // ------------------------- + // Damage Reduction + // Reference Table: + // HD 1–3 : none + // HD 4–7 : DR 5 / magic + // HD 8–11 : DR 5 / magic + // HD >=12 : DR 10 / magic + // ------------------------- + + int nDR; + if(nHD <= 3) { nDR = 0; } + else if(nHD <= 11) { nDR = 5; } + else { nDR = 10; } + + effect eDR = EffectDamageReduction(nDR, DAMAGE_POWER_PLUS_ONE, 0, FALSE); + eDR = TagEffect(eDR, "PSEUDO_DR_MAGIC"); + eDR = EffectLinkEffects(eDR, UnyieldingEffect(eDR)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDR, oCreature); +} + //::---------------------------------------------| //:: JSON functions | @@ -129,6 +702,23 @@ int json_GetCONValue(json jCreature) return nCon; } +//:: Returns the Challenge Rating from a GFF creature UTC +float json_GetChallengeRating(json jCreature) +{ + float fCR = 0.25; // default if missing + + if (GffGetFieldExists(jCreature, "ChallengeRating")) + { + json jCR = GffGetFloat(jCreature, "ChallengeRating"); + if (jCR != JsonNull()) + { + fCR = JsonGetFloat(jCR); + } + } + + return fCR; +} + //:: Returns the integer value of a VarTable entry named sVarName, or 0 if not found. int json_GetLocalIntFromVarTable(json jCreature, string sVarName) { @@ -169,6 +759,46 @@ int json_GetLocalIntFromVarTable(json jCreature, string sVarName) return 0; } +//:: Returns the string value of a VarTable entry named sVarName, or "" if not found. +string json_GetLocalStringFromVarTable(json jCreature, string sVarName) +{ + json jVarTable = GffGetList(jCreature, "VarTable"); + if (jVarTable == JsonNull()) + return ""; + + int nCount = JsonGetLength(jVarTable); + int i; + for (i = 0; i < nCount; i++) + { + json jEntry = JsonArrayGet(jVarTable, i); + if (jEntry == JsonNull()) continue; + + // Get the Name field using GFF functions + json jName = GffGetString(jEntry, "Name"); + if (jName == JsonNull()) continue; + string sName = JsonGetString(jName); + + if (sName == sVarName) + { + // Get the Type field to verify it's a string + json jType = GffGetDword(jEntry, "Type"); + if (jType != JsonNull()) + { + int nType = JsonGetInt(jType); + if (nType == 3) // Type 3 = string + { + // Get the Value field using GFF functions + json jValue = GffGetString(jEntry, "Value"); + if (jValue == JsonNull()) return ""; + return JsonGetString(jValue); + } + } + } + } + + return ""; +} + //:: Returns the total Hit Dice from a JSON'd creature GFF. int json_GetCreatureHD(json jCreature) { @@ -198,17 +828,47 @@ int json_GetCreatureHD(json jCreature) return nHD; } - json json_RecalcMaxHP(json jCreature, int iHitDieValue) { int iHD = json_GetCreatureHD(jCreature); - int iCON = json_GetCONValue(jCreature); - int iMod = GetAbilityModFromValue(iCON); + + //:: Retrieve the RacialType field + json jRacialTypeField = JsonObjectGet(jCreature, "Race"); + int nRacialType = JsonGetInt(jRacialTypeField); + + //:: Retrieve the CreatureSize from the creature appearance field + json jAppearanceField = JsonObjectGet(jCreature, "Appearance_Type"); + int nAppearance = JsonGetInt(jAppearanceField); + + int nSize = StringToInt(Get2DAString("appearance", "SizeCategory", nAppearance)); + + //CEP adds other sizes, take them into account too + if(nSize == 20) + nSize = CREATURE_SIZE_DIMINUTIVE; + else if(nSize == 21) + nSize = CREATURE_SIZE_FINE; + else if(nSize == 22) + nSize = CREATURE_SIZE_GARGANTUAN; + else if(nSize == 23) + nSize = CREATURE_SIZE_COLOSSAL; + + int iNewMaxHP = (iHitDieValue * iHD); - int nConBonusHP = iMod * iHD; - int iNewMaxHP = (iHitDieValue * iHD); /* nConBonusHP */ + if(nRacialType == RACIAL_TYPE_CONSTRUCT) + { + if(nSize == CREATURE_SIZE_FINE) iNewMaxHP += 0; + if(nSize == CREATURE_SIZE_DIMINUTIVE) iNewMaxHP += 0; + if(nSize == CREATURE_SIZE_TINY) iNewMaxHP += 0; + if(nSize == CREATURE_SIZE_SMALL) iNewMaxHP += 10; + if(nSize == CREATURE_SIZE_MEDIUM) iNewMaxHP += 20; + if(nSize == CREATURE_SIZE_LARGE) iNewMaxHP += 30; + if(nSize == CREATURE_SIZE_HUGE) iNewMaxHP += 40; + if(nSize == CREATURE_SIZE_GARGANTUAN) iNewMaxHP += 60; + } + + if(DEBUG) DoDebug("prc_inc_json >> json_RecalcMaxHP | New MaxHP is: "+IntToString(iNewMaxHP)+ "."); - //jCreature = GffReplaceShort(jCreature, "MaxHitPoints", iNewMaxHP); + jCreature = GffReplaceShort(jCreature, "MaxHitPoints", iNewMaxHP); jCreature = GffReplaceShort(jCreature, "CurrentHitPoints", iNewMaxHP); jCreature = GffReplaceShort(jCreature, "HitPoints", iNewMaxHP); @@ -221,9 +881,8 @@ json json_RecalcMaxHP(json jCreature, int iHitDieValue) return jCreature; } - //:: Reads ABILITY_TO_INCREASE from creature's VarTable and applies stat boosts based on increased HD -json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifierCap) +json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD) { if (jCreature == JsonNull()) return jCreature; @@ -260,6 +919,8 @@ json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifier } } } + + if(DEBUG) DoDebug("prc_inc_json >> json_ApplyAbilityBoostFromHD: nCurrentTotalHD = "+IntToString(nCurrentTotalHD)+"."); if (nCurrentTotalHD <= 0) { @@ -309,7 +970,7 @@ json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifier // Clamp to valid byte range if (nNewScore < 1) nNewScore = 1; - if (nNewScore > 255) nNewScore = 255; + if (nNewScore > 250) nNewScore = 250; if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Increasing " + sAbilityField + " from " + IntToString(nCurrentScore) + " to " + IntToString(nNewScore)); @@ -365,7 +1026,7 @@ json json_AdjustCreatureSkillByID(json jCreature, int nSkillID, int nMod) // Clamp to valid range if (nNewRank < 0) nNewRank = 0; - if (nNewRank > 255) nNewRank = 255; + if (nNewRank > 127) nNewRank = 127; // Update the rank in the skill struct jSkill = GffReplaceByte(jSkill, "Rank", nNewRank); @@ -424,7 +1085,7 @@ json json_AddFeatsFromCreatureVars(json jCreature, int nOriginalHD) if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Original feat count: " + IntToString(nOriginalFeatCount)); int nAdded = 0; - int i = 1; + int i = 0; int nMaxIterations = 100; // Safety valve int nIterations = 0; @@ -661,7 +1322,6 @@ json json_AddHitDice(json jCreature, int nAmount) // Grab the first class entry json jFirstClass = JsonArrayGet(jClasses, 0); - // Only touch ClassLevel; do NOT modify Class type json jCurrentLevel = GffGetShort(jFirstClass, "ClassLevel"); int nCurrentLevel = JsonGetInt(jCurrentLevel); int nNewLevel = nCurrentLevel + nAmount; @@ -679,7 +1339,7 @@ json json_AddHitDice(json jCreature, int nAmount) } //:: Adjusts a creature's size by nSizeChange (-4 to +4) and updates ability scores accordingly. -json json_AdjustCreatureSize(json jCreature, int nSizeDelta) +json json_AdjustCreatureSize(json jCreature, int nSizeDelta, int nIncorporeal = FALSE) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Entering function. nSizeDelta=" + IntToString(nSizeDelta)); @@ -713,6 +1373,11 @@ json json_AdjustCreatureSize(json jCreature, int nSizeDelta) int conMod = nSteps * 2; int naturalAC = nSteps * 1; int dexSkillMod = nSteps * -2; + + if(nIncorporeal) + { + strMod = 0; + } if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Applying stat modifiers: STR=" + IntToString(strMod) + " DEX=" + IntToString(dexMod) + @@ -817,16 +1482,16 @@ json json_AdjustCreatureSize(json jCreature, int nSizeDelta) } //:: Changes jCreature's creature type. -json JsonModifyRacialType(json jCreature, int nNewRacialType) +json json_ModifyRacialType(json jCreature, int nNewRacialType) { - if(DEBUG)DoDebug("prc_inc_function >> JsonModifyRacialType: Entering function"); + if(DEBUG)DoDebug("prc_inc_json >> json_ModifyRacialType: Entering function"); // Retrieve the RacialType field json jRacialTypeField = JsonObjectGet(jCreature, "Race"); if (JsonGetType(jRacialTypeField) == JSON_TYPE_NULL) { - DoDebug("prc_inc_function >> JsonModifyRacialType: JsonGetType error 1: " + JsonGetError(jRacialTypeField)); + DoDebug("prc_inc_json >> json_ModifyRacialType: JsonGetType error 1: " + JsonGetError(jRacialTypeField)); //SpeakString("JsonGetType error 1: " + JsonGetError(jRacialTypeField)); return JsonNull(); } @@ -836,7 +1501,7 @@ json JsonModifyRacialType(json jCreature, int nNewRacialType) if (JsonGetType(jRacialTypeValue) != JSON_TYPE_INTEGER) { - DoDebug("prc_inc_function >> JsonModifyRacialType: JsonGetType error 2: " + JsonGetError(jRacialTypeValue)); + DoDebug("prc_inc_json >> json_ModifyRacialType: JsonGetType error 2: " + JsonGetError(jRacialTypeValue)); //SpeakString("JsonGetType error 2: " + JsonGetError(jRacialTypeValue)); return JsonNull(); } @@ -847,6 +1512,482 @@ json JsonModifyRacialType(json jCreature, int nNewRacialType) return jCreature; } +//:: Updates CR for Celestial template +json json_UpdateCelestialCR(json jCreature, int nBaseCR, int nHD) +{ + int nNewCR; + + //:: Calculate CR based on HD + if (nHD <= 3) + { + nNewCR = nBaseCR; + } + else if (nHD <= 7) + { + nNewCR = nBaseCR + 1; + } + else + { + nNewCR = nBaseCR + 2; + } + + //:: Modify Challenge Rating + jCreature = GffReplaceFloat(jCreature, "ChallengeRating", IntToFloat(nNewCR)); + return jCreature; +} + +//:: Adds Celestial SLA's to creature +json json_AddCelestialPowers(json jCreature) +{ + // Get the existing SpecAbilityList + json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList"); + if (jSpecAbilityList == JsonNull()) + { + jSpecAbilityList = JsonArray(); + } + + //:: Add Smite Evil 1x / day + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", SPELLABILITY_SMITE_EVIL); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", json_GetCreatureHD(jCreature)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + + //:: Add the list to the creature + jCreature = GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList); + return jCreature; +} + +//:: Apply Celestial template to a creature JSON template +json json_MakeCelestial(json jCreature, int nBaseHD, int nBaseCR) +{ + if (jCreature == JsonNull()) + return JsonNull(); + + //:: Get current HD for scaling + int nHD = json_GetCreatureHD(jCreature); + if (nHD <= 0) + { + DoDebug("prc_inc_json >> json_MakeCelestial: Invalid HD"); + return JsonNull(); + } + + //:: Get current CR + float fCR = json_GetChallengeRating(jCreature); + + //:: Update CR using Celestial formula + jCreature = json_UpdateCelestialCR(jCreature, FloatToInt(fCR), nHD); + if (jCreature == JsonNull()) + { + DoDebug("prc_inc_json >> json_MakeCelestial: json_UpdateCelestialCR failed"); + return JsonNull(); + } + + //:: Ensure Intelligence is at least 4 + json jInt = GffGetByte(jCreature, "Int"); + if (jInt != JsonNull() && JsonGetInt(jInt) < 4) + { + jCreature = GffReplaceByte(jCreature, "Int", 4); + } + + //:: Add celestial Smite Evil + jCreature = json_AddCelestialPowers(jCreature); + if (jCreature == JsonNull()) + { + DoDebug("prc_inc_json >> json_MakeCelestial: json_AddCelestialPowers failed"); + return JsonNull(); + } + + //:: Change creature type if animal/beast/vermin to magical beast + int nRacialType = JsonGetInt(GffGetByte(jCreature, "Race")); + if (nRacialType == RACIAL_TYPE_ANIMAL || nRacialType == RACIAL_TYPE_VERMIN || nRacialType == RACIAL_TYPE_BEAST) + { + jCreature = json_ModifyRacialType(jCreature, RACIAL_TYPE_MAGICAL_BEAST); + } + + //:: Update creature CR + jCreature = json_UpdateCelestialCR(jCreature, nBaseCR, nHD); + if (jCreature == JsonNull()) + { + DoDebug("prc_inc_json >> json_MakeCelestial: json_UpdateCelestialCR failed"); + return JsonNull(); + } + + return jCreature; +} + +//:: Spawns a Celestial Companion from a template +object MakeCelestialCompanionFromTemplate(string sResref, location lSpawnLoc, int nHealerLvl) +{ + int nBonus = GetHealerCompanionBonus(nHealerLvl); + + json jCelestial = TemplateToJson(sResref, RESTYPE_UTC); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCompanionFromTemplate: TemplateToJson failed — bad resref or resource missing."); + return OBJECT_INVALID; + } + + //:: Get local vars to transfer over. + int iMinHD = json_GetLocalIntFromVarTable(jCelestial, "iMinHD"); + int iMaxHD = json_GetLocalIntFromVarTable(jCelestial, "iMaxHD"); + int nOriginalHD = json_GetLocalIntFromVarTable(jCelestial, "nOriginalHD"); + int iClass2 = json_GetLocalIntFromVarTable(jCelestial, "Class2"); + int iClass2Package = json_GetLocalIntFromVarTable(jCelestial, "Class2Package"); + int iClass2Start = json_GetLocalIntFromVarTable(jCelestial, "Class2Start"); + int iBaseCL = json_GetLocalIntFromVarTable(jCelestial, "iBaseCL"); + int iMagicUse = json_GetLocalIntFromVarTable(jCelestial, "X2_L_BEH_MAGIC"); + string sAI = json_GetLocalStringFromVarTable(jCelestial, "X2_SPECIAL_COMBAT_AI_SCRIPT"); + + //:: Get the original Challenge Rating + int nBaseCR = FloatToInt(json_GetChallengeRating(jCelestial)); + + //:: Apply celestial template modifications + jCelestial = json_MakeCelestial(jCelestial, nBonus, nBaseCR); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCompanionFromTemplate failed — json_MakeCelestial returned invalid JSON."); + return OBJECT_INVALID; + } + + //:: Apply +2 Natural AC bonus per 3 Healer levels + jCelestial = json_IncreaseBaseAC(jCelestial, nBonus); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCompanionFromTemplate failed — json_IncreaseBaseAC returned invalid JSON."); + return OBJECT_INVALID; + } + + //:: +2 STR, DEX & INT per 3 Healer levels + jCelestial = json_UpdateTemplateStats(jCelestial, nBonus, nBonus, 0, nBonus, 0, 0); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCompanionFromTemplate failed — json_UpdateTemplateStats returned invalid JSON."); + return OBJECT_INVALID; + } + + //:: The Companion always has Improved Evasion if the healer qualifies, + //:: but adding it this way gives the base creature more utility for builders. + if (nHealerLvl > 7) + { + //:: Add Improved Evasion feat directly to FeatList + json jFeatList = GffGetList(jCelestial, "FeatList"); + if (jFeatList == JsonNull()) + jFeatList = JsonArray(); + + //:: Check if creature already has Improved Evasion + int bHasFeat = FALSE; + int nFeatCount = JsonGetLength(jFeatList); + int j; + + for (j = 0; j < nFeatCount; j++) + { + json jFeatStruct = JsonArrayGet(jFeatList, j); + if (jFeatStruct != JsonNull()) + { + json jFeatValue = GffGetWord(jFeatStruct, "Feat"); + if (jFeatValue != JsonNull() && JsonGetInt(jFeatValue) == FEAT_IMPROVED_EVASION) + { + bHasFeat = TRUE; + break; + } + } + } + + //:: Add feat only if not already present + if (!bHasFeat) + { + json jNewFeat = JsonObject(); + jNewFeat = JsonObjectSet(jNewFeat, "__struct_id", JsonInt(1)); + jNewFeat = GffAddWord(jNewFeat, "Feat", FEAT_IMPROVED_EVASION); + + jFeatList = JsonArrayInsert(jFeatList, jNewFeat); + jCelestial = GffReplaceList(jCelestial, "FeatList", jFeatList); + } + } + + //:: Spawn the creature + object oCelestial = JsonToObject(jCelestial, lSpawnLoc); + + //:: Set variables for LevelUpSummon() + SetLocalInt(oCelestial, "TEMPLATE_CELESTIAL", 1); + SetLocalInt(oCelestial, "iMinHD", iMinHD); + SetLocalInt(oCelestial, "iMaxHD", iMaxHD); + SetLocalInt(oCelestial, "nOriginalHD", nOriginalHD); + SetLocalInt(oCelestial, "Class2", iClass2); + SetLocalInt(oCelestial, "Class2Package", iClass2Package); + SetLocalInt(oCelestial, "Class2Start", iClass2Start); + SetLocalInt(oCelestial, "iBaseCL", iBaseCL); + SetLocalInt(oCelestial, "X2_L_BEH_MAGIC", iMagicUse); + SetLocalString(oCelestial, "X2_SPECIAL_COMBAT_AI_SCRIPT", sAI); + + return oCelestial; +} + +//:: Spawns a Celestial creature from a template +object MakeCelestialCreatureFromTemplate(string sResref, location lSpawnLoc) +{ + json jCelestial = TemplateToJson(sResref, RESTYPE_UTC); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCreatureFromTemplate: TemplateToJson failed — bad resref or resource missing."); + return OBJECT_INVALID; + } + + //:: Get current HD + int nCurrentHD = json_GetCreatureHD(jCelestial); + if (nCurrentHD <= 0) + { + DoDebug("make_celestial >> MakeCelestialCreatureFromTemplate failed — template missing HD data."); + return OBJECT_INVALID; + } + + //:: Get current CR + int nBaseCR = 1; + nBaseCR = FloatToInt(json_GetChallengeRating(jCelestial)); + if (nBaseCR <= 0) + { + DoDebug("make_celestial >> MakeCelestialCreatureFromTemplate failed — template missing CR data."); + return OBJECT_INVALID; + } + + //:: Get local vars to transfer over. + int iMinHD = json_GetLocalIntFromVarTable(jCelestial, "iMinHD"); + int iMaxHD = json_GetLocalIntFromVarTable(jCelestial, "iMaxHD"); + int nOriginalHD = json_GetLocalIntFromVarTable(jCelestial, "nOriginalHD"); + int iClass2 = json_GetLocalIntFromVarTable(jCelestial, "Class2"); + int iClass2Package = json_GetLocalIntFromVarTable(jCelestial, "Class2Package"); + int iClass2Start = json_GetLocalIntFromVarTable(jCelestial, "Class2Start"); + int iBaseCL = json_GetLocalIntFromVarTable(jCelestial, "iBaseCL"); + int iMagicUse = json_GetLocalIntFromVarTable(jCelestial, "X2_L_BEH_MAGIC"); + string sAI = json_GetLocalStringFromVarTable(jCelestial, "X2_SPECIAL_COMBAT_AI_SCRIPT"); + + //:: Apply celestial template modifications + jCelestial = json_MakeCelestial(jCelestial, nCurrentHD, nBaseCR); + if (jCelestial == JSON_NULL) + { + DoDebug("make_celestial >> MakeCelestialCreatureFromTemplate failed — MakeCelestial returned invalid JSON."); + return OBJECT_INVALID; + } + + //:: Spawn the creature + object oCelestial = JsonToObject(jCelestial, lSpawnLoc); + + //:: Set variables + SetLocalInt(oCelestial, "TEMPLATE_CELESTIAL", 1); + SetLocalInt(oCelestial, "iMinHD", iMinHD); + SetLocalInt(oCelestial, "iMaxHD", iMaxHD); + SetLocalInt(oCelestial, "nOriginalHD", nOriginalHD); + SetLocalInt(oCelestial, "Class2", iClass2); + SetLocalInt(oCelestial, "Class2Package", iClass2Package); + SetLocalInt(oCelestial, "Class2Start", iClass2Start); + SetLocalInt(oCelestial, "Class2Start", iClass2Start); + SetLocalInt(oCelestial, "iBaseCL", iBaseCL); + SetLocalInt(oCelestial, "X2_L_BEH_MAGIC", iMagicUse); + SetLocalString(oCelestial, "X2_SPECIAL_COMBAT_AI_SCRIPT", sAI); + + return oCelestial; +} + +//:: Adds Paragon SLA's to jCreature. +//:: +json json_AddParagonPowers(json jCreature) +{ + // Get the existing SpecAbilityList (if it exists) + json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList"); + + // Create the SpecAbilityList if it doesn't exist + if (jSpecAbilityList == JsonNull()) + { + jSpecAbilityList = JsonArray(); + } + +//:: Greater Dispelling 3x / Day + int i; + for (i = 0; i < 3; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 67); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: Add Haste 3x / Day + for (i = 0; i < 3; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 78); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: See Invisiblity 3x / Day + for (i = 0; i < 3; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 157); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: Add the list to the creature + jCreature = GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList); + + return jCreature; +} + +//:: Directly modifies jCreature's Challenge Rating. +//:: This is useful for most XP calculations. +//:: +json json_UpdateParagonCR(json jCreature, int nBaseCR, int nBaseHD) +{ + int nNewCR; + +//:: Calculate additional CR by HD + if(nBaseHD <= 6) + { + nNewCR = nBaseCR + 18; + } + else if(nBaseHD <= 16) + { + nNewCR = nBaseCR + 15; + } + else + {nNewCR = nBaseCR + 12;} + +//:: Modify Challenge Rating + jCreature = GffReplaceFloat(jCreature, "ChallengeRating"/* /value" */, IntToFloat(nNewCR)); + + return jCreature; +} + +//:: Adds Psuedonatural SLA's to jCreature. +//:: +json json_AddPsuedonaturalPowers(json jCreature) +{ + // Get the existing SpecAbilityList (if it exists) + json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList"); + + // Create the SpecAbilityList if it doesn't exist + if (jSpecAbilityList == JsonNull()) + { + jSpecAbilityList = JsonArray(); + } + +//:: True Strike 1x / Day + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 415); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: Add the list to the creature + jCreature = GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList); + + return jCreature; +} + +//:: Directly modifies jCreature's Challenge Rating. +//:: This is useful for most XP calculations. +//:: +json json_UpdatePsuedonaturalCR(json jCreature, int nBaseCR, int nBaseHD) +{ + int nNewCR; + +//:: Calculate additional CR by HD + if (nBaseHD >= 4 && nBaseHD <= 11) + { + nNewCR = nBaseCR + 1; + } + else if (nBaseHD >= 12) + { + nNewCR = nBaseCR + 2; + } + +//:: Modify Challenge Rating + jCreature = GffReplaceFloat(jCreature, "ChallengeRating"/* /value" */, IntToFloat(nNewCR)); + + return jCreature; +} + + +//:: Spawns a Psuedonatural creature from a template +object MakePsuedonaturalCreatureFromTemplate(string sResref, location lSpawnLoc) +{ + json jPsuedo = TemplateToJson(sResref, RESTYPE_UTC); + if (jPsuedo == JSON_NULL) + { + DoDebug("prc_inc_json >> SpawnPsuedonaturalCreatureFromTemplate: TemplateToJson failed — bad resref or resource missing."); + return OBJECT_INVALID; + } + + //:: Get current HD + int nCurrentHD = json_GetCreatureHD(jPsuedo); + if (nCurrentHD <= 0) + { + DoDebug("make_psuedonat >> MakePsuedonaturalCreatureFromTemplate failed — template missing HD data."); + return OBJECT_INVALID; + } + + //:: Get current CR + int nBaseCR = 1; + nBaseCR = json_GetCreatureHD(jPsuedo); + if (nBaseCR <= 0) + { + DoDebug("make_psuedonat >> MakePsuedonaturalCreatureFromTemplate failed — template missing CR data."); + return OBJECT_INVALID; + } + + //:: Get local vars to transfer over. + int iMinHD = json_GetLocalIntFromVarTable(jPsuedo, "iMinHD"); + int iMaxHD = json_GetLocalIntFromVarTable(jPsuedo, "iMaxHD"); + int nOriginalHD = json_GetLocalIntFromVarTable(jPsuedo, "nOriginalHD"); + int iClass2 = json_GetLocalIntFromVarTable(jPsuedo, "Class2"); + int iClass2Package = json_GetLocalIntFromVarTable(jPsuedo, "Class2Package"); + int iClass2Start = json_GetLocalIntFromVarTable(jPsuedo, "Class2Start"); + int iMagicUse = json_GetLocalIntFromVarTable(jPsuedo, "X2_L_BEH_MAGIC"); + string sAI = json_GetLocalStringFromVarTable(jPsuedo, "X2_SPECIAL_COMBAT_AI_SCRIPT"); + + //:: Adds True Strike 1x / day to jCreature. + jPsuedo = json_AddPsuedonaturalPowers(jPsuedo); + + //:: Change jCreature's racialtype to outsider + jPsuedo = json_ModifyRacialType(jPsuedo, RACIAL_TYPE_OUTSIDER); + + jPsuedo = json_UpdatePsuedonaturalCR(jPsuedo, nBaseCR, nCurrentHD); + + //:: Spawn the creature + object oPsuedo = JsonToObject(jPsuedo, lSpawnLoc); + + //:: Set variables + SetLocalInt(oPsuedo, "TEMPLATE_PSUEDONATURAL", 1); + SetLocalInt(oPsuedo, "iMinHD", iMinHD); + SetLocalInt(oPsuedo, "iMaxHD", iMaxHD); + SetLocalInt(oPsuedo, "nOriginalHD", nOriginalHD); + SetLocalInt(oPsuedo, "Class2", iClass2); + SetLocalInt(oPsuedo, "Class2Package", iClass2Package); + SetLocalInt(oPsuedo, "Class2Start", iClass2Start); + SetLocalInt(oPsuedo, "X2_L_BEH_MAGIC", iMagicUse); + SetLocalString(oPsuedo, "X2_SPECIAL_COMBAT_AI_SCRIPT", sAI); + + return oPsuedo; + +} //:: Test void -//:: void main (){} \ No newline at end of file +//::void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_nat_hb.nss b/src/include/prc_inc_nat_hb.nss index c8f7c27..10bc2fb 100644 --- a/src/include/prc_inc_nat_hb.nss +++ b/src/include/prc_inc_nat_hb.nss @@ -1,9 +1,68 @@ +//:: prc_inc_nat_hb +//:: +//:: void main(){} + + void DoNaturalWeaponHB(object oPC = OBJECT_SELF); #include "prc_inc_combat" #include "prc_inc_template" - -object GetProperTarget(object oPC, object oTarget) +/** + * Finds a valid enemy target in melee range when the original target is invalid. + * Now includes input validation, LOS checks, configurable radius, and target priority. + * + * @param oPC The creature seeking a new target + * @param oTarget The original (invalid) target + * @param fRadius Search radius in meters (optional, defaults to melee range) + * @return A valid enemy target or OBJECT_INVALID if none found + */ +object GetProperTarget(object oPC, object oTarget, float fRadius = MELEE_RANGE_METERS) +{ + // Input validation + if(!GetIsObjectValid(oPC)) + { + DoDebug("GetProperTarget(): Invalid oPC parameter"); + return OBJECT_INVALID; + } + + // Use target list system for better target selection + PurgeTargetList(oPC); + + location lPC = GetLocation(oPC); + object oTest = MyFirstObjectInShape(SHAPE_SPHERE, fRadius, lPC, TRUE, OBJECT_TYPE_CREATURE); + + while(GetIsObjectValid(oTest)) + { + // Basic validation checks + if(oTest != oPC && // Not self + GetIsEnemy(oPC, oTest) && // Is enemy + GetIsInMeleeRange(oPC, oTest) && // In melee range + !GetIsDead(oTest) && // Is alive + LineOfSightObject(oPC, oTest)) // Has line of sight + { + // Add to target list with priority based on distance (nearest first) + AddToTargetList(oTest, oPC, INSERTION_BIAS_DISTANCE, FALSE); + } + + oTest = MyNextObjectInShape(SHAPE_SPHERE, fRadius, lPC, TRUE, OBJECT_TYPE_CREATURE); + } + + // Get the highest priority target (nearest enemy) + object oBestTarget = GetTargetListHead(oPC); + PurgeTargetList(oPC); + + if(GetIsObjectValid(oBestTarget)) + { + DoDebug("GetProperTarget(): Selected target " + GetName(oBestTarget) + + " for " + GetName(oPC)); + return oBestTarget; + } + + // No valid target found + DoDebug("GetProperTarget(): No valid target found for " + GetName(oPC)); + return OBJECT_INVALID; +} +/* object GetProperTarget(object oPC, object oTarget) { location lTarget = GetLocation(oPC); // Use the function to get the closest creature as a target @@ -21,7 +80,7 @@ object GetProperTarget(object oPC, object oTarget) } return oTarget; -} +} */ void DoNaturalAttack(object oWeapon) { @@ -289,59 +348,72 @@ void DoOverflowOnhandAttack(int nAttackMod) ); } -void DoNaturalWeaponHB(object oPC = OBJECT_SELF) +/* void DoNaturalWeaponHB(object oPC = OBJECT_SELF) { //not in combat, abort if(!GetIsInCombat(oPC)) return; -// if(DEBUG) DoDebug("entered DoNaturalWeaponHB"); + if(DEBUG) DoDebug("prc_inc_nat_hb: entered DoNaturalWeaponHB"); float fDelay = 0.1 + IntToFloat(Random(10))/100.0; //no natural weapons, abort //in a different form, abort for now fix it later - if(array_exists(oPC, ARRAY_NAT_SEC_WEAP_RESREF) - && !GetIsPolyMorphedOrShifted(oPC)) +if(array_exists(oPC, ARRAY_NAT_SEC_WEAP_RESREF) + && !GetIsPolyMorphedOrShifted(oPC)) +{ + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: creature has natural secondary weapons"); + UpdateSecondaryWeaponSizes(oPC); + int i; + while(i < array_get_size(oPC, ARRAY_NAT_SEC_WEAP_RESREF)) { - // DoDebug("DoNaturalWeaponHB: creature has natural secondary weapons"); - UpdateSecondaryWeaponSizes(oPC); - int i; - while(i < array_get_size(oPC, ARRAY_NAT_SEC_WEAP_RESREF)) + string sResRef = array_get_string(oPC, ARRAY_NAT_SEC_WEAP_RESREF, i); + if(sResRef != "") { - //get the resref to use - string sResRef = array_get_string(oPC, ARRAY_NAT_SEC_WEAP_RESREF, i); - //if null, move to next - if(sResRef != "") + // Get stored weapon object, or create if doesn't exist + object oWeapon = GetLocalObject(oPC, "NAT_SEC_WEAP_" + sResRef); + + if(!GetIsObjectValid(oWeapon)) { - //get the created item - object oWeapon = GetObjectByTag(sResRef); + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: creating and storing creature weapon " + sResRef); + oWeapon = CreateItemOnObject(sResRef, oPC); + if(!GetIsObjectValid(oWeapon)) { - object oLimbo = GetObjectByTag("HEARTOFCHAOS"); - location lLimbo = GetLocation(oLimbo); - if(!GetIsObjectValid(oLimbo)) - lLimbo = GetStartingLocation(); - oWeapon = CreateObject(OBJECT_TYPE_ITEM, sResRef, lLimbo); + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: ERROR - CreateItemOnObject FAILED for " + sResRef); } + else + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: SUCCESS - weapon created, tag=" + GetTag(oWeapon) + ", name=" + GetName(oWeapon)); + SetIdentified(oWeapon, TRUE); + SetLocalObject(oPC, "NAT_SEC_WEAP_" + sResRef, oWeapon); + } + } + else + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: using stored creature weapon object"); + } - // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay)); - //do the attack within a delay - /* - // motu99: commented this out; AssignCommand ist not needed, because OBJECT_SELF is oPC - using AssignCommand will only degrade performance - AssignCommand(oPC, DelayCommand(fDelay, DoNaturalAttack(oWeapon))); - */ - + // Double-check validity before scheduling + if(GetIsObjectValid(oWeapon)) + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay)); DelayCommand(fDelay, DoNaturalAttack(oWeapon)); - //calculate the delay to use next time fDelay += 2.05; if(fDelay > 6.0) - fDelay -= 6.0; + fDelay -= 6.0; + } + else + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: ERROR - weapon object is INVALID, cannot schedule attack"); } - i++; } + i++; } +} + int iMod = 5; // motu99: added check for monk weapon if(GetHasMonkWeaponEquipped(oPC)) iMod = 3; @@ -357,10 +429,10 @@ void DoNaturalWeaponHB(object oPC = OBJECT_SELF) for(i = 0; i < nOverflowAttackCount; i++) { // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted overflow attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); - /* + // motu99: see comment above why this is commented out - AssignCommand(oPC, DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty))); - */ + //AssignCommand(oPC, DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty))); + DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty)); //calculate the delay to use @@ -399,6 +471,128 @@ void DoNaturalWeaponHB(object oPC = OBJECT_SELF) } } } + */ + +void DoNaturalWeaponHB(object oPC = OBJECT_SELF) +{ + //not in combat, abort + if(!GetIsInCombat(oPC)) + return; + + if(DEBUG) DoDebug("prc_inc_nat_hb: entered DoNaturalWeaponHB"); + + float fDelay = 0.1 + IntToFloat(Random(10))/100.0; + + //no natural weapons, abort + //in a different form, abort for now fix it later + if(array_exists(oPC, ARRAY_NAT_SEC_WEAP_RESREF) + && !GetIsPolyMorphedOrShifted(oPC)) + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: creature has natural secondary weapons"); + UpdateSecondaryWeaponSizes(oPC); + int i; + while(i < array_get_size(oPC, ARRAY_NAT_SEC_WEAP_RESREF)) + { + //get the resref to use + string sResRef = array_get_string(oPC, ARRAY_NAT_SEC_WEAP_RESREF, i); + //if null, move to next + if(sResRef != "") + { + //get the created item + object oWeapon = GetObjectByTag(sResRef); + if(!GetIsObjectValid(oWeapon)) + { + object oLimbo = GetObjectByTag("HEARTOFCHAOS"); + location lLimbo = GetLocation(oLimbo); + if(!GetIsObjectValid(oLimbo)) + lLimbo = GetStartingLocation(); + oWeapon = CreateObject(OBJECT_TYPE_ITEM, sResRef, lLimbo); + DoDebug(PRC_TEXT_WHITE + "prc_inc_nat_hb >> DoNaturalWeaponHB: creature weapon object found!!!"); + } + + // Check for enhancements after creating the weapon object + int nEnhance = GetLocalInt(oPC, "PRC_NAT_WEAPON_ENHANCE"); + if(nEnhance > 0) + { + + DoDebug(PRC_TEXT_WHITE + "prc_inc_nat_hb >> DoNaturalWeaponHB: Applying enhancement."); + float fDuration = GetLocalFloat(oPC, "PRC_NAT_WEAPON_ENH_DUR"); + IPSafeAddItemProperty(oWeapon, ItemPropertyEnhancementBonus(nEnhance), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE); + } + + if(DEBUG) DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay)); + //do the attack within a delay + // motu99: commented this out; AssignCommand ist not needed, because OBJECT_SELF is oPC - using AssignCommand will only degrade performance + //AssignCommand(oPC, DelayCommand(fDelay, DoNaturalAttack(oWeapon))); + + DelayCommand(fDelay, DoNaturalAttack(oWeapon)); + + //calculate the delay to use next time + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + } + i++; + } + } + + int iMod = 5; // motu99: added check for monk weapon + if(GetHasMonkWeaponEquipped(oPC)) iMod = 3; + + // check for overflow (main hand) attacks + int nOverflowAttackCount = GetLocalInt(oPC, "OverflowBaseAttackCount"); + if(nOverflowAttackCount) + { + int i; + // the first overflow attack would be the seventh main hand attack, at an AB of -30 + int nAttackPenalty = -6 * iMod; // -30 for normal bab, -18 for monks + // DoDebug("DoNaturalWeaponHB(): number of scripted overflow attacks: "+IntToString(nOverflowAttackCount)); + for(i = 0; i < nOverflowAttackCount; i++) + { + // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted overflow attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); + + // motu99: see comment above why this is commented out + // AssignCommand(oPC, DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty))); + + DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty)); + + //calculate the delay to use + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + //calculate new attack penalty + nAttackPenalty -= iMod; // motu99: usually -5, for monks -3 (unarmed or kama) + } + } + + // motu99: this is only here for debugging in order to test PerformAttackRound() + // must be deleted after debugging!!! + //if (GetPRCSwitch(PRC_PNP_TRUESEEING)) DelayCommand(0.01, DoOffhandAttackRound()); + + + // check for overflow offhand attacks + int nOffhandAttackCount = GetLocalInt(oPC, "OffhandOverflowAttackCount"); +// if (DEBUG) DoDebug("DoNaturalWeaponHB: number of scripted offhand attacks = "+IntToString(nOffhandAttackCount)); + if(nOffhandAttackCount) + { + int i; + int nAttackPenalty = -2 * iMod; // offhand attacks always come at -5 per additional attack (but for monks we assume -3) + for(i = 0; i < nOffhandAttackCount; i++) + { + // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted offhand attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); + + DelayCommand(fDelay, DoOffhandAttack(nAttackPenalty)); + + //calculate the delay to use + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + //calculate new attack penalty + nAttackPenalty -= iMod; + } + } +} + /* * motu99's test functions. Not actually used by PRC scripts diff --git a/src/include/prc_inc_natweap.nss b/src/include/prc_inc_natweap.nss index 8d3668b..78115a5 100644 --- a/src/include/prc_inc_natweap.nss +++ b/src/include/prc_inc_natweap.nss @@ -277,6 +277,32 @@ void ClearNaturalWeapons(object oPC) array_delete(oPC, ARRAY_NAT_PRI_WEAP_ATTACKS); } +/** + * @brief Adds a natural primary weapon to a creature (PC/NPC). + * + * This function manages a creature's natural primary weapons by storing their + * resource references and attack counts in persistent arrays. If the weapon + * being added is the first natural weapon, it may automatically become the + * creature's active primary natural weapon, unless the creature is a Monk or + * Brawler. Optionally, the weapon can be forced to become the active primary + * weapon regardless of class. + * + * @param oPC The creature object to which the natural weapon will be added. + * @param sResRef The resource reference string of the natural weapon. + * @param nCount (Optional) The number of attacks this natural weapon provides. + * Default is 1. + * @param nForceUse (Optional) If TRUE, forces this weapon to become the active + * primary natural weapon regardless of the creature's class. + * Default is FALSE. + * + * @details + * - Creates persistent arrays for weapon references and attack counts if they + * do not already exist. + * - Checks if the weapon is already present to avoid duplicates. + * - Adds the weapon and attack count to the arrays. + * - Sets the primary natural weapon index to this weapon if it is the first + * natural weapon added, unless the creature is a Monk or Brawler. + */ void AddNaturalPrimaryWeapon(object oPC, string sResRef, int nCount = 1, int nForceUse = FALSE) { int nFirstNaturalWeapon = FALSE; diff --git a/src/include/prc_inc_shifting.nss b/src/include/prc_inc_shifting.nss index 7cac1ed..1e0fadc 100644 --- a/src/include/prc_inc_shifting.nss +++ b/src/include/prc_inc_shifting.nss @@ -1370,7 +1370,9 @@ void _prc_inc_shifting_ShiftIntoTemplateAux(object oShifter, int nShifterType, o if(GetIsObjectValid(oShifterCWpR)) MyDestroyObject(oShifterCWpR); if(GetIsObjectValid(oShifterCWpL)) MyDestroyObject(oShifterCWpL); if(GetIsObjectValid(oShifterCWpB)) MyDestroyObject(oShifterCWpB); - oShifterCWpR = oShifterCWpL = oShifterCWpR = OBJECT_INVALID; + oShifterCWpR = OBJECT_INVALID; + oShifterCWpL = OBJECT_INVALID; + oShifterCWpB = OBJECT_INVALID; // Copy the template's weapons and assign equipping diff --git a/src/include/prc_inc_size.nss b/src/include/prc_inc_size.nss new file mode 100644 index 0000000..286015b --- /dev/null +++ b/src/include/prc_inc_size.nss @@ -0,0 +1,154 @@ +#include "prc_inc_util" +#include "prc_inc_spells" +#include "prc_inc_function" + +// Wrapper function for delayed visual transform with generation tracking +void DelayedSetVisualTransform(int nExpectedGeneration, object oTarget, int nTransform, float fValue) +{ + // Read current generation at execution time, not when scheduled + if (nExpectedGeneration != GetLocalInt(oTarget, "PRC_SIZE_GENERATION")) + { + // Generation has changed, don't apply the transform + return; + } + SetObjectVisualTransform(oTarget, nTransform, fValue); +} + +// Main wrapper function that handles generation tracking +void DelaySetVisualTransform(float fDelay, object oTarget, string sGenerationName, int nTransform, float fValue) +{ + int nExpectedGeneration = GetLocalInt(oTarget, sGenerationName); + DelayCommand(fDelay, DelayedSetVisualTransform(nExpectedGeneration, oTarget, nTransform, fValue)); +} + +/** + * Creates a size change effect that can enlarge or reduce a creature + * + * @param oTarget Object to affect + * @param nObjectType Object type filter (OBJECT_TYPE_CREATURE, etc.) + * @param bEnlarge TRUE to enlarge, FALSE to reduce + * @param nChanges Number of size categories to change (0 = reset to original) + * @return The size change effect + */ + +effect EffectSizeChange(object oTarget, int nObjectType, int bEnlarge, int nChanges) +{ + effect eBlank; + + // Increment generation for any size change + int nGeneration = PRC_NextGeneration(GetLocalInt(oTarget, "PRC_SIZE_GENERATION")); + SetLocalInt(oTarget, "PRC_SIZE_GENERATION", nGeneration); + + // Store original size if not already stored - READ ACTUAL CURRENT SCALE + if(GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE") == 0.0f) + { + float fCurrentScale = GetObjectVisualTransform(oTarget, OBJECT_VISUAL_TRANSFORM_SCALE); + SetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE", fCurrentScale); + } + + // Reset to original size + if(nChanges == 0) + { + float fOriginalSize = GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE"); + DelaySetVisualTransform(0.0f, oTarget, "PRC_SIZE_GENERATION", OBJECT_VISUAL_TRANSFORM_SCALE, fOriginalSize); + // DON'T delete PRC_ORIGINAL_SIZE here - keep it for future casts + return eBlank; + } + + // Get the original scale + float fOriginalScale = GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE"); + + // Calculate scale factor relative to original size + float fScale = fOriginalScale; + if(bEnlarge) + fScale = fOriginalScale * pow(1.5f, IntToFloat(nChanges)); + else + fScale = fOriginalScale * pow(0.5f, IntToFloat(nChanges)); + + // Create the effect link with sanctuary VFX + effect eReturn = EffectLinkEffects(EffectVisualEffect(VFX_DUR_SANCTUARY), + EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE)); + + if(bEnlarge) + { + eReturn = EffectLinkEffects(eReturn, EffectAttackDecrease(nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityDecrease(ABILITY_DEXTERITY, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityIncrease(ABILITY_STRENGTH, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectACDecrease(nChanges)); + eReturn = TagEffect(eReturn, "PRC_SIZE_INCREASE"); + } + else + { + eReturn = EffectLinkEffects(eReturn, EffectAttackIncrease(nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityIncrease(ABILITY_DEXTERITY, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityDecrease(ABILITY_STRENGTH, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectACIncrease(nChanges)); + eReturn = TagEffect(eReturn, "PRC_SIZE_DECREASE"); + } + + // Apply visual transform using wrapper + DelaySetVisualTransform(0.0f, oTarget, "PRC_SIZE_GENERATION", OBJECT_VISUAL_TRANSFORM_SCALE, fScale); + + return eReturn; +} + + + + +/* effect EffectSizeChange(object oTarget, int nObjectType, int bEnlarge, int nChanges) +{ + effect eBlank; + + // Increment generation for any size change + int nGeneration = PRC_NextGeneration(GetLocalInt(oTarget, "PRC_SIZE_GENERATION")); + SetLocalInt(oTarget, "PRC_SIZE_GENERATION", nGeneration); + + // Store original size if not already stored (fixed check) + if(GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE") == 0.0f) + { + SetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE", 1.0f); + } + + // Reset to original size + if(nChanges == 0) + { + float fOriginalSize = GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE"); + DelaySetVisualTransform(0.0f, oTarget, "PRC_SIZE_GENERATION", OBJECT_VISUAL_TRANSFORM_SCALE, fOriginalSize); + DeleteLocalFloat(oTarget, "PRC_ORIGINAL_SIZE"); + return eBlank; + } + + // Calculate scale factor using pow() for multi-step changes + float fScale = 1.0f; + if(bEnlarge) + fScale = pow(1.5f, IntToFloat(nChanges)); // 1.5, 2.25, 3.375... + else + fScale = pow(0.5f, IntToFloat(nChanges)); // 0.5, 0.25, 0.125... + + // Create the effect link based on enlarge/reduce + effect eReturn; + if(bEnlarge) + { + eReturn = EffectLinkEffects(EffectAttackDecrease(nChanges), + EffectAbilityDecrease(ABILITY_DEXTERITY, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityIncrease(ABILITY_STRENGTH, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectACDecrease(nChanges)); + eReturn = TagEffect(eReturn, "PRC_SIZE_INCREASE"); + } + else + { + eReturn = EffectLinkEffects(EffectAttackIncrease(nChanges), + EffectAbilityIncrease(ABILITY_DEXTERITY, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityDecrease(ABILITY_STRENGTH, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectACIncrease(nChanges)); + eReturn = TagEffect(eReturn, "PRC_SIZE_DECREASE"); + } + + // Apply visual transform using wrapper + DelaySetVisualTransform(0.0f, oTarget, "PRC_SIZE_GENERATION", OBJECT_VISUAL_TRANSFORM_SCALE, fScale); + + return eReturn; +} + */ + +//:: void main(){} \ No newline at end of file diff --git a/src/include/prc_inc_skills.nss b/src/include/prc_inc_skills.nss index 0674604..1bfc354 100644 --- a/src/include/prc_inc_skills.nss +++ b/src/include/prc_inc_skills.nss @@ -363,8 +363,11 @@ int PRCIsFlying(object oCreature) bFlying = TRUE; } if(!bFlying - && ((nWings > 0 && nWings < 79) || nWings == 90))//CEP and Project Q wing models - bFlying = TRUE; + && ((nWings > 0 && nWings < 79) + || (nWings > 1959 && nWings < 1962) + || (nWings > 1962 && nWings < 1966) + || nWings == 90))//CEP and Project Q wing models + bFlying = TRUE; if (GetHasSpellEffect(MOVE_SH_BALANCE_SKY, oCreature)) bFlying = TRUE; diff --git a/src/include/prc_inc_spells.nss b/src/include/prc_inc_spells.nss index 18c0efa..1c7ad71 100644 --- a/src/include/prc_inc_spells.nss +++ b/src/include/prc_inc_spells.nss @@ -1011,11 +1011,16 @@ int PRCMySavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = // Plague Resistant gives a +4 bonus on disease saves if(GetHasFeat(FEAT_PLAGUE_RESISTANT, oTarget)) nDC -= 4; + // Racial +2 vs disease saves + if(GetHasFeat(FEAT_RACE_HARDINESS_VS_DISEASE, oTarget)) + nDC -= 2; // +4/+2 bonus on saves against disease, done here if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 13) nDC -= 4; else if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 3) nDC -= 2; + + } else if(nSaveType == SAVING_THROW_TYPE_POISON) { diff --git a/src/include/prc_inc_switch.nss b/src/include/prc_inc_switch.nss index b2c7160..e2a3dfe 100644 --- a/src/include/prc_inc_switch.nss +++ b/src/include/prc_inc_switch.nss @@ -70,13 +70,13 @@ 43 PRC_CRAFTING_BASE_ITEMS int 1 44 PRC_XP_USE_SIMPLE_LA int 1 45 PRC_XP_USE_SIMPLE_RACIAL_HD int 1 -46 PRC_CREATE_INFUSION_CASTER_LEVEL int 1 -47 PRC_CREATE_INFUSION_OPTIONAL_HERBS int 0 +46 PRC_CREATE_INFUSION_CASTER_LEVEL int 1 +47 PRC_CREATE_INFUSION_OPTIONAL_HERBS int 0 */ /* This variable MUST be updated with every new version of the PRC!!! */ - const string PRC_VERSION = "PRC 3.9.0"; + const string PRC_VERSION = "PRC8 4.76"; /* This variable MUST be updated every time 'assemble_spellbooks.bat' is run!!! */ @@ -91,11 +91,11 @@ * This allows material components in NWN through the materialcomp.2da * Just put the SpellID and UTC resref in, MINUS the .utc on the end. * This also requires the names of the items, formatted like so ("" included): "Object Name" - * + * * Set switch to 2 to activate this * Deducts gold instead of requiring material components * Put the gold value in the Cost column - + * Set switch to 3 to activate both at the same time * WARNING: This will slow spellcasting down due to 2da reads and inventory loops */ @@ -129,7 +129,7 @@ const string PRC_BIOWARE_NEUTRALIZE_POISON = "PRC_BIOWARE_NEUTRALIZE_P /** Remove the cap PRC added to this spell */ const string PRC_BIOWARE_REMOVE_DISEASE = "PRC_BIOWARE_REMOVE_DISEASE"; -/** +/** * This replaces the 3.0 Spell Focus bonuses with the 3.5 edition ones */ const string PRC_35_SPELL_FOCUS = "PRC_35_SPELL_FOCUS"; @@ -263,8 +263,8 @@ const string PRC_165_DEATH_IMMUNITY = "PRC_165_DEATH_IMMUNITY"; /* * PRC_ACTIVATE_MAX_SPELL_DC_CAP: activate a max cap on DC casted by creature/player - * PRC_SET_MAX_SPELL_DC_CAP: the max value ex: 99 - * + * PRC_SET_MAX_SPELL_DC_CAP: the max value ex: 99 + * */ const string PRC_ACTIVATE_MAX_SPELL_DC_CAP = "PRC_ACTIVATE_MAX_SPELL_DC_CAP"; const string PRC_SET_MAX_SPELL_DC_CAP = "PRC_SET_MAX_SPELL_DC_CAP"; @@ -291,8 +291,8 @@ const string PRC_DC_BASE_OVERRIDE = "PRC_DC_BASE_OVERRIDE"; const string PRC_DC_ADJUSTMENT = "PRC_DC_ADJUSTMENT"; /* - * By default when calculating caster level for characters with PrCs, the highest class rule will - * be used (ie. Bard 2/Wizard 4/Elemental Savant 6 - Wizard is the highest arcane class so levels + * By default when calculating caster level for characters with PrCs, the highest class rule will + * be used (ie. Bard 2/Wizard 4/Elemental Savant 6 - Wizard is the highest arcane class so levels * form PrC will be added to that class, and the caster level will be 2 for Bard and 10 for Wizard). * When this is set, the first class rule will be used (with the same character caster level would * be 8 for Bard and 4 for Wizard). @@ -572,12 +572,12 @@ const string PRC_SOUL_EATER_MAX_SLAVES = "PRC_SOUL_EATER_MAX_SLAVES"; * For the Psionic Slayer prestige class, this switch limits the Favored Enemy selection * to the Aberration racial type. * - * This switch is provided to allow builders to more closely represent the Pen and Paper + * This switch is provided to allow builders to more closely represent the Pen and Paper * Illithid Slayer class, instead of the broader Open Game License "Slayer" class. * - * Type: Int + * Type: Int * Values: 0 [Default] (Favored Enemy racial type is not limited) - * 1 (Favored Enemy race is limited to Aberration only) + * 1 (Favored Enemy race is limited to Aberration only) */ const string PRC_PSIONIC_SLAYER_FAV_ENEMY_ABERRATION_ONLY = "PRC_PSIONIC_SLAYER_FAV_ENEMY_ABERRATION_ONLY"; @@ -585,20 +585,20 @@ const string PRC_PSIONIC_SLAYER_FAV_ENEMY_ABERRATION_ONLY = "PRC_PSIONIC_S * For the Psionic Slayer prestige class, this switch requires a character to make a "kill" * of a specific type of creature before the class becomes available. * - * Use of this switch requires that the module builder add a "Psionic Slayer Kill Token" - * (included in the PRC Items) to the designated creature. + * Use of this switch requires that the module builder add a "Psionic Slayer Kill Token" + * (included in the PRC Items) to the designated creature. * - * Alternately, a script or item can be made that will run the script "prc_psysly_killt" + * Alternately, a script or item can be made that will run the script "prc_psysly_killt" * on the PC. This script will set the flag that allows the target PC to take the Psionic Slayer Class. - * Example code: - * ExecuteScript("prc_psysly_killt", oPC); // Where oPC is an player charcter object + * Example code: + * ExecuteScript("prc_psysly_killt", oPC); // Where oPC is an player charcter object * - * This switch is provided to allow builders to more closely represent the Pen and Paper + * This switch is provided to allow builders to more closely represent the Pen and Paper * Illithid Slayer class, instead of the broader Open Game License "Slayer" class. * - * Type: Int + * Type: Int * Values: 0 [Default] (Kill Token / Script NOT required for taking the Psionic Slayer Class) - * 1 (Kill Token / Script REQUIRED before the Psionic Slayer Class is available to take) + * 1 (Kill Token / Script REQUIRED before the Psionic Slayer Class is available to take) */ const string PRC_PSIONIC_SLAYER_REQUIRE_KILL_TOKEN = "PRC_PSIONIC_SLAYER_REQUIRE_KILL_TOKEN"; @@ -607,19 +607,19 @@ const string PRC_PSIONIC_SLAYER_REQUIRE_KILL_TOKEN = "PRC_PSIONIC_SLAYER_R * By default the Werewolf class uses the Bioware Polymorph effect to perfrom its * Hybrid Form Shapchange. * - * This switch allows the Werewolf class to be toggled to use the PRC Shifter - * Shapchange code instead. + * This switch allows the Werewolf class to be toggled to use the PRC Shifter + * Shapchange code instead. * - * Type: Int + * Type: Int * Values: 0 [Default] (Werewolf Hybrid Shapchange uses Bioware Polymorph) - * 1 (Werewolf Hybrid Shapchange uses PRC Shifter shape change code) + * 1 (Werewolf Hybrid Shapchange uses PRC Shifter shape change code) */ -const string PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPCHANGE = "PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPCHANGE"; +const string PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPECHANGE = "PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPECHANGE"; /** * Sets the max bonus for the PnP Shifter shifting systems * - * Type: Int + * Type: Int * Values: any greater than 0 */ const string PRC_PNP_SHIFTER_BONUS = "PRC_PNP_SHIFTER_BONUS"; @@ -783,7 +783,7 @@ const string PRC_STAFF_CASTER_LEVEL = "PRC_STAFF_CASTER_LEVEL"; /** * [DEFUNCT] * A wand must be equipped before it can cast a spell - * + * * Any value above 0 turns off the requirement to have a wand equipped to use it * * This switch is defunct, wands must *always* be equipped to use them. @@ -931,7 +931,7 @@ const string PRC_PNP_FAMILIAR_FEEDING = "PRC_PNP_FAMILIAR_FEEDING /** * Use PRC henchmen-familiars instead of BioWare's - this will allow - * new classes to have familiars, but summoned creatures will no longer + * new classes to have familiars, but summoned creatures will no longer * be 'true' familiars (ie. can't possess PRC familiar) */ const string PRC_FAMILIARS = "PRC_FAMILIARS"; @@ -1114,7 +1114,7 @@ const string PRC_SPELL_ALIGNMENT_RESTRICT = "PRC_SPELL_ALIGNMENT_REST * Disable registration of custom cohorts */ const string PRC_DISABLE_REGISTER_COHORTS = "PRC_DISABLE_REGISTER_COHORTS"; - + /* * Disable cohorts starting with gear */ @@ -1144,19 +1144,19 @@ const string PRC_SPELL_ALIGNMENT_RESTRICT = "PRC_SPELL_ALIGNMENT_REST * Medium armor is a 25% speed reduction, Heavy is a 33% reduction */ const string PRC_PNP_ARMOR_SPEED = "PRC_PNP_ARMOR_SPEED"; - + /* * Applies a 99% speed boost when out of combat * Warning that it will likely cause PCs to be overly speedy when combat starts * Potential problem causer */ const string PRC_FAST_TRAVEL_SPEED = "PRC_FAST_TRAVEL_SPEED"; - + /* * Applys a Discipline bonus equal to BAB to all characters if turned on * Bonus only applies to characters with 0 ranks in Discipline */ - const string PRC_PNP_KNOCKDOWN = "PRC_PNP_KNOCKDOWN"; + const string PRC_PNP_KNOCKDOWN = "PRC_PNP_KNOCKDOWN"; /* * by Bioware rules, PCs have approximatly a 7th faster movement than NPCs @@ -1204,8 +1204,11 @@ const string PRC_SPELL_ALIGNMENT_RESTRICT = "PRC_SPELL_ALIGNMENT_REST */ const string PRC_APPEARNCE_CHANGE_DISABLE = "PRC_APPEARNCE_CHANGE_DISABLE"; - - +/* + * Allow "Monk" gloves to merge with a creature weapons when Wildshaped. + * Will also merge bracers with creature hides when Wildshaped. +*/ +const string PRC_WILDSHAPE_ALLOWS_ARMS_SLOT = "PRC_WILDSHAPE_ALLOWS_ARMS_SLOT"; /******************************************************************************\ * Death/Bleeding system * @@ -1223,7 +1226,7 @@ const string PRC_PNP_DEATH_ENABLE = "PRC_PNP_DEATH_ENA * if FALSE, dont bleed just die * By PnP this would be 1 round, or 6 seconds */ -const string PRC_DEATH_OR_BLEED = "PRC_DEATH_OR_BLEED"; +const string PRC_DEATH_OR_BLEED = "PRC_DEATH_OR_BLEED"; /* * Damage when bleeding @@ -1316,7 +1319,7 @@ const string PRC_ACP_DELAY = "PRC_ACP_DELAY"; /****************************************************************************** -* File End switches +* File End switches ******************************************************************************/ /** @@ -1490,10 +1493,10 @@ const string PRC_POISON_IS_FOOD_SCRIPT_NAME = "PRC_POISON_IS_FOOD_SCRIP const string PRC_POISON_ALLOW_CLEAN_IN_EQUIP = "PRC_POISON_ALLOW_CLEAN_IN_EQUIP"; /** - * + * * Default: crafting requires only gold and xp */ -const string PRC_CRAFT_POISON_USE_INGREDIENST = "PRC_CRAFT_POISON_USE_INGREDIENST"; +const string PRC_CRAFT_POISON_USE_INGREDIENTS = "PRC_CRAFT_POISON_USE_INGREDIENTS"; /******************************************************************************\ * PRGT system switches * @@ -1542,10 +1545,10 @@ const string PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD = "PRC_PSI_ASTRAL_CONSTRUCT /** * If this is set, The Astral Seed power will attempt to use the provided string as - * the ResRef to create the Astral Seed object instead of the of the phylactery + * the ResRef to create the Astral Seed object instead of the of the phylactery * ResRef("x2_plc_phylact"). * May be used by builders to create an object that CAN be destroyed, or has other traits, - * as desired. + * as desired. * Type: String * Values: "" [Default] (Blank, or not set: Use default phylactery ResRef for Astral Seed) * STRING (Entered String will be used as the ResRef of created Astral Seed object) @@ -1553,14 +1556,14 @@ const string PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD = "PRC_PSI_ASTRAL_CONSTRUCT const string PRC_PSI_ASTRAL_SEED_RESREF = "PRC_PSI_ASTRAL_SEED_RESREF"; /** - * By default the Astral Seed power respawns the player, and then makes them immobile for + * By default the Astral Seed power respawns the player, and then makes them immobile for * 24-game-hours. - * If this switch is set, it will adjust the imobility time period; shortening it, lengthing it, or + * If this switch is set, it will adjust the imobility time period; shortening it, lengthing it, or * effectively eliminating it. * Type: Int * Values: 0 [Default] (Not set: Use default 24 hour duration) * -1 (Any negative value will result in a fixed duratoion of 2 seconds, which effectively eliminates the wait period) - * 1 (Any potitive value: multiply duration by the value provided and then divide result by 1000. + * 1 (Any potitive value: multiply duration by the value provided and then divide result by 1000. * Values less than 1000 will shorten the duration, values higher than 1000 will lengthen it.) */ const string PRC_PSI_ASTRAL_SEED_RESPAWN_DELAY_X1000 = "PRC_PSI_ASTRAL_SEED_RESPAWN_DELAY_X1000"; @@ -1571,7 +1574,7 @@ const string PRC_PSI_ASTRAL_SEED_RESPAWN_DELAY_X1000 = "PRC_PSI_ASTRAL_SE * If this flag is set, the XP loss is completely eliminated. The standard PRC event hook script * of "prc_pw_astralseed" may be used to script any additional effects to occure upon Astral Seed * respawning, including scripting specific XP loss amount. - * Type: Int + * Type: Int * Values: 0 [Default] (Not set: lose 1 level worth of XP upon Astral Seed respawn) * 1 (Any potitive value: Remove all XP loss from Astral Seed respawn) */ @@ -1908,7 +1911,7 @@ const string PRC_CRAFT_TIMER_MAX = "PRC_CRAFT_TIMER_MAX"; */ const string PRC_CRAFT_TIMER_MIN = "PRC_CRAFT_TIMER_MIN"; -/** +/* * These three switches modify Bioware crafting so that the items produced have the * casterlevel of the spellcaster who created them. Normally under Bioware, it is possible * for a level 3 caster to produce level 9 items and for a level 40 caster to only produce @@ -1955,16 +1958,21 @@ const string PRC_CRAFT_ROD_CASTER_LEVEL = "PRC_CRAFT_ROD_CASTER_LEVE const string PRC_CRAFT_STAFF_CASTER_LEVEL = "PRC_CRAFT_STAFF_CASTER_LEVEL"; /* - * As above, except it applies to herbal infusions + * As above, except it applies to scepters */ -const string PRC_CREATE_INFUSION_CASTER_LEVEL = "PRC_CREATE_INFUSION_CASTER_LEVEL"; +const string PRC_CRAFT_SCEPTER_CASTER_LEVEL = "PRC_CRAFT_SCEPTER_CASTER_LEVEL"; /* - * Builder's Option: Enables the optional PnP herbs for creating infusions. + * As above, except it applies to herbal infusions + */ +const string PRC_CREATE_INFUSION_CASTER_LEVEL = "PRC_CREATE_INFUSION_CASTER_LEVEL"; + +/* + * Builder's Option: Enables the optional PnP herbs for creating infusions. * Each herb is keyed to a spell circle level & spell school as shown on pg. 33 * of the Master's of the Wild sourcebook. */ -const string PRC_CREATE_INFUSION_OPTIONAL_HERBS = "PRC_CREATE_INFUSION_OPTIONAL_HERBS"; +const string PRC_CREATE_INFUSION_OPTIONAL_HERBS = "PRC_CREATE_INFUSION_OPTIONAL_HERBS"; /* * Characters with a crafting feat always have the appropriate base item in their inventory @@ -2015,6 +2023,13 @@ const string PRC_X2_CRAFTWAND_COSTMODIFIER = "PRC_X2_CRAFTWAND_COSTMO */ const string PRC_X2_CRAFTROD_COSTMODIFIER = "PRC_X2_CRAFTROD_COSTMODIFIER"; +/* + * cost modifier of spells crafted into scepters + * note that adding a second spell costs 75% + * defaults to 750 + */ +const string PRC_X2_CRAFTSCEPTER_COSTMODIFIER = "PRC_X2_CRAFTSCEPTER_COSTMODIFIER"; + /* * cost modifier of spells crafted into staffs * note that adding a second spell costs 75% and 3 or more costs 50% @@ -2342,7 +2357,7 @@ const string PRC_XP_GIVE_XP_TO_NPCS = "PRC_XP_GIVE_XP_TO_NPCS"; /** * Setting this switch will turn off the messages about being too far awy to gain XP */ -const string PRC_XP_DISABLE_SPAM = "PRC_XP_DISABLE_SPAM"; +const string PRC_XP_DISABLE_SPAM = "PRC_XP_DISABLE_SPAM"; /** * PCs must be in the same area as the CR to gain XP. @@ -2901,13 +2916,13 @@ const string PRC_PERFECTED_MAP_MULTIPLIER = "PRC_PERFECTED_MAP_MULTIP \******************************************************************************/ /** - * Sets how many seconds it takes to contact a vestige. + * Sets how many seconds it takes to contact a vestige. * Any number less than 6 is ignored */ const string PRC_CONTACT_VESTIGE_TIMER = "PRC_CONTACT_VESTIGE_TIMER"; /** - * Sets how many seconds it takes to bind a vestige. + * Sets how many seconds it takes to bind a vestige. * Any number less than 12 is ignored */ const string PRC_BIND_VESTIGE_TIMER = "PRC_BIND_VESTIGE_TIMER"; @@ -2941,6 +2956,20 @@ const string PRC_PW_SECURITY_CD_CHECK = "PRC_PW_SECURITY_CD_CHECK"; */ const string PRC_DEBUG = "PRC_DEBUG"; +/******************************************************************************\ +* Duration NUI Switches * +\******************************************************************************/ + +/** + * Toggles allowing player to remove friendly PC spells on player through Duration NUI + * instead of just their own spells. + */ +const string PRC_ALLOWED_TO_REMOVE_FRIENDLY_SPELLS = "PRC_ALLOWED_TO_REMOVE_FRIENDLY_SPELLS"; +/** + * Toggles allowing players to see the duration of hostile spells on them. + */ +const string PRC_ALLOWED_TO_SEE_HOSTILE_SPELLS = "PRC_ALLOWED_TO_SEE_HOSTILE_SPELLS"; + diff --git a/src/include/prc_inc_turning.nss b/src/include/prc_inc_turning.nss index 6c819d4..2b6852f 100644 --- a/src/include/prc_inc_turning.nss +++ b/src/include/prc_inc_turning.nss @@ -10,6 +10,8 @@ //::////////////////////////////////////////////// //::////////////////////////////////////////////// +#include "prc_spell_const" + ////////////////////////////////////////////////// /* Function prototypes */ ////////////////////////////////////////////////// diff --git a/src/include/prc_inc_unarmed.nss b/src/include/prc_inc_unarmed.nss index 096b334..27c3245 100644 --- a/src/include/prc_inc_unarmed.nss +++ b/src/include/prc_inc_unarmed.nss @@ -91,6 +91,107 @@ float DamageAvg(int iDamage); /* Function defintions */ ////////////////////////////////////////////////// +// StepDie: increases a damage die by 'nSteps' steps according to d20 SRD progression +// Increment the unarmed damage by nSteps +int StepDie(int nDamage, int nSteps) +{ + int i; + for (i = 0; i < nSteps; i++) + { + switch (nDamage) + { + // 1-dice increments + case IP_CONST_MONSTERDAMAGE_1d2: nDamage = IP_CONST_MONSTERDAMAGE_1d3; break; + case IP_CONST_MONSTERDAMAGE_1d3: nDamage = IP_CONST_MONSTERDAMAGE_1d4; break; + case IP_CONST_MONSTERDAMAGE_1d4: nDamage = IP_CONST_MONSTERDAMAGE_1d6; break; + case IP_CONST_MONSTERDAMAGE_1d6: nDamage = IP_CONST_MONSTERDAMAGE_1d8; break; + case IP_CONST_MONSTERDAMAGE_1d8: nDamage = IP_CONST_MONSTERDAMAGE_1d10; break; + case IP_CONST_MONSTERDAMAGE_1d10: nDamage = IP_CONST_MONSTERDAMAGE_1d12; break; + case IP_CONST_MONSTERDAMAGE_1d12: nDamage = IP_CONST_MONSTERDAMAGE_2d8; break; + + // 2-dice increments + //case IP_CONST_MONSTERDAMAGE_2d3: nDamage = IP_CONST_MONSTERDAMAGE_2d4; break; + case IP_CONST_MONSTERDAMAGE_2d4: nDamage = IP_CONST_MONSTERDAMAGE_2d6; break; + case IP_CONST_MONSTERDAMAGE_2d6: nDamage = IP_CONST_MONSTERDAMAGE_2d8; break; + case IP_CONST_MONSTERDAMAGE_2d8: nDamage = IP_CONST_MONSTERDAMAGE_2d10; break; + case IP_CONST_MONSTERDAMAGE_2d10: nDamage = IP_CONST_MONSTERDAMAGE_2d12; break; + case IP_CONST_MONSTERDAMAGE_2d12: nDamage = IP_CONST_MONSTERDAMAGE_3d10; break; + + // 3-dice increments + case IP_CONST_MONSTERDAMAGE_3d4: nDamage = IP_CONST_MONSTERDAMAGE_3d6; break; + case IP_CONST_MONSTERDAMAGE_3d6: nDamage = IP_CONST_MONSTERDAMAGE_3d8; break; + case IP_CONST_MONSTERDAMAGE_3d8: nDamage = IP_CONST_MONSTERDAMAGE_3d10; break; + case IP_CONST_MONSTERDAMAGE_3d10: nDamage = IP_CONST_MONSTERDAMAGE_3d12; break; + case IP_CONST_MONSTERDAMAGE_3d12: nDamage = IP_CONST_MONSTERDAMAGE_4d8; break; + + // 4-dice increments + case IP_CONST_MONSTERDAMAGE_4d4: nDamage = IP_CONST_MONSTERDAMAGE_4d6; break; + case IP_CONST_MONSTERDAMAGE_4d6: nDamage = IP_CONST_MONSTERDAMAGE_4d8; break; + case IP_CONST_MONSTERDAMAGE_4d8: nDamage = IP_CONST_MONSTERDAMAGE_4d10; break; + case IP_CONST_MONSTERDAMAGE_4d10: nDamage = IP_CONST_MONSTERDAMAGE_4d12; break; + case IP_CONST_MONSTERDAMAGE_4d12: nDamage = IP_CONST_MONSTERDAMAGE_5d8; break; + + // 5-dice increments + case IP_CONST_MONSTERDAMAGE_5d4: nDamage = IP_CONST_MONSTERDAMAGE_5d6; break; + case IP_CONST_MONSTERDAMAGE_5d6: nDamage = IP_CONST_MONSTERDAMAGE_5d8; break; + case IP_CONST_MONSTERDAMAGE_5d8: nDamage = IP_CONST_MONSTERDAMAGE_5d10; break; + case IP_CONST_MONSTERDAMAGE_5d10: nDamage = IP_CONST_MONSTERDAMAGE_5d12; break; + case IP_CONST_MONSTERDAMAGE_5d12: nDamage = IP_CONST_MONSTERDAMAGE_6d10; break; + + // 6-dice increments + //case IP_CONST_MONSTERDAMAGE_6d4: nDamage = IP_CONST_MONSTERDAMAGE_6d6; break; + case IP_CONST_MONSTERDAMAGE_6d6: nDamage = IP_CONST_MONSTERDAMAGE_6d8; break; + case IP_CONST_MONSTERDAMAGE_6d8: nDamage = IP_CONST_MONSTERDAMAGE_6d10; break; + case IP_CONST_MONSTERDAMAGE_6d10: nDamage = IP_CONST_MONSTERDAMAGE_6d12; break; + case IP_CONST_MONSTERDAMAGE_6d12: nDamage = IP_CONST_MONSTERDAMAGE_7d10; break; + + // 7-dice increments + case IP_CONST_MONSTERDAMAGE_7d4: nDamage = IP_CONST_MONSTERDAMAGE_7d6; break; + case IP_CONST_MONSTERDAMAGE_7d6: nDamage = IP_CONST_MONSTERDAMAGE_7d8; break; + case IP_CONST_MONSTERDAMAGE_7d8: nDamage = IP_CONST_MONSTERDAMAGE_7d10; break; + case IP_CONST_MONSTERDAMAGE_7d10: nDamage = IP_CONST_MONSTERDAMAGE_7d12; break; + case IP_CONST_MONSTERDAMAGE_7d12: nDamage = IP_CONST_MONSTERDAMAGE_9d10; break; + + // 8-dice increments + //case IP_CONST_MONSTERDAMAGE_8d4: nDamage = IP_CONST_MONSTERDAMAGE_8d6; break; + case IP_CONST_MONSTERDAMAGE_8d6: nDamage = IP_CONST_MONSTERDAMAGE_8d8; break; + case IP_CONST_MONSTERDAMAGE_8d8: nDamage = IP_CONST_MONSTERDAMAGE_8d10; break; + case IP_CONST_MONSTERDAMAGE_8d10: nDamage = IP_CONST_MONSTERDAMAGE_8d12; break; + case IP_CONST_MONSTERDAMAGE_8d12: nDamage = IP_CONST_MONSTERDAMAGE_10d10; break; + + // 9-dice increments + //case IP_CONST_MONSTERDAMAGE_9d4: nDamage = IP_CONST_MONSTERDAMAGE_9d6; break; + case IP_CONST_MONSTERDAMAGE_9d6: nDamage = IP_CONST_MONSTERDAMAGE_9d8; break; + case IP_CONST_MONSTERDAMAGE_9d8: nDamage = IP_CONST_MONSTERDAMAGE_9d10; break; + case IP_CONST_MONSTERDAMAGE_9d10: nDamage = IP_CONST_MONSTERDAMAGE_9d12; break; + case IP_CONST_MONSTERDAMAGE_9d12: nDamage = IP_CONST_MONSTERDAMAGE_6d20; break; + + // 10-dice increments + //case IP_CONST_MONSTERDAMAGE_10d4: nDamage = IP_CONST_MONSTERDAMAGE_10d6; break; + case IP_CONST_MONSTERDAMAGE_10d6: nDamage = IP_CONST_MONSTERDAMAGE_10d8; break; + case IP_CONST_MONSTERDAMAGE_10d8: nDamage = IP_CONST_MONSTERDAMAGE_10d10; break; + case IP_CONST_MONSTERDAMAGE_10d10: nDamage = IP_CONST_MONSTERDAMAGE_10d12; break; + case IP_CONST_MONSTERDAMAGE_10d12: nDamage = IP_CONST_MONSTERDAMAGE_7d20; break; + + // d20 increments + case IP_CONST_MONSTERDAMAGE_1d20: nDamage = IP_CONST_MONSTERDAMAGE_3d8; break; + case IP_CONST_MONSTERDAMAGE_2d20: nDamage = IP_CONST_MONSTERDAMAGE_4d12; break; + case IP_CONST_MONSTERDAMAGE_3d20: nDamage = IP_CONST_MONSTERDAMAGE_8d8; break; + case IP_CONST_MONSTERDAMAGE_4d20: nDamage = IP_CONST_MONSTERDAMAGE_8d12; break; + case IP_CONST_MONSTERDAMAGE_5d20: nDamage = IP_CONST_MONSTERDAMAGE_9d12; break; //:: Everything breaks down here + case IP_CONST_MONSTERDAMAGE_6d20: nDamage = IP_CONST_MONSTERDAMAGE_1d20; break; + case IP_CONST_MONSTERDAMAGE_7d20: nDamage = IP_CONST_MONSTERDAMAGE_8d20; break; + case IP_CONST_MONSTERDAMAGE_8d20: nDamage = IP_CONST_MONSTERDAMAGE_9d20; break; + case IP_CONST_MONSTERDAMAGE_9d20: nDamage = IP_CONST_MONSTERDAMAGE_10d20; break; + + default: break; // top tier or unknown + } + } + + return nDamage; +} + + // Clean up any extras in the inventory. void CleanExtraFists(object oCreature) { @@ -168,12 +269,169 @@ void ApplyUnarmedAttackEffects(object oCreature) } // Determines the amount of damage a character can do. -// IoDM: +1 dice at level 4, +2 dice at level 8 +// IoDM: +1 die at level 4, +2 dice at level 8 // Sacred Fist: Levels add to monk levels, or stand alone as monk levels. // Shou: 1d6 at level 1, 1d8 at level 2, 1d10 at level 3, 2d6 at level 5 // Monk: 1d6 at level 1, 1d8 at level 4, 1d10 at level 8, 2d6 at level 12, 2d8 at level 16, 2d10 at level 20 // Frostrager: 1d6 at level 1, 1d8 at level 4 int FindUnarmedDamage(object oCreature) +{ + int iDamage = 0; + int iMonk = GetLevelByClass(CLASS_TYPE_MONK, oCreature) + GetLocalInt(oCreature, "LiPengMonk"); + int iShou = GetLevelByClass(CLASS_TYPE_SHOU, oCreature); + int iBrawler = GetLevelByClass(CLASS_TYPE_BRAWLER, oCreature); + int iSacredFist = GetLevelByClass(CLASS_TYPE_SACREDFIST, oCreature); + int iEnlightenedFist = GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCreature); + int iHenshin = GetLevelByClass(CLASS_TYPE_HENSHIN_MYSTIC, oCreature); + int iZuoken = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature); + int iShadowSunNinja = GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oCreature); + int iFrost = GetLevelByClass(CLASS_TYPE_FROSTRAGER, oCreature); + int iAscetic = GetLevelByClass(CLASS_TYPE_NINJA, oCreature); + int iRonove = 0; + int iMonkDamage = 1; + int iShouDamage = 1; + int iBrawlerDamage = 1; + int iFrostDamage = 1; + int iSUSDamage = 1; + int iDieIncrease = 0; + int iSize; + + if (GetHasSpellEffect(VESTIGE_RONOVE, oCreature) && GetLevelByClass(CLASS_TYPE_BINDER, oCreature)) + iRonove = GetLocalInt(oCreature, "RonovesFists"); + + //:: Determine creature size + if( GetIsPolyMorphedOrShifted(oCreature) || GetPRCSwitch(PRC_APPEARANCE_SIZE)) + { + iSize = PRCGetCreatureSize(oCreature) - CREATURE_SIZE_MEDIUM + 5; + } + else + { + iSize = 5; // medium + if (GetHasFeat(FEAT_TINY, oCreature)) iSize = 3; + if (GetHasFeat(FEAT_SMALL, oCreature)) iSize = 4; + if (GetHasFeat(FEAT_LARGE, oCreature)) iSize = 6; + if (GetHasFeat(FEAT_HUGE, oCreature)) iSize = 7; + iSize += PRCGetCreatureSize(oCreature) - PRCGetCreatureSize(oCreature, PRC_SIZEMASK_NONE); + if (iSize < 1) iSize = 1; + if (iSize > 9) iSize = 9; + } + + // Sacred Fist code break protection + if (GetHasFeat(FEAT_SF_CODE, oCreature)) iSacredFist = 0; + + // Combine monk-like levels + iMonk += iSacredFist + iHenshin + iEnlightenedFist + iShou + iZuoken + iShadowSunNinja; + + // Superior Unarmed Strike + if (GetHasFeat(FEAT_SUPERIOR_UNARMED_STRIKE, oCreature)) + { + iMonk += 4; + int nHD = GetHitDice(oCreature); + if (nHD >= 16) iSUSDamage = IP_CONST_MONSTERDAMAGE_2d6; + else if (nHD >= 12) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d10; + else if (nHD >= 8) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d8; + else if (nHD >= 4) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d6; + else if (nHD >= 3) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d4; + } + + // Ascetic Stalker + if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature)) + iMonk += iAscetic; + + // Cap monk progression + if (iMonk > 16 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) iMonk = 16; + else if (iMonk > 20) iMonk = 20; + + // Ronove replacement + if (iRonove > iMonk) iMonk = iRonove; + + // Monk damage calculation (2DA row) + if (iMonk > 0) iMonkDamage = iMonk / 4 + 3; + if (iSize == 5 && iMonkDamage == 7 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) + iMonkDamage = 8; + + // Shou Disciple base damage + if (iShou > 0) + { + int nRow; + if (iShou == 1) nRow = 3; + else if (iShou == 2) nRow = 4; + else if (iShou == 3) nRow = 5; + else if (iShou == 4) nRow = 5; + else if (iShou == 5) nRow = 6; + else nRow = 3; + + if (nRow > 6) nRow = 6; + + iShouDamage = StringToInt(Get2DACache("unarmed_dmg", "size" + IntToString(iSize), nRow)); + } + + // Frostrager + if (iFrost > 0) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d6; + if (iFrost > 3) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d8; + + // Brawler + if (iBrawler > 0) iBrawlerDamage = iBrawler / 6 + 3; + if (iBrawler >= 36) iBrawlerDamage += 2; + + // Armor/shield penalties + if (iMonkDamage > 1) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + int bShieldEq = GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oShield) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; + if (GetBaseAC(oArmor) > 0 || bShieldEq) + iMonkDamage = 1; + } + + if (iShouDamage > 1) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + int bShieldEq = GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oShield) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; + if (GetBaseAC(oArmor) > 3 || bShieldEq) + iShouDamage = 1; + } + + // Determine IoDM die increase + if (GetHasFeat(FEAT_INCREASE_DAMAGE2, oCreature)) iDieIncrease = 2; + else if (GetHasFeat(FEAT_INCREASE_DAMAGE1, oCreature)) iDieIncrease = 1; + + // Lookup monk damage in 2DA + iMonkDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iMonkDamage)); + + // 3.0e monk special cases + if (iSize <= 5 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) + { + if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d6) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d12; + if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d10) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d20; + } + + // Apply IoDM die increase last, after 2DA lookups + if (iMonkDamage > 0) iMonkDamage = StepDie(iMonkDamage, iDieIncrease); + if (iShouDamage > 0) iShouDamage = StepDie(iShouDamage, iDieIncrease); + if (iBrawlerDamage > 0) iBrawlerDamage = StepDie(iBrawlerDamage, iDieIncrease); + if (iFrostDamage > 0) iFrostDamage = StepDie(iFrostDamage, iDieIncrease); + if (iSUSDamage > 0) iSUSDamage = StepDie(iSUSDamage, iDieIncrease); + + // Select best damage + iDamage = iMonkDamage; + iDamage = (DamageAvg(iShouDamage ) > DamageAvg(iDamage)) ? iShouDamage : iDamage; + iDamage = (DamageAvg(iFrostDamage ) > DamageAvg(iDamage)) ? iFrostDamage : iDamage; + iDamage = (DamageAvg(iSUSDamage ) > DamageAvg(iDamage)) ? iSUSDamage : iDamage; + iDamage = (DamageAvg(iBrawlerDamage) > DamageAvg(iDamage)) ? iBrawlerDamage : iDamage; + + if (DEBUG) DoDebug("prc_inc_unarmed: iDamage "+IntToString(iDamage)); + + return iDamage; +} + + +/* int FindUnarmedDamage(object oCreature) { int iDamage = 0; int iMonk = GetLevelByClass(CLASS_TYPE_MONK, oCreature) + GetLocalInt(oCreature, "LiPengMonk"); @@ -195,36 +453,30 @@ int FindUnarmedDamage(object oCreature) int iDieIncrease = 0; int iSize; - if (GetHasSpellEffect(VESTIGE_RONOVE, oCreature) && GetLevelByClass(CLASS_TYPE_BINDER, oCreature)) iRonove = GetLocalInt(oCreature, "RonovesFists"); + if (GetHasSpellEffect(VESTIGE_RONOVE, oCreature) && GetLevelByClass(CLASS_TYPE_BINDER, oCreature)) + iRonove = GetLocalInt(oCreature, "RonovesFists"); - // if the creature is shifted, use model size - // otherwise, we want to stick to what the feats say they "should" be. - // No making pixies with Dragon Appearance for "huge" fist damage. - if( GetIsPolyMorphedOrShifted(oCreature) - || GetPRCSwitch(PRC_APPEARANCE_SIZE)) + // Determine creature size + if( GetIsPolyMorphedOrShifted(oCreature) || GetPRCSwitch(PRC_APPEARANCE_SIZE)) { - iSize = PRCGetCreatureSize(oCreature) - CREATURE_SIZE_MEDIUM + 5; // medium is size 5 for us + iSize = PRCGetCreatureSize(oCreature) - CREATURE_SIZE_MEDIUM + 5; } else { - // Determine creature size by feats. - iSize = 5; // medium is size 5 for us + iSize = 5; // medium if (GetHasFeat(FEAT_TINY, oCreature)) iSize = 3; if (GetHasFeat(FEAT_SMALL, oCreature)) iSize = 4; if (GetHasFeat(FEAT_LARGE, oCreature)) iSize = 6; if (GetHasFeat(FEAT_HUGE, oCreature)) iSize = 7; - // include size changes iSize += PRCGetCreatureSize(oCreature) - PRCGetCreatureSize(oCreature, PRC_SIZEMASK_NONE); - // cap if needed if (iSize < 1) iSize = 1; if (iSize > 9) iSize = 9; } - // Sacred Fist cannot add their levels if they've broken their code. + // Sacred Fist code break protection if (GetHasFeat(FEAT_SF_CODE, oCreature)) iSacredFist = 0; - // several classes add their levels to the monk class, - // or use monk progression if the character has no monk levels + // Combine monk-like levels iMonk += iSacredFist + iHenshin + iEnlightenedFist + iShou + iZuoken + iShadowSunNinja; // Superior Unarmed Strike @@ -232,49 +484,66 @@ int FindUnarmedDamage(object oCreature) { iMonk += 4; int nHD = GetHitDice(oCreature); - if (nHD >= 16) iSUSDamage = IP_CONST_MONSTERDAMAGE_2d6; + if (nHD >= 16) iSUSDamage = IP_CONST_MONSTERDAMAGE_2d6; else if (nHD >= 12) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d10; - else if (nHD >= 8) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d8; - else if (nHD >= 4) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d6; - else if (nHD >= 3) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d4; + else if (nHD >= 8) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d8; + else if (nHD >= 4) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d6; + else if (nHD >= 3) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d4; } // Ascetic Stalker if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature)) iMonk += iAscetic; - // In 3.0e, Monk progression stops after level 16: - if (iMonk > 16 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE) ) iMonk = 16; - // in 3.5e, monk progression stops at 20. - else if(iMonk > 20) iMonk = 20; + // Cap monk progression + if (iMonk > 16 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) iMonk = 16; + else if (iMonk > 20) iMonk = 20; - // Ronove is in place of monk, does not stack + // Ronove replacement if (iRonove > iMonk) iMonk = iRonove; - // monks damage progesses every four levels, starts at 1d6 - if (iMonk > 0) - iMonkDamage = iMonk / 4 + 3; + // Monk damage calculation + if (iMonk > 0) iMonkDamage = iMonk / 4 + 3; - // For medium monks in 3.0e skip 2d8 and go to 1d20 - if(iSize == 5 && iMonkDamage == 7 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE) ) iMonkDamage = 8; + if(iSize == 5 && iMonkDamage == 7 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) + iMonkDamage = 8; // Shou Disciple either adds its level to existing class or does its own damage, depending // on which is better. Here we will determine how much damage the Shou Disciple does // without stacking. - if (iShou > 0) iShouDamage = iShou + 2; // Lv. 1: 1d6, Lv. 2: 1d8, Lv. 3: 1d10 - if (iShou > 3) iShouDamage--; // Lv. 4: 1d10, Lv. 5: 2d6 - iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iShouDamage)); - - // Frostrager does not stack with other damage types - if (iFrost > 0) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d6; // Lv. 1: 1d6 - if (iFrost > 3) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d8; // Lv. 3: 1d8 + //if (iShou > 0) iShouDamage = iShou + 2; // Lv. 1: 1d6, Lv. 2: 1d8, Lv. 3: 1d10 + //if (iShou > 3) iShouDamage--; // Lv. 4: 1d10, Lv. 5: 2d6 + //iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iShouDamage)); + + if (iShou > 0) + { + // Determine 2DA row for Shou progression + int nRow; + if (iShou == 1) nRow = 3; // monk1 + else if (iShou == 2) nRow = 4; // monk2 + else if (iShou == 3) nRow = 5; // monk3 + else if (iShou == 4) nRow = 6; // monk4 + else if (iShou == 5) nRow = 7; // monk5 + else if (iShou == 6) nRow = 8; // monk6 + else if (iShou == 7) nRow = 9; // monk7 + else nRow = 10; // monk8+ + + nRow += iDieIncrease; + if (nRow > 10) nRow = 10; // clamp to max row - // Brawler follows monk progression except for the last one (3d8) - if (iBrawler > 0) iBrawlerDamage = iBrawler / 6 + 3; // 1d6, 1d8, 1d10, 2d6, 2d8, 2d10 - if (iBrawler >= 36) iBrawlerDamage += 2; // 3d8 + // Lookup damage in unarmed_damage.2da using size column + iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), nRow)); + } - // Monks and monk-like classes deal no additional damage when wearing any armor, at - // least in NWN. This is to reflect that. No shields too. + // Frostrager + if (iFrost > 0) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d6; + if (iFrost > 3) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d8; + + // Brawler + if (iBrawler > 0) iBrawlerDamage = iBrawler / 6 + 3; + if (iBrawler >= 36) iBrawlerDamage += 2; + + // Armor/shield penalties if (iMonkDamage > 1) { object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); @@ -284,13 +553,10 @@ int FindUnarmedDamage(object oCreature) GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; if (GetBaseAC(oArmor) > 0 || bShieldEq) - { iMonkDamage = 1; - } } -// Shou Disciples can wear light armor - if (iShouDamage > 1) + if (iShouDamage > 1) { object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); @@ -299,53 +565,31 @@ int FindUnarmedDamage(object oCreature) GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; if (GetBaseAC(oArmor) > 3 || bShieldEq) - { - iShouDamage = 1; - } + iShouDamage = 1; } - // For Initiate of Draconic Mysteries - if (GetHasFeat(FEAT_INCREASE_DAMAGE2, oCreature)) iDieIncrease = 2; - else if (GetHasFeat(FEAT_INCREASE_DAMAGE1, oCreature)) iDieIncrease = 1; - -/* //:: Expansion / Compression powers (Double dipping?) - int nExpansion = GetLocalInt(oCreature, "PRC_Power_Expansion_SizeIncrease"); - int nCompression = GetLocalInt(oCreature, "PRC_Power_Compression_SizeReduction"); - - if (nExpansion) - { - iSize += nExpansion; - } - - if (nCompression) - { - iSize -= nCompression; - } */ + // IoDM die increase + if (GetHasFeat(FEAT_INCREASE_DAMAGE2, oCreature)) iDieIncrease += 2; + else if (GetHasFeat(FEAT_INCREASE_DAMAGE1, oCreature)) iDieIncrease += 1; iMonkDamage += iDieIncrease; iShouDamage += iDieIncrease; iBrawlerDamage += iDieIncrease; iFrostDamage += iDieIncrease; iSUSDamage += iDieIncrease; - - //FloatingTextStringOnCreature("prc_inc_unarmed: Size is: "+IntToString(iSize)+".", oCreature); - //FloatingTextStringOnCreature("prc_inc_unarmed: Pre 2DA Lookup >> iMonkDamage = "+IntToString(iMonkDamage)+".", oCreature); - // now, read the damage from the table in unarmed_dmg.2da + // Lookup final monk damage in 2DA iMonkDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iMonkDamage)); - iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iShouDamage)); - - //FloatingTextStringOnCreature("prc_inc_unarmed: Post 2DA Lookup >> iMonkDamage = "+IntToString(iMonkDamage)+".", oCreature); - // Medium+ monks have some special values on the table in 3.0: + // 3.0e monk special cases if (iSize >= 5 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) { if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d6) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d12; if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d10) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d20; } + // Select best damage iDamage = iMonkDamage; - // Future unarmed classes: if you do your own damage, add in "comparisons" below here. iDamage = (DamageAvg(iShouDamage ) > DamageAvg(iDamage)) ? iShouDamage : iDamage; iDamage = (DamageAvg(iFrostDamage ) > DamageAvg(iDamage)) ? iFrostDamage : iDamage; iDamage = (DamageAvg(iSUSDamage ) > DamageAvg(iDamage)) ? iSUSDamage : iDamage; @@ -354,6 +598,8 @@ int FindUnarmedDamage(object oCreature) return iDamage; } + */ + // Adds appropriate feats to the skin. Stolen from SoulTaker + expanded with overwhelming/devastating critical. void UnarmedFeats(object oCreature) @@ -426,13 +672,13 @@ void UnarmedFists(object oCreature) // Sacred Fists who break their code get no benefits. if (GetHasFeat(FEAT_SF_CODE,oCreature)) iSacFist = 0; - + // The monk adds all these classes. - int iMonkEq = iMonk + iShou + iSacFist + iHenshin + iZuoken + iShadowSunNinja; - + int iMonkEq = iMonk + iShou + iSacFist + iHenshin + iZuoken + iShadowSunNinja; + // Ascetic Stalker if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature)) - iMonkEq += iAscetic; + iMonkEq += iAscetic; // Determine the type of damage the character should do. string sWeapType; diff --git a/src/include/prc_inc_wpnrest.nss b/src/include/prc_inc_wpnrest.nss index 1734b3b..0a59892 100644 --- a/src/include/prc_inc_wpnrest.nss +++ b/src/include/prc_inc_wpnrest.nss @@ -15,6 +15,56 @@ #include "inc_item_props" #include "prc_x2_itemprop" +//:: Detects if "monk" gloves are being equipped & set a +//:: variable if TRUE for use with other functions +void DetectMonkGloveEquip(object oItem) +{ + int nItemType = GetBaseItemType(oItem); + + object oPC = GetItemPossessor(oItem); + if (!GetIsObjectValid(oItem)) + { + if (DEBUG) DoDebug("prc_inc_wpnrest >> DetectMonkGloveEquip(): Unable to determine item possessor"); + return; + } + + if(nItemType != BASE_ITEM_GLOVES && nItemType != BASE_ITEM_BRACER) {return;} + + else if (nItemType == BASE_ITEM_BRACER) + { + if(DEBUG) DoDebug("prc_inc_wpnrest >> DetectMonkGloveEquip(): Bracer found!"); + DeleteLocalInt(oPC, "WEARING_MONK_GLOVES"); + return; + } + else + { + itemproperty ipG = GetFirstItemProperty(oItem); + + while(GetIsItemPropertyValid(ipG)) + { + int nTypeG = GetItemPropertyType(ipG); + + // Damage related properties we care about + if(nTypeG == ITEM_PROPERTY_DAMAGE_BONUS + || nTypeG == ITEM_PROPERTY_ATTACK_BONUS + || nTypeG == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP + || nTypeG == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP + || nTypeG == ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT) + { + if(DEBUG) DoDebug("prc_inc_wpnrest >> DetectMonkGloves(): Monk gloves found!"); + SetLocalInt(oPC, "WEARING_MONK_GLOVES", 1); + return; + } + else + { + if(DEBUG) DoDebug("prc_inc_wpnrest >> DetectMonkGloves(): Monk gloves not found! You should never see this."); + DeleteLocalInt(oPC, "WEARING_MONK_GLOVES"); + return; + } + } + } +} + /** * All of the following functions use the following parameters: * @@ -23,7 +73,6 @@ * @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) { @@ -827,6 +876,7 @@ int IsMeleeWeapon(int nBaseItemType) case BASE_ITEM_CLOAK: case BASE_ITEM_CRAFTED_ROD: case BASE_ITEM_CRAFTED_STAFF: + case BASE_ITEM_CRAFTED_SCEPTER: case BASE_ITEM_CRAFTMATERIALMED: case BASE_ITEM_CRAFTMATERIALSML: case BASE_ITEM_CREATUREITEM: diff --git a/src/include/prc_ipfeat_const.nss b/src/include/prc_ipfeat_const.nss index d419f0b..672fd04 100644 --- a/src/include/prc_ipfeat_const.nss +++ b/src/include/prc_ipfeat_const.nss @@ -262,7 +262,7 @@ const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE = 4638; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_PICK = 4639; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_PICK = 4640; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SAI = 4641; -const int IP_CONST_FEAT_WEAPON_PROFICIENCY_NUNCHUKU = 4642; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_NUNCHAKU = 4642; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_FALCHION = 4643; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SAP = 4644; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_KATAR = 4645; diff --git a/src/include/prc_misc_const.nss b/src/include/prc_misc_const.nss index b083a8e..0171bbb 100644 --- a/src/include/prc_misc_const.nss +++ b/src/include/prc_misc_const.nss @@ -29,8 +29,8 @@ const int BASE_ITEM_CRAFTED_STAFF = 201; const int BASE_ITEM_ELVEN_LIGHTBLADE = 202; const int BASE_ITEM_ELVEN_THINBLADE = 203; const int BASE_ITEM_ELVEN_COURTBLADE = 204; -const int BASE_ITEM_CRAFT_SCEPTER = 249; -const int BASE_ITEM_MAGIC_SCEPTER = 250; +const int BASE_ITEM_CRAFTED_SCEPTER = 249; +const int BASE_ITEM_CRAFTED_VIAL = 250; const int BASE_ITEM_MUNDANE_HERB = 252; const int BASE_ITEM_INFUSED_HERB = 253; diff --git a/src/include/prc_nui_com_inc.nss b/src/include/prc_nui_com_inc.nss index bf9bc51..4bba080 100644 --- a/src/include/prc_nui_com_inc.nss +++ b/src/include/prc_nui_com_inc.nss @@ -3,6 +3,7 @@ #include "psi_inc_psifunc" #include "inc_lookups" #include "nw_inc_nui" +#include "tob_inc_tobfunc" // // GetCurrentSpellLevel @@ -153,8 +154,53 @@ json GreyOutButton(json jButton, float w, float h); // json CreateGreyOutRectangle(float w, float h); +// +// GetTrueClassType +// Gets the true class Id for a provided class Id, mostly for RHD and for +// ToB prestige classes +// +// Arguments: +// nClass:int classId +// +// Returns: +// int the true classId based off nClass +// +int GetTrueClassType(int nClass, object oPC=OBJECT_SELF); + void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0, int nClass=0); + +void CallSpellUnlevelScript(object oPC, int nClass, int nLevel); void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF); +void RemoveIPFeat(object oPC, int ipFeatID); + +void CallSpellUnlevelScript(object oPC, int nClass, int nLevel) +{ + SetScriptParam("UnLevel_ClassChoice", IntToString(nClass)); + SetScriptParam("UnLevel_LevelChoice", IntToString(nLevel)); + ExecuteScript("prc_unlvl_script", oPC); +} + +void RemoveIPFeat(object oPC, int ipFeatID) +{ + object oSkin = GetPCSkin(oPC); + itemproperty ipTest = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ipTest)) + { + // Check if the itemproperty is a bonus feat that has been marked for removal + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT) + { + if (GetItemPropertySubType(ipTest) == ipFeatID) + { + if(DEBUG) DoDebug("_ManeuverRecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest)); + // If so, remove it + RemoveItemProperty(oSkin, ipTest); + } + + } + + ipTest = GetNextItemProperty(oSkin); + } +} int GetCurrentSpellLevel(int nClass, int nLevel) { @@ -401,6 +447,9 @@ json GetSpellIcon(int spellId,int featId=0,int nClass=0) // the FeatID holds the accurate spell icon, not the SpellID int nFeatID = StringToInt(Get2DACache("spells", "FeatID", spellId)); + // however if no featId was found use the spell's icon instead + if (!nFeatID) + return JsonString(Get2DACache("spells", "IconResRef", spellId)); return JsonString(Get2DACache("feat", "Icon", nFeatID)); } @@ -528,3 +577,39 @@ void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF) DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_CLASSID_VAR); } +int GetTrueClassType(int nClass, object oPC=OBJECT_SELF) +{ + if (nClass == CLASS_TYPE_JADE_PHOENIX_MAGE + || nClass == CLASS_TYPE_MASTER_OF_NINE + || nClass == CLASS_TYPE_DEEPSTONE_SENTINEL + || nClass == CLASS_TYPE_BLOODCLAW_MASTER + || nClass == CLASS_TYPE_RUBY_VINDICATOR + || nClass == CLASS_TYPE_ETERNAL_BLADE + || nClass == CLASS_TYPE_SHADOW_SUN_NINJA) + { + int trueClass = GetPrimaryBladeMagicClass(oPC); + return trueClass; + } + + if ((nClass == CLASS_TYPE_SHAPECHANGER + && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) + || (nClass == CLASS_TYPE_OUTSIDER + && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + || (nClass == CLASS_TYPE_ABERRATION + && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT)) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_FEY + && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) + return CLASS_TYPE_BARD; + + return nClass; +} + diff --git a/src/include/prc_nui_consts.nss b/src/include/prc_nui_consts.nss index e1e9c5c..745c860 100644 --- a/src/include/prc_nui_consts.nss +++ b/src/include/prc_nui_consts.nss @@ -156,3 +156,16 @@ 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"; + +////////////////////////////////////////////////// +// // +// Spell Duration NUI // +// // +////////////////////////////////////////////////// + +const string DURATION_NUI_WINDOW_ID = "DurationNUI"; +const string NUI_DURATION_MANUALLY_OPENED_PARAM = "DurationNUIManuallyOpenedParam"; +const string NUI_DURATION_NO_LOOP_PARAM = "DurationNUINoLoopParam"; +const string NUI_DURATION_TRACKED_SPELLS = "durationNUI_trackedSpellList"; +const string NUI_SPELL_DURATION_BASE_BIND = "durationNUI_durationSpellId"; +const string NUI_SPELL_DURATION_SPELLID_BASE_CANCEL_BUTTON = "NuiDurationCancelButtonSpellID"; diff --git a/src/include/prc_nui_lv_inc.nss b/src/include/prc_nui_lv_inc.nss index 9f9adb6..b3d862f 100644 --- a/src/include/prc_nui_lv_inc.nss +++ b/src/include/prc_nui_lv_inc.nss @@ -98,21 +98,9 @@ void OpenNUILevelUpWindow(int nClass, object oPC=OBJECT_SELF); // // CloseNUILevelUpWindow // Closes the NUI Level Up Window if its open +// setting reset to 1 will make it clear the entire cache as if the NUI was never opened // -void CloseNUILevelUpWindow(object oPC=OBJECT_SELF); - -// -// GetTrueClassType -// Gets the true class Id for a provided class Id, mostly for RHD and for -// ToB prestige classes -// -// Arguments: -// nClass:int classId -// -// Returns: -// int the true classId based off nClass -// -int GetTrueClassType(int nClass, object oPC=OBJECT_SELF); +void CloseNUILevelUpWindow(object oPC=OBJECT_SELF, int reset=0); // // GetRemainingSpellChoices @@ -432,7 +420,7 @@ json GetChosenReplaceListObject(object oPC=OBJECT_SELF); // Returns: // int:Boolean TRUE if the spell is a expanded knowledge spell, FALSE otherwise // -int IsExpKnowledgePower(int nClass, int spellbookId); +int IsExpKnowledgePower(int nClass, int spellbookId, object oPC=OBJECT_SELF); // // GetExpKnowledgePowerListRequired @@ -861,7 +849,7 @@ void AddSpellToChosenList(int nClass, int spellbookId, int spellCircle, object o // if the power is a expanded knowledge than we immediatly add it to the // extra list, otherwise check to make sure we have made all choices in our // base list first before adding it to the extra list. - if (IsExpKnowledgePower(nClass, spellbookId) + if (IsExpKnowledgePower(nClass, spellbookId, oPC) || GetRemainingPowerChoices(nClass, spellCircle, oPC, FALSE) == 0) { string sFile = GetClassSpellbookFile(nClass); @@ -1061,53 +1049,20 @@ void OpenNUILevelUpWindow(int nClass, object oPC=OBJECT_SELF) // figure out what the true base class is (mostly true for RHD) int chosenClass = GetTrueClassType(nClass, oPC); SetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR, chosenClass); - ExecuteScript("prc_nui_lv_view", oPC); } -int GetTrueClassType(int nClass, object oPC=OBJECT_SELF) -{ - if (nClass == CLASS_TYPE_JADE_PHOENIX_MAGE - || nClass == CLASS_TYPE_MASTER_OF_NINE - || nClass == CLASS_TYPE_DEEPSTONE_SENTINEL - || nClass == CLASS_TYPE_BLOODCLAW_MASTER - || nClass == CLASS_TYPE_RUBY_VINDICATOR - || nClass == CLASS_TYPE_ETERNAL_BLADE - || nClass == CLASS_TYPE_SHADOW_SUN_NINJA) - { - int trueClass = GetPrimaryBladeMagicClass(oPC); - return trueClass; - } - - if ((nClass == CLASS_TYPE_SHAPECHANGER - && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) - || (nClass == CLASS_TYPE_OUTSIDER - && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) - || (nClass == CLASS_TYPE_ABERRATION - && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) - || (nClass == CLASS_TYPE_MONSTROUS - && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) - || (nClass == CLASS_TYPE_MONSTROUS - && GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) - || (nClass == CLASS_TYPE_MONSTROUS - && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) - || (nClass == CLASS_TYPE_MONSTROUS - && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT)) - return CLASS_TYPE_SORCERER; - if (nClass == CLASS_TYPE_FEY - && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) - return CLASS_TYPE_BARD; - - return nClass; -} - -void CloseNUILevelUpWindow(object oPC=OBJECT_SELF) +void CloseNUILevelUpWindow(object oPC=OBJECT_SELF, int reset=0) { int currentClass = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR); // if we are refreshing the NUI but not finished we need to clear some caching done // to save computation time as they will need to be reprocessed. DeleteLocalJson(oPC, NUI_LEVEL_UP_DISCIPLINE_INFO_VAR + IntToString(currentClass)); SetLocalInt(oPC, NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR, -20); + if (reset) + { + ClearLevelUpNUICaches(currentClass, oPC); + } int nPreviousToken = NuiFindWindow(oPC, NUI_LEVEL_UP_WINDOW_ID); if (nPreviousToken != 0) { @@ -1126,7 +1081,7 @@ int ShouldSpellButtonBeEnabled(int nClass, int circleLevel, int spellbookId, obj // if its an expanded knowledge choice and we have already made all our // exp knowledge choices then it needs to be disabled. - if (IsExpKnowledgePower(nClass, spellbookId)) + if (IsExpKnowledgePower(nClass, spellbookId, oPC)) { int remainingExp = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC) + GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC); @@ -1539,7 +1494,7 @@ int GetRemainingSpellChoices(int nClass, int circleLevel, object oPC=OBJECT_SELF // default logic for spont casters totalSpellsKnown = GetSpellKnownMaxCount(casterLevel, circleLevel, nClass, oPC); // Favoured Soul has more 0 choices than there are spells for some reason - if (nClass == CLASS_TYPE_FAVOURED_SOUL && circleLevel == 0 && totalSpellsKnown > 7) + if (nClass == CLASS_TYPE_FAVOURED_SOUL && circleLevel == 0 && totalSpellsKnown > 6) totalSpellsKnown = 7; // logic for spont casters @@ -1554,6 +1509,7 @@ int GetRemainingSpellChoices(int nClass, int circleLevel, object oPC=OBJECT_SELF if (chosenCircle == circleLevel) SetLocalInt(oPC, NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR, remainingChoices); + if (DEBUG) DoDebug("Remaining spell choices is " + IntToString(remainingChoices)); return remainingChoices; } @@ -1664,11 +1620,13 @@ void RemoveSpells(int nClass, object oPC=OBJECT_SELF) { string sFile = GetClassSpellbookFile(nClass); string sSpellBook = GetSpellsKnown_Array(nClass); + string spellsAtLevelList = "SpellsKnown_" + IntToString(nClass) + "_AtLevel" + IntToString(GetHitDice(oPC)); // remove the spell from the spellbook array_extract_int(oPC, sSpellBook, nSpellbookID); + array_extract_int(oPC, spellsAtLevelList, nSpellbookID); // wipe the spell from the player int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)); - WipeSpellFromHide(ipFeatID, oPC); + RemoveIPFeat(oPC, ipFeatID); } } } @@ -1779,6 +1737,7 @@ void LearnSpells(int nClass, object oPC=OBJECT_SELF) // get location of persistant storage on the hide string sSpellbook = GetSpellsKnown_Array(nClass, nSpellLevel); + if (DEBUG) DoDebug("Adding spell " + IntToString(nSpellbookID) + "to " + sSpellbook); //object oToken = GetHideToken(oPC); // Create spells known persistant array if it is missing @@ -1789,13 +1748,25 @@ void LearnSpells(int nClass, object oPC=OBJECT_SELF) nSize = 0; } + string spellsAtLevelList = "SpellsKnown_" + IntToString(nClass) + "_AtLevel" + IntToString(GetHitDice(oPC)); + int spellsAtLevelSize = persistant_array_get_size(oPC, spellsAtLevelList); + if (spellsAtLevelSize < 0) + { + persistant_array_create(oPC, spellsAtLevelList); + spellsAtLevelSize = 0; + } + // set the list of spells learned at this level + string sFile = GetClassSpellbookFile(nClass); + int spellId = StringToInt(Get2DACache(sFile, "SpellID", nSpellbookID)); + persistant_array_set_int(oPC, spellsAtLevelList, spellsAtLevelSize, spellId); + if (DEBUG) DoDebug("Adding spells to array " + spellsAtLevelList); + // Mark the spell as known (e.g. add it to the end of oPCs spellbook) persistant_array_set_int(oPC, sSpellbook, nSize, nSpellbookID); if (nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) { // add spell - string sFile = GetClassSpellbookFile(nClass); string sArrayName = "NewSpellbookMem_" + IntToString(nClass); int featId = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)); int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)); @@ -1975,7 +1946,7 @@ void RemoveSpellKnown(int nClass, int spellbookId, object oPC=OBJECT_SELF, int n //if we could not find the spell here, something went wrong if (persistant_array_extract_int(oPC, sTestArray, spellID) < 0) { - SendMessageToPC(oPC, "Could not find spellID " + IntToString(spellID) + " in the class's spellbook!"); + if (DEBUG) DoDebug("Could not find spellID " + IntToString(spellID) + " in the class's spellbook!"); return; } } @@ -2002,10 +1973,7 @@ void RemoveSpellKnown(int nClass, int spellbookId, object oPC=OBJECT_SELF, int n // remove spell from player int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", spellbookId)); - itemproperty ipFeat = PRCItemPropertyBonusFeat(ipFeatID); - object oSkin = GetPCSkin(oPC); - RemoveItemProperty(oSkin, ipFeat); - CheckAndRemoveFeat(oSkin, ipFeat); + RemoveIPFeat(oPC, ipFeatID); } json GetSpellIDsKnown(int nClass, object oPC=OBJECT_SELF, int nList=0) @@ -2088,7 +2056,7 @@ string ReasonForDisabledSpell(int nClass, int spellbookId, object oPC=OBJECT_SEL // if its an expanded knowledge choice and we have already made all our // exp knowledge choices then it needs to be disabled. - if (IsExpKnowledgePower(nClass, spellbookId)) + if (IsExpKnowledgePower(nClass, spellbookId, oPC)) { int remainingExp = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC) + GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC); @@ -2322,11 +2290,15 @@ json GetChosenReplaceListObject(object oPC=OBJECT_SELF) //////////////////////////////////////////////////////////////////////////// -int IsExpKnowledgePower(int nClass, int spellbookId) +int IsExpKnowledgePower(int nClass, int spellbookId, object oPC=OBJECT_SELF) { string sFile = GetClassSpellbookFile(nClass); int isExp = StringToInt(Get2DACache(sFile, "Exp", spellbookId)); - return isExp; + if (isExp) + return TRUE; + int featId = StringToInt(Get2DACache(sFile, "FeatID", spellbookId)); + int isOuterDomain = (featId) ? !CheckPowerPrereqs(featId, oPC) : FALSE; + return isOuterDomain; } json GetCurrentPowerList(object oPC=OBJECT_SELF) @@ -2346,12 +2318,8 @@ int ShouldAddPower(int nClass, int spellbookId, object oPC=OBJECT_SELF) string sFile = GetClassSpellbookFile(nClass); int featId = StringToInt(Get2DACache(sFile, "FeatID", spellbookId)); int isExp = StringToInt(Get2DACache(sFile, "Exp", spellbookId)); - // if you don't have the prereqs for a power then don't add it. Specific for - // psions - if (!CheckPowerPrereqs(featId, oPC)) - return FALSE; // if the power is a expanded knowledge power - if (isExp) + if (!CheckPowerPrereqs(featId, oPC) || isExp) { // and we have a expanded knowledge choice left to make then show // the button @@ -2360,10 +2328,12 @@ int ShouldAddPower(int nClass, int spellbookId, object oPC=OBJECT_SELF) int currentCircle = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CIRCLE_VAR); int choicesLeft = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC); + if (DEBUG) DoDebug("You still have " + IntToString(choicesLeft) + " expanded power choices left!"); if (choicesLeft && (currentCircle <= (maxLevel-1))) addPower = TRUE; choicesLeft = GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC); - if (choicesLeft) + if (DEBUG) DoDebug("You still have " + IntToString(choicesLeft) + " epic expanded power choices left!"); + if (choicesLeft && (currentCircle <= (maxLevel-1))) addPower = TRUE; // otherwise don't show the button. return addPower; @@ -2383,7 +2353,7 @@ void LearnPowers(int nClass, object oPC=OBJECT_SELF) int nSpellbookID = JsonGetInt(JsonArrayGet(powerList, i)); // get the expanded knowledge list we are adding to if any int expKnow = GetExpKnowledgePowerListRequired(nClass, nSpellbookID, oPC); - AddPowerKnown(oPC, nClass, nSpellbookID, TRUE, GetManifesterLevel(oPC, nClass, TRUE), expKnow); + AddPowerKnown(oPC, nClass, nSpellbookID, TRUE, GetHitDice(oPC), expKnow); } } @@ -2570,7 +2540,7 @@ int HasPreRequisitesForManeuver(int nClass, int spellbookId, object oPC=OBJECT_S if (chosenDisc != JsonNull()) { int nManCount = (JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_MANEUVER))) - + JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_STANCE)))); + + JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_STANCE)))); if (nManCount >= prereqs) return TRUE; } @@ -3166,13 +3136,18 @@ json GetInvokerKnownListObject(int nClass, object oPC=OBJECT_SELF) } SetLocalJson(oPC, NUI_LEVEL_UP_KNOWN_INVOCATIONS_CACHE_VAR + IntToString(nClass), knownObject); + if (DEBUG) DoDebug("Printing json representation of allowed invocations for class " + IntToString(nClass)); + if (DEBUG) DoDebug(JsonDump(knownObject, 2)); return knownObject; } int GetRemainingInvocationChoices(int nClass, int chosenCircle, object oPC=OBJECT_SELF, int extra=TRUE) { + if (DEBUG) DoDebug ("Getting remaining invocation choices at " + IntToString(chosenCircle) + " circle"); int remaining = 0; int nLevel = GetInvokerLevel(oPC, nClass); + if (nClass == CLASS_TYPE_DRAGON_SHAMAN) nLevel = GetLevelByClass(nClass, oPC); + if (DEBUG) DoDebug("Invoker level is " + IntToString(nLevel)); json knownObject = GetInvokerKnownListObject(nClass, oPC); json chosenInv = GetChosenSpellListObject(nClass, oPC); @@ -3200,8 +3175,10 @@ int GetRemainingInvocationChoices(int nClass, int chosenCircle, object oPC=OBJEC currentChosen += 1; } } + if (DEBUG) DoDebug(IntToString(currentChosen) + " incantations chosen at " + IntToString(chosenCircle) + " circle"); int allowedAtCircle = JsonGetInt(JsonObjectGet(currentLevelKnown, IntToString(i))); + if (DEBUG) DoDebug(IntToString(allowedAtCircle) + " incantations allowed at " + IntToString(chosenCircle) + " circle"); remaining = (allowedAtCircle - currentChosen + remaining); // if the circle is below the chosen circle and we have a positive remaining, diff --git a/src/include/prc_nui_sbd_inc.nss b/src/include/prc_nui_sbd_inc.nss index 4f84658..8027a70 100644 --- a/src/include/prc_nui_sbd_inc.nss +++ b/src/include/prc_nui_sbd_inc.nss @@ -28,11 +28,17 @@ void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int re void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0) { // look for existing window and destroy - int nPreviousToken = NuiFindWindow(OBJECT_SELF, NUI_SPELL_DESCRIPTION_WINDOW_ID); + int nPreviousToken = NuiFindWindow(oPlayer, NUI_SPELL_DESCRIPTION_WINDOW_ID); + if(nPreviousToken != 0) + { + NuiDestroy(oPlayer, nPreviousToken); + } + +/* int nPreviousToken = NuiFindWindow(OBJECT_SELF, NUI_SPELL_DESCRIPTION_WINDOW_ID); if(nPreviousToken != 0) { NuiDestroy(OBJECT_SELF, nPreviousToken); - } + } */ // in order of accuracy for names it goes RealSpellID > SpellID > FeatID string spellName; diff --git a/src/include/prc_spell_const.nss b/src/include/prc_spell_const.nss index 1537b2f..c93417f 100644 --- a/src/include/prc_spell_const.nss +++ b/src/include/prc_spell_const.nss @@ -13,6 +13,9 @@ const int SPELL_BLACKLIGHT = 2091; const int SPELL_BARD_SONG = 411; const int SPELL_BARD_CURSE_SONG = 644; +//:: Monk +const int SPELL_MONK_ABUNDANT_STEP = 17986; + //:: Epic Level Handbook const int SPELL_EPIC_SWARM_OF_ARROWS = 17996; @@ -1411,6 +1414,10 @@ const int SPELL_FOT_LEONALS_ROAR = 17993; const int SPELL_FOT_LIONS_SWIFTNESS = 17994; const int SPELL_FAVORED_OF_THE_COMPANIONS = 17995; +//:: Magic Item Compendium +const int SPELL_AROMA_OF_CURDLED_DEATH = 17987; +const int SPELL_ELIXIR_OF_THE_BEETLE = 17987; + //x const int SPELL_TENSERS_FLOATING_DISK = 3849; const int SPELL_WOLFSKIN = 3850; diff --git a/src/include/prc_spellf_inc.nss b/src/include/prc_spellf_inc.nss index 1b6042f..f40f0f6 100644 --- a/src/include/prc_spellf_inc.nss +++ b/src/include/prc_spellf_inc.nss @@ -487,7 +487,8 @@ void SpellfireChargeItem(object oPC, object oItem) AddSpellfireLevels(oPC, nNewCharges - 50); nNewCharges = 50; } - SetItemCharges(oItem, nCharges + nExpend); + //SetItemCharges(oItem, nCharges + nExpend); + SetItemCharges(oItem, nNewCharges); //Assuming 50 is the maximum //refunding excess charges } diff --git a/src/include/prc_weap_apt.nss b/src/include/prc_weap_apt.nss index 8fa1043..7bda1b7 100644 --- a/src/include/prc_weap_apt.nss +++ b/src/include/prc_weap_apt.nss @@ -47,12 +47,10 @@ int GetWeaponFocusFeatItemProperty(int nFeatNumber) if(nItemProperty != -1) return nItemProperty; nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT, IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT); if(nItemProperty != -1) return nItemProperty; - nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_LANCE, IP_CONST_FEAT_WEAPON_FOCUS_GOAD); + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW, IP_CONST_FEAT_WEAPON_FOCUS_GOAD); if(nItemProperty != -1) return nItemProperty; nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE); if(nItemProperty != -1) return nItemProperty; - nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW, IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW); - if(nItemProperty != -1) return nItemProperty; nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE); if(nItemProperty != -1) return nItemProperty; nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE); diff --git a/src/include/prc_x2_craft.nss b/src/include/prc_x2_craft.nss index 608438a..c18dcc3 100644 --- a/src/include/prc_x2_craft.nss +++ b/src/include/prc_x2_craft.nss @@ -61,6 +61,7 @@ const int X2_CI_CRAFTROD_EPIC_FEAT_ID = 3490; const int X2_CI_CRAFTSTAFF_FEAT_ID = 2928; const int X2_CI_CRAFTSTAFF_EPIC_FEAT_ID = 3491; const int X2_CI_CREATEINFUSION_FEAT_ID = 25960; +const int X2_CI_CRAFTSCEPTER_FEAT_ID = 25962; const string X2_CI_BREWPOTION_NEWITEM_RESREF = "x2_it_pcpotion"; // ResRef for new potion item const string X2_CI_SCRIBESCROLL_NEWITEM_RESREF = "x2_it_pcscroll"; // ResRef for new scroll item @@ -276,6 +277,7 @@ int CIGetIsCraftFeatBaseItem(object oItem) nBt == BASE_ITEM_BLANK_WAND || nBt == BASE_ITEM_CRAFTED_ROD || nBt == BASE_ITEM_CRAFTED_STAFF || + nBt == BASE_ITEM_CRAFTED_SCEPTER || nBt == BASE_ITEM_MUNDANE_HERB) return TRUE; else @@ -302,7 +304,7 @@ object CICraftBrewPotion(object oCreator, int nSpellID ) return OBJECT_INVALID; } - /* //just a tad retarded, don't you think? other crafting feats are not similarly restricted + /* //just a tad silly, don't you think? other crafting feats are not similarly restricted //Uses per day int nUsesAllowed; @@ -710,6 +712,7 @@ object CICraftScribeScroll(object oCreator, int nSpellID) return oTarget; } */ + // ----------------------------------------------------------------------------- // Returns TRUE if the player used the last spell to brew a potion // ----------------------------------------------------------------------------- @@ -865,7 +868,6 @@ These dont work as IPs since they are hardcoded */ } - // ----------------------------------------------------------------------------- // Returns TRUE if the player used the last spell to create a scroll // ----------------------------------------------------------------------------- @@ -1133,6 +1135,169 @@ These dont work as IPs since they are hardcoded */ return FALSE; } +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to craft a scepter +// ----------------------------------------------------------------------------- +int CICraftCheckCraftScepter(object oSpellTarget, object oCaster, int nSpellID = 0) +{ + + if(nSpellID == 0) nSpellID = PRCGetSpellId(); + int nCasterLevel = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); + int bSuccess = TRUE; + int nCount = 0; + itemproperty ip = GetFirstItemProperty(oSpellTarget); + int nMetaMagic = PRCGetMetaMagicFeat(); + + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) + nCount++; + ip = GetNextItemProperty(oSpellTarget); + } + if(nCount >= 2) //:: Scepters are limited to two spells + { + FloatingTextStringOnCreature("* Failure - Too many castspell itemproperties *", oCaster); + return TRUE; + } + if(!GetHasFeat(X2_CI_CRAFTSCEPTER_FEAT_ID, oCaster)) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; // tried item creation but do not know how to do it + } + if(CIGetIsSpellRestrictedFromCraftFeat(nSpellID, X2_CI_CRAFTSCEPTER_FEAT_ID)) + { + FloatingTextStrRefOnCreature(16829169, oCaster); // can not be used with this feat + return TRUE; + } + + // Get the base spell level (circle) before metamagic adjustments + int nBaseLevel = CIGetSpellInnateLevel(nSpellID, TRUE); + + // Check if spell circle is 7th level or lower + if (nBaseLevel > 7) + { + //FloatingTextStrRefOnCreature(83623, oCaster); // Spell level too high + FloatingTextStringOnCreature("* Failure - scepters can not hold spells higher than level 7", oCaster); + return TRUE; + } + + int nLevel = nBaseLevel; + + if(GetPRCSwitch(PRC_CRAFT_SCEPTER_CASTER_LEVEL)) + { + switch(nMetaMagic) + { + case METAMAGIC_EMPOWER: + nLevel += 2; + break; + case METAMAGIC_EXTEND: + nLevel += 1; + break; + case METAMAGIC_MAXIMIZE: + nLevel += 3; + break; +/* case METAMAGIC_QUICKEN: + nLevel += 1; + break; + case METAMAGIC_SILENT: + nLevel += 5; + break; + case METAMAGIC_STILL: + nLevel += 6; + break; + These dont work as IPs since they are hardcoded */ + } + } + + int nCostMod = GetPRCSwitch(PRC_X2_CRAFTSCEPTER_COSTMODIFIER); + if(!nCostMod) nCostMod = 750; + int nLvlRow = IPGetIPConstCastSpellFromSpellID(nSpellID); + int nCLevel = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow)); + int nCost = CIGetCraftGPCost(nLevel, nCostMod, PRC_CRAFT_SCEPTER_CASTER_LEVEL); + + //discount for second spell + if(nCount+1 == 2) + nCost = (nCost/2); + + //takes epic xp costs into account + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_CRAFT_SCEPTER, (nMetaMagic > 0)); + + if(costs.nGoldCost < 1) costs.nXPCost = 1; + if(costs.nXPCost < 1) costs.nXPCost = 1; + //if(GetGold(oCaster) < nGoldCost) // enough gold? + if (!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStrRefOnCreature(3786, oCaster); // Item Creation Failed - not enough gold! + return TRUE; + } + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = (nHD * (nHD - 1)) * 500; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + //if (nMinXPForLevel > nNewXP || nNewXP == 0 ) + if (!GetHasXPToSpend(oCaster, costs.nXPCost)) + { + FloatingTextStrRefOnCreature(3785, oCaster); // Item Creation Failed - Not enough XP + return TRUE; + } + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nSpellID, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID); + if (nPropID == 0 && nSpellID != 0) + { + FloatingTextStrRefOnCreature(84544,oCaster); + return TRUE; + } + if (nPropID != -1) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE),oSpellTarget); + + if(GetPRCSwitch(PRC_CRAFT_SCEPTER_CASTER_LEVEL)) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellCasterLevel(nSpellID, nCasterLevel),oSpellTarget); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellMetamagic(nSpellID, PRCGetMetaMagicFeat()),oSpellTarget); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellDC(nSpellID, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)),oSpellTarget); + } + } + else + bSuccess = FALSE; + + if(bSuccess) + { + //TakeGoldFromCreature(nGoldCost, oCaster, TRUE); + //SetXP(oCaster, nNewXP); + SpendXP(oCaster, costs.nXPCost); + SpendGP(oCaster, costs.nGoldCost); + //DestroyObject (oSpellTarget); + FloatingTextStrRefOnCreature(8502, oCaster); // Item Creation successful + + //advance time here + if(!costs.nTimeCost) costs.nTimeCost = 1; + AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost)); + string sName; + sName = GetName(oCaster)+"'s Magic Scepter"; + SetItemCharges(oSpellTarget, 50); + //sName = Get2DACache("spells", "Name", nID); + //sName = "Wand of "+GetStringByStrRef(StringToInt(sName)); + SetName(oSpellTarget, sName); + SetItemCursedFlag(oSpellTarget, FALSE); + SetDroppableFlag(oSpellTarget, TRUE); + return TRUE; + } + else + { + FloatingTextStrRefOnCreature(76417, oCaster); // Item Creation Failed + return TRUE; + } + return TRUE; +} + +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to craft a staff +// ----------------------------------------------------------------------------- int CICraftCheckCraftStaff(object oSpellTarget, object oCaster, int nSpellID = 0) { @@ -1281,6 +1446,9 @@ These dont work as IPs since they are hardcoded */ return TRUE; } +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to craft a rod +// ----------------------------------------------------------------------------- int CICraftCheckCraftRod(object oSpellTarget, object oCaster, int nSpellID = 0) { @@ -1427,6 +1595,7 @@ These dont work as IPs since they are hardcoded */ return TRUE; } + int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0) { if(!GetIsObjectValid(oCaster)) oCaster = OBJECT_SELF; @@ -1478,8 +1647,9 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject(); int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); -//:: [TO DO] make Inscribe Epic Rune. - if(nCaster > 20) nCaster = 20; +//:: Check for Inscribe Epic Runes and cap CL at 20 if it doesn't exist. + int bEpicRunes = GetHasFeat(EPIC_FEAT_INSCRIBE_EPIC_RUNES, oCaster); + if (!bEpicRunes) { if(nCaster > 20) nCaster = 20; } int nDC = PRCGetSaveDC(oTarget, oCaster); if(!nSpell) nSpell = PRCGetSpellId(); @@ -1612,9 +1782,14 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI // If they have this active, the bonuses are already added, so skip // If they don't, add the bonuses down below if(GetHasSpellEffect(SPELL_RUNE_CHANT)) - nRuneChant = 0; + nRuneChant = 0; - itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, PRCGetCasterLevel()); + //:: Check for Inscribe Epic Runes and cap CL at 20 if it doesn't exist. + nCaster = PRCGetCasterLevel(); + + if (!bEpicRunes) { if(nCaster > 20) nCaster = 20; } + + itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, nCaster); AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel,oRune); itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpell, PRCGetMetaMagicFeat()); AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta,oRune); @@ -1721,13 +1896,23 @@ int AttuneGem(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, } // oTarget here should be the gem. If it's not, fail. if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject(); - // Only accepts bioware gems - if (GetStringLeft(GetResRef(oTarget), 5) == "it_gem") + // Only accepts bioware gems & Craftable Natural Resources gems, but not gem dust. + int bIsBioGem = (GetStringLeft(GetResRef(oTarget), 5) == "it_gem"); + int bIsCNRGem = (GetStringLeft(GetResRef(oTarget), 6) == "cnrgem"); + int bIsDust = (GetStringLeft(GetResRef(oTarget), 10) == "cnrgemdust"); + + if (!(bIsBioGem || bIsCNRGem) || bIsDust) + { + FloatingTextStringOnCreature("Spell target is not a valid gem.", oCaster, FALSE); + return TRUE; + } + +/* if ((GetStringLeft(GetResRef(oTarget), 5) == "it_gem") || (GetStringLeft(GetResRef(oTarget), 6) == "cnrgem") && (GetStringLeft(GetResRef(oTarget), 10) != "cnrgemdust")) { FloatingTextStringOnCreature("Spell target is not a valid gem.", oCaster, FALSE); // And out we go return TRUE; - } + } */ int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); int nDC = PRCGetSaveDC(oTarget, oCaster); @@ -2123,6 +2308,13 @@ int CIGetSpellWasUsedForItemCreation(object oSpellTarget) // ------------------------------------------------- nRet = CICraftCheckCraftStaff(oSpellTarget,oCaster); break; + + case BASE_ITEM_CRAFTED_SCEPTER : + // ------------------------------------------------- + // Craft Scepter + // ------------------------------------------------- + nRet = CICraftCheckCraftScepter(oSpellTarget,oCaster); + break; case BASE_ITEM_MUNDANE_HERB : // ------------------------------------------------- @@ -2922,7 +3114,12 @@ int GetMagicalArtisanFeat(int nCraftingFeat) { nReturn = FEAT_MAGICAL_ARTISAN_CREATE_INFUSION; break; - } + } + case FEAT_CRAFT_SCEPTER: + { + nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_SCEPTER; + break; + } default: { if(DEBUG) DoDebug("GetMagicalArtisanFeat: invalid crafting feat"); diff --git a/src/include/prc_x2_itemprop.nss b/src/include/prc_x2_itemprop.nss index 17885cd..1abf20c 100644 --- a/src/include/prc_x2_itemprop.nss +++ b/src/include/prc_x2_itemprop.nss @@ -20,9 +20,6 @@ //:: Last Update: 2003-10-07 //::////////////////////////////////////////////// -//:: Test void -//:: void main (){} - //Changed by primogenitor to include CEP itemtypes // * The tag of the ip work container, a placeable which has to be set into each @@ -697,6 +694,7 @@ if(nItem == BASE_ITEM_BASTARDSWORD || nItem == BASE_ITEM_SICKLE || nItem == BASE_ITEM_TWOBLADEDSWORD || nItem == BASE_ITEM_CLUB + || nItem == BASE_ITEM_CRAFTED_SCEPTER || nItem == BASE_ITEM_DAGGER || nItem == BASE_ITEM_DIREMACE || nItem == BASE_ITEM_HEAVYFLAIL @@ -729,6 +727,7 @@ if(nItem == BASE_ITEM_BASTARDSWORD || nItem == BASE_ITEM_ELVEN_THINBLADE || nItem == BASE_ITEM_ELVEN_COURTBLADE || nItem == BASE_ITEM_CRAFTED_STAFF + || nItem == BASE_ITEM_CRAFTED_SCEPTER || nItem == 300 //CEP Trident || nItem == 303 //CEP Sai || nItem == 304 //CEP nunchaku @@ -1612,31 +1611,140 @@ int IPGetDamageBonusConstantFromNumber(int nNumber) // oOld - Item equipped before polymorphing (source for item props) // oNew - Item equipped after polymorphing (target for item props) // bWeapon - Must be set TRUE when oOld is a weapon. -// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- void IPWildShapeCopyItemProperties(object oOld, object oNew, int bWeapon = FALSE) { - if (GetIsObjectValid(oOld) && GetIsObjectValid(oNew)) - { - itemproperty ip = GetFirstItemProperty(oOld); - while (GetIsItemPropertyValid(ip)) - { - if (bWeapon) - { - if (GetWeaponRanged(oOld) == GetWeaponRanged(oNew) ) - { - AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); - } - } - else - { - AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); - } - ip = GetNextItemProperty(oOld); + // Invalid source/target + if (!GetIsObjectValid(oOld) || !GetIsObjectValid(oNew)) + return; + + // Determine possessor + object oPC = GetItemPossessor(oOld); + if (!GetIsObjectValid(oPC)) + oPC = GetItemPossessor(oNew); - } - } + if (!GetIsObjectValid(oPC)) + { + if (DEBUG) DoDebug("IPWS: Unable to determine item possessor"); + return; + } + + // Determine glove state once + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + + // Weapon ranged mismatch = do nothing (intent is no partial copy) + if (bWeapon && GetWeaponRanged(oOld) != GetWeaponRanged(oNew)) + { + if (DEBUG) DoDebug("IPWS: Weapon ranged mismatch — skipping all IP copy"); + return; + } + + // Begin property copy + itemproperty ip = GetFirstItemProperty(oOld); + while (GetIsItemPropertyValid(ip)) + { + int nType = GetItemPropertyType(ip); + + // If copying from gloves and monk gloves are active + if (bMonkGloves + && (nType == ITEM_PROPERTY_DAMAGE_BONUS + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP)) + { + // Always apply glove damage IPs + AddItemProperty(DURATION_TYPE_PERMANENT, ip, oNew); + ip = GetNextItemProperty(oOld); + continue; + } + + // Normal weapon pass + if (bWeapon) + { + // If monk gloves active ? skip ALL weapon damage IPs + if (bMonkGloves + && (nType == ITEM_PROPERTY_DAMAGE_BONUS + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP)) + { + ip = GetNextItemProperty(oOld); + continue; + } + + AddItemProperty(DURATION_TYPE_PERMANENT, ip, oNew); + } + else + { + AddItemProperty(DURATION_TYPE_PERMANENT, ip, oNew); + } + + ip = GetNextItemProperty(oOld); + } } + +/* // ---------------------------------------------------------------------------- +// GZ, Sept. 30 2003 +// Special Version of Copy Item Properties for use with greater wild shape +// oOld - Item equipped before polymorphing (source for item props) +// oNew - Item equipped after polymorphing (target for item props) +// bWeapon - Must be set TRUE when oOld is a weapon. +// ---------------------------------------------------------------------------- +void IPWildShapeCopyItemProperties(object oOld, object oNew, int bWeapon = FALSE) +{ + if (!GetIsObjectValid(oOld) || !GetIsObjectValid(oNew)) + return; + + object oPC = GetItemPossessor(oOld); + if (!GetIsObjectValid(oPC)) + { + oPC = GetItemPossessor(oNew); + } + if (!GetIsObjectValid(oPC)) + { + if (DEBUG) DoDebug("IPWS: Unable to determine item possessor"); + return; + } + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + + itemproperty ip = GetFirstItemProperty(oOld); + while (GetIsItemPropertyValid(ip)) + { + if (bWeapon) + { + // Gloves override weapon damage — skip weapon damage properties + if (bMonkGloves) + { + int nType = GetItemPropertyType(ip); + + // skip damage props + if (nType == ITEM_PROPERTY_DAMAGE_BONUS + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP) + { + if (DEBUG) DoDebug("IPWS: SKIPPED weapon damage IP"); + } + else + { + if (DEBUG) DoDebug("IPWS: Applied non-damage weapon IP"); + AddItemProperty(DURATION_TYPE_PERMANENT, ip, oNew); + } + + } + else if (GetWeaponRanged(oOld) == GetWeaponRanged(oNew) ) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); + } + } + else + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); + } + + ip = GetNextItemProperty(oOld); + } +} */ + // ---------------------------------------------------------------------------- // Returns the current enhancement bonus of a weapon (+1 to +20), 0 if there is // no enhancement bonus. You can test for a specific type of enhancement bonus @@ -2018,4 +2126,6 @@ int IPOnHitSaveDC(int nSaveDC) if (nSaveDC > 26) nIPBonus = IP_CONST_ONHIT_SAVEDC_26; return nIPBonus; -} */ \ No newline at end of file +} */ + +//:: void main(){} \ No newline at end of file diff --git a/src/include/prcsp_archmaginc.nss b/src/include/prcsp_archmaginc.nss index 4287a19..72e5d44 100644 --- a/src/include/prcsp_archmaginc.nss +++ b/src/include/prcsp_archmaginc.nss @@ -73,6 +73,7 @@ void SetMasteryOfElements(); //#include "lookup_2da_spell" #include "prcsp_reputation" +#include "prc_inc_core" //#include "prc_inc_spells" diff --git a/src/include/psi_inc_ac_spawn.nss b/src/include/psi_inc_ac_spawn.nss index 5b3947e..43765dd 100644 --- a/src/include/psi_inc_ac_spawn.nss +++ b/src/include/psi_inc_ac_spawn.nss @@ -11,6 +11,7 @@ #include "prc_ipfeat_const" #include "prc_feat_const" #include "inc_vfx_const" +#include "prc_inc_nwscript" ////////////////////////////////////////////////// diff --git a/src/include/psi_inc_core.nss b/src/include/psi_inc_core.nss index a7acfb4..b4ca245 100644 --- a/src/include/psi_inc_core.nss +++ b/src/include/psi_inc_core.nss @@ -41,6 +41,8 @@ const int POWER_LIST_WARMIND = CLASS_TYPE_WARMIND; /* Function prototypes */ ////////////////////////////////////////////////// +int IsHiddenTalent(object oPC = OBJECT_SELF); + /** * Attempts to use psionic focus. If the creature was focused, it * loses the focus. If it has Epic Psionic Focus feats, it will @@ -786,69 +788,12 @@ int GetIsPsionicCharacter(object oCreature) GetHasFeat(FEAT_KALASHTAR_PP, oCreature) || GetHasFeat(FEAT_NATPSIONIC_1, oCreature) || GetHasFeat(FEAT_NATPSIONIC_2, oCreature) || - GetHasFeat(FEAT_NATPSIONIC_3, oCreature) + GetHasFeat(FEAT_NATPSIONIC_3, oCreature) || + IsHiddenTalent(oCreature) // Racial psionicity signifying feats go here ); } -int IsHiddenTalent(object oPC = OBJECT_SELF) -{ - if (GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC) || - //GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) - { - return TRUE; - } - else - { - return FALSE; - } -} - - void LocalCleanExtraFists(object oCreature) { int iIsCWeap, iIsEquip; @@ -985,6 +930,48 @@ int PracticedManifesting(object oManifester, int iManifestingClass, int iManifes int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVALID, int nMaxPowerLevel = FALSE) { + // Handle POWER_LIST_MISC (Hidden Talent) powers + // Check if this is a power list call + int nPowerType = GetLocalInt(oManifester, "PRC_UsePowerList"); + + if(nSpecificClass == CLASS_TYPE_INVALID && nPowerType == POWER_LIST_MISC) + { + if(DEBUG) DoDebug("psi_inc_core >> GetManifesterLevel: CLASS_TYPE_INVALID + POWER_LIST_MISC found!"); + // Check if character has psionic class levels + int nPsionLevel = GetLevelByClass(CLASS_TYPE_PSION, oManifester); + int nPsywarLevel = GetLevelByClass(CLASS_TYPE_PSYWAR, oManifester); + int nWilderLevel = GetLevelByClass(CLASS_TYPE_WILDER, oManifester); + int nWarmindLevel = GetLevelByClass(CLASS_TYPE_WARMIND, oManifester); + int nFistOfZuokenLevel = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oManifester); + int nPsychicRogueLevel = GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oManifester); + + // If no psionic levels, use Charisma-based calculation (treat as 1st level) + if(nPsionLevel + nPsywarLevel + nWilderLevel + nWarmindLevel + + nFistOfZuokenLevel + nPsychicRogueLevel == 0) + { + // Hidden Talent: considered 1st-level manifester, but must have CHA 11+ + if(DEBUG) DoDebug("psi_inc_core >> GetManifesterLevel: Hidden Talent found!"); + if(GetAbilityScore(oManifester, ABILITY_CHARISMA) >= 11) + return 1; + else + return 0; // Cannot manifest without CHA 11+ + } + + if(DEBUG) DoDebug("psi_inc_core >> GetManifesterLevel: nSpecificClass=" + IntToString(nSpecificClass) + + ", nPowerType=" + IntToString(nPowerType)); + + // Has psionic levels - return highest manifester level + int nHighest = 0; + if(nPsionLevel > 0) nHighest = GetManifesterLevel(oManifester, CLASS_TYPE_PSION); + if(nPsywarLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_PSYWAR)); + if(nWilderLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_WILDER)); + if(nWarmindLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_WARMIND)); + if(nFistOfZuokenLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_FIST_OF_ZUOKEN)); + if(nPsychicRogueLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_PSYCHIC_ROGUE)); + + return nHighest; + } + int nLevel; int nAdjust = GetLocalInt(oManifester, PRC_CASTERLEVEL_ADJUSTMENT); nAdjust -= GetLocalInt(oManifester, "WoLManifPenalty"); @@ -1049,17 +1036,27 @@ int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVAL DelayCommand(1.0, DeleteLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE)); nLevel = GetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE); - } - else if(GetManifestingClass(oManifester) != CLASS_TYPE_INVALID) - { - //Gets the manifesting class - int nManifestingClass = GetManifestingClass(oManifester); - if(DEBUG) DoDebug("Manifesting Class #2: " + IntToString(nManifestingClass), oManifester); - nLevel = GetLevelByClass(nManifestingClass, oManifester); - // Add levels from +ML PrCs only for the first manifesting class - nLevel += GetPsionicPRCLevels(oManifester, nManifestingClass); - //nLevel += nManifestingClass == GetPrimaryPsionicClass(oManifester) ? GetPsionicPRCLevels(oManifester) : 0; - + } + else if(GetManifestingClass(oManifester) != CLASS_TYPE_INVALID) + { + //Gets the manifesting class + int nManifestingClass = GetManifestingClass(oManifester); + if(DEBUG) DoDebug("Manifesting Class #2: " + IntToString(nManifestingClass), oManifester); + + nLevel = GetLevelByClass(nManifestingClass, oManifester); + // Add levels from +ML PrCs only for the first manifesting class + nLevel += GetPsionicPRCLevels(oManifester, nManifestingClass); + + // CHECK: If this is Hidden Talent and character has no levels, set to 1 + if(nLevel == 0 && GetLocalInt(oManifester, "PRC_UsePowerList") == TRUE && + GetLocalInt(oManifester, "PRC_PowerListType") == POWER_LIST_MISC) + { + if(GetAbilityScore(oManifester, ABILITY_CHARISMA) >= 11) + { + if(DEBUG) DoDebug("GetManifesterLevel: Hidden Talent with no psionic levels, returning 1"); + nLevel = 1; + } + } // Psionic vestiges are tucked in here to override things. // This assumes that there will never be a psion with this spell effect manifesting things if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !nMaxPowerLevel) @@ -1085,7 +1082,37 @@ int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVAL // if(DEBUG) DoDebug("Level gotten via GetLevelByClass: " + IntToString(nLevel), oManifester); } - // If you have a primary psionic class and no manifester level yet, get levels based on that + // If you have a primary psionic class and no manifester level yet, get levels based on that + if (GetPrimaryPsionicClass(oManifester) && nLevel == 0) + { + int nClass = GetPrimaryPsionicClass(oManifester); + nLevel = GetLevelByClass(nClass, oManifester); + nLevel += GetPsionicPRCLevels(oManifester, nClass); + nLevel += PracticedManifesting(oManifester, nClass, nLevel); + } + + // If everything else fails, check for Hidden Talent before returning 0 + if(nLevel == 0) + { + // Check if this is a Hidden Talent power + if(GetLocalInt(oManifester, "PRC_UsePowerList") == POWER_LIST_MISC) + { + // Hidden Talent: manifester level is 1 if they have CHA 11+ + if(GetAbilityScore(oManifester, ABILITY_CHARISMA) >= 11) + { + if(DEBUG) DoDebug("GetManifesterLevel: Hidden Talent character, returning level 1"); + return 1; + } + } + + if(DEBUG) DoDebug("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot"); + //else WriteTimestampedLogEntry("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot"); + + return 0; + } + + +/* // If you have a primary psionic class and no manifester level yet, get levels based on that if (GetPrimaryPsionicClass(oManifester) && nLevel == 0) { int nClass = GetPrimaryPsionicClass(oManifester); @@ -1102,7 +1129,7 @@ int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVAL return 0; } - + */ // The bonuses inside only apply to normal manifestation if(!GetLocalInt(oManifester, PRC_IS_PSILIKE)) @@ -1665,4 +1692,225 @@ int GetMaxPowerLevel(object oManifester) int nMax = StringToInt(Get2DACache(sFile, "MaxPowerLevel", nLevel)); if (DEBUG) DoDebug("GetMaxPowerLevel is "+IntToString(nMax)); return nMax; -} \ No newline at end of file +} + +////////////////////////////////////////////////////// +/* START HIDDEN TALENT */ +////////////////////////////////////////////////////// + +int IsHiddenTalent(object oPC = OBJECT_SELF) +{ + if (GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC) || + //GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) + { + return TRUE; + } + else + { + return FALSE; + } +} + +int GetHiddenTalentPowerFromFeat(int nFeatID) +{ + // Map Hidden Talent feats to their corresponding power IDs + // Using the same mappings as GetIsHiddenTalentPower() + if(nFeatID == FEAT_HIDDEN_TALENT_BIOFEEDBACK) return POWER_BIOFEEDBACK; + if(nFeatID == FEAT_HIDDEN_TALENT_BITE_WOLF) return POWER_BITE_WOLF; + if(nFeatID == FEAT_HIDDEN_TALENT_BOLT) return POWER_BOLT; + if(nFeatID == FEAT_HIDDEN_TALENT_BURST) return POWER_BURST; + if(nFeatID == FEAT_HIDDEN_TALENT_CALLTOMIND) return POWER_CALLTOMIND; + if(nFeatID == FEAT_HIDDEN_TALENT_CALL_WEAPONRY) return POWER_CALL_WEAPONRY; + if(nFeatID == FEAT_HIDDEN_TALENT_CHAMELEON) return POWER_CHAMELEON; + if(nFeatID == FEAT_HIDDEN_TALENT_CLAWS_BEAST) return POWER_CLAWS_BEAST; + if(nFeatID == FEAT_HIDDEN_TALENT_COMPRESSION) return POWER_COMPRESSION; + if(nFeatID == FEAT_HIDDEN_TALENT_CONCEALTHOUGHT) return POWER_CONCEALTHOUGHT; + if(nFeatID == FEAT_HIDDEN_TALENT_CREATESOUND) return POWER_CREATESOUND; + if(nFeatID == FEAT_HIDDEN_TALENT_CRYSTALSHARD) return POWER_CRYSTALSHARD; + if(nFeatID == FEAT_HIDDEN_TALENT_DAZE) return POWER_DAZE; + if(nFeatID == FEAT_HIDDEN_TALENT_DECELERATION) return POWER_DECELERATION; + if(nFeatID == FEAT_HIDDEN_TALENT_DEFPRECOG) return POWER_DEFPRECOG; + if(nFeatID == FEAT_HIDDEN_TALENT_DEMORALIZE) return POWER_DEMORALIZE; + if(nFeatID == FEAT_HIDDEN_TALENT_DISABLE) return POWER_DISABLE; + if(nFeatID == FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH)return POWER_DISSIPATINGTOUCH; + if(nFeatID == FEAT_HIDDEN_TALENT_DISTRACT) return POWER_DISTRACT; + if(nFeatID == FEAT_HIDDEN_TALENT_ELFSIGHT) return POWER_ELFSIGHT; + if(nFeatID == FEAT_HIDDEN_TALENT_EMPATHY) return POWER_EMPATHY; + if(nFeatID == FEAT_HIDDEN_TALENT_EMPTYMIND) return POWER_EMPTYMIND; + if(nFeatID == FEAT_HIDDEN_TALENT_ENTANGLE) return POWER_ENTANGLE; + if(nFeatID == FEAT_HIDDEN_TALENT_EXPANSION) return POWER_EXPANSION; + if(nFeatID == FEAT_HIDDEN_TALENT_FARHAND) return POWER_FARHAND; + if(nFeatID == FEAT_HIDDEN_TALENT_FORCESCREEN) return POWER_FORCESCREEN; + if(nFeatID == FEAT_HIDDEN_TALENT_GREASE) return POWER_GREASE; + if(nFeatID == FEAT_HIDDEN_TALENT_HAMMER) return POWER_HAMMER; + if(nFeatID == FEAT_HIDDEN_TALENT_INERTIALARMOUR) return POWER_INERTIALARMOUR; + if(nFeatID == FEAT_HIDDEN_TALENT_MATTERAGITATION) return POWER_MATTERAGITATION; + if(nFeatID == FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW) return POWER_METAPHYSICAL_CLAW; + if(nFeatID == FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON) return POWER_METAPHYSICAL_WEAPON; + if(nFeatID == FEAT_HIDDEN_TALENT_MINDTHRUST) return POWER_MINDTHRUST; + if(nFeatID == FEAT_HIDDEN_TALENT_MYLIGHT) return POWER_MYLIGHT; + if(nFeatID == FEAT_HIDDEN_TALENT_OFFPRECOG) return POWER_OFFPRECOG; + if(nFeatID == FEAT_HIDDEN_TALENT_OFFPRESC) return POWER_OFFPRESC; + if(nFeatID == FEAT_HIDDEN_TALENT_PREVENOM) return POWER_PREVENOM; + if(nFeatID == FEAT_HIDDEN_TALENT_PREVENOM_WEAPON) return POWER_PREVENOM_WEAPON; + if(nFeatID == FEAT_HIDDEN_TALENT_SKATE) return POWER_SKATE; + if(nFeatID == FEAT_HIDDEN_TALENT_STOMP) return POWER_STOMP; + if(nFeatID == FEAT_HIDDEN_TALENT_SYNESTHETE) return POWER_SYNESTHETE; + if(nFeatID == FEAT_HIDDEN_TALENT_TELEMPATHICPRO) return POWER_TELEMPATHICPRO; + if(nFeatID == FEAT_HIDDEN_TALENT_THICKSKIN) return POWER_THICKSKIN; + if(nFeatID == FEAT_HIDDEN_TALENT_VIGOR) return POWER_VIGOR; + if(nFeatID == FEAT_HIDDEN_TALENT_GRIP_IRON) return POWER_GRIP_IRON; + + return -1; // Not found +} + +int GetHiddenTalentCount(object oPC = OBJECT_SELF) +{ + int nCount = 0; + + if (GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC)) nCount++; + //if (GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC))nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) nCount++; + + return nCount; +} + +int GetIsHiddenTalentPower(object oPC, int nPower) +{ + // Check each Hidden Talent feat to see if it grants this power + if(nPower == POWER_BIOFEEDBACK && GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC)) return TRUE; + if(nPower == POWER_BITE_WOLF && GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC)) return TRUE; + if(nPower == POWER_BOLT && GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC)) return TRUE; + if(nPower == POWER_BURST && GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC)) return TRUE; + if(nPower == POWER_CALLTOMIND && GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC)) return TRUE; + if(nPower == POWER_CALL_WEAPONRY && GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC)) return TRUE; + if(nPower == POWER_CHAMELEON && GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC)) return TRUE; + if(nPower == POWER_CLAWS_BEAST && GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC)) return TRUE; + if(nPower == POWER_COMPRESSION && GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC)) return TRUE; + if(nPower == POWER_CONCEALTHOUGHT && GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC)) return TRUE; + if(nPower == POWER_CREATESOUND && GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC)) return TRUE; + if(nPower == POWER_CRYSTALSHARD && GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC)) return TRUE; + if(nPower == POWER_DAZE && GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC)) return TRUE; + if(nPower == POWER_DECELERATION && GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC)) return TRUE; + if(nPower == POWER_DEFPRECOG && GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC)) return TRUE; + if(nPower == POWER_DEMORALIZE && GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC)) return TRUE; + if(nPower == POWER_DISABLE && GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC)) return TRUE; + if(nPower == POWER_DISSIPATINGTOUCH && GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC)) return TRUE; + if(nPower == POWER_DISTRACT && GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC)) return TRUE; + if(nPower == POWER_ELFSIGHT && GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC)) return TRUE; + if(nPower == POWER_EMPATHY && GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC)) return TRUE; + if(nPower == POWER_EMPTYMIND && GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC)) return TRUE; + //if(nPower == POWER_ENERGYRAY && GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC)) return TRUE; + if(nPower == POWER_ENTANGLE && GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC)) return TRUE; + if(nPower == POWER_EXPANSION && GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC)) return TRUE; + if(nPower == POWER_FARHAND && GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC)) return TRUE; + if(nPower == POWER_FORCESCREEN && GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC)) return TRUE; + if(nPower == POWER_GREASE && GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC)) return TRUE; + if(nPower == POWER_HAMMER && GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC)) return TRUE; + if(nPower == POWER_INERTIALARMOUR && GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC)) return TRUE; + if(nPower == POWER_MATTERAGITATION && GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC)) return TRUE; + if(nPower == POWER_METAPHYSICAL_CLAW && GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC)) return TRUE; + if(nPower == POWER_METAPHYSICAL_WEAPON && GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC)) return TRUE; + if(nPower == POWER_MINDTHRUST && GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC)) return TRUE; + if(nPower == POWER_MYLIGHT && GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC)) return TRUE; + if(nPower == POWER_OFFPRECOG && GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC)) return TRUE; + if(nPower == POWER_OFFPRESC && GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC)) return TRUE; + if(nPower == POWER_PREVENOM && GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC)) return TRUE; + if(nPower == POWER_PREVENOM_WEAPON && GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC)) return TRUE; + if(nPower == POWER_SKATE && GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC)) return TRUE; + if(nPower == POWER_STOMP && GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC)) return TRUE; + if(nPower == POWER_SYNESTHETE && GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC)) return TRUE; + if(nPower == POWER_TELEMPATHICPRO && GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC)) return TRUE; + if(nPower == POWER_THICKSKIN && GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC)) return TRUE; + if(nPower == POWER_VIGOR && GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC)) return TRUE; + if(nPower == POWER_GRIP_IRON && GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) return TRUE; + + return FALSE; +} diff --git a/src/include/psi_inc_metapsi.nss b/src/include/psi_inc_metapsi.nss index c005f0c..3148718 100644 --- a/src/include/psi_inc_metapsi.nss +++ b/src/include/psi_inc_metapsi.nss @@ -110,6 +110,7 @@ object GetSplitPsionicRayTarget(struct manifestation manif, object oPrimaryTarge ////////////////////////////////////////////////// #include "psi_inc_core" +#include "psi_inc_psifunc" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/psi_inc_powknown.nss b/src/include/psi_inc_powknown.nss index 51e304f..20c1951 100644 --- a/src/include/psi_inc_powknown.nss +++ b/src/include/psi_inc_powknown.nss @@ -572,7 +572,11 @@ int GetMaxPowerCount(object oCreature, int nList) int GetHasPower(int nPower, object oCreature = OBJECT_SELF) { - if((GetLevelByClass(CLASS_TYPE_PSION, oCreature) + // Check MISC list first (for Hidden Talent and similar feats) + if(GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_INVALID), oCreature)) + return TRUE; + + if((GetLevelByClass(CLASS_TYPE_PSION, oCreature) && GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_PSION), oCreature) ) || (GetLevelByClass(CLASS_TYPE_PSYWAR, oCreature) diff --git a/src/include/psi_inc_ppoints.nss b/src/include/psi_inc_ppoints.nss index a98f0fb..ca66fba 100644 --- a/src/include/psi_inc_ppoints.nss +++ b/src/include/psi_inc_ppoints.nss @@ -93,9 +93,8 @@ void GainPowerPoints(object oChar, int nGain, int bCanExceedMax = FALSE, int bIn * @param bInform If TRUE, runs TellCharacterPowerPointStatus() on oChar * after making the modification. */ -/* void GainTemporaryPowerPoints(object oChar, int nGain, float fDuration, int bInform = TRUE); -*/ + /** * Decreases the character's current power point count by up to the given * amount, limited to not going below 0. @@ -138,8 +137,12 @@ int _GetFeatBonusPP(object oChar) { int nBonusPP = 0; -//:: Wild Talent & Hidden Talents - if(GetHasFeat(FEAT_WILD_TALENT, oChar) || IsHiddenTalent()) +//:: Wild Talent + if(GetHasFeat(FEAT_WILD_TALENT, oChar)) + nBonusPP += 2; + +//:: Hidden Talent + if(GetHasFeat(FEAT_HIDDEN_TALENT, oChar)) nBonusPP += 2; //:: Psionic Feats diff --git a/src/include/psi_inc_psicraft.nss b/src/include/psi_inc_psicraft.nss index 058ba59..e5e244d 100644 --- a/src/include/psi_inc_psicraft.nss +++ b/src/include/psi_inc_psicraft.nss @@ -40,7 +40,8 @@ void IdentifyPower(object oManifester, int nPowerId); // Always access via psi_inc_psifunc. -//#include "psi_inc_core" +#include "psi_inc_core" +#include "inc_2dacache" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/psi_inc_psifunc.nss b/src/include/psi_inc_psifunc.nss index 5e96a40..c35b122 100644 --- a/src/include/psi_inc_psifunc.nss +++ b/src/include/psi_inc_psifunc.nss @@ -407,6 +407,8 @@ void _CleanManifestationVariables(object oManifester) DeleteLocalInt(oManifester, PRC_POWER_LEVEL); DeleteLocalInt(oManifester, PRC_IS_PSILIKE); DeleteLocalInt(oManifester, PRC_AUGMENT_OVERRIDE); + DeleteLocalInt(oManifester, "PRC_UsePowerList"); + DeleteLocalInt(oManifester, "PRC_PowerListType"); } /** Internal function. @@ -692,10 +694,28 @@ void _UsePowerAux(object oManifester, object oMfToken, int nSpellId, struct manifestation EvaluateManifestation(object oManifester, object oTarget, struct power_augment_profile pap, int nMetaPsiFlags) { - /* Get some data */ + //:: Handle Hidden Talent + int nSpellID = PRCGetSpellId(); + int bIsHiddenTalent = GetIsHiddenTalentPower(oManifester, nSpellID); + if(bIsHiddenTalent) + { + SetLocalInt(oManifester, "PRC_UsePowerList", TRUE); + SetLocalInt(oManifester, "PRC_PowerListType", POWER_LIST_MISC); + } + /* Get some data */ int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + // Manifester-related stuff - int nManifesterLevel = GetManifesterLevel(oManifester); + //int nManifesterLevel = GetManifesterLevel(oManifester); + int nManifesterLevel; + if(bIsHiddenTalent) + { + nManifesterLevel = GetManifesterLevel(oManifester, CLASS_TYPE_INVALID); + } + else + { + nManifesterLevel = GetManifesterLevel(oManifester); + } int nPowerLevel = GetPowerLevel(oManifester); int nClass = GetManifestingClass(oManifester); int nWildSurge = GetWildSurge(oManifester); @@ -714,6 +734,8 @@ struct manifestation EvaluateManifestation(object oManifester, object oTarget, s manif.nManifesterLevel = nManifesterLevel; manif.nSpellID = PRCGetSpellId(); + + // Run an ability score check to see if the manifester can manifest the power at all if (bIsPsiLike) { @@ -767,7 +789,9 @@ struct manifestation EvaluateManifestation(object oManifester, object oTarget, s //If the manifester does not have enough points before hostile modifiers, cancel power if(manif.nPPCost > nManifesterPP && !bIsPsiLike && !bIgnoreConstraints) { - FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power" + // DEBUG: show why the cost over cap branch triggered + FloatingTextStringOnCreature("DEBUG: manif.nManifesterLevel=" + IntToString(manif.nManifesterLevel) + " manif.nPPCost=" + IntToString(manif.nPPCost) +" PRC_UsePowerList=" + IntToString(GetLocalInt(manif.oManifester, "PRC_UsePowerList")), manif.oManifester, FALSE); + FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power" manif.bCanManifest = FALSE; } // The manifester has enough power points that they would be able to use the power, barring extra costs diff --git a/src/include/psi_inc_pwresist.nss b/src/include/psi_inc_pwresist.nss index e1c5a96..447c917 100644 --- a/src/include/psi_inc_pwresist.nss +++ b/src/include/psi_inc_pwresist.nss @@ -14,6 +14,7 @@ #include "prc_class_const" */ #include "prc_alterations" +#include "prcsp_engine" // Constants that dictate ResistPower results const int POWER_RESIST_FAIL = 1; diff --git a/src/include/psi_spellhook.nss b/src/include/psi_spellhook.nss index 12c651d..aef699f 100644 --- a/src/include/psi_spellhook.nss +++ b/src/include/psi_spellhook.nss @@ -75,6 +75,15 @@ int PsiPrePowerCastCode() int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oManifester); + //--------------------------------------------------------------------------- + // Forsakers can't use psionics + //--------------------------------------------------------------------------- + if (nContinue && GetLevelByClass(CLASS_TYPE_FORSAKER, oManifester)) + { + FloatingTextStringOnCreature("Forsakers cannot manifest psionic powers!", oManifester, FALSE); + nContinue = FALSE; + } + //--------------------------------------------------------------------------- // Break any spell require maintaining concentration //--------------------------------------------------------------------------- @@ -209,3 +218,4 @@ int PsiPrePowerCastCode() return nContinue; } +//:: void main (){} \ No newline at end of file diff --git a/src/include/shd_inc_mystknwn.nss b/src/include/shd_inc_mystknwn.nss index b335485..0189d05 100644 --- a/src/include/shd_inc_mystknwn.nss +++ b/src/include/shd_inc_mystknwn.nss @@ -192,7 +192,8 @@ int PathFeatToIPFeat(int nFeat); /* Includes */ ////////////////////////////////////////////////// - +#include "inc_lookups" +#include "inc_pers_array" ////////////////////////////////////////////////// /* Internal functions */ @@ -519,7 +520,7 @@ int GetMaxMysteryLevelLearnable(object oShadow, int nClass, int nType) if(DEBUG) DoDebug("GetMaxMysteryLevelLearnable nType: " + IntToString(nType)); // Rules Quote: - // Within a category—Apprentice, Initiate, Master—you must have at least two mysteries of any given level + // Within a category�Apprentice, Initiate, Master�you must have at least two mysteries of any given level // before you can take any mysteries of the next higher level. For instance, you must have two 1st-level // mysteries before you can take any 2nds, and at least two 2nds before you can take any 3rds. int nMaxLrn, i, nMystLevel, nCount1, nCount2; diff --git a/src/include/shd_inc_shdfunc.nss b/src/include/shd_inc_shdfunc.nss index 6ae4f63..2d19e9f 100644 --- a/src/include/shd_inc_shdfunc.nss +++ b/src/include/shd_inc_shdfunc.nss @@ -210,6 +210,7 @@ int GetHasNocturnal(object oShadow, int nPath); #include "prc_alterations" #include "shd_inc_myst" #include "shd_inc_mystknwn" +#include "lookup_2da_spell" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/shd_mysthook.nss b/src/include/shd_mysthook.nss index 51c63be..dc9ad89 100644 --- a/src/include/shd_mysthook.nss +++ b/src/include/shd_mysthook.nss @@ -17,6 +17,8 @@ #include "prc_inc_spells" #include "inc_utility" #include "prc_inc_itmrstr" +#include "shd_inc_shdfunc" +#include "lookup_2da_spell" // This function holds all functions that are supposed to run before the actual // spellscript gets run. If this functions returns FALSE, the spell is aborted @@ -132,6 +134,15 @@ int ShadPreMystCastCode() int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oShadow); + //--------------------------------------------------------------------------- + // Block forsakers from using shadowcasting + //--------------------------------------------------------------------------- + if(GetLevelByClass(CLASS_TYPE_FORSAKER, oShadow) > 0) + { + SendMessageToPC(oShadow, "Forsakers cannot use the power of shadowcasting."); + return FALSE; + } + //--------------------------------------------------------------------------- // Break any spell require maintaining concentration //--------------------------------------------------------------------------- @@ -277,4 +288,6 @@ int ShadPreMystCastCode() if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #6: " + IntToString(nContinue)); return nContinue; -} \ No newline at end of file +} + +//:: void main (){} diff --git a/src/include/tob_movehook.nss b/src/include/tob_movehook.nss index 3ac1493..614e2a8 100644 --- a/src/include/tob_movehook.nss +++ b/src/include/tob_movehook.nss @@ -14,6 +14,7 @@ #include "prc_inc_spells" #include "inc_utility" #include "x2_inc_spellhook" +#include "tob_inc_tobfunc" // This function holds all functions that are supposed to run before the actual // spellscript gets run. If this functions returns FALSE, the spell is aborted @@ -78,7 +79,14 @@ int PreManeuverCastCode() //--------------------------------------------------------------------------- if(nContinue) nContinue = !GetLocalInt(oInitiator, "CrusaderBreak"); - + //--------------------------------------------------------------------------- + // Forsakers can't use supernatural maneuvers + //--------------------------------------------------------------------------- + if (nContinue && GetIsManeuverSupernatural(nMoveId) && GetLevelByClass(CLASS_TYPE_FORSAKER, oInitiator)) + { + FloatingTextStringOnCreature("Forsakers cannot use supernatural maneuvers!", oInitiator, FALSE); + nContinue = FALSE; + } //--------------------------------------------------------------------------- // Run NullPsionicsField Check //--------------------------------------------------------------------------- diff --git a/src/include/true_inc_truespk.nss b/src/include/true_inc_truespk.nss index 7523024..4787683 100644 --- a/src/include/true_inc_truespk.nss +++ b/src/include/true_inc_truespk.nss @@ -109,11 +109,14 @@ int GetIsSyllable(int nSpellId); */ int DoSpellTruenameCheck(object oTrueSpeaker, object oTarget, int nPersonal = FALSE); +string GetNormalUtterSpellId(int nSpellId); + ////////////////////////////////////////////////// /* Includes */ ////////////////////////////////////////////////// #include "prc_inc_spells" +#include "true_inc_trufunc" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/true_inc_trufunc.nss b/src/include/true_inc_trufunc.nss index 355783a..ad010b3 100644 --- a/src/include/true_inc_trufunc.nss +++ b/src/include/true_inc_trufunc.nss @@ -260,6 +260,7 @@ int GetCadenceCount(object oTrueSpeaker); #include "prc_alterations" #include "true_inc_utter" #include "true_inc_truknwn" +#include "true_inc_truespk" ////////////////////////////////////////////////// /* Function definitions */ diff --git a/src/include/true_inc_truknwn.nss b/src/include/true_inc_truknwn.nss index 22fbea0..9f9a606 100644 --- a/src/include/true_inc_truknwn.nss +++ b/src/include/true_inc_truknwn.nss @@ -141,6 +141,7 @@ int GetHasUtterance(int nUtter, object oCreature = OBJECT_SELF); #include "inc_pers_array" #include "prc_inc_nwscript" #include "inc_lookups" +#include "prc_x2_itemprop" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/true_utterhook.nss b/src/include/true_utterhook.nss index 36b6b8c..dfb3671 100644 --- a/src/include/true_utterhook.nss +++ b/src/include/true_utterhook.nss @@ -18,6 +18,7 @@ #include "prc_inc_spells" #include "inc_utility" #include "prc_inc_itmrstr" +#include "true_inc_trufunc" // This function holds all functions that are supposed to run before the actual @@ -42,6 +43,16 @@ int TruePreUtterCastCode() int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oTrueSpeaker); + //--------------------------------------------------------------------------- + // Block forsakers from using truenaming + //--------------------------------------------------------------------------- + + if(GetLevelByClass(CLASS_TYPE_FORSAKER, oTrueSpeaker) > 0) + { + SendMessageToPC(oTrueSpeaker, "Forsakers cannot use the power of truenaming."); + return FALSE; + } + //--------------------------------------------------------------------------- // Break any spell require maintaining concentration //--------------------------------------------------------------------------- diff --git a/src/include/x2_inc_spellhook.nss b/src/include/x2_inc_spellhook.nss index 92668bf..5449590 100644 --- a/src/include/x2_inc_spellhook.nss +++ b/src/include/x2_inc_spellhook.nss @@ -2902,7 +2902,7 @@ int WandEquipped(object oCaster, object oSpellCastItem) int nType = GetBaseItemType(oSpellCastItem); - if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND) // Has to be a wand, obv + if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND || nType == BASE_ITEM_CRAFTED_SCEPTER) // Has to be a wand, obv { if(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCaster) == oSpellCastItem || GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster) == oSpellCastItem) // Needs to be equipped { @@ -2910,7 +2910,7 @@ int WandEquipped(object oCaster, object oSpellCastItem) } else { - FloatingTextStringOnCreature("You must equip a wand to cast from it.", oCaster, FALSE); + FloatingTextStringOnCreature("You must equip this item to cast from it.", oCaster, FALSE); return FALSE; // It's a wand not equipped } } diff --git a/src/module/ifo/module.ifo.json b/src/module/ifo/module.ifo.json index c38b347..3a5c0ec 100644 --- a/src/module/ifo/module.ifo.json +++ b/src/module/ifo/module.ifo.json @@ -1078,7 +1078,7 @@ "Mod_Name": { "type": "cexolocstring", "value": { - "0": "Lord of Terror [CCOH-PRC8-CEP3]" + "0": "Lord of Terror [PRC8-CEP3]" } }, "Mod_OnAcquirItem": { @@ -1135,7 +1135,7 @@ }, "Mod_OnPlrGuiEvt": { "type": "resref", - "value": "" + "value": "prc_onplayergui" }, "Mod_OnPlrLvlUp": { "type": "resref", diff --git a/src/module/itp/creaturepalcus.itp.json b/src/module/itp/creaturepalcus.itp.json index b117f20..4b3677a 100644 --- a/src/module/itp/creaturepalcus.itp.json +++ b/src/module/itp/creaturepalcus.itp.json @@ -11090,7 +11090,7 @@ "__struct_id": 0, "CR": { "type": "float", - "value": 17.0 + "value": 16.0 }, "FACTION": { "type": "cexostring", @@ -19955,7 +19955,7 @@ "__struct_id": 0, "CR": { "type": "float", - "value": 0.3333 + "value": 84.0 }, "FACTION": { "type": "cexostring",