2026/01/08 Update

Hexblade shouldn't get Medium Armor prof.
Weapon Specialization Whip isn't a Warblade bonus feat.
Reorganized packages.2da.
Fixed potential issue with SetCompositeBonusT().
Added materials iprops to material based crafting.
Fixed bug w/ DoTrip().
Added heartbeat script to Monk to properly handle monk sizes.
Cleaned up PRCGetCreatureSize().
Set Shielded Casting to use TagItemProperty().
Archivist now has a Lore check to learn spells from scrolls.
Set Dragonfire Strike to use TagItemProperty().
Setup Forsaker to use TagItemProperty().
Fixed distance mismatch with Necrocarnum Shroud.
Added too much debugging for the unarmed stuff.
Cloudkill now obeys Mastery of Shapes.
This commit is contained in:
Jaysyn904
2026-01-08 20:31:11 -05:00
parent 6ca160ca26
commit 6420e5dbb8
28 changed files with 1707 additions and 1117 deletions

View File

@@ -1,15 +1,211 @@
//::///////////////////////////////////////////////
//:: Name Forsaker
//:: FileName prc_forsaker.nss
//:: Created By: Stratosvarious
//:: Edited By: Fencas
//:://////////////////////////////////////////////
#include "prc_inc_function"
#include "prc_inc_combat"
#include "inc_dynconv"
#include "prc_alterations"
//::///////////////////////////////////////////////
//:: Name Forsaker
//:: FileName prc_forsaker.nss
//:: Created By: Stratosvarious
//:: Edited By: Fencas
//:://////////////////////////////////////////////
#include "prc_inc_function"
#include "prc_inc_combat"
#include "inc_dynconv"
#include "prc_alterations"
void main()
{
int nEvent = GetRunningEvent();
if(DEBUG) DoDebug("prc_forsaker running, event: " + IntToString(nEvent));
// Get the PC. This is event-dependent
object oPC;
switch(nEvent)
{
case EVENT_ITEM_ONHIT: oPC = OBJECT_SELF; break;
case EVENT_ONPLAYEREQUIPITEM: oPC = GetItemLastEquippedBy(); break;
case EVENT_ONPLAYERUNEQUIPITEM: oPC = GetItemLastUnequippedBy(); break;
case EVENT_ONHEARTBEAT: oPC = OBJECT_SELF; break;
default:
oPC = OBJECT_SELF;
}
object oItem;
object oArmor;
object oShield;
object oSkin = GetPCSkin(oPC);
int nSlot;
int nForsakerLvl = GetLevelByClass(CLASS_TYPE_FORSAKER, oPC);
int nForsakerLvlCheck;
int nBonus = nForsakerLvl/2;
int nRegen = 1 + nForsakerLvl/4;
int nSR = 10 + nForsakerLvl;
if(nEvent == FALSE)
{
//Check if level up bonus has already been chosen and given for any of past Forsaker levels
for(nForsakerLvlCheck=1; nForsakerLvlCheck <= nForsakerLvl; nForsakerLvlCheck++)
{
if(!GetPersistantLocalInt(oPC, "ForsakerBoost"+IntToString(nForsakerLvlCheck)))
{
//Level up box for stat bonus
AssignCommand(oPC, ClearAllActions(TRUE));
SetPersistantLocalInt(oPC,"ForsakerBoostCheck",nForsakerLvlCheck);
StartDynamicConversation("prc_forsake_abil", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
}
}
//Fast healing 1 (+1 each 4 levels)
SetCompositeBonus(oSkin,"ForsakerFH",nRegen,ITEM_PROPERTY_REGENERATION);
//SR = 10 + Forsaker level
IPSafeAddItemProperty(oSkin, ItemPropertyBonusSpellResistance(GetSRByValue(nSR)), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE);
//DR starting on level 2 = (level+1)/(Level/2)
if (nForsakerLvl >=2) ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectDamageReduction((nForsakerLvl+1),(nForsakerLvl/2)),oPC);
//Natural AC increase by CON starting on level 3
if (nForsakerLvl >= 3)
{
effect eEffect1 = EffectACIncrease(GetAbilityModifier(ABILITY_CONSTITUTION, oPC), AC_NATURAL_BONUS);
eEffect1 = ExtraordinaryEffect(eEffect1);
eEffect1 = TagEffect(eEffect1, "EffectToughDefense");
//Remove any prior bonus to avoid duplication
effect eCheckEffect = GetFirstEffect(oPC);
while (GetIsEffectValid(eCheckEffect))
{
if(GetEffectTag(eCheckEffect) == "EffectToughDefense") RemoveEffect(oPC, eCheckEffect);
eCheckEffect = GetNextEffect(oPC);
}
//Give player the bonus
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect1, oPC);
}
if(!GetHasFeat(FEAT_VOWOFPOVERTY,oPC))
{
// REMOVED: Aggressive inventory scanning that was removing all magical items
// This was causing permanent property loss for players
// The Forsaker class should still prevent magical item usage through equip events
if(GetIsUnarmed(oPC) && (nForsakerLvl >= 3)) //If it is unarmed, give DR bypass
{
ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectAttackIncrease(nBonus),oPC);
ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectAttackDecrease(nBonus),oPC);
}
}
// Hook in the events, needed from level 1 for Magic Hatred
if(DEBUG) DoDebug("prc_forsaker: Adding eventhooks");
AddEventScript(oPC, EVENT_ONPLAYEREQUIPITEM, "prc_forsaker", TRUE, FALSE);
AddEventScript(oPC, EVENT_ONPLAYERUNEQUIPITEM, "prc_forsaker", TRUE, FALSE);
}
// We are called from the OnPlayerEquipItem eventhook. Handle magical item restriction
else if(nEvent == EVENT_ONPLAYEREQUIPITEM)
{
oPC = GetItemLastEquippedBy();
oItem = GetItemLastEquipped();
if(DEBUG) DoDebug("prc_forsaker - OnEquip\n"
+ "oPC = " + DebugObject2Str(oPC) + "\n"
+ "oItem = " + DebugObject2Str(oItem) + "\n");
if(!GetHasFeat(FEAT_VOWOFPOVERTY,oPC))
{
// Check if item is a creature item - if so, skip magical item check
int nItemType = GetBaseItemType(oItem);
if(nItemType == BASE_ITEM_CBLUDGWEAPON ||
nItemType == BASE_ITEM_CPIERCWEAPON ||
nItemType == BASE_ITEM_CREATUREITEM ||
nItemType == BASE_ITEM_CSLASHWEAPON ||
nItemType == BASE_ITEM_CSLSHPRCWEAP)
{
// Item is a creature weapon, allow it
return;
}
// Check if the item being equipped is magical
// Only check the item being equipped, not entire inventory
int bIsMagical = FALSE;
itemproperty ipCheck = GetFirstItemProperty(oItem);
while (GetIsItemPropertyValid(ipCheck))
{
// Skip protected properties
if(GetItemPropertyTag(ipCheck) != "Tag_PRC_OnHitKeeper")
{
bIsMagical = TRUE;
break;
}
ipCheck = GetNextItemProperty(oItem);
}
// If item is magical and not a torch, unequip it
if(bIsMagical && GetResRef(oItem) != "nw_it_torch001")
{
AssignCommand(oPC, ClearAllActions(TRUE));
AssignCommand(oPC, ActionUnequipItem(oItem));
FloatingTextStringOnCreature(GetName(oItem)+" is a magical item!", oPC, FALSE);
}
// If non-magical weapon and Forsaker has DR bypass, add bonuses
else if(!bIsMagical && (IPGetIsMeleeWeapon(oItem) || GetWeaponRanged(oItem)) && (nForsakerLvl >= 3))
{
// Add DR bypass bonuses to non-magical weapons
itemproperty ipAttack = ItemPropertyAttackBonus(nBonus);
ipAttack = TagItemProperty(ipAttack, "ForsakerDRBypass");
IPSafeAddItemProperty(oItem, ipAttack, 99999.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE);
itemproperty ipPenalty = ItemPropertyAttackPenalty(nBonus);
ipPenalty = TagItemProperty(ipPenalty, "ForsakerDRBypass");
IPSafeAddItemProperty(oItem, ipPenalty, 99999.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE);
// Remove unarmed bonus
effect eLoop = GetFirstEffect(oPC);
while(GetIsEffectValid(eLoop))
{
if(GetEffectType(eLoop) == EFFECT_TYPE_ATTACK_INCREASE
|| GetEffectType(eLoop) == EFFECT_TYPE_ATTACK_DECREASE) RemoveEffect(oPC,eLoop);
eLoop = GetNextEffect(oPC);
}
}
}
}
// We are called from the OnPlayerUnEquipItem eventhook. Clean up Forsaker properties
else if(nEvent == EVENT_ONPLAYERUNEQUIPITEM)
{
oPC = GetItemLastUnequippedBy();
oItem = GetItemLastUnequipped();
if(DEBUG) DoDebug("prc_forsaker - OnUnEquip\n"
+ "oPC = " + DebugObject2Str(oPC) + "\n"
+ "oItem = " + DebugObject2Str(oItem) + "\n"
);
// Only remove properties that Forsaker actually added
if(IPGetIsMeleeWeapon(oItem) || GetWeaponRanged(oItem))
{
if (nForsakerLvl >= 3)
{
// Remove only tagged Forsaker properties
itemproperty ipCheck = GetFirstItemProperty(oItem);
while (GetIsItemPropertyValid(ipCheck))
{
if (GetItemPropertyTag(ipCheck) == "ForsakerDRBypass")
{
RemoveItemProperty(oItem, ipCheck);
}
ipCheck = GetNextItemProperty(oItem);
}
}
}
// If now unarmed, give DR bypass back to player
if(GetIsUnarmed(oPC) && (nForsakerLvl >= 3))
{
ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectAttackIncrease(nBonus),oPC);
ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectAttackDecrease(nBonus),oPC);
}
}
}
void main()
/* void main()
{
int nEvent = GetRunningEvent();
if(DEBUG) DoDebug("prc_forsaker running, event: " + IntToString(nEvent));
@@ -173,7 +369,7 @@ void main()
if (nForsakerLvl >= 3) RemoveSpecificProperty(oItem, ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER, -1, -1, 1, "", -1, DURATION_TYPE_TEMPORARY);
}
}
}
} */
/* // We aren't being called from any event, instead from EvalPRCFeats