/* This include governs all the new itemproperties Both restrictions and features */ //:: Updated for .35 by Jaysyn 2023/03/10 ////////////////////////////////////////////////// /* Constants */ ////////////////////////////////////////////////// const string PLAYER_SPEED_INCREASE = "player_speed_increase"; const string PLAYER_SPEED_DECREASE = "player_speed_decrease"; ////////////////////////////////////////////////// /* Function prototypes */ ////////////////////////////////////////////////// int DoUMDCheck(object oItem, object oPC, int nDCMod); int CheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID); /** * Non-returning wrapper for CheckPRCLimitations. */ void VoidCheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID); void CheckForPnPHolyAvenger(object oItem); ////////////////////////////////////////////////// /* Includes */ ////////////////////////////////////////////////// #include "inc_utility" #include "prc_inc_newip" ////////////////////////////////////////////////// /* Internal functions */ ////////////////////////////////////////////////// /*void _prc_inc_itmrstr_ApplySpeedModification(object oPC, int nEffectType, int nSpeedMod) { if(DEBUG) DoDebug("_prc_inc_itmrstr_ApplySpeedModification(" + DebugObject2Str(oPC) + ", " + IntToString(nEffectType) + ", " + IntToString(nSpeedMod) + ")"); // The skin object should be OBJECT_SELF here // Clean up existing speed modification effect eTest = GetFirstEffect(oPC); while(GetIsEffectValid(eTest)) { if(GetEffectCreator(eTest) == OBJECT_SELF && GetEffectType(eTest) == nEffectType && GetEffectSubType(eTest) == SUBTYPE_SUPERNATURAL ) RemoveEffect(oPC, eTest); eTest = GetNextEffect(oPC); } // Apply speed mod if there is any if(nSpeedMod > 0) { effect eSpeedMod = SupernaturalEffect(nEffectType == EFFECT_TYPE_MOVEMENT_SPEED_INCREASE ? EffectMovementSpeedIncrease(nSpeedMod) : EffectMovementSpeedDecrease(nSpeedMod) ); /// @todo Determine if the delay is actually needed here DelayCommand(0.5, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSpeedMod, oPC)); } } void _prc_inc_itmrstr_ApplySpeedIncrease(object oPC) { // Get target speed modification value. Limit to 99, since that's the effect constructor maximum value int nSpeedMod = PRCMin(99, GetLocalInt(oPC, PLAYER_SPEED_INCREASE)); object oSkin = GetPCSkin(oPC); AssignCommand(oSkin, _prc_inc_itmrstr_ApplySpeedModification(oPC, EFFECT_TYPE_MOVEMENT_SPEED_INCREASE, nSpeedMod)); } void _prc_inc_itmrstr_ApplySpeedDecrease(object oPC) { // Get target speed modification value. Limit to 99, since that's the effect constructor maximum value int nSpeedMod = GetLocalInt(oPC, PLAYER_SPEED_DECREASE); object oSkin = GetPCSkin(oPC); AssignCommand(oSkin, _prc_inc_itmrstr_ApplySpeedModification(oPC, EFFECT_TYPE_MOVEMENT_SPEED_DECREASE, nSpeedMod)); }*/ void _prc_inc_itmrstr_ApplyAoE(object oPC, object oItem, int nSubType, int nCost) { int nAoEID = StringToInt(Get2DACache("iprp_aoe", "AoEID", nSubType)); string sTag = Get2DACache("vfx_persistent", "LABEL", nAoEID); effect eAoE = EffectAreaOfEffect(nAoEID, Get2DACache("iprp_aoe", "EnterScript", nSubType), Get2DACache("iprp_aoe", "HBScript", nSubType), Get2DACache("iprp_aoe", "ExitScript", nSubType)); // The item applies the AoE effect ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAoE, oPC); // Get an object reference to the newly created AoE location lLoc = GetLocation(oPC); object oAoE = GetFirstObjectInShape(SHAPE_SPHERE, 1.0f, lLoc, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); while(GetIsObjectValid(oAoE)) { // Test if we found the correct AoE if(GetTag(oAoE) == sTag && !GetLocalInt(oAoE, "PRC_AoE_IPRP_Init") ) { SetLocalInt(oAoE, "PRC_AoE_IPRP_Init", TRUE); break; } // Didn't find, get next oAoE = GetNextObjectInShape(SHAPE_SPHERE, 1.0f, lLoc, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); } if(!GetIsObjectValid(oAoE)) DoDebug("ERROR: _prc_inc_itmrstr_ApplyAoE: Can't find AoE created by " + DebugObject2Str(oItem)); // Set caster level override on the AoE SetLocalInt(oAoE, PRC_CASTERLEVEL_OVERRIDE, nCost); //if(DEBUG) DoDebug("_prc_inc_itmrstr_ApplyAoE: AoE level: " + IntToString(nCost)); } void _prc_inc_itmrstr_ApplyWizardry(object oPC, object oItem, int nSpellLevel, string sType) { //properties were already applied - happens when loading a saved game if(GetLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel))) return; SetLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel), TRUE); int nClass, nSlots, i; for(i = 1; i <= 8; i++) { nClass = GetClassByPosition(i, oPC); if((sType == "A" && GetIsArcaneClass(nClass)) || (sType == "D" && GetIsDivineClass(nClass))) { if(GetAbilityScoreForClass(nClass, oPC) < nSpellLevel + 10) continue; int nSpellSlotLevel = GetSpellslotLevel(nClass, oPC) - 1; string sFile = Get2DACache("classes", "SpellGainTable", nClass); nSlots = StringToInt(Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nSpellSlotLevel)); //if(DEBUG) DoDebug("Adding "+IntToString(nSlots)" bonus slots for "+IntToString(nClass)" class."); if(nSlots) { string sVar = "PRC_IPRPBonSpellSlots_" + IntToString(nClass) + "_" + IntToString(nSpellLevel); int j = 0; while(j < nSlots) { //DoDebug(IntToString(j)); AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusLevelSpell(nClass, nSpellLevel), oItem); //nsb compatibility SetLocalInt(oPC, sVar, (GetLocalInt(oPC, sVar) + 1)); j++; } } } } SetPlotFlag(oItem, TRUE); } void _prc_inc_itmrstr_RemoveWizardry(object oPC, object oItem, int nSpellLevel, string sType) { DeleteLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel)); SetPlotFlag(oItem, FALSE); itemproperty ipTest = GetFirstItemProperty(oItem); string sVar; while(GetIsItemPropertyValid(ipTest)) { if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) { if(GetItemPropertyCostTableValue(ipTest) == nSpellLevel) { int nClass = GetItemPropertySubType(ipTest); if((sType == "A" && GetIsArcaneClass(nClass)) || (sType == "D" && GetIsDivineClass(nClass))) { RemoveItemProperty(oItem, ipTest); //remove bonus slots from nsb classes sVar = "PRC_IPRPBonSpellSlots_" + IntToString(nClass) + "_" + IntToString(nSpellLevel); SetLocalInt(oPC, sVar, (GetLocalInt(oPC, sVar) - 1)); int nCount, nSpellbook = GetSpellbookTypeForClass(nClass); string sArray = "NewSpellbookMem_"+IntToString(nClass); if(nSpellbook == SPELLBOOK_TYPE_SPONTANEOUS) { nCount = persistant_array_get_int(oPC, sArray, nSpellLevel); if(nCount) { nCount--; persistant_array_set_int(oPC, sArray, nSpellLevel, nCount); } } else if(nSpellbook == SPELLBOOK_TYPE_PREPARED) { string sIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + IntToString(nClass); int i, nSpellbookID, nMax = persistant_array_get_size(oPC, sIDX) - 1; for(i = nMax; i >= 0; i--) { nSpellbookID = persistant_array_get_int(oPC, sIDX, i); nCount = persistant_array_get_int(oPC, sArray, nSpellbookID); if(nCount) { nCount--; persistant_array_set_int(oPC, sArray, nSpellbookID, nCount); break; } } } } } } ipTest = GetNextItemProperty(oItem); } } ////////////////////////////////////////////////// /* Function definitions */ ////////////////////////////////////////////////// int GetUMDForItemCost(object oItem) { string s2DAEntry; int nValue = GetGoldPieceValue(oItem); int n2DAValue = StringToInt(s2DAEntry); int i; while(n2DAValue < nValue) { s2DAEntry = Get2DACache("skillvsitemcost", "DeviceCostMax", i); n2DAValue = StringToInt(s2DAEntry); i++; } i--; string s2DAReqSkill = Get2DACache("skillvsitemcost", "SkillReq_Class", i); if(s2DAReqSkill == "") return -1; return StringToInt(s2DAReqSkill); } //this is a scripted version of the bioware UMD check for using restricted items //this also applies effects relating to new itemproperties int DoUMDCheck(object oItem, object oPC, int nDCMod) { //doesnt have UMD if(!GetHasSkill(SKILL_USE_MAGIC_DEVICE, oPC)) return FALSE; int nSkill = GetSkillRank(SKILL_USE_MAGIC_DEVICE, oPC); int nReqSkill = GetUMDForItemCost(oItem); //class is a dc20 test nReqSkill = nReqSkill - 20 + nDCMod; if(nReqSkill > nSkill) return FALSE; else return TRUE; } void VoidCheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID) { CheckPRCLimitations(oItem, oPC); } //tests for use restrictions //also appies effects for those IPs tat need them /// @todo Rename. It's not just limitations anymore int CheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID) { // Sanity check - the item needs to be valid if(!GetIsObjectValid(oItem)) return FALSE; /// @todo Might be better to auto-pass the limitation aspect in case of invalid item // In case no item owner was given, find it out if(!GetIsObjectValid(oPC)) oPC = GetItemPossessor(oItem); // Sanity check - the item needs to be in some creature's possession for this function to make sense if(!GetIsObjectValid(oPC)) return FALSE; // Equip and Unequip events need some special handling int bUnequip = GetItemLastUnequipped() == oItem && GetLocalInt(oPC, "ONEQUIP") == 1; int bEquip = GetItemLastEquipped() == oItem && GetLocalInt(oPC, "ONEQUIP") == 2; // Use restriction and UMD use int bPass = TRUE; int nUMDDC = 0; // Speed modification. Used to determine if effects need to be applied int nSpeedIncrease = GetLocalInt(oPC, PLAYER_SPEED_INCREASE); int nSpeedDecrease = GetLocalInt(oPC, PLAYER_SPEED_DECREASE); // Loop over all itemproperties on the item itemproperty ipTest = GetFirstItemProperty(oItem); while(GetIsItemPropertyValid(ipTest)) { int ipType = GetItemPropertyType(ipTest); /* Use restrictions. All of these can be skipped when unequipping */ if(!bUnequip) { if (ipType == ITEM_PROPERTY_USE_LIMITATION_ABILITY_SCORE) { int nValue = GetItemPropertyCostTableValue(ipTest); if(GetAbilityScore(oPC, GetItemPropertySubType(ipTest), TRUE) < nValue) bPass = FALSE; nUMDDC += nValue - 15; } else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SKILL_RANKS) { int nValue = GetItemPropertyCostTableValue(ipTest); if(GetSkillRank(GetItemPropertySubType(ipTest), oPC) < nValue) bPass = FALSE; nUMDDC += nValue - 10; } else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SPELL_LEVEL) { int nLevel = GetItemPropertyCostTableValue(ipTest); if(GetLocalInt(oPC, "PRC_AllSpell" + IntToString(nLevel))) bPass = FALSE; nUMDDC += (nLevel * 2) - 20; } else if(ipType == ITEM_PROPERTY_USE_LIMITATION_ARCANE_SPELL_LEVEL) { int nLevel = GetItemPropertyCostTableValue(ipTest); if(GetLocalInt(oPC, "PRC_ArcSpell" + IntToString(nLevel))) bPass = FALSE; nUMDDC += (nLevel * 2) - 20; } else if(ipType == ITEM_PROPERTY_USE_LIMITATION_DIVINE_SPELL_LEVEL) { int nLevel = GetItemPropertyCostTableValue(ipTest); if(GetLocalInt(oPC, "PRC_DivSpell" + IntToString(nLevel))) bPass = FALSE; nUMDDC += (nLevel * 2) - 20; } else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SNEAK_ATTACK) { int nLevel = GetItemPropertyCostTableValue(ipTest); if(GetLocalInt(oPC, "PRC_SneakLevel" + IntToString(nLevel))) bPass = FALSE; nUMDDC += (nLevel * 2) - 20; } else if(ipType == ITEM_PROPERTY_USE_LIMITATION_GENDER) { if(GetGender(oPC) != GetItemPropertySubType(ipTest)) bPass = FALSE; nUMDDC += 5; } } /* Properties that apply effects. Unequip should cause cleanup here */ if(ipType == ITEM_PROPERTY_SPEED_INCREASE) { int iItemAdjust; switch(GetItemPropertyCostTableValue(ipTest)) { case 0: iItemAdjust = 10; break; case 1: iItemAdjust = 20; break; case 2: iItemAdjust = 30; break; case 3: iItemAdjust = 40; break; case 4: iItemAdjust = 50; break; case 5: iItemAdjust = 60; break; case 6: iItemAdjust = 70; break; case 7: iItemAdjust = 80; break; case 8: iItemAdjust = 90; break; case 9: iItemAdjust = 100; break; } if(bUnequip) nSpeedIncrease -= iItemAdjust; else if(bEquip) nSpeedIncrease += iItemAdjust; } else if(ipType == ITEM_PROPERTY_SPEED_DECREASE) { int iItemAdjust; switch(GetItemPropertyCostTableValue(ipTest)) { case 0: iItemAdjust = 10; break; case 1: iItemAdjust = 20; break; case 2: iItemAdjust = 30; break; case 3: iItemAdjust = 40; break; case 4: iItemAdjust = 50; break; case 5: iItemAdjust = 60; break; case 6: iItemAdjust = 70; break; case 7: iItemAdjust = 80; break; case 8: iItemAdjust = 90; break; case 9: iItemAdjust = 99; break; } if(bUnequip) nSpeedDecrease -= iItemAdjust; else if(bEquip) nSpeedDecrease += iItemAdjust; } else if(ipType == ITEM_PROPERTY_PNP_HOLY_AVENGER) { if(bEquip) { int nPaladinLevels = GetLevelByClass(CLASS_TYPE_PALADIN, oPC); if(!nPaladinLevels) { //not a paladin? fake it //not really a true PnP test //instead it sets the paladin level //to the UMD ranks minus the amount required //to use a class restricted item of that value int nSkill = GetSkillRank(SKILL_USE_MAGIC_DEVICE, oPC); if(nSkill) { int nReqSkill = GetUMDForItemCost(oItem); nSkill -= nReqSkill; if(nSkill > 0) nPaladinLevels = nSkill; } } // Add Holy Avenger specials for Paladins (or successfull fake-Paladins) if(nPaladinLevels) { DelayCommand(0.1, IPSafeAddItemProperty(oItem, ItemPropertyEnhancementBonus(5), 99999.9)); DelayCommand(0.1, IPSafeAddItemProperty(oItem, ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, IP_CONST_DAMAGETYPE_DIVINE, IP_CONST_DAMAGEBONUS_2d6), 99999.9)); //this is a normal dispel magic useage, should be specific DelayCommand(0.1, IPSafeAddItemProperty(oItem, ItemPropertyCastSpell(IP_CONST_CASTSPELL_DISPEL_MAGIC_5, IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE), 99999.9)); DelayCommand(0.1, IPSafeAddItemProperty(oItem, ItemPropertyCastSpellCasterLevel(SPELL_DISPEL_MAGIC, nPaladinLevels), 99999.9)); } // Non-Paladin's get +2 enhancement bonus else { DelayCommand(0.1, IPSafeAddItemProperty(oItem, ItemPropertyEnhancementBonus(2), 99999.9)); // Remove Paladin specials IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS, DURATION_TYPE_TEMPORARY, -1); IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP, DURATION_TYPE_TEMPORARY, IP_CONST_ALIGNMENTGROUP_EVIL); IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL, DURATION_TYPE_TEMPORARY); IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL, DURATION_TYPE_TEMPORARY); } } else if(bUnequip) { IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS, DURATION_TYPE_TEMPORARY, -1); IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP, DURATION_TYPE_TEMPORARY, IP_CONST_ALIGNMENTGROUP_EVIL); IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL, DURATION_TYPE_TEMPORARY); IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL, DURATION_TYPE_TEMPORARY); } } else if(ipType == ITEM_PROPERTY_AREA_OF_EFFECT) { // This should only happen on equip or unequip if(bEquip || bUnequip) { // Remove existing AoE effect eTest = GetFirstEffect(oPC); while(GetIsEffectValid(eTest)) { if(GetEffectCreator(eTest) == oItem && GetEffectType(eTest) == EFFECT_TYPE_AREA_OF_EFFECT) { RemoveEffect(oPC, eTest); if(DEBUG) DoDebug("CheckPRCLimitations: Removing old AoE effect"); } eTest = GetNextEffect(oPC); } // Create new AoE - Only when equipping if(bEquip) { AssignCommand(oItem, _prc_inc_itmrstr_ApplyAoE(oPC, oItem, GetItemPropertySubType(ipTest), GetItemPropertyCostTable(ipTest))); }// end if - Equip event }// end if - Equip or Unequip event }// end if - AoE iprp else if(ipType == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) { // Only equippable items can provide bonus spell slots if(bEquip || bUnequip) { int nSubType = GetItemPropertySubType(ipTest); int nCost = GetItemPropertyCostTable(ipTest); SetLocalInt(oPC, "PRC_IPRPBonSpellSlots_" + IntToString(nSubType) + "_" + IntToString(nCost), GetLocalInt(oPC, "PRC_IPRPBonSpellSlots_" + IntToString(nSubType) + "_" + IntToString(nCost) ) + (bEquip ? 1 : -1) ); } } else if(ipType == ITEM_PROPERTY_WIZARDRY) { int nCost = GetItemPropertyCostTableValue(ipTest); if(bEquip) AssignCommand(oItem, _prc_inc_itmrstr_ApplyWizardry(oPC, oItem, nCost, "A")); else if(bUnequip) AssignCommand(oItem, _prc_inc_itmrstr_RemoveWizardry(oPC, oItem, nCost, "A")); } else if(ipType == ITEM_PROPERTY_DIVINITY) { int nCost = GetItemPropertyCostTableValue(ipTest); if(bEquip) AssignCommand(oItem, _prc_inc_itmrstr_ApplyWizardry(oPC, oItem, nCost, "D")); else if(bUnequip) AssignCommand(oItem, _prc_inc_itmrstr_RemoveWizardry(oPC, oItem, nCost, "D")); } ipTest = GetNextItemProperty(oItem); }// end while - Loop over all itemproperties // Determine if speed modification totals had changed if(nSpeedDecrease != GetLocalInt(oPC, PLAYER_SPEED_DECREASE)) { SetLocalInt(oPC, PLAYER_SPEED_DECREASE, nSpeedDecrease); //_prc_inc_itmrstr_ApplySpeedDecrease(oPC); } if(nSpeedIncrease != GetLocalInt(oPC, PLAYER_SPEED_INCREASE)) { SetLocalInt(oPC, PLAYER_SPEED_INCREASE, nSpeedIncrease); //_prc_inc_itmrstr_ApplySpeedIncrease(oPC); } // If some restriction would prevent item use, perform UMD skill check // Skip in case of unequip if(!bUnequip && !bPass) bPass = DoUMDCheck(oItem, oPC, nUMDDC); return bPass; } void CheckForPnPHolyAvenger(object oItem) { if(!GetPRCSwitch(PRC_PNP_HOLY_AVENGER_IPROP)) return; itemproperty ipTest = GetFirstItemProperty(oItem); while(GetIsItemPropertyValid(ipTest)) { if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_HOLY_AVENGER) { DelayCommand(0.1, RemoveItemProperty(oItem, ipTest)); DelayCommand(0.1, IPSafeAddItemProperty(oItem, ItemPropertyPnPHolyAvenger())); } ipTest = GetNextItemProperty(oItem); } } //:: Test Void //void main (){}