2025/12/04 Update

Added Intrinsic Armor builder's feat to prevent any Disarming.
Added Intrinsic Weapon builder's feat to prevent Ruin Armor.
GetEpicSorcerer() should allow racial hit dice casters.
Enlarge / Reduce Person now stores object's original scale.
Added prc_inc_size for the above changes.
Updated PRC8 version.
Reverted Vow of Poverty to use PRC event system.
Reverted Forsaker to use PRC event system.
Updated Spell Cancel NUI to not show "system" spells @Rakiov)
Added notes on instanced Maze system.
Organized notes.
This commit is contained in:
Jaysyn904
2025-12-04 18:44:36 -05:00
parent 3b6c74985e
commit f7d00cf6f8
421 changed files with 2130 additions and 393 deletions

View File

@@ -0,0 +1,151 @@
//::///////////////////////////////////////////////
//:: Name Forsaker
//:: FileName prc_forsaker.nss
//:: Created By: Stratosvarious
//:: Edited By: Fencas
//:://////////////////////////////////////////////
#include "prc_inc_combat"
#include "inc_dynconv"
#include "prc_class_const"
#include "prc_alterations"
#include "prc_ipfeat_const"
#include "nw_i0_spells"
void main()
{
object oPC = OBJECT_SELF;
object oItem;
object oArmor;
object oShield;
object oSkin = GetPCSkin(oPC);
int nSlot;
int nClass = GetLevelByClass(CLASS_TYPE_FORSAKER, oPC);
int nClassCheck;
int nBonus = nClass/2;
int nRegen = 1 + nClass/4;
int nSR = 10 + nClass;
int nEvent = GetCurrentlyRunningEvent();
// We aren't being called from any event, instead from EvalPRCFeats
if(nEvent == FALSE)
{
//Check if level up bonus has already been chosen and given for any of past Forsaker levels
for(nClassCheck=1; nClassCheck <= nClass; nClassCheck++)
{
if(!GetPersistantLocalInt(oPC, "ForsakerBoost"+IntToString(nClassCheck)))
{
//Level up box for stat bonus
AssignCommand(oPC, ClearAllActions(TRUE));
SetPersistantLocalInt(oPC,"ForsakerBoostCheck",nClassCheck);
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 (nClass >=2) ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectDamageReduction((nClass+1),(nClass/2)),oPC);
//Natural AC increase by CON starting on level 3
if (nClass >= 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);
}
// For some reason, EVENT_ONPLAYEREQUIPITEM just works with weapons, so armors and shields should be checked elsewhere
if(!GetHasFeat(FEAT_VOWOFPOVERTY,oPC))
{
for (nSlot=0; nSlot < 13; nSlot++) //All but creatures slots
{
oItem=GetItemInSlot(nSlot, oPC);
//Check if it is magical
if(GetIsItemPropertyValid(GetFirstItemProperty(oItem)) && !(GetItemPropertyTag(GetFirstItemProperty(oItem)) == "Tag_PRC_OnHitKeeper"))
{
AssignCommand(oPC, ClearAllActions(TRUE));
AssignCommand(oPC, ActionUnequipItem(oItem));
FloatingTextStringOnCreature(GetName(oItem)+" is a magical item!", oPC, FALSE);
}
}
if(GetIsUnarmed(oPC) && (nClass >= 3)) //If it is unarmed, give DR bypass
{
ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectAttackIncrease(nBonus),oPC);
ApplyEffectToObject(DURATION_TYPE_PERMANENT,EffectAttackDecrease(nBonus),oPC);
//Remove last weapon(s) bonus
oItem = GetPCItemLastUnequipped();
if((IPGetIsMeleeWeapon(oItem) || GetWeaponRanged(oItem)))
{
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ATTACK_BONUS, -1, -1, 1, "", -1, DURATION_TYPE_TEMPORARY);
RemoveSpecificProperty(oItem, ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER, -1, -1, 1, "", -1, DURATION_TYPE_TEMPORARY);
}
}
}
// Hook in the events
AddEventScript(oPC, EVENT_SCRIPT_MODULE_ON_EQUIP_ITEM, "prc_forsaker", TRUE, FALSE);
}
// We are called from the OnPlayerEquipItem eventhook. Add OnHitCast: Unique Power to oPC's weapon
else if((nEvent == EVENT_SCRIPT_MODULE_ON_EQUIP_ITEM) && (!GetHasFeat(FEAT_VOWOFPOVERTY,oPC)))
{
oItem = GetPCItemLastEquipped();
//Check if the magic is JUST Sanctify
int iMagic = 0;
itemproperty eCheckIP = GetFirstItemProperty(oItem);
while (GetIsItemPropertyValid(eCheckIP))
{
if(!(GetItemPropertyTag(eCheckIP) == "Sanctify1") && !(GetItemPropertyTag(eCheckIP) == "Sanctify2") && !(GetItemPropertyTag(eCheckIP) == "Sanctify3")
&& !(GetItemPropertyTag(eCheckIP) == "Sanctify4")) iMagic = 1;
eCheckIP = GetNextItemProperty(oItem);
}
//Check if weapons are magical
if(iMagic && (IPGetIsMeleeWeapon(oItem) || GetWeaponRanged(oItem)) &&
!(GetBaseItemType(oItem) == BASE_ITEM_SLING && GetItemPropertyType(GetFirstItemProperty(oItem)) == ITEM_PROPERTY_MIGHTY))
//Check if weapon is magical or not on allowed list
{
AssignCommand(oPC, ClearAllActions(TRUE));
AssignCommand(oPC, ActionUnequipItem(oItem));
FloatingTextStringOnCreature(GetName(oItem)+" is a magical item!", oPC, FALSE);
}
else
{
if(nClass>=3)
{
//Give bonus to weapon(s)
IPSafeAddItemProperty(oItem, ItemPropertyAttackBonus(nBonus), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
IPSafeAddItemProperty(oItem, ItemPropertyAttackPenalty(nBonus), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_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);
}
}
}
}
}

View File

