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.
168 lines
5.0 KiB
Plaintext
168 lines
5.0 KiB
Plaintext
//::////////////////////////////////////////////////////////
|
||
//:: ;-. ,-. ,-. ,-.
|
||
//:: | ) | ) / ( )
|
||
//:: |-' |-< | ;-:
|
||
//:: | | \ \ ( )
|
||
//:: ' ' ' `-' `-'
|
||
//::///////////////////////////////////////////////////////
|
||
//::
|
||
/*
|
||
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));
|
||
} |