Add Shadow Servant and unarmed system updates

Fixed a bunch of TLK typos.
Updated Master of Shadow's Shadow Servant.
Added Maul to the Metal Domain feat choice.
Drider is a large creature.
Added missing Eagle Claw base weapon.
Tentatively fixed NUI spell selector hang issue.
Fixed Monk / Shou / IoDM die increasing bug.
Added StepDie() function to progressively increase Monster damage constants.
Clamped Skill increases to 128 in json_AdjustCreatureSkillByID
Updated PRC8 Tester module to have new weapon types.
Added all spell focus feats to Wizard's bonus feat list.
Updated PRC8 manual.
Added notes.
This commit is contained in:
Jaysyn904
2025-11-12 19:16:17 -05:00
parent a36c854fc0
commit 8e82907d07
26 changed files with 2585 additions and 1268 deletions

View File

@@ -0,0 +1,19 @@
This is a version of prc_unarmed_dmg.2da that has been modified to show the damage amounts along with the constant
2DA V2.0
Label size1 size2 size3 size4 size5 size6 size7 size8 size9
0 **** **** **** **** **** **** **** **** **** ****
1 normal1 "1 / 1d2" "1 / 1d2" "1 / 1d2" "1 / 1d2" "2 / 1d3" "3 /1d4" "4 / 1d8" "18 / 1d8" "9 / 2d6"
2 normal2 "1 / 1d2" "1 / 1d2" "1 / 1d2" "2 / 1d3" "3 /1d4" "8 / 1d6" "18 / 1d8" "9 / 2d6" "10 / 3d6"
3 monk1 "1 / 1d2" "1 / 1d2" "2 / 1d3" "3 /1d4" "8 / 1d6" "18 / 1d8" "9 / 2d6" "10 / 3d6" "11 / 4d6"
4 monk2 "1 / 1d2" "2 / 1d3" "3 /1d4" "8 / 1d6" "18 / 1d8" "9 / 2d6" "10 / 3d6" "11 / 4d6" "13 / 2d6"
5 monk3 "2 / 1d3" "3 /1d4" "8 / 1d6" "18 / 1d8" "28 / 1d10" "19 /2d8" "20 / 3d8" "21 / 4d8" "23 / 6d8"
6 monk4 "3 /1d4" "8 / 1d6" "18 / 1d8" "28 / 1d10" "9 / 2d6" "10 / 3d6" "11 / 4d6" "13 / 2d6" "15 / 8d6"
7 monk5 "8 / 1d6" "18 / 1d8" "28 / 1d10" "9 / 2d6" "19 /2d8" "20 / 3d8" "21 / 4d8" "23 / 6d8" "25 / 8d8"
8 monk6 "18 / 1d8" "28 / 1d10" "9 / 2d6" "19 /2d8" "29 / 2d10" "21 / 4d8" "23 / 6d8" "25 / 8d8" "52 / 5d20"
9 monk7 "28 / 1d10" "9 / 2d6" "19 /2d8" "29 / 2d10" "11 / 4d6" "13 / 2d6" "15 / 8d6" "51 / 4d20" "37 / 10d10"
10 monk8 "9 / 2d6" "19 /2d8" "29 / 2d10" "11 / 4d6" "21 / 4d8" "23 / 6d8" "25 / 8d8" "52 / 5d20" "54 / 7d20"
11 brawler7 "18 / 1d8" "28 / 1d10" "5 / 3d4" "10 / 3d6" "20 / 3d8" "21 / 4d8" "23 / 6d8" "25 / 8d8" "52 / 5d20"
12 brawler8 "28 / 1d10" "5 / 3d4" "10 / 3d6" "20 / 3d8" "30 / 3d10" "23 / 6d8" "25 / 8d8" "52 / 5d20" "54 / 7d20"
13 brawler9 "5 / 3d4" "10 / 3d6" "20 / 3d8" "30 / 3d10" "13 / 6d6" "15 / 8d6" "51 / 4d20" "37 / 10d10" "55 / 8d20"

View File

@@ -0,0 +1,57 @@
Shadow Servant
Medium Elemental (Extraplanar and Incorporeal)
Initiative: +5; Senses: blindsight 60 ft., Listen +3, and Spot +4
AC: 12 (+1 Dex, +1 deflection), touch 12, flat-footed 11
Hit Dice: 4d8+8 (26 hp)
Fort +3, Ref +5, Will +1 Speed: Fly 40 ft. (perfect)
Space: 5 ft./5 ft.
Base Attack +3; Grapple +3
Attack: Incorporeal touch +4 melee
Damage: Incorporeal touch 1d6 plus 1d6 cold
Special Attacks/Actions: Shadow mastery, dusk and dawn
Abilities: Str -, Dex 12, Con 14, Int 4, Wis 11, Cha 11
Special Qualities: elemental traits, incorporeal traits
Feats: Combat Reflexes; Improved Initiative
Skills: Listen +3 and Spot +4
PC Lvl Bonus HD Dex Adj. Stat Increase Special Feat Master's Bidding
0th +0 - - Weapon Finesse Alertness# -
0th +0 - - - - -
0th +0 - - - Imp. Initiative -
0th +0 - - - - -
1st +0 - - - - Interact with corporeal, fast healing 1
2nd +1 - - Resistance to cold 5 WF: Creature (291) Extra attack
3rd +2 - +4 CON,-2 DEX Size becomes Large - -
4th +3 - +1 CON Resistance to cold 10 - Cold damage +1d8
5th +4 +2 - Deliver touch spells Dodge (10) -
6th +5 - - Resistance to cold 20 - -
7th +6 +4 - - - Reach +5 ft.
8th +7 - +1 CON - Mobility (26) DR 5/-
9th +8 +6 - - - Speed +20 ft.
10th +9 - - Immunity to cold - -
11th +10 +8 - - Spr. Attack (392) -
12th +11 - +1 CON - - -
13th +12 +10 - - - -
14th +13 - - - Iron Will (22) -
15th +14 +12 - - - -
16th +15 - +1 CON - - -
17th +16 +14 - - Lightn. Ref. (24) -
18th +17 - - - - -
19th +18 +16 - - - -
20th +19 - +1 CON - Imp. Crit (292) -
21st +20 +18 - - - -
22nd +21 - - - - -
23rd +22 +20 - - Blind-Fight (408) -
24th +23 - +1 CON - - -
25th +24 +22 - - - -
26th +25 - - - Toughness (40) -
27th +26 +24 - - - -
28th +27 - +1 CON - - -
29th +28 +26 - - Gr. Fort (14) -
30th +29 - - - - -
+1 Listen per Bonus HD
# Replaces Combat Reflexes

168
Notes/mshdw_shadserv.nss Normal file
View File

@@ -0,0 +1,168 @@
//::////////////////////////////////////////////////////////
//:: ;-. ,-. ,-. ,-.
//:: | ) | ) / ( )
//:: |-' |-< | ;-:
//:: | | \ \ ( )
//:: ' ' ' `-' `-'
//::///////////////////////////////////////////////////////
//::
/*
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 = GetSpellTargetLocation();
// 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;
}
}
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);
// Full round wait then move
AssignCommand(oShadow, ClearAllActions());
AssignCommand(oShadow, ActionWait(6.0));
AssignCommand(oShadow, ActionMoveToObject(oPC));
// Start watch loop
DelayCommand(6.1, ShadowServantWatch(oShadow, oPC));
}

596
Notes/prc_inc_unarmed.nss Normal file
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 (){}