@@ -0,0 +1,237 @@
//::///////////////////////////////////////////////
//:: Name Vow of Poverty
//:: FileName ft_vowofpoverty.nss
//:: Created By: Fencas
//:: Created On: 2024-12-02
//:: Based On: Stratovarius' Forsaker
//:://////////////////////////////////////////////
#include "prc_inc_combat"
#include "inc_dynconv"
#include "prc_class_const"
#include "prc_inc_clsfunc"
#include "prc_alterations"
#include "NW_I0_GENERIC"
#include "nw_i0_spells"
#include "inc_persist_loca"
effect VoPDamage(int nTotalEnhancement)
{
effect eDamage;
if (nTotalEnhancement>=15) eDamage = EffectDamageIncrease(DAMAGE_BONUS_15,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=14) eDamage = EffectDamageIncrease(DAMAGE_BONUS_14,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=13) eDamage = EffectDamageIncrease(DAMAGE_BONUS_13,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=12) eDamage = EffectDamageIncrease(DAMAGE_BONUS_12,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=11) eDamage = EffectDamageIncrease(DAMAGE_BONUS_11,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=10) eDamage = EffectDamageIncrease(DAMAGE_BONUS_10,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=9) eDamage = EffectDamageIncrease(DAMAGE_BONUS_9,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=8) eDamage = EffectDamageIncrease(DAMAGE_BONUS_8,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=7) eDamage = EffectDamageIncrease(DAMAGE_BONUS_7,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=6) eDamage = EffectDamageIncrease(DAMAGE_BONUS_6,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=5) eDamage = EffectDamageIncrease(DAMAGE_BONUS_5,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=4) eDamage = EffectDamageIncrease(DAMAGE_BONUS_4,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=3) eDamage = EffectDamageIncrease(DAMAGE_BONUS_3,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=2) eDamage = EffectDamageIncrease(DAMAGE_BONUS_2,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
else if (nTotalEnhancement>=1) eDamage = EffectDamageIncrease(DAMAGE_BONUS_1,DAMAGE_TYPE_BLUDGEONING || DAMAGE_TYPE_SLASHING || DAMAGE_TYPE_PIERCING);
return eDamage;
}
void main()
{
object oPC;
oPC = OBJECT_SELF;
object oItem;
object oArmor;
object oShield;
object oSkin = GetPCSkin(oPC);
int nEvent = GetCurrentlyRunningEvent();
int nLevel = GetCharacterLevel(oPC)-GetPersistantLocalInt(oPC,"VoPLevel1")+1;
int nACArmor = 4+nLevel/3;
int nACDeflection = nLevel/6;
int nACNatural = nLevel/8;
int nRegen = 1+(nLevel-17)/7;
int nDR = 5*(1+(nLevel-10)/9);
int nER = (10*(1+(nLevel-13)/7))-5;
int nResist = 0;//Resistance (Ex): At 7th level, an ascetic gains a +1 resistance bonus on all saving throws. This bonus increases to +2 at 13th level, and to +3 at 17th level.
if (nLevel >= 17) nResist = 1 + (nLevel - 7) / 5;
else if (nLevel >= 13) nResist = 2;
else if (nLevel >= 7) nResist = 1;
int nForsakerBonus = GetLevelByClass(CLASS_TYPE_FORSAKER, oPC)/2;
int nSlot, nLevelCheck, nExaltedStrike, nTotalEnhancement;
//Enhancement bonus for unarmed damage and weapons; consider the bigger, Forsaker or VoP
if(nLevel>=10) nExaltedStrike = 1+(nLevel-7)/3;
else if(nLevel>=4) nExaltedStrike = 1;
if(nForsakerBonus>=nExaltedStrike) nTotalEnhancement = nForsakerBonus;
else nTotalEnhancement = nExaltedStrike;
// We aren't being called from any event, instead from EvalPRCFeats
if(nEvent == FALSE)
{
//Check if level up bonus has already been chosen and given for any of past VoP levels
for(nLevelCheck=1; nLevelCheck <= nLevel; nLevelCheck++)
{
//Call stat boost dialogue for level 7 and each 4 levels after that
if (!GetPersistantLocalInt(oPC, "VoPBoost"+IntToString(nLevelCheck)) && (nLevelCheck-(nLevelCheck/4)*4 == 3) && (nLevelCheck >= 7) && (nLevelCheck <= 27))
{
AssignCommand(oPC, ClearAllActions(TRUE));
SetPersistantLocalInt(oPC,"VoPBoostCheck",nLevelCheck);
StartDynamicConversation("ft_vowpoverty_ab", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
}
//Applying stat boosts
if(GetPersistantLocalInt(oPC, "VoPBoost"+IntToString(nLevelCheck)) >= 10)
{
int stat = GetPersistantLocalInt(oPC, "VoPBoost"+IntToString(nLevelCheck)) - 10;
int value = 2 * (1 + (nLevel - nLevelCheck) / 4);
SetCompositeBonus(oSkin, "VoPBoostStat"+IntToString(stat), value, ITEM_PROPERTY_ABILITY_BONUS, stat);
}
//Call exalted feat for each even level
if (!GetPersistantLocalInt(oPC, "VoPFeat"+IntToString(nLevelCheck)) && (nLevelCheck-(nLevelCheck/2)*2 == 0))
{
AssignCommand(oPC, ClearAllActions(TRUE));
SetPersistantLocalInt(oPC,"VoPFeatCheck",nLevelCheck);
StartDynamicConversation("ft_vowpoverty_ft", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
}
}
//AC Armor +4 on 1st, then +1 each 3 levels
ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectACIncrease(nACArmor, AC_ARMOUR_ENCHANTMENT_BONUS)), oPC);
//Deflection Armor +1 each 6 levels
ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectACIncrease(nACDeflection, AC_DEFLECTION_BONUS)), oPC);
//Resistance (Ex): At 7th level, an ascetic gains a +1 resistance bonus on all saving throws. This bonus increases to +2 at 13th level, and to +3 at 17th level.
if (nLevel>=7)
{
SetCompositeBonus(oSkin, "VoPResist", nResist, ITEM_PROPERTY_SAVING_THROW_BONUS, SAVING_THROW_ALL);
}
//Natural Armor +1 each 8 levels
ApplyEffectToObject(DURATION_TYPE_PERMANENT, ExtraordinaryEffect(EffectACIncrease(nACNatural, AC_NATURAL_BONUS)), oPC);
//DR 5/Enhancement starting 10, +5 each 9 levels
if (nLevel >= 10) ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectDamageReduction(nDR,nTotalEnhancement)),oPC);
//Energy resistance 5 for acid, cold ,electrical, fire and sonic on 13th, than +10 each 7 levels
if (nLevel>=13)
{
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageResistance(DAMAGE_TYPE_ACID, nER, 0, FALSE), oPC);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageResistance(DAMAGE_TYPE_COLD, nER, 0, FALSE), oPC);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nER, 0, FALSE), oPC);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageResistance(DAMAGE_TYPE_FIRE, nER, 0, FALSE), oPC);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageResistance(DAMAGE_TYPE_SONIC, nER, 0, FALSE), oPC);
}
//Freedom of Movement at 14th
if (nLevel>=14) IPSafeAddItemProperty(oSkin, ItemPropertyFreeAction(), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, TRUE, TRUE);
//Regeneration 1 at 17th, +1 at each 7 levels
if (nLevel>=17) SetCompositeBonus(oSkin, "VoPFH", nRegen, ITEM_PROPERTY_REGENERATION);
//True Seeing at 18th
if (nLevel>=18) IPSafeAddItemProperty(oSkin, ItemPropertyTrueSeeing(), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, TRUE, TRUE);
// Exalted Strike - only applies to weapons or unarmed
effect eEffect1 = EffectAttackIncrease(nTotalEnhancement);
effect eEffect2 = VoPDamage(nTotalEnhancement);
effect eLink = EffectLinkEffects(eEffect1,eEffect2);
eLink = SupernaturalEffect(eLink);
eLink = TagEffect(eLink, "EffectExaltedStrike");
//Remove any prior bonus to avoid duplication
effect eCheckEffect = GetFirstEffect(oPC);
while (GetIsEffectValid(eCheckEffect))
{
if(GetEffectTag(eCheckEffect) == "EffectExaltedStrike") RemoveEffect(oPC, eCheckEffect);
eCheckEffect = GetNextEffect(oPC);
}
//Give player the bonus, regardless of the weapon
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oPC);
// For some reason, EVENT_ONPLAYEREQUIPITEM just works with weapons, so it is better to check all other items for magic elsewhere
for (nSlot=0; nSlot < 13; nSlot++) //All but creatures slots
{
oItem=GetItemInSlot(nSlot, oPC);
if (!(GetTag(oItem) == "xp1_mystrashand")
&& !(GetTag(oItem) == "H2_SenseiAmulet")
&& !(GetResRef(oItem) == "prc_sk_mblade_bs")
&& !(GetResRef(oItem) == "prc_sk_mblade_th")
&& !(GetResRef(oItem) == "prc_sk_mblade_ss")
&& !(GetResRef(oItem) == "prc_sk_mblade_ls"))
{
if((GetIsItemPropertyValid(GetFirstItemProperty(oItem)) && !(GetItemPropertyTag(GetFirstItemProperty(oItem)) == "Tag_PRC_OnHitKeeper")
&& !(nSlot == 4 || nSlot == 5)) //Check if it is magical (all items but on the hands)
|| (nSlot == 1 && GetBaseAC(oItem) >= 1) //Check if it is an armor (AC>0)
|| (nSlot == 5 && GetBaseItemType(oItem) == BASE_ITEM_SMALLSHIELD || //Check if it is a shield
GetBaseItemType(oItem) == BASE_ITEM_LARGESHIELD ||
GetBaseItemType(oItem) == BASE_ITEM_TOWERSHIELD))
{
AssignCommand(oPC, ClearAllActions(TRUE));
AssignCommand(oPC, ActionUnequipItem(oItem));
FloatingTextStringOnCreature(GetName(oItem)+" would break your vow!", oPC, FALSE);
}
}
}
//Remove bonus from unequiped weapons
oItem = GetPCItemLastUnequipped();
if(IPGetIsMeleeWeapon(oItem) || GetWeaponRanged(oItem)) IPRemoveAllItemProperties(oItem, DURATION_TYPE_PERMANENT);
AddEventScript(oPC, EVENT_SCRIPT_MODULE_ON_EQUIP_ITEM, "ft_vowofpoverty", TRUE, FALSE);
}
// We are called from the OnPlayerUnEquipItem eventhook. Remove OnHitCast: Unique Power from oPC's weapon
else if(nEvent == EVENT_SCRIPT_MODULE_ON_EQUIP_ITEM)
{
oItem = GetPCItemLastEquipped();
int iWeaponAllowed = GetBaseItemType(oItem) == BASE_ITEM_CLUB
|| GetBaseItemType(oItem) == BASE_ITEM_DAGGER
|| GetBaseItemType(oItem) == BASE_ITEM_DART
|| GetBaseItemType(oItem) == BASE_ITEM_HEAVYCROSSBOW
|| GetBaseItemType(oItem) == BASE_ITEM_LIGHTCROSSBOW
|| GetBaseItemType(oItem) == BASE_ITEM_LIGHTMACE
|| GetBaseItemType(oItem) == BASE_ITEM_MORNINGSTAR
|| GetBaseItemType(oItem) == BASE_ITEM_QUARTERSTAFF
|| GetBaseItemType(oItem) == BASE_ITEM_SICKLE
|| GetBaseItemType(oItem) == BASE_ITEM_SLING
|| GetBaseItemType(oItem) == BASE_ITEM_SHORTSPEAR
|| GetBaseItemType(oItem) == BASE_ITEM_BOLT
|| GetBaseItemType(oItem) == BASE_ITEM_GOAD
|| GetBaseItemType(oItem) == BASE_ITEM_KATAR
|| GetBaseItemType(oItem) == BASE_ITEM_HEAVY_MACE
|| GetBaseItemType(oItem) == BASE_ITEM_BULLET;
//Check if the magic is JUST Sanctify
int iMagic = 0;
itemproperty eCheckIP = GetFirstItemProperty(oItem);
while (GetIsItemPropertyValid(eCheckIP))
{
if(!(GetItemPropertyTag(eCheckIP) == "Sanctify1") && !(GetItemPropertyTag(eCheckIP) == "Sanctify2") && !(GetItemPropertyTag(eCheckIP) == "Sanctify3")
&& !(GetItemPropertyTag(eCheckIP) == "Sanctify4")) iMagic = 1;
eCheckIP = GetNextItemProperty(oItem);
}
if (!(GetTag(oItem) == "xp1_mystrashand")
&& !(GetTag(oItem) == "H2_SenseiAmulet")
&& !(GetResRef(oItem) == "prc_sk_mblade_bs")
&& !(GetResRef(oItem) == "prc_sk_mblade_th")
&& !(GetResRef(oItem) == "prc_sk_mblade_ss")
&& !(GetResRef(oItem) == "prc_sk_mblade_ls"))
{
if((IPGetIsMeleeWeapon(oItem) || GetWeaponRanged(oItem)) && (iMagic || !iWeaponAllowed)) //Check if weapon is magical or not on allowed list
{
if(!(GetBaseItemType(oItem) == BASE_ITEM_SLING && GetItemPropertyType(GetFirstItemProperty(oItem)) == ITEM_PROPERTY_MIGHTY)) //Allow Mighty Bonus on Slings
{
AssignCommand(oPC, ClearAllActions(TRUE));
AssignCommand(oPC, ActionUnequipItem(oItem));
FloatingTextStringOnCreature(GetName(oItem)+" would break your vow!", oPC, FALSE);
}
}
}
}
}

View File

@@ -0,0 +1,171 @@
//::////////////////////////////////////////////////////////
//:: ;-. ,-. ,-. ,-.
//:: | ) | ) / ( )
//:: |-' |-< | ;-:
//:: | | \ \ ( )
//:: ' ' ' `-' `-'
//::///////////////////////////////////////////////////////
//::
/*
Impactscript for Shadow Servant.
(this is handled in the Familiar script)
Shadow Servant (Su): At 1st level, your shadow familiar permanently
transforms into a Medium shadow elemental. It loses all familiar
traits, but gains new abilities as your shadow servant.
Should your shadow servant die, you can summon a replacement after
24 hours pass. Your shadow servant cannot travel farther from you
than 30 feet + 10 feet for each of your master of shadow levels
(40 feet at 1st level and a maximum of 130 feet at 10th level). If
it is forcibly separated from you by more than this distance, the
servant dissipates instantly, and you must wait 24 hours to summon
a new one.
*/
//::
//:://////////////////////////////////////////////
//:: Script: mshadw_shadserv.nss
//:: Author: Jaysyn
//:: Created: 2025-11-11 19:25:58
//:://////////////////////////////////////////////
#include "prc_inc_json"
#include "prc_inc_spells"
const string SHADOW_SERVANT_RESREF = "prc_shadow_serv";
// Watch function: despawns Shadow Servant if master is dead or out of range
void ShadowServantWatch(object oShadow, object oPC)
{
if(DEBUG) DoDebug("mshadw_shadserv >> ShadowServantWatch: Starting function.");
int nMaster = GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oPC);
float fRange = 30.0 + (nMaster * 10);
if (!GetIsObjectValid(oShadow) || !GetIsObjectValid(oPC)) return;
if (GetIsDead(oPC) ||
GetDistanceBetween(oShadow, oPC) > FeetToMeters(fRange))
{
DestroyObject(oShadow);
return;
}
DelayCommand(1.0, ShadowServantWatch(oShadow, oPC));
}
void main()
{
object oPC = OBJECT_SELF;
int nMaster = GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oPC);
int nDexBonus = (nMaster >= 5 && (nMaster % 2)) ? (nMaster - 3) : 0;
float fRange = 30.0 + (nMaster * 10);
// Target location
location lTarget = PRCGetSpellTargetLocation();
// Distance check
if (GetDistanceBetweenLocations(GetLocation(oPC), lTarget) > FeetToMeters(fRange))
{
SendMessageToPC(oPC, "That location is too far away.");
return;
}
// Load template
json jShadow = TemplateToJson(SHADOW_SERVANT_RESREF, RESTYPE_UTC);
if (jShadow == JSON_NULL)
{
SendMessageToPC(oPC, "mshdw_shadserv: TemplateToJson failed <20> bad resref or resource missing.");
return;
}
// Original HD
int nOriginalHD = json_GetCreatureHD(jShadow);
if (nOriginalHD <= 0)
{
SendMessageToPC(oPC, "mshdw_shadserv: json_GetCreatureHD failed <20> template missing HD data.");
return;
}
//:: Add Hit Dice
int nHDToAdd = nMaster -1;
if (nHDToAdd < 0) nHDToAdd = 0;
jShadow = json_AddHitDice(jShadow, nHDToAdd);
if (jShadow == JSON_NULL)
{
SendMessageToPC(oPC, "mshdw_shadserv: json_AddHitDice failed - JSON became invalid.");
return;
}
//:: Update feats
jShadow = json_AddFeatsFromCreatureVars(jShadow, nOriginalHD);
if (jShadow == JSON_NULL)
{
SendMessageToPC(oPC, "mshdw_shadserv: json_AddFeatsFromCreatureVars failed <20> JSON became invalid.");
return;
}
//:: Update stats
jShadow = json_ApplyAbilityBoostFromHD(jShadow, nOriginalHD);
if (jShadow == JSON_NULL)
{
SendMessageToPC(oPC, "mshdw_shadserv: json_ApplyAbilityBoostFromHD failed <20> JSON became invalid.");
return;
}
//:: Bonus DEX from Shadow Servant class ability
jShadow = json_UpdateTemplateStats(jShadow, 0, nDexBonus);
// Size increase
if (nMaster > 2)
{
jShadow = json_AdjustCreatureSize(jShadow, 1, TRUE);
if (jShadow == JSON_NULL)
{
SendMessageToPC(oPC, "mshdw_shadserv: json_AdjustCreatureSize failed - JSON became invalid.");
return;
}
}
//:: One skill point per HD
jShadow = json_AdjustCreatureSkillByID(jShadow, SKILL_SPOT, nHDToAdd);
if (jShadow == JSON_NULL)
{
DoDebug("mdshdw_shadserv >> json_AdjustCreatureSkillByID failed <20> JSON became invalid.");
return;
}
object oShadow = JsonToObject(jShadow, lTarget);
effect eSummon = ExtraordinaryEffect(EffectSummonCreature("", VFX_FNF_SUMMON_UNDEAD, 0.0, 0, VFX_IMP_UNSUMMON, oShadow));
ApplyEffectAtLocation(DURATION_TYPE_PERMANENT, eSummon, lTarget);
if (!GetIsObjectValid(oShadow))
{
SendMessageToPC(oPC, "mshdw_shadserv: JsonToObject failed - could not create creature from edited template.");
return;
}
// Set faction to caster<65>s
ChangeFaction(oShadow, oPC);
SetLocalObject(oShadow, "ANIMATOR", oPC);
SetCurrentHitPoints(oShadow, GetMaxPossibleHP(oShadow));
effect eGhost = EffectVisualEffect(VFX_DUR_GHOST_TRANSPARENT);
eGhost = UnyieldingEffect(eGhost);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, oShadow);
// Start watch loop
DelayCommand(6.1, ShadowServantWatch(oShadow, oPC));
}

View File

@@ -0,0 +1,682 @@
//:://////////////////////////////////////////////
//:: Alternate magic system gain evaluation script
//:: prc_amagsys_gain
//:://////////////////////////////////////////////
/** @file
This file determines if the given character
has gained new spells / powers / utterances /
whathaveyou since the last time it was run.
If so, it starts the relevant selection
conversations.
Add new classes to their respective magic
user type block, or if such doesn't exist
yet for the system the class belongs to,
make a new block for them at the end of main().
@author Ornedan
@date Created - 2006.12.14
*/
//:://////////////////////////////////////////////
//:: Updated for .35 by Jaysyn 2023/03/11
//:://////////////////////////////////////////////
#include "inc_dynconv"
#include "psi_inc_psifunc"
#include "inc_newspellbook"
#include "true_inc_trufunc"
#include "tob_inc_tobfunc"
#include "shd_inc_shdfunc"
#include "inv_inc_invfunc"
#include "prc_nui_lv_inc"
//////////////////////////////////////////////////
/* Function prototypes */
//////////////////////////////////////////////////
void CheckSpellbooks(object oPC);
void CheckPsionics(object oPC);
void CheckInvocations(object oPC);
void CheckToB(object oPC);
void CheckShadow(object oPC);
void CheckTruenaming(object oPC);
int CheckMissingPowers(object oPC, int nClass);
int CheckMissingSpells(object oPC, int nClass, int nMinLevel, int nMaxLevel);
int CheckMissingUtterances(object oPC, int nClass, int nLexicon);
int CheckMissingManeuvers(object oPC, int nClass);
int CheckMissingMysteries(object oPC, int nClass);
int CheckMissingInvocations(object oPC, int nClass);
void AMSCompatibilityCheck(object oPC);
//////////////////////////////////////////////////
/* Function definitions */
//////////////////////////////////////////////////
void main()
{
object oPC = OBJECT_SELF;
// Sanity checks - Shifted or polymorphed characters may have their hide fucked up, and might be missing access to their hide-feats
// @todo Shifting probably doesn't do this anymore, could be ditchable - Ornedan, 20061214
if(GetLocalInt(oPC, "nPCShifted"))
return;
effect eTest = GetFirstEffect(oPC);
while(GetIsEffectValid(eTest))
{
if(GetEffectType(eTest) == EFFECT_TYPE_POLYMORPH)
return;
eTest = GetNextEffect(oPC);
}
DelayCommand(0.0f, CheckSpellbooks(oPC));
}
// Handle new spellbooks
void CheckSpellbooks(object oPC)
{
if(GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oPC) > 0)
{
CheckMissingSpells(oPC, CLASS_TYPE_SUBLIME_CHORD, 4, 9);
if(GetHasFeat(FEAT_SUBLIME_CHORD_SPELLCASTING_BARD, oPC))
{
CheckMissingSpells(oPC, CLASS_TYPE_BARD, 0, 3);
}
if(GetHasFeat(FEAT_SUBLIME_CHORD_SPELLCASTING_SORCERER))
{
CheckMissingSpells(oPC, CLASS_TYPE_SORCERER, 0, 3);
}
if(GetHasFeat(FEAT_SUBLIME_CHORD_SPELLCASTING_WARMAGE, oPC))
{
CheckMissingSpells(oPC, CLASS_TYPE_WARMAGE, 0, 3);
}
if(GetHasFeat(FEAT_SUBLIME_CHORD_SPELLCASTING_DUSKBLADE, oPC))
{
CheckMissingSpells(oPC, CLASS_TYPE_DUSKBLADE, 0, 3);
}
if(GetHasFeat(FEAT_SUBLIME_CHORD_SPELLCASTING_BEGUILER, oPC))
{
CheckMissingSpells(oPC, CLASS_TYPE_BEGUILER, 0, 3);
}
}
// Check all classes that might need a spellbook update
if(GetIsRHDSorcerer(oPC)) CheckMissingSpells(oPC, CLASS_TYPE_SORCERER, 0, 9);
if(GetIsRHDBard(oPC)) CheckMissingSpells(oPC, CLASS_TYPE_BARD, 0, 6);
if(!GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK))
CheckMissingSpells(oPC, CLASS_TYPE_BARD, 0, 6);
if(!GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK))
CheckMissingSpells(oPC, CLASS_TYPE_SORCERER, 0, 9);
CheckMissingSpells(oPC, CLASS_TYPE_SUEL_ARCHANAMACH, 1, 5);
CheckMissingSpells(oPC, CLASS_TYPE_FAVOURED_SOUL, 0, 9);
CheckMissingSpells(oPC, CLASS_TYPE_WARMAGE, 0, 9);
CheckMissingSpells(oPC, CLASS_TYPE_DREAD_NECROMANCER, 1, 9);
CheckMissingSpells(oPC, CLASS_TYPE_HEXBLADE, 1, 4);
CheckMissingSpells(oPC, CLASS_TYPE_DUSKBLADE, 0, 5);
CheckMissingSpells(oPC, CLASS_TYPE_JUSTICEWW, 1, 4);
CheckMissingSpells(oPC, CLASS_TYPE_KNIGHT_WEAVE, 1, 6);
CheckMissingSpells(oPC, CLASS_TYPE_ARCHIVIST, 0, 9);
CheckMissingSpells(oPC, CLASS_TYPE_BEGUILER, 0, 9);
CheckMissingSpells(oPC, CLASS_TYPE_HARPER, 1, 3);
CheckMissingSpells(oPC, CLASS_TYPE_CELEBRANT_SHARESS, 1, 4);
//CheckMissingSpells(oPC, CLASS_TYPE_ASSASSIN, 1, 4);
// Check psionics
DelayCommand(0.0f, CheckPsionics(oPC));
}
/* void CheckSpellbooks(object oPC)
{
if(GetIsRHDSorcerer(oPC) && CheckMissingSpells(oPC, CLASS_TYPE_SORCERER, 0, 9))
return;
if(GetIsRHDBard(oPC) && CheckMissingSpells(oPC, CLASS_TYPE_BARD, 0, 6))
return;
if(!GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK) && CheckMissingSpells(oPC, CLASS_TYPE_BARD, 0, 6))
return;
if(!GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK) && CheckMissingSpells(oPC, CLASS_TYPE_SORCERER, 0, 9))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_SUEL_ARCHANAMACH, 1, 5))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_FAVOURED_SOUL, 0, 9))
return;
// if(CheckMissingSpells(oPC, CLASS_TYPE_MYSTIC, 0, 9))
// return;
if(CheckMissingSpells(oPC, CLASS_TYPE_WARMAGE, 0, 9))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_DREAD_NECROMANCER, 1, 9))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_HEXBLADE, 1, 4))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_DUSKBLADE, 0, 5))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_JUSTICEWW, 1, 4))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_KNIGHT_WEAVE, 1, 6))
return;
// if(CheckMissingSpells(oPC, CLASS_TYPE_WITCH, 0, 9))
// return;
if(CheckMissingSpells(oPC, CLASS_TYPE_SUBLIME_CHORD, 4, 9))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_ARCHIVIST, 0, 9))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_BEGUILER, 0, 9))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_HARPER, 1, 3))
return;
// if(CheckMissingSpells(oPC, CLASS_TYPE_TEMPLAR, 0, 9))
// return;
if(CheckMissingSpells(oPC, CLASS_TYPE_ASSASSIN, 1, 4))
return;
if(CheckMissingSpells(oPC, CLASS_TYPE_CELEBRANT_SHARESS, 1, 4))
return;
DelayCommand(0.0f, CheckPsionics(oPC));
}
*/
// Handle psionics
void CheckPsionics(object oPC)
{
if(CheckMissingPowers(oPC, CLASS_TYPE_PSION))
return;
if(CheckMissingPowers(oPC, CLASS_TYPE_WILDER))
return;
if(CheckMissingPowers(oPC, CLASS_TYPE_PSYWAR))
return;
if(CheckMissingPowers(oPC, CLASS_TYPE_PSYCHIC_ROGUE))
return;
if(CheckMissingPowers(oPC, CLASS_TYPE_FIST_OF_ZUOKEN))
return;
if(CheckMissingPowers(oPC, CLASS_TYPE_WARMIND))
return;
//expanded knowledge
if(CheckMissingPowers(oPC, -1))
return;
//epic expanded knowledge
if(CheckMissingPowers(oPC, -2))
return;
DelayCommand(0.0f, CheckInvocations(oPC));
}
// Handle Invocations
void CheckInvocations(object oPC)
{
if(CheckMissingInvocations(oPC, CLASS_TYPE_DRAGONFIRE_ADEPT))
return;
if(CheckMissingInvocations(oPC, CLASS_TYPE_WARLOCK))
return;
if(CheckMissingInvocations(oPC, CLASS_TYPE_DRAGON_SHAMAN))
return;
//extra invocations
if(CheckMissingInvocations(oPC, CLASS_TYPE_INVALID))
return;
//epic extra invocations
if(CheckMissingInvocations(oPC, -2))
return;
DelayCommand(0.0f, CheckToB(oPC));
}
// Handle Tome of Battle
void CheckToB(object oPC)
{
if(CheckMissingManeuvers(oPC, CLASS_TYPE_CRUSADER))
return;
if(CheckMissingManeuvers(oPC, CLASS_TYPE_SWORDSAGE))
return;
if(CheckMissingManeuvers(oPC, CLASS_TYPE_WARBLADE))
return;
DelayCommand(0.0f, CheckShadow(oPC));
}
// Handle Shadowcasting
void CheckShadow(object oPC)
{
if(CheckMissingMysteries(oPC, CLASS_TYPE_SHADOWCASTER))
return;
if(CheckMissingMysteries(oPC, CLASS_TYPE_SHADOWSMITH))
return;
DelayCommand(0.0f, CheckTruenaming(oPC));
}
// Handle Truenaming - Three different Lexicons to check
void CheckTruenaming(object oPC)
{
if(CheckMissingUtterances(oPC, CLASS_TYPE_TRUENAMER, LEXICON_EVOLVING_MIND))
return;
if(CheckMissingUtterances(oPC, CLASS_TYPE_TRUENAMER, LEXICON_CRAFTED_TOOL))
return;
if(CheckMissingUtterances(oPC, CLASS_TYPE_TRUENAMER, LEXICON_PERFECTED_MAP))
return;
if(!GetIsDM(oPC))
DelayCommand(0.0f, AMSCompatibilityCheck(oPC));
}
int CheckMissingPowers(object oPC, int nClass)
{
int nLevel = GetLevelByClass(nClass, oPC);
if(!nLevel && nClass != -1 && nClass != -2)
return FALSE;
else if(nClass == -1 && !GetHasFeat(FEAT_EXPANDED_KNOWLEDGE_1))
return FALSE;
else if(nClass == -2 && !GetHasFeat(FEAT_EPIC_EXPANDED_KNOWLEDGE_1))
return FALSE;
int nCurrentPowers = GetPowerCount(oPC, nClass);
int nMaxPowers = GetMaxPowerCount(oPC, nClass);
if(nCurrentPowers < nMaxPowers)
{
if (nClass <= 0)
nClass = GetPrimaryPsionicClass(oPC);
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain powers and start the conversation
SetLocalInt(oPC, "nClass", nClass);
StartDynamicConversation("psi_powconv", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
return FALSE;
}
int CheckMissingInvocations(object oPC, int nClass)
{
int nLevel = GetLevelByClass(nClass, oPC);
if(!nLevel && (nClass == CLASS_TYPE_DRAGONFIRE_ADEPT || nClass == CLASS_TYPE_WARLOCK || nClass == CLASS_TYPE_DRAGON_SHAMAN))
return FALSE;
else if(nClass == CLASS_TYPE_INVALID && !GetHasFeat(FEAT_EXTRA_INVOCATION_I))
return FALSE;
else if(nClass == -2 && !GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_I))
return FALSE;
int nCurrentInvocations = GetInvocationCount(oPC, nClass);
if(DEBUG) DoDebug("Current Invocations: " + IntToString(nCurrentInvocations));
int nMaxInvocations = GetMaxInvocationCount(oPC, nClass);
if(DEBUG) DoDebug("Max Invocations: " + IntToString(nMaxInvocations));
if(nCurrentInvocations < nMaxInvocations)
{
if (nClass == CLASS_TYPE_INVALID || nClass == -2)
nClass = GetPrimaryInvocationClass(oPC);
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain invocations and start the conversation
SetLocalInt(oPC, "nClass", nClass);
StartDynamicConversation("inv_invokeconv", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
return FALSE;
}
void AddSpellsForLevel(int nClass, int nLevel)
{
object oPC = OBJECT_SELF;
object oSkin = GetPCSkin(oPC);
//object oToken = GetHideToken(oPC);
string sFile = GetFileForClass(nClass);
string sSpellbook;
int nSpellbookType = GetSpellbookTypeForClass(nClass);
if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS)
sSpellbook = "Spellbook"+IntToString(nClass);
else
sSpellbook = "Spellbook_Known_"+IntToString(nClass)+"_"+IntToString(nLevel);
// Create spells known persistant array if it is missing
int nSize = persistant_array_get_size(oPC, sSpellbook);
if (nSize < 0)
{
persistant_array_create(oPC, sSpellbook);
nSize = 0;
}
//check for learnable spells
object oToken_Class = GetObjectByTag("SpellLvl_" + IntToString(nClass) + "_Level_" + IntToString(nLevel));
int nSpells_Total = persistant_array_get_size(oToken_Class, "Lkup");
int i;
for(i = 0; i < nSpells_Total; i++)
{
int nSpellbookID = persistant_array_get_int(oToken_Class, "Lkup", i);
if(Get2DAString(sFile, "AL", nSpellbookID) != "1")
{
persistant_array_set_int(oPC, sSpellbook, nSize, nSpellbookID);
nSize++;
if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS)
{
int nIPFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID));
int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID));
AddSpellUse(oPC, nSpellbookID, nClass, sFile, "NewSpellbookMem_" + IntToString(nClass), nSpellbookType, oSkin, nFeatID, nIPFeatID);
}
}
}
}
int CheckMissingSpells(object oPC, int nClass, int nMinLevel, int nMaxLevel)
{
int nLevel;
//:: Rakshasa cast as sorcerers
if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA)
nLevel = GetSpellslotLevel(nClass, oPC); //GetLevelByClass(CLASS_TYPE_OUTSIDER, oPC);
//:: Aranea cast as sorcerers
else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ARANEA)
nLevel = GetSpellslotLevel(nClass, oPC); //GetLevelByClass(CLASS_TYPE_SHAPECHANGER, oPC);
//::Arkamoi cast as sorcerers
else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI)
nLevel = GetSpellslotLevel(nClass, oPC); //GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC);
//::Hobgoblin Warsouls cast as sorcerers
else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL)
nLevel = GetSpellslotLevel(nClass, oPC); //GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC);
//:: Driders cast as sorcerers
else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_DRIDER)
nLevel = GetSpellslotLevel(nClass, oPC); //GetLevelByClass(CLASS_TYPE_ABERRATION, oPC);
//:: Marrutact cast as 6/7 sorcerers
else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT)
nLevel = GetSpellslotLevel(nClass, oPC); //GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC);
//:: Redspawn Arcaniss cast as 3/4 sorcerers
else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS)
nLevel = GetSpellslotLevel(nClass, oPC); //GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC);
//:: Gloura cast as bards
else if(nClass == CLASS_TYPE_BARD && !GetLevelByClass(CLASS_TYPE_BARD, oPC) && GetRacialType(oPC) == RACIAL_TYPE_GLOURA)
nLevel = GetSpellslotLevel(nClass, oPC); //GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC);
else
nLevel = nClass == CLASS_TYPE_SUBLIME_CHORD ? GetLevelByClass(nClass, oPC) : GetSpellslotLevel(nClass, oPC);
if (DEBUG) DoDebug("CheckMissingSpells 1 Class: " + IntToString(nClass));
if (DEBUG) DoDebug("CheckMissingSpells 1 Level: " + IntToString(nLevel));
if(!nLevel)
return FALSE;
if(nClass == CLASS_TYPE_BARD || nClass == CLASS_TYPE_SORCERER)
{
if((GetLevelByClass(nClass, oPC) == nLevel) //no PrC
&& !(GetHasFeat(FEAT_DRACONIC_GRACE, oPC) || GetHasFeat(FEAT_DRACONIC_BREATH, oPC))) //no Draconic feats that apply
return FALSE;
}
else if(nClass == CLASS_TYPE_ARCHIVIST)
{
int nLastGainLevel = GetPersistantLocalInt(oPC, "LastSpellGainLevel");
nLevel = GetLevelByClass(CLASS_TYPE_ARCHIVIST, oPC);
//add cleric spells known for level 0
if(persistant_array_get_size(oPC, "Spellbook_Known_"+IntToString(CLASS_TYPE_ARCHIVIST)+"_0") < 5) // TODO: replace with GetSpellKnownCurrentCount
{
ActionDoCommand(AddSpellsForLevel(CLASS_TYPE_ARCHIVIST, 0));
}
if(nLastGainLevel < nLevel)
{
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
SetLocalInt(oPC, "SpellGainClass", CLASS_TYPE_ARCHIVIST);
SetLocalInt(oPC, "SpellbookMinSpelllevel", nMinLevel);
StartDynamicConversation("prc_s_spellgain", oPC, DYNCONV_EXIT_NOT_ALLOWED, TRUE, FALSE, oPC);
*/
return TRUE;
}
else
return FALSE;
}
if (DEBUG) DoDebug("CheckMissingSpells 2 Class: " + IntToString(nClass));
if (DEBUG) DoDebug("CheckMissingSpells 2 Level: " + IntToString(nLevel));
int i;
for(i = nMinLevel; i <= nMaxLevel; i++)
{
int nMaxSpells = GetSpellKnownMaxCount(nLevel, i, nClass, oPC);
if(nMaxSpells > 0)
{
int nCurrentSpells = GetSpellKnownCurrentCount(oPC, i, nClass);
int nSpellsAvailable = GetSpellUnknownCurrentCount(oPC, i, nClass);
if(nCurrentSpells < nMaxSpells && nSpellsAvailable > 0)
{
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS && bKnowsAllClassSpells(nClass))
{
ActionDoCommand(AddSpellsForLevel(nClass, i));
}
else
{
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain powers and start the conversation
SetLocalInt(oPC, "SpellGainClass", nClass);
SetLocalInt(oPC, "SpellbookMinSpelllevel", nMinLevel);
SetLocalInt(oPC, "SpellbookMaxSpelllevel", nMaxLevel);
StartDynamicConversation("prc_s_spellgain", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
}
}
}
//Advanced Learning check
nLevel = GetLevelByClass(nClass, oPC);
int nALSpells = GetPersistantLocalInt(oPC, "AdvancedLearning_"+IntToString(nClass));
if(nClass == CLASS_TYPE_BEGUILER && nALSpells < (nLevel+1)/4)//one every 4 levels starting at 3.
{
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain powers and start the conversation
SetLocalInt(oPC, "SpellGainClass", CLASS_TYPE_BEGUILER);
SetLocalInt(oPC, "SpellbookMinSpelllevel", nMinLevel);
SetLocalInt(oPC, "AdvancedLearning", 1);
StartDynamicConversation("prc_s_spellgain", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
else if(nClass == CLASS_TYPE_DREAD_NECROMANCER && nALSpells < nLevel/4)//one every 4 levels
{
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain powers and start the conversation
SetLocalInt(oPC, "SpellGainClass", CLASS_TYPE_DREAD_NECROMANCER);
SetLocalInt(oPC, "SpellbookMinSpelllevel", nMinLevel);
SetLocalInt(oPC, "AdvancedLearning", 1);
StartDynamicConversation("prc_s_spellgain", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
else if(nClass == CLASS_TYPE_WARMAGE)
{
if((nLevel >= 40 && nALSpells < 9) ||// :/
(nLevel >= 36 && nLevel < 40 && nALSpells < 8) ||
(nLevel >= 32 && nLevel < 36 && nALSpells < 7) ||
(nLevel >= 28 && nLevel < 32 && nALSpells < 6) ||
(nLevel >= 24 && nLevel < 28 && nALSpells < 5) ||
(nLevel >= 16 && nLevel < 24 && nALSpells < 4) ||
(nLevel >= 11 && nLevel < 16 && nALSpells < 3) ||
(nLevel >= 6 && nLevel < 11 && nALSpells < 2) ||
(nLevel >= 3 && nLevel < 6 && nALSpells < 1))
{
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain powers and start the conversation
SetLocalInt(oPC, "SpellGainClass", CLASS_TYPE_WARMAGE);
SetLocalInt(oPC, "SpellbookMinSpelllevel", nMinLevel);
SetLocalInt(oPC, "AdvancedLearning", 1);
StartDynamicConversation("prc_s_spellgain", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
}
else if(nClass == CLASS_TYPE_NIGHTSTALKER && nALSpells < (nLevel+1)/6)//one every 6 levels starting at 5th
{
// Mark the class for which the PC is to gain powers and start the conversation
SetLocalInt(oPC, "SpellGainClass", CLASS_TYPE_NIGHTSTALKER);
SetLocalInt(oPC, "SpellbookMinSpelllevel", nMinLevel);
SetLocalInt(oPC, "AdvancedLearning", 1);
StartDynamicConversation("prc_s_spellgain", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
return TRUE;
}
return FALSE;
}
int CheckMissingUtterances(object oPC, int nClass, int nLexicon)
{
int nLevel = GetLevelByClass(nClass, oPC);
if(!nLevel)
return FALSE;
int nCurrentUtterances = GetUtteranceCount(oPC, nClass, nLexicon);
int nMaxUtterances = GetMaxUtteranceCount(oPC, nClass, nLexicon);
if(DEBUG) DoDebug("CheckMissingUtterances(" + IntToString(nClass) + ", " + IntToString(nLexicon) + ", " + GetName(oPC) + ") = " + IntToString(nCurrentUtterances) + ", " + IntToString(nMaxUtterances));
if(nCurrentUtterances < nMaxUtterances)
{
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain Utterances and start the conversation
SetLocalInt(oPC, "nClass", nClass);
StartDynamicConversation("true_utterconv", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
return FALSE;
}
int CheckMissingManeuvers(object oPC, int nClass)
{
int nLevel = GetLevelByClass(nClass, oPC);
if(!nLevel)
return FALSE;
int nCurrentManeuvers = GetManeuverCount(oPC, nClass, MANEUVER_TYPE_MANEUVER);
int nMaxManeuvers = GetMaxManeuverCount(oPC, nClass, MANEUVER_TYPE_MANEUVER);
int nCurrentStances = GetManeuverCount(oPC, nClass, MANEUVER_TYPE_STANCE);
int nMaxStances = GetMaxManeuverCount(oPC, nClass, MANEUVER_TYPE_STANCE);
if(nCurrentManeuvers < nMaxManeuvers || nCurrentStances < nMaxStances)
{
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain powers and start the conversation
SetLocalInt(oPC, "nClass", nClass);
StartDynamicConversation("tob_moveconv", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
return FALSE;
}
int CheckMissingMysteries(object oPC, int nClass)
{
int nLevel = GetLevelByClass(nClass, oPC);
if(!nLevel)
return FALSE;
int nCurrentMysteries = GetMysteryCount(oPC, nClass);
int nMaxMysteries = GetMaxMysteryCount(oPC, nClass);
if(nCurrentMysteries < nMaxMysteries)
{
if (!IsLevelUpNUIOpen(oPC))
OpenNUILevelUpWindow(nClass, oPC);
/*
// Mark the class for which the PC is to gain powers and start the conversation
SetLocalInt(oPC, "nClass", nClass);
StartDynamicConversation("shd_mystconv", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC);
*/
return TRUE;
}
return FALSE;
}
//AMS Compatibility functions - xwarren:
void CopyAMSArray(object oPC, object oAMSToken, int nClass, string sArray, int nMin, int nMax, int nLoopSize = 100)
{
string sFile = GetFileForClass(nClass);
int i = nMin;
while(i < nMin + nLoopSize && i < nMax)
{
int nSpellbookID = persistant_array_get_int(oPC, sArray, i);
int nSpell = StringToInt(Get2DACache(sFile, "RealSpellID", nSpellbookID));
if(DEBUG) DoDebug("Copying spell "+IntToString(nSpell));
array_set_int(oAMSToken, sArray, i, nSpell);
i++;
}
if(i < nMax)
DelayCommand(0.0, CopyAMSArray(oPC, oAMSToken, nClass, sArray, i, nMax));
}
void DoBuckUpAMS(object oPC, int nClass, string sSpellbook, object oHideToken, object oAMSToken)
{
if(DEBUG) DoDebug("Creating buck-up copy of "+sSpellbook);
if(array_exists(oAMSToken, sSpellbook))
array_delete(oAMSToken, sSpellbook);
array_create(oAMSToken, sSpellbook);
int nSize = persistant_array_get_size(oPC, sSpellbook);
DelayCommand(0.0, CopyAMSArray(oPC, oAMSToken, nClass, sSpellbook, 0, nSize));
}
void AMSCompatibilityCheck(object oPC)
{
//Get an extra hide token with amagsys info
object oAMSToken = GetHideToken(oPC, TRUE);
object oHideToken = GetHideToken(oPC); //ebonfowl: no longer used but I'm leaving it to not have to edit other functions
int i;
for(i = 1; i <= 8; i++)
{
int nClass = GetClassByPosition(i, oPC);
string sSpellbook;
int nSpellbookType = GetSpellbookTypeForClass(nClass);
if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS)
{
sSpellbook = "Spellbook"+IntToString(nClass);
int nSize1 = persistant_array_get_size(oPC, sSpellbook);
int nSize2 = array_get_size(oAMSToken, sSpellbook);
if(nSize1 > nSize2)
DelayCommand(0.1f, DoBuckUpAMS(oPC, nClass, sSpellbook, oHideToken, oAMSToken));
}
else if(nSpellbookType == SPELLBOOK_TYPE_PREPARED)
{
int j;
for(j = 0; j <= 9; j++)
{
sSpellbook = "Spellbook_Known_"+IntToString(nClass)+"_"+IntToString(j);
int nSize1 = persistant_array_get_size(oPC, sSpellbook);
int nSize2 = array_get_size(oAMSToken, sSpellbook);
if(nSize1 > nSize2)
DelayCommand(0.1f, DoBuckUpAMS(oPC, nClass, sSpellbook, oHideToken, oAMSToken));
}
}
}
}

View File

@@ -0,0 +1,596 @@
//:://////////////////////////////////////////////
//:: Unarmed evaluation include
//:: prc_inc_unarmed
//:://////////////////////////////////////////////
/*
Handles attack bonus, damage and itemproperties
for creature weapons created based on class
and race abilities.
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////
//////////////////////////////////////////////////
/* Constant declarations */
//////////////////////////////////////////////////
const int ITEM_PROPERTY_WOUNDING = 69;
const string CALL_UNARMED_FEATS = "CALL_UNARMED_FEATS";
const string CALL_UNARMED_FISTS = "CALL_UNARMED_FISTS";
const string UNARMED_CALLBACK = "UNARMED_CALLBACK";
//////////////////////////////////////////////////
/* Function prototypes */
//////////////////////////////////////////////////
// Determines the amount of unarmed damage a character can do
// ==========================================================
// oCreature a creature whose unarmed damage dice are
// being evaluated
//
// Returns one of the IP_CONST_MONSTERDAMAGE_* constants
int FindUnarmedDamage(object oCreature);
// Adds appropriate unarmed feats to the skin. Goes with UnarmedFists()
// ====================================================================
// oCreature a creature whose unarmed combat feats to handle
//
// Do not call this directly from your evaluation script. Instead, set
// the local variable CALL_UNARMED_FEATS on the creature to TRUE.
// This is done to avoid bugs from redundant calls to these functions.
void UnarmedFeats(object oCreature);
// Creates/strips a creature weapon and applies bonuses. Goes with UnarmedFeats()
// ==============================================================================
// oCreature a creature whose creature weapon to handle
//
// Do not call this directly from your evaluation script. Instead, set
// the local variable CALL_UNARMED_FISTS on the creature to TRUE.
// This is done to avoid bugs from redundant calls to these functions.
//
// If you are going to add properties to the creature weapons, hook
// your script for callback after this is evaluated by calling
// AddEventScript(oPC, CALLBACKHOOK_UNARMED, "your_script", FALSE, FALSE);
// When the callback is running, a local int UNARMED_CALLBACK will be
// set on OBJECT_SELF
void UnarmedFists(object oCreature);
/**
* Determines whether the given object is one of the PRC creature weapons based
* on it's resref and tag. Resref is tested first, then tag.
*
* @param oTest Object to test
* @return TRUE if the object is a PRC creature weapon, FALSE otherwise
*/
int GetIsPRCCreatureWeapon(object oTest);
/**
* Determines the average damage of a IP_CONST_MONSTERDAMAGE_*** constant.
* Used to compare different unarmed damages.
*
* @param iDamage IP_CONST_MONSTERDAMAGE_*** constant
* @return average damage of that constant
*/
float DamageAvg(int iDamage);
//#include "prc_alterations"
//#include "pnp_shft_poly"
//#include "prc_feat_const"
//#include "prc_ipfeat_const"
//#include "prc_class_const"
//#include "prc_racial_const"
//#include "prc_spell_const"
//#include "inc_utility"
#include "prc_inc_natweap"
//////////////////////////////////////////////////
/* Function defintions */
//////////////////////////////////////////////////
// Clean up any extras in the inventory.
void CleanExtraFists(object oCreature)
{
int nItemType;
object oItem = GetFirstItemInInventory(oCreature);
object oCWPB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCreature);
object oCWPL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCreature);
object oCWPR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oCreature);
object oCSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oCreature);
while(GetIsObjectValid(oItem))
{
nItemType = GetBaseItemType(oItem);
if(nItemType == BASE_ITEM_CBLUDGWEAPON ||
nItemType == BASE_ITEM_CPIERCWEAPON ||
nItemType == BASE_ITEM_CREATUREITEM ||
nItemType == BASE_ITEM_CSLASHWEAPON ||
nItemType == BASE_ITEM_CSLSHPRCWEAP
)
{
if(oItem != oCWPB &&
oItem != oCWPL &&
oItem != oCWPR &&
oItem != oCSkin
)
MyDestroyObject(oItem);
}
oItem = GetNextItemInInventory(oCreature);
}
}
int GetIsPRCCreatureWeapon(object oTest)
{
string sTest = GetStringUpperCase(GetResRef(oTest));
return // First, test ResRef
sTest == "PRC_UNARMED_B" ||
sTest == "PRC_UNARMED_S" ||
sTest == "PRC_UNARMED_P" ||
sTest == "PRC_UNARMED_SP" ||
sTest == "NW_IT_CREWPB010" || // Legacy item, should not be used anymore
// If resref doesn't match, try tag
(sTest = GetStringUpperCase(GetTag(oTest))) == "PRC_UNARMED_B" ||
sTest == "PRC_UNARMED_S" ||
sTest == "PRC_UNARMED_P" ||
sTest == "PRC_UNARMED_SP" ||
sTest == "NW_IT_CREWPB010"
;
}
// Remove the unarmed penalty effect
void RemoveUnarmedAttackEffects(object oCreature)
{
effect e = GetFirstEffect(oCreature);
while (GetIsEffectValid(e))
{
if (GetEffectSpellId(e) == SPELL_UNARMED_ATTACK_PEN)
RemoveEffect(oCreature, e);
e = GetNextEffect(oCreature);
}
}
// Add the unarmed penalty effect -- the DR piercing property gives an unwanted
// attack bonus. This clears it up.
void ApplyUnarmedAttackEffects(object oCreature)
{
object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(OBJECT_SELF));
AssignCommand(oCastingObject, ActionCastSpellAtObject(SPELL_UNARMED_ATTACK_PEN, oCreature, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
DestroyObject(oCastingObject, 6.0);
}
// Determines the amount of damage a character can do.
// IoDM: +1 dice 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;
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");
// 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))
{
iSize = PRCGetCreatureSize(oCreature) - CREATURE_SIZE_MEDIUM + 5; // medium is size 5 for us
}
else
{
// Determine creature size by feats.
iSize = 5; // medium is size 5 for us
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.
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
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;
// 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;
// Ronove is in place of monk, does not stack
if (iRonove > iMonk) iMonk = iRonove;
// monks damage progesses every four levels, starts at 1d6
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;
// 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
// 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
// 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.
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;
}
}
// Shou Disciples can wear light armor
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;
}
}
// 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;
} */
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
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:
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;
}
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;
if (DEBUG) DoDebug("prc_inc_unarmed: iDamage "+IntToString(iDamage));
return iDamage;
}
// Adds appropriate feats to the skin. Stolen from SoulTaker + expanded with overwhelming/devastating critical.
void UnarmedFeats(object oCreature)
{
// If we are polymorphed/shifted, do not mess with the creature weapon.
if (GetIsPolyMorphedOrShifted(oCreature)) return;
object oSkin = GetPCSkin(oCreature);
if (!GetHasFeat(FEAT_WEAPON_PROFICIENCY_CREATURE, oCreature))
AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WEAPON_PROF_CREATURE),oSkin);
//only roll unarmed feats into creature feats when not using natural weapons
if(!GetIsUsingPrimaryNaturalWeapons(oCreature))
{
if (GetHasFeat(FEAT_WEAPON_FOCUS_UNARMED_STRIKE, oCreature) && !GetHasFeat(FEAT_WEAPON_FOCUS_CREATURE, oCreature))
AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature),oSkin);
if (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE, oCreature) && !GetHasFeat(FEAT_WEAPON_SPECIALIZATION_CREATURE, oCreature))
AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapSpecCreature),oSkin);
if (GetHasFeat(FEAT_IMPROVED_CRITICAL_UNARMED_STRIKE, oCreature) && !GetHasFeat(FEAT_IMPROVED_CRITICAL_CREATURE, oCreature))
AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_ImpCritCreature),oSkin);
if (GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_UNARMED, oCreature) && !GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_CREATURE, oCreature))
AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapEpicFocCreature),oSkin);
if (GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_UNARMED, oCreature) && !GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_CREATURE, oCreature))
AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapEpicSpecCreature),oSkin);
if (GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_UNARMED, oCreature) && !GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_CREATURE, oCreature))
AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_OVERCRITICAL_CREATURE),oSkin);
if (GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_UNARMED, oCreature) && !GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_CREATURE, oCreature))
AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_DEVCRITICAL_CREATURE),oSkin);
}
}
// Creates/strips a creature weapon and applies bonuses. Large chunks stolen from SoulTaker.
void UnarmedFists(object oCreature)
{
// If we are polymorphed/shifted, do not mess with the creature weapon.
if (GetIsPolyMorphedOrShifted(oCreature)) return;
RemoveUnarmedAttackEffects(oCreature);
object oRighthand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature);
object oLefthand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature);
object oWeapL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCreature);
// Clean up the mess of extra fists made on taking first level.
DelayCommand(6.0f, CleanExtraFists(oCreature));
// Determine the character's capacity to pierce DR.
// only applies when not using natural weapons
if(!GetIsUsingPrimaryNaturalWeapons(oCreature))
{
int iRace = GetRacialType(oCreature);
int iMonk = GetLevelByClass(CLASS_TYPE_MONK, oCreature) + GetLocalInt(oCreature, "LiPengMonk");
int iShou = GetLevelByClass(CLASS_TYPE_SHOU, oCreature);
int iSacFist = GetLevelByClass(CLASS_TYPE_SACREDFIST, oCreature);
int iHenshin = GetLevelByClass(CLASS_TYPE_HENSHIN_MYSTIC, oCreature);
int iIoDM = GetLevelByClass(CLASS_TYPE_INITIATE_DRACONIC, oCreature);
int iBrawler = GetLevelByClass(CLASS_TYPE_BRAWLER, oCreature);
int iZuoken = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature);
int iShadowSunNinja = GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oCreature);
int iAscetic = GetLevelByClass(CLASS_TYPE_NINJA, 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;
// Ascetic Stalker
if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature))
iMonkEq += iAscetic;
// Determine the type of damage the character should do.
string sWeapType;
if (GetHasFeat(FEAT_CLAWDRAGON, oCreature))
sWeapType = "PRC_UNARMED_S";
else
sWeapType = "PRC_UNARMED_B";
// Equip the creature weapon.
if (!GetIsObjectValid(oWeapL) || GetTag(oWeapL) != sWeapType)
{
if (GetHasItem(oCreature, sWeapType))
{
oWeapL = GetItemPossessedBy(oCreature, sWeapType);
SetIdentified(oWeapL, TRUE);
AssignCommand(oCreature, ActionEquipItem(oWeapL, INVENTORY_SLOT_CWEAPON_L));
}
else
{
oWeapL = CreateItemOnObject(sWeapType, oCreature);
SetIdentified(oWeapL, TRUE);
AssignCommand(oCreature,ActionEquipItem(oWeapL, INVENTORY_SLOT_CWEAPON_L));
}
}
int iKi = (iMonkEq > 9) ? 1 : 0;
iKi = (iMonkEq > 12) ? 2 : iKi;
iKi = (iMonkEq > 15) ? 3 : iKi;
int iDragClaw = GetHasFeat(FEAT_CLAWDRAGON,oCreature) ? 1: 0;
iDragClaw = GetHasFeat(FEAT_CLAWENH2,oCreature) ? 2: iDragClaw;
iDragClaw = GetHasFeat(FEAT_CLAWENH3,oCreature) ? 3: iDragClaw;
int iBrawlEnh = iBrawler / 6;
int iEpicKi = GetHasFeat(FEAT_EPIC_IMPROVED_KI_STRIKE_4,oCreature) ? 1 : 0 ;
iEpicKi = GetHasFeat(FEAT_EPIC_IMPROVED_KI_STRIKE_5,oCreature) ? 2 : iEpicKi ;
// The total enhancement to the fist is the sum of all the enhancements above
int iEnh = iKi + iDragClaw + iBrawlEnh + iEpicKi;
// Strip the Fist.
itemproperty ip = GetFirstItemProperty(oWeapL);
while (GetIsItemPropertyValid(ip))
{
RemoveItemProperty(oWeapL, ip);
ip = GetNextItemProperty(oWeapL);
}
// Leave the fist blank if weapons are equipped. The only way a weapon will
// be equipped on the left hand is if there is a weapon in the right hand.
if (GetIsObjectValid(oRighthand)) return;
// Add glove bonuses.
object oItem = GetItemInSlot(INVENTORY_SLOT_ARMS,oCreature);
int iGloveEnh = 0;
if (GetIsObjectValid(oItem))
{
int iType = GetBaseItemType(oItem);
if (iType == BASE_ITEM_GLOVES)
{
ip = GetFirstItemProperty(oItem);
while(GetIsItemPropertyValid(ip))
{
iType = GetItemPropertyType(ip);
switch (iType)
{
case ITEM_PROPERTY_DAMAGE_BONUS:
case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP:
case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP:
case ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT:
case ITEM_PROPERTY_ON_HIT_PROPERTIES:
case ITEM_PROPERTY_ONHITCASTSPELL:
case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE:
case ITEM_PROPERTY_KEEN:
case ITEM_PROPERTY_MASSIVE_CRITICALS:
case ITEM_PROPERTY_POISON:
case ITEM_PROPERTY_REGENERATION_VAMPIRIC:
case ITEM_PROPERTY_WOUNDING:
case ITEM_PROPERTY_DECREASED_DAMAGE:
case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER:
DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT,ip,oWeapL));
break;
case ITEM_PROPERTY_ATTACK_BONUS:
int iCost = GetItemPropertyCostTableValue(ip);
iGloveEnh = (iCost>iGloveEnh) ? iCost:iGloveEnh;
iEnh = (iCost>iEnh) ? iCost:iEnh;
break;
}
ip = GetNextItemProperty(oItem);
}
// handles these seperately so as not to create "attack penalties vs. xxxx"
ip = GetFirstItemProperty(oItem);
while(GetIsItemPropertyValid(ip))
{
iType = GetItemPropertyType(ip);
switch (iType)
{
case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT:
case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP:
case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP:
if (GetItemPropertyCostTableValue(ip) > iEnh)
DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT,ip,oWeapL));
break;
}
ip = GetNextItemProperty(oItem);
}
}
}
// Add damage resistance penetration.
DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyAttackBonus(iEnh), oWeapL));
// Cool VFX when striking unarmed
if (iMonkEq > 9)
//DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT, PRCItemPropertyBonusFeat(IP_CONST_FEAT_KI_STRIKE), oWeapL));
DelayCommand(0.1, IPSafeAddItemProperty(oWeapL, PRCItemPropertyBonusFeat(IP_CONST_FEAT_KI_STRIKE), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE));
// This adds creature weapon finesse and a penalty to offset the DR penetration attack bonus.
SetLocalInt(oCreature, "UnarmedEnhancement", iEnh);
SetLocalInt(oCreature, "UnarmedEnhancementGlove", iGloveEnh);
}
// Weapon finesse or intuitive attack?
SetLocalInt(oCreature, "UsingCreature", TRUE);
ExecuteScript("prc_intuiatk", oCreature);
DelayCommand(1.0f, DeleteLocalInt(oCreature, "UsingCreature"));
ApplyUnarmedAttackEffects(oCreature);
// Add the appropriate damage to the fist.
int iMonsterDamage = FindUnarmedDamage(oCreature);
AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyMonsterDamage(iMonsterDamage),oWeapL);
// Add OnHitCast: Unique if necessary. Frostrager level 5 grants Rend too
if(GetHasFeat(FEAT_REND, oCreature) || GetLevelByClass(CLASS_TYPE_FROSTRAGER, oCreature) > 4)
IPSafeAddItemProperty(oWeapL,
ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1));
// Friendly message to remind players that certain things won't appear correct.
if (GetLocalInt(oCreature, "UnarmedSubSystemMessage") != TRUE
&& GetHasSpellEffect(SPELL_UNARMED_ATTACK_PEN, oCreature))
{
SetLocalInt(oCreature, "UnarmedSubSystemMessage", TRUE);
DelayCommand(3.001f, SendMessageToPC(oCreature, "This character uses the PRC's unarmed system. This system has been created to"));
DelayCommand(3.002f, SendMessageToPC(oCreature, "work around many Aurora engine bugs and limitations. Your attack roll may appear to be"));
DelayCommand(3.003f, SendMessageToPC(oCreature, "incorrect on the character's stats. However, the attack rolls should be correct in"));
DelayCommand(3.004f, SendMessageToPC(oCreature, "combat. Disregard any attack effects that seem extra: they are part of the workaround."));
DelayCommand(600.0f, DeleteLocalInt(oCreature, "UnarmedSubSystemMessage"));
}
}
float DamageAvg(int iDamage)
{
int iDie = StringToInt(Get2DACache("iprp_monstcost", "Die", iDamage));
int iNum = StringToInt(Get2DACache("iprp_monstcost", "NumDice", iDamage));
return IntToFloat(iNum * (iDie+1)) / 2;
}
//:: void main (){}

View File

@@ -0,0 +1,530 @@
#include "prc_nui_consts"
#include "inc_newspellbook"
#include "psi_inc_psifunc"
#include "inc_lookups"
#include "nw_inc_nui"
//
// GetCurrentSpellLevel
// Gets the current spell level the class can achieve at the current
// caster level (ranging from 0-9)
//
// Arguments:
// nClass:int the ClassID
// nLevel:int the caster level
//
// Returns:
// int the circle the class can achieve currently
//
int GetCurrentSpellLevel(int nClass, int nLevel);
//
// GetMaxSpellLevel
// Gets the highest possible circle the class can achieve (from 0-9)
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the highest circle that can be achieved
//
int GetMaxSpellLevel(int nClass);
//
// GetMinSpellLevel
// Gets the lowest possible circle the class can achieve (from 0-9)
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the lowest circle that can be achieved
//
int GetMinSpellLevel(int nClass);
//
// GetHighestLevelPossibleInClass
// Given a class Id this will determine what the max level of a class can be
// achieved
//
// Arguments:
// nClass:int the ClassID
//
// Returns:
// int the highest possible level the class can achieve
//
int GetHighestLevelPossibleInClass(int nClass);
//
// GetClassSpellbookFile
// Gets the class 2da spellbook/ability for the given class Id
//
// Arguments:
// nClass:int the classID
//
// Returns:
// string the 2da file name for the spell/abilities of the ClassID
//
string GetClassSpellbookFile(int nClass);
//
// GetBinderSpellToFeatDictionary
// Sets up the Binder Spell Dictionary that is used to match a binder's vestige
// to their feat. This is constructed based off the binder's known location of
// their feat and spell ranges in the base 2das respectivly. After constructing
// this it will be saved to the player locally as a cached result since we do
// not need to call this again.
//
// Argument:
// oPlayer:object the player
//
// Returns:
// json:Dictionary<String,Int> a dictionary of mapping between the SpellID
// and the FeatID of a vestige ability
//
json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF);
//
// GetSpellLevelIcon
// Takes the spell circle int and gets the icon appropriate for it (i.e. 0 turns
// into "ir_cantrips"
//
// Arguments:
// spellLevel:int the spell level we want the icon for
//
// Returns:
// string the spell level icon
//
string GetSpellLevelIcon(int spellLevel);
//
// GetSpellLevelToolTip
// Gets the spell level tool tip text based on the int spell level provided (i.e.
// 0 turns into "Cantrips")
//
// Arguments:
// spellLevel:int the spell level we want the tooltip for
//
// Returns:
// string the spell level toop tip
//
string GetSpellLevelToolTip(int spellLevel);
//
// GetSpellIcon
// Gets the spell icon based off the spellId, or featId supplied
//
// Arguments:
// nClass:int the class Id
// featId:int the featId we can use the icon for
// spellId:int the spell Id we want the icon for
//
// Returns:
// json:String the string of the icon we want.
//
json GetSpellIcon(int spellId, int featId=0, int nClass=0);
string GetSpellName(int spellId, int realSpellID=0, int featId=0, int nClass=0);
//
// GreyOutButton
// Takes NUI Button along with it's width and height and greys it out it with a drawn
// colored rectangle to represent it's not been selected or not valid.
//
// Arguments:
// jButton:json the NUI Button
// w:float the width of the button
// h:float the height of the button
//
// Returns:
// json the NUI button greyed out
//
json GreyOutButton(json jButton, float w, float h);
//
// CreateGreyOutRectangle
// Creates a grey out rectangle for buttons
//
// Arguments:
// w:float the width of the button
// h:float the height of the button
//
// Returns:
// json the transparant black rectangle
//
json CreateGreyOutRectangle(float w, float h);
void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0, int nClass=0);
void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF);
int GetCurrentSpellLevel(int nClass, int nLevel)
{
int currentLevel = nLevel;
// ToB doesn't have a concept of spell levels, but still match up to it
if(nClass == CLASS_TYPE_WARBLADE
|| nClass == CLASS_TYPE_SWORDSAGE
|| nClass == CLASS_TYPE_CRUSADER
|| nClass == CLASS_TYPE_SHADOWCASTER)
{
return 9;
}
// Binders don't really have a concept of spell level
if (nClass == CLASS_TYPE_BINDER
|| nClass == CLASS_TYPE_DRAGON_SHAMAN) // they can only reach 1st circle
return 1;
//Shadowsmith has no concept of spell levels
if (nClass == CLASS_TYPE_SHADOWSMITH)
return 2;
if (nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
return 4;
// Spont casters have their own function
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
int maxLevel = GetMaxSpellLevelForCasterLevel(nClass, currentLevel);
return maxLevel;
}
else
{
// everyone else uses this
string spellLevel2da = GetAMSKnownFileName(nClass);
currentLevel = nLevel - 1; // Level is 1 off of the row in the 2da
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_PSION
|| nClass == CLASS_TYPE_PSYWAR
|| nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_WARMIND)
currentLevel = GetManifesterLevel(OBJECT_SELF, nClass, TRUE) - 1;
int totalLevel = Get2DARowCount(spellLevel2da);
// in case we somehow go over bounds just don't :)
if (currentLevel >= totalLevel)
currentLevel = totalLevel - 1;
//Psionics have MaxPowerLevel as their column name
string columnName = "MaxPowerLevel";
//Invokers have MaxInvocationLevel
if (nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
columnName = "MaxInvocationLevel";
// Truenamers have 3 sets of utterances, ranging from 1-6, EvolvingMind covers the entire range
if (nClass == CLASS_TYPE_TRUENAMER)
{
columnName = "EvolvingMind";
spellLevel2da = "cls_true_maxlvl"; //has a different 2da we want to look at
}
if (nClass == CLASS_TYPE_BINDER)
{
columnName = "VestigeLvl";
spellLevel2da = "cls_bind_binder";
}
// ToB doesn't have a concept of this, but we don't care.
int maxLevel = StringToInt(Get2DACache(spellLevel2da, columnName, currentLevel));
return maxLevel;
}
}
int GetMinSpellLevel(int nClass)
{
// again sponts have their own function
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
return GetMinSpellLevelForCasterLevel(nClass, GetHighestLevelPossibleInClass(nClass));
}
else
{
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_PSION
|| nClass == CLASS_TYPE_PSYWAR
|| nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_WARMIND
|| nClass == CLASS_TYPE_WARBLADE
|| nClass == CLASS_TYPE_SWORDSAGE
|| nClass == CLASS_TYPE_CRUSADER
|| nClass == CLASS_TYPE_WARLOCK
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|| nClass == CLASS_TYPE_SHADOWCASTER
|| nClass == CLASS_TYPE_SHADOWSMITH
|| nClass == CLASS_TYPE_BINDER)
return 1;
return GetCurrentSpellLevel(nClass, 1);
}
}
int GetMaxSpellLevel(int nClass)
{
if (nClass == CLASS_TYPE_WILDER
|| nClass == CLASS_TYPE_PSION)
return 9;
if (nClass == CLASS_TYPE_PSYCHIC_ROGUE
|| nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|| nClass == CLASS_TYPE_WARMIND)
return 5;
if (nClass == CLASS_TYPE_PSYWAR)
return 6;
return GetCurrentSpellLevel(nClass, GetHighestLevelPossibleInClass(nClass));
}
int GetHighestLevelPossibleInClass(int nClass)
{
string sFile;
//sponts have their spells in the classes.2da
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
sFile = Get2DACache("classes", "SpellGainTable", nClass);
}
else
{
// everyone else uses this
sFile = GetAMSKnownFileName(nClass);
if (nClass == CLASS_TYPE_TRUENAMER)
{
sFile = "cls_true_maxlvl"; //has a different 2da we want to look at
}
if (nClass == CLASS_TYPE_BINDER)
{
sFile = "cls_bind_binder";
}
}
return Get2DARowCount(sFile);
}
string GetClassSpellbookFile(int nClass)
{
string sFile;
// Spontaneous casters use a specific file name structure
if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|| nClass == CLASS_TYPE_ARCHIVIST)
{
sFile = GetFileForClass(nClass);
}
// everyone else uses this structure
else
{
sFile = GetAMSDefinitionFileName(nClass);
if (nClass == CLASS_TYPE_BINDER)
{
sFile = "vestiges";
}
}
return sFile;
}
string GetSpellLevelIcon(int spellLevel)
{
switch (spellLevel)
{
case 0: return "ir_cantrips";
case 1: return "ir_level1";
case 2: return "ir_level2";
case 3: return "ir_level3";
case 4: return "ir_level4";
case 5: return "ir_level5";
case 6: return "ir_level6";
case 7: return "ir_level789";
case 8: return "ir_level789";
case 9: return "ir_level789";
}
return "";
}
string GetSpellLevelToolTip(int spellLevel)
{
switch (spellLevel)
{
case 0: return "Cantrips";
case 1: return "Level 1";
case 2: return "Level 2";
case 3: return "Level 3";
case 4: return "Level 4";
case 5: return "Level 5";
case 6: return "Level 6";
case 7: return "Level 7";
case 8: return "Level 8";
case 9: return "Level 9";
}
return "";
}
json GetSpellIcon(int spellId,int featId=0,int nClass=0)
{
// Binder's spells don't have the FeatID on the spells.2da, so we have to use
// the mapping we constructed to get it.
if (nClass == CLASS_TYPE_BINDER)
{
json binderDict = GetBinderSpellToFeatDictionary();
int nFeatID = JsonGetInt(JsonObjectGet(binderDict, IntToString(spellId)));
return JsonString(Get2DACache("feat", "Icon", featId));
}
if (featId)
return JsonString(Get2DACache("feat", "Icon", featId));
int masterSpellID = StringToInt(Get2DACache("spells", "Master", spellId));
// if this is a sub radial spell, then we use spell's icon instead
if (masterSpellID)
return JsonString(Get2DACache("spells", "IconResRef", spellId));
// the FeatID holds the accurate spell icon, not the SpellID
int nFeatID = StringToInt(Get2DACache("spells", "FeatID", spellId));
return JsonString(Get2DACache("feat", "Icon", nFeatID));
}
string GetSpellName(int spellId, int realSpellID=0, int featId=0, int nClass=0)
{
if ((nClass == CLASS_TYPE_SHADOWSMITH
|| nClass == CLASS_TYPE_SHADOWCASTER) && spellId)
return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellId)));
if (nClass == CLASS_TYPE_TRUENAMER && featId)
return GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", featId)));
if (realSpellID)
return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", realSpellID)));
if (spellId)
return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellId)));
if (featId)
return GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", featId)));
return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellId)));
}
json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF)
{
// a dictionary of <SpellID, FeatID>
json binderDict = GetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR);
// if this hasn't been created, create it now.
if (binderDict == JsonNull())
binderDict = JsonObject();
else
return binderDict;
// the starting row for binder spells
int spellIndex = 19070;
// the starting row for binder feats
int featIndex = 9030;
//the end of the binder spells/feats
while (spellIndex <= 19156 && featIndex <= 9104)
{
// get the SpellID tied to the feat
int spellID = StringToInt(Get2DACache("feat", "SPELLID", featIndex));
// if the spellID matches the current index, then this is the spell
// attached to the feat
if (spellID == spellIndex)
{
binderDict = JsonObjectSet(binderDict, IntToString(spellID), JsonInt(featIndex));
// move to next spell/feat
featIndex++;
spellIndex++;
}
// else we have reached a subdial spell
else
{
// loop through until we reach back at spellID
while (spellIndex < spellID)
{
int masterSpell = StringToInt(Get2DACache("spells", "Master", spellIndex));
// add the sub radial to the dict, tied to the master's FeatID
int featId = JsonGetInt(JsonObjectGet(binderDict, IntToString(masterSpell)));
binderDict = JsonObjectSet(binderDict, IntToString(spellIndex), JsonInt(featId));
spellIndex++;
}
// some feats overlap the same FeatID, can cause this to get stuck.
// if it happens then move on
if (spellIndex > spellID)
featIndex++;
}
}
// cache the result
SetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR, binderDict);
return binderDict;
}
json GreyOutButton(json jButton, float w, float h)
{
json retValue = jButton;
json jBorders = JsonArray();
jBorders = JsonArrayInsert(jBorders, CreateGreyOutRectangle(w, h));
return NuiDrawList(jButton, JsonBool(FALSE), jBorders);
}
json CreateGreyOutRectangle(float w, float h)
{
// set the points of the button shape
json jPoints = JsonArray();
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(h));
jPoints = JsonArrayInsert(jPoints, JsonFloat(w));
jPoints = JsonArrayInsert(jPoints, JsonFloat(h));
jPoints = JsonArrayInsert(jPoints, JsonFloat(w));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
jPoints = JsonArrayInsert(jPoints, JsonFloat(0.0));
return NuiDrawListPolyLine(JsonBool(TRUE), NuiColor(0, 0, 0, 127), JsonBool(TRUE), JsonFloat(2.0), jPoints);
}
void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0, int nClass=0)
{
SetLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_FEATID_VAR, featID);
SetLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_SPELLID_VAR, spellId);
SetLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_REAL_SPELLID_VAR, realSpellId);
SetLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_CLASSID_VAR, nClass);
ExecuteScript("prc_nui_dsc_view", oPlayer);
}
void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF)
{
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_FEATID_VAR);
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_SPELLID_VAR);
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_REAL_SPELLID_VAR);
DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_CLASSID_VAR);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,165 @@
//::///////////////////////////////////////////////
//:: OnPlayerChat eventscript
//:: prc_onplayerchat
//:://////////////////////////////////////////////
/*
A OnChat script that parses what is said and
uses any commands or NUI associated with
commands.
*/
//:://////////////////////////////////////////////
//:: Updated By: Rakiov
//:: Created On: 22.05.2005
//:://////////////////////////////////////////////
/*
PRC Chat Command Format:
~~command arg1 arg2 arg3 arg4 arg5
OR:
..command arg1 arg2 arg3 arg4 arg5
*/
#include "prc_alterations"
#include "prc_inc_chat"
#include "prc_inc_chat_dm"
#include "prc_inc_chat_pow"
#include "prc_inc_chat_shf"
#include "nw_inc_nui"
#include "prc_string_inc"
#include "prc_nui_sb_inc"
#include "prc_nui_consts"
#include "prc_nui_lv_inc"
const string CHAT_COMMAND_INDICATOR_1 = "~~";
const string CHAT_COMMAND_INDICATOR_2 = "..";
const int CHAT_COMMAND_INDICATOR_LENGHT = 2;
int GetIsChatCommand(string sString)
{
string sTest = GetStringLeft(sString, CHAT_COMMAND_INDICATOR_LENGHT);
if(sTest == CHAT_COMMAND_INDICATOR_1
|| sTest == CHAT_COMMAND_INDICATOR_2)
return TRUE;
return FALSE;
}
string RemoveChatCommandIndicator(string sString)
{
return GetStringRight(sString, GetStringLength(sString) - CHAT_COMMAND_INDICATOR_LENGHT);
}
void main()
{
object oPC = GetPCChatSpeaker();
string sChat = GetPCChatMessage();
if(GetIsChatCommand(sChat))
{
sChat = RemoveChatCommandIndicator(sChat);
SetPCChatVolume(TALKVOLUME_TELL); //Set volume for all chat commands
if(GetStringWord(sChat, 1) == "help")
{
if(GetStringWord(sChat, 2) == "")
{
HelpText(oPC, "=== HELP SUMMARY");
HelpText(oPC, "");
HelpText(oPC, "Chat commands start with ~~ or .. followed by the command name and then any parameters.");
HelpText(oPC, " For example '~~execute test_script' will run the script named 'test_script'.");
HelpText(oPC, "");
HelpText(oPC, "A hyphen in a command name indicates that the word may be abbreviated as short as the point where the hyphen is.");
HelpText(oPC, " For example, 'exec-ute' may be entered as 'execute', 'execu', or 'exec', but not as 'exe'.");
HelpText(oPC, "");
HelpText(oPC, "Typing '~~help' displays a summary of the available commands (what you're reading now).");
HelpText(oPC, "Typing '~~help <command-name>' displays more detailed information about the specified command.");
HelpText(oPC, "");
}
if (Debug_ProcessChatCommand_Help(oPC, sChat))
{}
else if (PowerAttack_ProcessChatCommand_Help(oPC, sChat))
{}
else if (PnPShifter_ProcessChatCommand_Help(oPC, sChat))
{}
}
else
{
if (Debug_ProcessChatCommand(oPC, sChat))
{}
else if (PowerAttack_ProcessChatCommand(oPC, sChat))
{}
else if (PnPShifter_ProcessChatCommand(oPC, sChat))
{}
else
SendMessageToPC(oPC, "Unrecognized chat command: " + sChat);
}
}
else if(GetLocalInt(oPC, PRC_CHAT_HOOK_ACTIVE))
{
SetPCChatVolume(TALKVOLUME_TELL);
SetLocalString(oPC, PRC_PLAYER_RESPONSE, sChat);
ExecuteScript(GetLocalString(oPC, PRC_CHAT_HOOK_SCRIPT), oPC);
_clear_chat_vars(oPC);
}
// get current player message and split it up into a list
string sCommand = GetPCChatMessage();
json sCommandSplit = StringSplit(sChat);
if(JsonGetLength(sCommandSplit) > 0)
{
string firstWord = JsonGetString(JsonArrayGet(sCommandSplit, 0));
// if first word is /pa we are using the power attack interface
if(firstWord == "/pa")
{
if(JsonGetLength(sCommandSplit) >= 2)
{
//if a parameter is given then run the power attack command directly.
string param1 = JsonGetString(JsonArrayGet(sCommandSplit, 1));
int paAmount = StringToInt(param1);
SetLocalInt(oPC, "PRC_PowerAttack_Level", paAmount);
ExecuteScript("prc_nui_pa_trggr", oPC);
// update the NUI so it is in sync
int nToken = NuiFindWindow(oPC, NUI_PRC_POWER_ATTACK_WINDOW);
if (nToken != 0)
{
NuiSetBind(oPC, nToken, NUI_PRC_PA_TEXT_BIND, JsonString(IntToString(paAmount)));
}
}
else
{
// if no param is given then open the NUI
ExecuteScript("prc_nui_pa_view", oPC);
}
// clear message from chat
SetPCChatMessage();
return;
}
// If the first word is /sb then we open the Spellbook NUI
if(firstWord == "/sb")
{
ExecuteScript("prc_nui_sb_view", oPC);
// clear message from chat
SetPCChatMessage();
return;
}
if (firstWord == "/lvl")
{
if (JsonGetLength(sCommandSplit) >= 2)
{
int classPos = StringToInt(JsonGetString(JsonArrayGet(sCommandSplit, 1)));
int nClass = GetClassByPosition(classPos, oPC);
OpenNUILevelUpWindow(nClass, oPC);
SetPCChatMessage();
return;
}
}
}
// Execute scripts hooked to this event for the player triggering it
ExecuteAllScriptsHookedToEvent(oPC, EVENT_ONPLAYERCHAT);
}