2026/02/14 Updates
Updated PRC8 version. Hathran can now select an ethran as a cohort. Preliminary Circle Magic work done. Added Choke Hold, Pain Touch, Ki Shout, Great Ki Shout, Freezing the Lifeblood, Falling Star Strikea nd Unbalancing Strike feats. Warforged get Immunity Energy Drain, not Immunity: Ability Drain. Forsakers can use alchemical items. Added VectorToPerpendicular(). Added GetIsAlchemical(). Added GenerateRandomName(). Added _DoChokeHold(). Updated Shaman bonus feat list. Updated fighter bonus feat lists. Added Favored of the Companions to the Vow of Poverty bonus feat list. Ur-Priest can't enter RKV, BFZ or Thrall of Orcus.
This commit is contained in:
58
nwn/nwnprc/trunk/scripts/ft_choke_hold.nss
Normal file
58
nwn/nwnprc/trunk/scripts/ft_choke_hold.nss
Normal file
@@ -0,0 +1,58 @@
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//;;
|
||||
//:: ft_choke_hold.nss
|
||||
//;:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:
|
||||
/*
|
||||
Choke Hold
|
||||
(Oriental Adventures, p. 61)
|
||||
|
||||
[General]
|
||||
|
||||
You have learned the correct way to apply pressure to render an
|
||||
opponent unconscious.
|
||||
Prerequisite
|
||||
|
||||
Improved Unarmed Strike, Improved Grapple, Stunning Fist
|
||||
|
||||
Required for
|
||||
Mighty Works Mastery I
|
||||
|
||||
Benefit
|
||||
If you pin your opponent while grappling and maintain the pin for
|
||||
1 full round, at the end of the round your opponent must make a
|
||||
Fortitude saving throw (DC 10 + 1/2 your level + your Wisdom
|
||||
modifier). If the saving throw fails, your opponent falls
|
||||
unconscious for 1d3 rounds.
|
||||
|
||||
*/
|
||||
//:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//::
|
||||
//:: Created by: Jaysyn
|
||||
//:: Created on: 2026-02-13 20:54:17
|
||||
//::
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
#include "prc_inc_combmove"
|
||||
|
||||
void main()
|
||||
{
|
||||
object oPC = OBJECT_SELF;
|
||||
object oTarget = GetSpellTargetObject();
|
||||
|
||||
// First, initiate a grapple. If this fails, attack normally.
|
||||
if (!DoGrapple(oPC, oTarget, 0, TRUE, FALSE))
|
||||
{
|
||||
AssignCommand(oPC, ActionAttack(oTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
DoGrappleOptions(oPC, oTarget, 0, GRAPPLE_PIN);
|
||||
}
|
||||
105
nwn/nwnprc/trunk/scripts/ft_fall_star_stk.nss
Normal file
105
nwn/nwnprc/trunk/scripts/ft_fall_star_stk.nss
Normal file
@@ -0,0 +1,105 @@
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//;;
|
||||
//:: ft_fall_star_stk.nss
|
||||
//;:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:
|
||||
/*
|
||||
Falling Star Strike
|
||||
(Oriental Adventures, p. 62)
|
||||
|
||||
[General]
|
||||
|
||||
You have mastered the art of striking a nerve that blinds a
|
||||
humanoid opponent.
|
||||
|
||||
Prerequisite
|
||||
Improved Unarmed Strike (PH) , Stunning Fist (PH) (or monk's
|
||||
stunning attack) , Base attack bonus +4, WIS 17
|
||||
|
||||
Required for
|
||||
Meditation of War Mastery (OA)
|
||||
|
||||
Benefit
|
||||
Against a humanoid opponent, you can make an unarmed attack that
|
||||
has a chance of blinding your target. If your attack is successful,
|
||||
your target must attempt a Fortitude saving throw (DC 10 + 1/2 your
|
||||
level + your Wisdom modifier). If the target fails this saving throw,
|
||||
he is blinded for 1 round per level you possess. In addition to the
|
||||
obvious effects, a blinded creature suffers a 50% miss chance in
|
||||
combat (all opponents have full concealment), loses any Dexterity
|
||||
bonus to AC, grants a +2 bonus on attackers' attack rolls (they
|
||||
are effectively invisible), moves at half speed, and suffers a <20>4
|
||||
penalty on most Strength- and Dexterity-based skills.
|
||||
|
||||
Using this feat uses up one of your stunning attacks for the day (
|
||||
either a monk stunning attack or a use of the Stunning Fist feat).
|
||||
|
||||
*/
|
||||
//:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//::
|
||||
//:: Created by: Jaysyn
|
||||
//:: Created on: 2026-02-13 07:58:24
|
||||
//::
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
#include "prc_inc_spells"
|
||||
#include "prc_inc_combat"
|
||||
#include "prc_inc_stunfist"
|
||||
|
||||
void DoFallingStarStrike(object oTarget, object oInitiator)
|
||||
{
|
||||
if (!PRCAmIAHumanoid(oTarget))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "Target is not humanoid.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget)); // fallback
|
||||
return;
|
||||
}
|
||||
if (!GetIsUnarmed(oInitiator))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "You must be unarmed to use this ability.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget)); // fallback
|
||||
return;
|
||||
}
|
||||
if (!ExpendStunfistUses(oInitiator, 1))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "You have no stunning fist uses remaining.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget)); // fallback
|
||||
return;
|
||||
}
|
||||
|
||||
int iLevel = GetHitDice(oInitiator);
|
||||
effect eHit = EffectVisualEffect(VFX_COM_HIT_SONIC);
|
||||
PerformAttack(oTarget, oInitiator, eHit, 0.0, 0, 0, 0,
|
||||
"Falling Star Strike Hit", "Falling Star Strike Miss");
|
||||
|
||||
if (GetLocalInt(oTarget, "PRCCombat_StruckByAttack"))
|
||||
{
|
||||
int nDC = 10 + (iLevel / 2) + GetAbilityModifier(ABILITY_WISDOM, oInitiator);
|
||||
if (!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE))
|
||||
{
|
||||
effect eBlind = EffectBlindness();
|
||||
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBlind, oTarget,
|
||||
RoundsToSeconds(iLevel));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//:: To prevent being flatfooted.
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget));
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
object oInitiator = OBJECT_SELF;
|
||||
object oTarget = PRCGetSpellTargetObject();
|
||||
|
||||
DoFallingStarStrike(oTarget, oInitiator);
|
||||
}
|
||||
112
nwn/nwnprc/trunk/scripts/ft_frz_lifeblood.nss
Normal file
112
nwn/nwnprc/trunk/scripts/ft_frz_lifeblood.nss
Normal file
@@ -0,0 +1,112 @@
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//;;
|
||||
//:: ft_frz_lifeblood.nss
|
||||
//;:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:
|
||||
/*
|
||||
Freezing The Lifeblood
|
||||
(Complete Warrior, p. 99)
|
||||
|
||||
[Fighter Bonus Feat, General]
|
||||
|
||||
You can paralyze a humanoid opponent with an unarmed attack.
|
||||
|
||||
Prerequisite
|
||||
Improved Unarmed Strike (PH) , Stunning Fist (PH) , WIS 17,
|
||||
base attack bonus +10,
|
||||
|
||||
Benefit
|
||||
Declare that you are using this feat before you make your attack
|
||||
roll (thus, a missed attack roll ruins the attempt). Against a
|
||||
humanoid opponent, you can make an unarmed attack that deals no
|
||||
damage but has a chance of paralyzing your target. If your attack
|
||||
is successful, your target must attempt a Fortitude saving throw
|
||||
(DC 10 + 1/2 your character level + your Wis modifier). If the
|
||||
target fails this saving throw, it is paralyzed for 1d4+1 rounds.
|
||||
Each attempt to paralyze an opponent counts as one of your uses of
|
||||
the Stunning Fist feat for the day. Creatures immune to stunning
|
||||
cannot be paralyzed in this manner.
|
||||
|
||||
Special
|
||||
A fighter may select Freezing the Lifeblood as one of his fighter
|
||||
bonus feats.
|
||||
*/
|
||||
//:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//::
|
||||
//:: Created by: Jaysyn
|
||||
//:: Created on: 2026-02-13 18:31:43
|
||||
//::
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
#include "prc_inc_spells"
|
||||
#include "prc_inc_combat"
|
||||
#include "prc_inc_stunfist"
|
||||
|
||||
void DoFreezingTheLifeblood(object oTarget, object oInitiator)
|
||||
{
|
||||
if (!PRCAmIAHumanoid(oTarget))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "Target is not humanoid.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget)); // fallback
|
||||
return;
|
||||
}
|
||||
if (!GetIsUnarmed(oInitiator))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "You must be unarmed to use this ability.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget)); // fallback
|
||||
return;
|
||||
}
|
||||
if (!ExpendStunfistUses(oInitiator, 1))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "You have no stunning fist uses remaining.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget)); // fallback
|
||||
return;
|
||||
}
|
||||
|
||||
// Simulate unarmed attack without damage
|
||||
object oWeap = GetUnarmedWeapon(oInitiator);
|
||||
int nHit = GetAttackRoll(oTarget, oInitiator, oWeap);
|
||||
if (nHit > 0) // Hit
|
||||
{
|
||||
int nDC = 10 + (GetHitDice(oInitiator) / 2) + GetAbilityModifier(ABILITY_WISDOM, oInitiator);
|
||||
if (!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE))
|
||||
{
|
||||
if (GetIsImmune(oTarget, IMMUNITY_TYPE_STUN))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "Target is immune to stunning.");
|
||||
return;
|
||||
}
|
||||
|
||||
float fDuration = RoundsToSeconds(d4() + 1);
|
||||
effect eParal = EffectParalyze();
|
||||
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
|
||||
effect eDur2 = EffectVisualEffect(VFX_DUR_PARALYZED);
|
||||
effect eDur3 = EffectVisualEffect(VFX_DUR_PARALYZE_HOLD);
|
||||
effect eLink = EffectLinkEffects(eDur2, eDur);
|
||||
eLink = EffectLinkEffects(eLink, eParal);
|
||||
eLink = EffectLinkEffects(eLink, eDur3);
|
||||
|
||||
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, fDuration, TRUE, -1, GetHitDice(oInitiator));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//:: To prevent being flatfooted.
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget));
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
object oInitiator = OBJECT_SELF;
|
||||
object oTarget = PRCGetSpellTargetObject();
|
||||
|
||||
DoFreezingTheLifeblood(oTarget, oInitiator);
|
||||
}
|
||||
182
nwn/nwnprc/trunk/scripts/ft_oa_ki_shout.nss
Normal file
182
nwn/nwnprc/trunk/scripts/ft_oa_ki_shout.nss
Normal file
@@ -0,0 +1,182 @@
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//;;
|
||||
//:: ft_oa_ki_shout.nss
|
||||
//;:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:
|
||||
/*
|
||||
Ki Shout
|
||||
(Oriental Adventures, p. 64)
|
||||
|
||||
[General]
|
||||
|
||||
You can bellow forth a ki-empowered shout that strikes terror into
|
||||
your enemies.
|
||||
|
||||
Prerequisite
|
||||
Base attack bonus +1, CHA 13
|
||||
|
||||
Required for
|
||||
Empty Hand Mastery (OA) , Great Ki Shout (OA) , Mighty Works
|
||||
Mastery II (OA)
|
||||
|
||||
Benefit
|
||||
Making a ki shout is a standard action. Opponents who can hear
|
||||
your shout and who are within 30 feet of you may become shaken for
|
||||
1d6 rounds. The ki shout affects only opponents with fewer Hit
|
||||
Dice or levels than you have. An affected opponent can resist the
|
||||
effects with a successful Will save against a DC of 10 + 1/2 your
|
||||
character level + your Charisma modifier. You can use Ki Shout
|
||||
once per day.
|
||||
|
||||
Shaken characters suffer a -2 morale penalty on attack rolls,
|
||||
saves, and checks.
|
||||
|
||||
|
||||
Great Ki Shout
|
||||
(Oriental Adventures, p. 63)
|
||||
|
||||
[General]
|
||||
|
||||
Your ki shout can panic your opponents.
|
||||
|
||||
Prerequisite
|
||||
Ki Shout (OA) , CHA 13, Base attack bonus +9
|
||||
|
||||
Benefit
|
||||
|
||||
When you make a ki shout, your opponents are panicked for 2d6
|
||||
rounds unless they succeed at their Will saves (DC 10 + 1/2 your
|
||||
character level + your Charisma modifier). Panicked characters
|
||||
suffer a <20>2 morale penalty on attack rolls, saves, and checks,
|
||||
they have a 50% chance to drop what they are holding, and they
|
||||
run away from you as quickly as they can. The effects of being
|
||||
panicked supersede the effects of being shaken.
|
||||
|
||||
*/
|
||||
//:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//::
|
||||
//:: Created by: Jaysyn
|
||||
//:: Created on: 2026-02-13 18:31:43
|
||||
//::
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
#include "prc_inc_spells"
|
||||
|
||||
// Copy non-plot item from a slot to the ground, confirm, then destroy original; notify victim
|
||||
void ForceDropSlot(object oTarget, int nSlot)
|
||||
{
|
||||
object oItem = GetItemInSlot(nSlot, oTarget);
|
||||
if (!GetIsObjectValid(oItem) || GetPlotFlag(oItem)) return;
|
||||
|
||||
// Ensure droppable and not stolen
|
||||
SetDroppableFlag(oItem, TRUE);
|
||||
SetStolenFlag(oItem, FALSE);
|
||||
|
||||
// Copy to the ground at target's location
|
||||
location lLoc = GetLocation(oTarget);
|
||||
object oCopy = CopyObject(oItem, lLoc, OBJECT_INVALID);
|
||||
|
||||
if (GetIsObjectValid(oCopy))
|
||||
{
|
||||
DestroyObject(oItem);
|
||||
SendMessageToPC(oTarget, "You dropped " + GetName(oItem) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
object oPC = OBJECT_SELF;
|
||||
int bGreat = GetHasFeat(FEAT_GREAT_KI_SHOUT, oPC);
|
||||
if (!bGreat && !GetHasFeat(FEAT_KI_SHOUT, oPC, TRUE)) return;
|
||||
|
||||
// Effects
|
||||
effect eShaken = ExtraordinaryEffect(EffectShaken());
|
||||
effect ePanicked = ExtraordinaryEffect(EffectLinkEffects(eShaken, EffectFrightened()));
|
||||
ePanicked = EffectLinkEffects(ePanicked, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_FEAR));
|
||||
|
||||
// Radius and duration
|
||||
float fRadius = FeetToMeters(30.0f);
|
||||
float fDuration = RoundsToSeconds(bGreat ? d6(2) : d6());
|
||||
|
||||
// DC
|
||||
int nDC = 10 + (GetHitDice(oPC) / 2) + GetAbilityModifier(ABILITY_CHARISMA, oPC);
|
||||
int nPCHD = GetHitDice(oPC);
|
||||
string sPCID = ObjectToString(oPC);
|
||||
int bDoVFX = FALSE;
|
||||
|
||||
// Loop targets
|
||||
location lLoc = GetLocation(oPC);
|
||||
object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, lLoc, TRUE);
|
||||
while (GetIsObjectValid(oTarget))
|
||||
{
|
||||
if(DEBUG) DoDebug("Evaluating: " + GetName(oTarget) + " HD=" + IntToString(GetHitDice(oTarget))
|
||||
+ " ImmFear=" + IntToString(GetIsImmune(oTarget, IMMUNITY_TYPE_FEAR))
|
||||
+ " Saved=" + IntToString(GetLocalInt(oTarget, "KiShout_SavedVs" + sPCID)));
|
||||
|
||||
if (oTarget != oPC &&
|
||||
!GetLocalInt(oTarget, "KiShout_SavedVs" + sPCID) &&
|
||||
spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, oPC) &&
|
||||
nPCHD > GetHitDice(oTarget))
|
||||
{
|
||||
bDoVFX = TRUE;
|
||||
SignalEvent(oTarget, EventSpellCastAt(oPC, -1, TRUE));
|
||||
|
||||
if (!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_FEAR, oPC) &&
|
||||
!GetIsImmune(oTarget, IMMUNITY_TYPE_FEAR, oPC))
|
||||
{
|
||||
if (bGreat)
|
||||
{
|
||||
object oItemL = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget);
|
||||
object oItemR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
|
||||
|
||||
if (d100() < 50)
|
||||
{
|
||||
if (GetIsObjectValid(oItemL))
|
||||
{
|
||||
//SendMessageToPC(oPC, GetName(oTarget)+" is dropping "+GetName(oItemL)+".");
|
||||
AssignCommand(oTarget, ForceDropSlot(oTarget, INVENTORY_SLOT_LEFTHAND));
|
||||
|
||||
}
|
||||
}
|
||||
if (d100() < 50)
|
||||
{
|
||||
if (GetIsObjectValid(oItemR))
|
||||
{
|
||||
//SendMessageToPC(oPC, GetName(oTarget)+" is dropping "+GetName(oItemR)+".");
|
||||
AssignCommand(oTarget, ForceDropSlot(oTarget, INVENTORY_SLOT_RIGHTHAND));
|
||||
}
|
||||
}
|
||||
|
||||
// Apply panicked effect after drops
|
||||
DelayCommand(0.1f, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ePanicked, oTarget, fDuration));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply shaken
|
||||
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eShaken, oTarget, fDuration);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 24h immunity
|
||||
SetLocalInt(oTarget, "KiShout_SavedVs" + sPCID, TRUE);
|
||||
AssignCommand(oTarget, DelayCommand(HoursToSeconds(24),
|
||||
DeleteLocalInt(oTarget, "KiShout_SavedVs" + sPCID)));
|
||||
}
|
||||
}
|
||||
oTarget = GetNextObjectInShape(SHAPE_SPHERE, fRadius, lLoc, TRUE);
|
||||
}
|
||||
|
||||
// VFX
|
||||
if (bDoVFX)
|
||||
ApplyEffectToObject(DURATION_TYPE_INSTANT,
|
||||
EffectVisualEffect(GetGender(oPC) == GENDER_FEMALE ? VFX_FNF_HOWL_WAR_CRY_FEMALE : VFX_FNF_HOWL_WAR_CRY),
|
||||
oPC);
|
||||
}
|
||||
106
nwn/nwnprc/trunk/scripts/ft_pain_touch.nss
Normal file
106
nwn/nwnprc/trunk/scripts/ft_pain_touch.nss
Normal file
@@ -0,0 +1,106 @@
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//;;
|
||||
//:: ft_pain_touch.nss
|
||||
//;:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:
|
||||
/*
|
||||
Pain Touch
|
||||
(Complete Warrior, p. 103)
|
||||
|
||||
[General]
|
||||
|
||||
You cause intense pain in an opponent with a successful stunning attack.
|
||||
|
||||
Prerequisite
|
||||
Stunning Fist, WIS 15, base attack bonus +2
|
||||
|
||||
Benefit
|
||||
Victims of a successful stunning attack are subject to such
|
||||
debilitating pain that they are nauseated for 1 round following
|
||||
the round they are stunned. Creatures that are immune to stunning
|
||||
attacks are also immune to the effect of this feat, as are any
|
||||
creatures that are more than one size category larger than the
|
||||
feat user.
|
||||
*/
|
||||
//:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//::
|
||||
//:: Created by: Jaysyn
|
||||
//:: Created on: 2026-02-14 19:37:06
|
||||
//::
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
#include "prc_inc_combat"
|
||||
#include "prc_inc_stunfist"
|
||||
#include "prc_effect_inc"
|
||||
|
||||
void main()
|
||||
{
|
||||
object oPC = OBJECT_SELF;
|
||||
object oTarget = PRCGetSpellTargetObject();
|
||||
|
||||
// Must target a humanoid
|
||||
if (!PRCAmIAHumanoid(oTarget))
|
||||
{
|
||||
SendMessageToPC(oPC, "Target is not humanoid.");
|
||||
AssignCommand(oPC, ActionAttack(oTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
// Must be unarmed
|
||||
if (!GetIsUnarmed(oPC))
|
||||
{
|
||||
SendMessageToPC(oPC, "You must be unarmed to use this ability.");
|
||||
AssignCommand(oPC, ActionAttack(oTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check size restriction
|
||||
int nAttackerSize = PRCGetCreatureSize(oPC);
|
||||
int nTargetSize = PRCGetCreatureSize(oTarget);
|
||||
if(nTargetSize > nAttackerSize + 1)
|
||||
{
|
||||
SendMessageToPC(oPC, "Target is too large for Pain Touch");
|
||||
AssignCommand(oPC, ActionAttack(oTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check stunning fist uses
|
||||
if(!ExpendStunfistUses(oPC, 1))
|
||||
{
|
||||
SendMessageToPC(oPC, "No stunning fist uses remaining");
|
||||
AssignCommand(oPC, ActionAttack(oTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare stun effect
|
||||
effect eStun = EffectStunned();
|
||||
effect eVis = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DISABLED);
|
||||
eStun = EffectLinkEffects(eStun, eVis);
|
||||
eStun = SupernaturalEffect(eStun);
|
||||
|
||||
// Perform attack
|
||||
string sSuccess = "*Pain Touch Hit*";
|
||||
string sMiss = "*Pain Touch Miss*";
|
||||
|
||||
PerformAttackRound(oTarget, oPC, eStun, RoundsToSeconds(1), 0, 0, 0, FALSE, sSuccess, sMiss);
|
||||
|
||||
// Check if attack hit and apply delayed nausea
|
||||
if(GetLocalInt(oTarget, "PRCCombat_StruckByAttack"))
|
||||
{
|
||||
// Apply nausea 1 round after stun (6 second delay)
|
||||
DelayCommand(6.0f, ApplyEffectToObject(DURATION_TYPE_TEMPORARY,
|
||||
EffectNausea(oTarget, 6.0f), oTarget, RoundsToSeconds(1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
//:: To prevent being flatfooted.
|
||||
AssignCommand(oPC, ActionAttack(oTarget));
|
||||
}
|
||||
}
|
||||
102
nwn/nwnprc/trunk/scripts/ft_unbal_strike.nss
Normal file
102
nwn/nwnprc/trunk/scripts/ft_unbal_strike.nss
Normal file
@@ -0,0 +1,102 @@
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//;;
|
||||
//:: ft_unbal_strike.nss
|
||||
//;:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//:
|
||||
/*
|
||||
Unbalancing Strike
|
||||
(Oriental Adventures, p. 66)
|
||||
|
||||
[General]
|
||||
|
||||
You can strike a humanoid opponent's joints to knock your target
|
||||
off balance. This feat is called kuzushi in Rokugan.
|
||||
|
||||
Prerequisite
|
||||
Improved Unarmed Strike (PH) , Stunning Fist (PH) (or monk's
|
||||
stunning attack) , WIS 15
|
||||
|
||||
Benefit
|
||||
Against a humanoid opponent, you can make an unarmed attack that
|
||||
has a chance of unbalancing your target. If your attack is
|
||||
successful, you deal normal damage and your target must attempt
|
||||
a Reflex saving throw (DC 10 + 1/2 your level + your Wisdom
|
||||
modifier). If the target fails this saving throw, he is thrown off
|
||||
balance for 1 round, losing any Dexterity bonus to AC and giving
|
||||
attackers a +2 bonus on their attack rolls.
|
||||
|
||||
*/
|
||||
//:
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
//::
|
||||
//:: Created by: Jaysyn
|
||||
//:: Created on: 2026-02-13 20:54:17
|
||||
//::
|
||||
//:://////////////////////////////////////////////////////////////////
|
||||
#include "prc_inc_spells"
|
||||
#include "prc_inc_combat"
|
||||
#include "prc_inc_stunfist"
|
||||
|
||||
void DoUnbalancingStrike(object oTarget, object oInitiator)
|
||||
{
|
||||
// Must target a humanoid
|
||||
if (!PRCAmIAHumanoid(oTarget))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "Target is not humanoid.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
// Must be unarmed
|
||||
if (!GetIsUnarmed(oInitiator))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "You must be unarmed to use this ability.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
// Optional: expend a Stunning Fist use if you want it to consume one
|
||||
if (!ExpendStunfistUses(oInitiator, 1))
|
||||
{
|
||||
SendMessageToPC(oInitiator, "You have no stunning fist uses remaining.");
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform a normal unarmed attack
|
||||
effect eNone;
|
||||
PerformAttack(oTarget, oInitiator, eNone, 0.0, 0, 0, 0,
|
||||
"Unbalancing Strike Hit", "Unbalancing Strike Miss");
|
||||
|
||||
if (GetLocalInt(oTarget, "PRCCombat_StruckByAttack"))
|
||||
{
|
||||
int nDC = 10 + (GetHitDice(oInitiator) / 2) + GetAbilityModifier(ABILITY_WISDOM, oInitiator);
|
||||
if (!PRCMySavingThrow(SAVING_THROW_REFLEX, oTarget, nDC, SAVING_THROW_TYPE_NONE))
|
||||
{
|
||||
// Lose Dex bonus to AC: apply an AC penalty equal to the target<65>s Dex modifier
|
||||
int nDexPenalty = GetAbilityModifier(ABILITY_DEXTERITY, oTarget);
|
||||
effect eLink = EffectLinkEffects(eLink, EffectACDecrease(2+nDexPenalty, AC_DODGE_BONUS));
|
||||
eLink = ExtraordinaryEffect(eLink);
|
||||
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//:: To prevent being flatfooted.
|
||||
AssignCommand(oInitiator, ActionAttack(oTarget));
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
object oInitiator = OBJECT_SELF;
|
||||
object oTarget = PRCGetSpellTargetObject();
|
||||
DoUnbalancingStrike(oTarget, oInitiator);
|
||||
}
|
||||
178
nwn/nwnprc/trunk/scripts/prc_circle_lead.nss
Normal file
178
nwn/nwnprc/trunk/scripts/prc_circle_lead.nss
Normal file
@@ -0,0 +1,178 @@
|
||||
//:://////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//:://////////////////////////////////////////////
|
||||
//::
|
||||
/*
|
||||
Circle Leader
|
||||
Type of Feat: Class Specific
|
||||
Prerequisite: Red Wizard level 5 or Hathran 5.
|
||||
Specifics: Allows caster to initiate Circle
|
||||
Magic. Two to four participants can sacrifice
|
||||
spells to augment the Circle Leader's spell
|
||||
casting abilities for one rest cycle.
|
||||
Use: Activate Feat.
|
||||
|
||||
*/
|
||||
//::
|
||||
//:://////////////////////////////////////////////
|
||||
//:: Script: prc_circle_lead.nss
|
||||
//:: Author: Jaysyn
|
||||
//:: Created: 2026-02-10 12:19:50
|
||||
//:://////////////////////////////////////////////
|
||||
#include "prc_inc_spells"
|
||||
#include "prc_inc_burn"
|
||||
#include "x2_inc_spellhook"
|
||||
|
||||
// Helper to apply delayed visual transform
|
||||
void DelayedApplyTransform(object oTarget)
|
||||
{
|
||||
if (DEBUG) DoDebug("Starting to float!");
|
||||
SetObjectVisualTransform(oTarget, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z, 1.0f,
|
||||
OBJECT_VISUAL_TRANSFORM_LERP_SMOOTHERSTEP, 3.0f);
|
||||
}
|
||||
|
||||
// Helper to clean up all circle effects and events
|
||||
void CleanupCircle(object oLeader)
|
||||
{
|
||||
if (DEBUG) DoDebug("Cleaning up circle for " + GetName(oLeader));
|
||||
// Clear animation
|
||||
if (GetLocalInt(oLeader, "CircleMagicAnimating"))
|
||||
{
|
||||
AssignCommand(oLeader, ClearAllActions());
|
||||
DeleteLocalInt(oLeader, "CircleMagicAnimating");
|
||||
}
|
||||
// Clear VFX by tag
|
||||
effect eVFX = GetFirstEffect(oLeader);
|
||||
while (GetIsEffectValid(eVFX))
|
||||
{
|
||||
if (GetEffectTag(eVFX) == "CircleMagicVFX")
|
||||
DelayCommand(2.5f, RemoveEffect(oLeader, eVFX));
|
||||
eVFX = GetNextEffect(oLeader);
|
||||
}
|
||||
// Reset floating transform with lerp
|
||||
if (GetLocalInt(oLeader, "CircleMagicFloating"))
|
||||
{
|
||||
SetObjectVisualTransform(oLeader, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z, 0.0f,
|
||||
OBJECT_VISUAL_TRANSFORM_LERP_SMOOTHERSTEP, 3.0f);
|
||||
DeleteLocalInt(oLeader, "CircleMagicFloating");
|
||||
}
|
||||
// Clear state variables
|
||||
DeleteLocalInt(oLeader, "CircleMagicActive");
|
||||
DeleteLocalInt(oLeader, "CircleMagicTotal");
|
||||
DeleteLocalString(oLeader, "CircleMagicClass");
|
||||
DeleteLocalInt(oLeader, "CircleMagicMaxParticipants");
|
||||
DeleteLocalInt(oLeader, "CircleMagicParticipants");
|
||||
DeleteLocalLocation(oLeader, "CircleMagicStartLoc");
|
||||
// Remove events
|
||||
if (DEBUG) DoDebug("Removing HB and OnDamaged events for " + GetName(oLeader));
|
||||
RemoveEventScript(oLeader, EVENT_ONHEARTBEAT, "prc_circle_lead");
|
||||
RemoveEventScript(oLeader, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (DEBUG) DoDebug("prc_circle_lead script executed for " + GetName(OBJECT_SELF));
|
||||
|
||||
int nEvent = GetRunningEvent();
|
||||
object oLeader = OBJECT_SELF;
|
||||
|
||||
// Initial feat activation
|
||||
if (nEvent == FALSE)
|
||||
{
|
||||
if (DEBUG) DoDebug("Circle Leader feat activation running");
|
||||
// Toggle off if already active
|
||||
if (GetLocalInt(oLeader, "CircleMagicActive"))
|
||||
{
|
||||
FloatingTextStringOnCreature("You stop leading the circle.", oLeader);
|
||||
CleanupCircle(oLeader);
|
||||
return;
|
||||
}
|
||||
// Determine max participants and class tag
|
||||
int bIsGreat = GetHasFeat(FEAT_GREAT_CIRCLE_LEADER_THAYAN, oLeader) ||
|
||||
GetHasFeat(FEAT_GREAT_CIRCLE_LEADER_RASHEMAN, oLeader);
|
||||
int nMaxParticipants = bIsGreat ? 9 : 4;
|
||||
string sClassTag;
|
||||
if (GetHasFeat(FEAT_CIRCLE_LEADER_THAYAN, oLeader) ||
|
||||
GetHasFeat(FEAT_GREAT_CIRCLE_LEADER_THAYAN, oLeader))
|
||||
sClassTag = "RED_WIZARD";
|
||||
else if (GetHasFeat(FEAT_CIRCLE_LEADER_RASHEMAN, oLeader) ||
|
||||
GetHasFeat(FEAT_GREAT_CIRCLE_LEADER_RASHEMAN, oLeader))
|
||||
sClassTag = "HATHRAN";
|
||||
// Class gating for Great Circle Leader variants
|
||||
if ((sClassTag == "RED_WIZARD" && GetLevelByClass(CLASS_TYPE_RED_WIZARD, oLeader) == 0) ||
|
||||
(sClassTag == "HATHRAN" && GetLevelByClass(CLASS_TYPE_HATHRAN, oLeader) == 0))
|
||||
{
|
||||
FloatingTextStringOnCreature("You do not qualify to lead this circle.", oLeader);
|
||||
return;
|
||||
}
|
||||
// Initialize circle state
|
||||
SetLocalInt(oLeader, "CircleMagicActive", TRUE);
|
||||
SetLocalInt(oLeader, "CircleMagicTotal", 0);
|
||||
SetLocalInt(oLeader, "CircleMagicParticipants", 0);
|
||||
SetLocalInt(oLeader, "CircleMagicMaxParticipants", nMaxParticipants);
|
||||
SetLocalString(oLeader, "CircleMagicClass", sClassTag);
|
||||
SetLocalLocation(oLeader, "CircleMagicStartLoc", GetLocation(oLeader));
|
||||
// Start animation and mark
|
||||
AssignCommand(oLeader, ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, HoursToSeconds(24)));
|
||||
SetLocalInt(oLeader, "CircleMagicAnimating", TRUE);
|
||||
// Apply VFX (tagged for removal)
|
||||
effect eVFX1 = EffectVisualEffect(VFX_DUR_GLOW_PURPLE);
|
||||
effect eVFX2 = EffectVisualEffect(VFX_DUR_BLUESHIELDPROTECT);
|
||||
effect eVFX3 = EffectVisualEffect(PSI_DUR_BURST);
|
||||
// Dummy valid effect so the link isn<73>t VFX-only
|
||||
effect eDummy = EffectCutsceneGhost();
|
||||
effect eLink = EffectLinkEffects(eDummy, eVFX1);
|
||||
eLink = EffectLinkEffects(eLink, eVFX2);
|
||||
eLink = EffectLinkEffects(eLink, eVFX3);
|
||||
eLink = TagEffect(eLink, "CircleMagicVFX");
|
||||
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oLeader, HoursToSeconds(24));
|
||||
// Apply floating transform after a short delay
|
||||
DelayCommand(0.1f, DelayedApplyTransform(oLeader));
|
||||
SetLocalInt(oLeader, "CircleMagicFloating", TRUE);
|
||||
// Register heartbeat and OnDamaged for concentration
|
||||
AddEventScript(oLeader, EVENT_ONHEARTBEAT, "prc_circle_lead", TRUE, FALSE);
|
||||
AddEventScript(oLeader, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc", TRUE, FALSE);
|
||||
// Command eligible henchmen to use Circle Magic feat
|
||||
int i = 1;
|
||||
object oAssoc = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oLeader, i);
|
||||
while (GetIsObjectValid(oAssoc))
|
||||
{
|
||||
if (GetHasFeat(FEAT_CIRCLE_MAGIC, oAssoc))
|
||||
{
|
||||
if ((sClassTag == "RED_WIZARD" && GetLevelByClass(CLASS_TYPE_RED_WIZARD, oAssoc) > 0) ||
|
||||
(sClassTag == "HATHRAN" && GetLevelByClass(CLASS_TYPE_HATHRAN, oAssoc) > 0))
|
||||
{
|
||||
AssignCommand(oAssoc, ActionUseFeat(FEAT_CIRCLE_MAGIC, oLeader));
|
||||
}
|
||||
}
|
||||
i++;
|
||||
oAssoc = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oLeader, i);
|
||||
}
|
||||
FloatingTextStringOnCreature("Circle opened (" + IntToString(nMaxParticipants) + " participants max).", oLeader);
|
||||
}
|
||||
else if (nEvent == EVENT_ONHEARTBEAT)
|
||||
{
|
||||
if (DEBUG) DoDebug("Circle Leader HB running");
|
||||
// Concentration break check
|
||||
if (X2GetBreakConcentrationCondition(oLeader))
|
||||
{
|
||||
FloatingTextStringOnCreature("Your concentration is broken; the circle collapses.", oLeader);
|
||||
CleanupCircle(oLeader);
|
||||
return;
|
||||
}
|
||||
// Movement check
|
||||
location lStart = GetLocalLocation(oLeader, "CircleMagicStartLoc");
|
||||
float fDist = GetDistanceBetweenLocations(lStart, GetLocation(oLeader));
|
||||
if (DEBUG) DoDebug("Distance from start: " + FloatToString(fDist));
|
||||
if (fDist > 2.0f)
|
||||
{
|
||||
FloatingTextStringOnCreature("You moved too far; the circle collapses.", oLeader);
|
||||
CleanupCircle(oLeader);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
150
nwn/nwnprc/trunk/scripts/prc_circle_magic.nss
Normal file
150
nwn/nwnprc/trunk/scripts/prc_circle_magic.nss
Normal file
@@ -0,0 +1,150 @@
|
||||
//:://////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//:://////////////////////////////////////////////
|
||||
//::
|
||||
/*
|
||||
Circle Magic
|
||||
Type of Feat: Class Specific
|
||||
Prerequisite: None
|
||||
Specifics: Allows caster to participate in
|
||||
Circle Magic. Caster sacrifices a spell to
|
||||
augment the casting power of the Circle Leader.
|
||||
Use: Activate feat, target circle leader,
|
||||
select and cast spell.
|
||||
|
||||
*/
|
||||
//::
|
||||
//:://////////////////////////////////////////////
|
||||
//:: Script: prc_circle_magic.nss
|
||||
//:: Author: Jaysyn
|
||||
//:: Created: 2026-02-10 12:19:50
|
||||
//:://////////////////////////////////////////////
|
||||
#include "prc_inc_spells"
|
||||
#include "x2_inc_spellhook"
|
||||
|
||||
void main()
|
||||
{
|
||||
int nEvent = GetRunningEvent();
|
||||
object oPC = OBJECT_SELF;
|
||||
|
||||
// Initial feat activation
|
||||
if (nEvent == FALSE)
|
||||
{
|
||||
object oLeader = GetSpellTargetObject();
|
||||
|
||||
// Prevent leader from joining their own circle
|
||||
if (oLeader == oPC)
|
||||
{
|
||||
FloatingTextStringOnCreature("You cannot join a circle you lead as a channeler.", oPC);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate target is a Circle Leader with active circle
|
||||
if (!GetHasFeat(FEAT_CIRCLE_LEADER_RASHEMAN, oLeader) &&
|
||||
!GetHasFeat(FEAT_CIRCLE_LEADER_THAYAN, oLeader) &&
|
||||
!GetHasFeat(FEAT_GREAT_CIRCLE_LEADER_THAYAN, oLeader) &&
|
||||
!GetHasFeat(FEAT_GREAT_CIRCLE_LEADER_RASHEMAN, oLeader))
|
||||
{
|
||||
FloatingTextStringOnCreature("Target is not a Circle Leader.", oPC);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetLocalInt(oLeader, "CircleMagicActive"))
|
||||
{
|
||||
FloatingTextStringOnCreature("Circle is not active.", oPC);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start channeling for 1 in-game hour; concentration/range checks in event script
|
||||
SetLocalInt(oPC, "CircleMagicChanneling", TRUE);
|
||||
SetLocalObject(oPC, "CircleMagicLeader", oLeader);
|
||||
SetLocalFloat(oPC, "CircleMagicTimeLeft", HoursToSeconds(1));
|
||||
// Only make associates non-commandable
|
||||
if (!GetIsPC(oPC))
|
||||
SetCommandable(FALSE, oPC);
|
||||
AssignCommand(oPC, ClearAllActions());
|
||||
AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_SPASM, 1.0, HoursToSeconds(1)));
|
||||
AddEventScript(oPC, EVENT_ONHEARTBEAT, "prc_circle_magic", TRUE, FALSE);
|
||||
AddEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc", TRUE, FALSE);
|
||||
FloatingTextStringOnCreature("Channeling Circle Magic for 1 hour...", oPC);
|
||||
}
|
||||
else if (nEvent == EVENT_ONHEARTBEAT)
|
||||
{
|
||||
if (!GetLocalInt(oPC, "CircleMagicChanneling")) return;
|
||||
|
||||
object oLeader = GetLocalObject(oPC, "CircleMagicLeader");
|
||||
float fTimeLeft = GetLocalFloat(oPC, "CircleMagicTimeLeft") - 6.0f;
|
||||
|
||||
// Concentration break check
|
||||
if (GetLocalInt(oPC, "CONC_BROKEN"))
|
||||
{
|
||||
FloatingTextStringOnCreature("Concentration broken. Channeling failed.", oPC);
|
||||
RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_circle_magic");
|
||||
RemoveEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
|
||||
DeleteLocalInt(oPC, "CircleMagicChanneling");
|
||||
DeleteLocalFloat(oPC, "CircleMagicTimeLeft");
|
||||
if (!GetIsPC(oPC))
|
||||
SetCommandable(TRUE, oPC);
|
||||
AssignCommand(oPC, ClearAllActions());
|
||||
return;
|
||||
}
|
||||
|
||||
// Range check
|
||||
if (GetIsObjectValid(oLeader) && GetDistanceBetween(oPC, oLeader) > 4.0f)
|
||||
{
|
||||
FloatingTextStringOnCreature("Too far from leader. Channeling failed.", oPC);
|
||||
RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_circle_magic");
|
||||
RemoveEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
|
||||
DeleteLocalInt(oPC, "CircleMagicChanneling");
|
||||
DeleteLocalFloat(oPC, "CircleMagicTimeLeft");
|
||||
if (!GetIsPC(oPC))
|
||||
SetCommandable(TRUE, oPC);
|
||||
AssignCommand(oPC, ClearAllActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (fTimeLeft <= 0.0f)
|
||||
{
|
||||
// Channeling complete: find highest spell and cast it at the leader
|
||||
int nHighestSpell = GetBestL9Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL8Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL7Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL6Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL5Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL4Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL3Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL2Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL1Spell(oPC, -1);
|
||||
if (nHighestSpell == -1) nHighestSpell = GetBestL0Spell(oPC, -1);
|
||||
|
||||
if (nHighestSpell != -1)
|
||||
{
|
||||
// Set flags for spellhook interception, then cast
|
||||
SetLocalInt(oPC, "CircleMagicSacrifice", TRUE);
|
||||
SetLocalObject(oPC, "CircleMagicLeader", oLeader);
|
||||
AssignCommand(oPC, ClearAllActions());
|
||||
AssignCommand(oPC, ActionCastSpellAtObject(nHighestSpell, oLeader));
|
||||
}
|
||||
else
|
||||
{
|
||||
FloatingTextStringOnCreature("No spells available to sacrifice.", oPC);
|
||||
}
|
||||
|
||||
// Cleanup channeling state
|
||||
RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_circle_magic");
|
||||
RemoveEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc");
|
||||
DeleteLocalInt(oPC, "CircleMagicChanneling");
|
||||
DeleteLocalFloat(oPC, "CircleMagicTimeLeft");
|
||||
if (!GetIsPC(oPC))
|
||||
SetCommandable(TRUE, oPC);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLocalFloat(oPC, "CircleMagicTimeLeft", fTimeLeft);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,46 +10,63 @@
|
||||
//:: Created On: January 3 , 2004
|
||||
//:: Modified By: Stratovarius, bugfixes.
|
||||
//:://////////////////////////////////////////////
|
||||
|
||||
#include "prc_class_const"
|
||||
|
||||
void main()
|
||||
{
|
||||
effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_GATE);
|
||||
string sSummon;
|
||||
int i = 1;
|
||||
object oHench = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, OBJECT_SELF, i);
|
||||
|
||||
while (GetIsObjectValid(oHench))
|
||||
{
|
||||
if (GetStringLeft(GetResRef(oHench), 8) == "prc_hath_")
|
||||
{
|
||||
FloatingTextStringOnCreature("You already have a Rashemi Cohort", OBJECT_SELF, FALSE);
|
||||
return;
|
||||
}
|
||||
i += 1;
|
||||
oHench = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, OBJECT_SELF, i);
|
||||
}
|
||||
|
||||
int nClass = GetLevelByClass(CLASS_TYPE_HATHRAN, OBJECT_SELF);
|
||||
|
||||
if (nClass > 27) sSummon = "prc_hath_rash10";
|
||||
else if (nClass > 24) sSummon = "prc_hath_rash9";
|
||||
else if (nClass > 21) sSummon = "prc_hath_rash8";
|
||||
else if (nClass > 18) sSummon = "prc_hath_rash7";
|
||||
else if (nClass > 15) sSummon = "prc_hath_rash6";
|
||||
else if (nClass > 12) sSummon = "prc_hath_rash5";
|
||||
else if (nClass > 9) sSummon = "prc_hath_rash4";
|
||||
else if (nClass > 6) sSummon = "prc_hath_rash3";
|
||||
else if (nClass > 3) sSummon = "prc_hath_rash2";
|
||||
else if (nClass > 0) sSummon = "prc_hath_rash";
|
||||
|
||||
object oCreature = CreateObject(OBJECT_TYPE_CREATURE, sSummon, GetSpellTargetLocation());
|
||||
int nMaxHenchmen = GetMaxHenchmen();
|
||||
SetMaxHenchmen(99);
|
||||
AddHenchman(OBJECT_SELF, oCreature);
|
||||
SetMaxHenchmen(nMaxHenchmen);
|
||||
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetSpellTargetLocation());
|
||||
#include "inc_persist_loca"
|
||||
#include "prc_class_const"
|
||||
|
||||
void main()
|
||||
{
|
||||
effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_GATE);
|
||||
string sSummon;
|
||||
int i = 1;
|
||||
object oHench = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, OBJECT_SELF, i);
|
||||
|
||||
while (GetIsObjectValid(oHench))
|
||||
{
|
||||
if (GetStringLeft(GetResRef(oHench), 8) == "prc_hath_")
|
||||
{
|
||||
FloatingTextStringOnCreature("You already have a Rashemi Cohort", OBJECT_SELF, FALSE);
|
||||
return;
|
||||
}
|
||||
i += 1;
|
||||
oHench = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, OBJECT_SELF, i);
|
||||
}
|
||||
|
||||
int nClass = GetLevelByClass(CLASS_TYPE_HATHRAN, OBJECT_SELF);
|
||||
int nCohortType = GetPersistantLocalInt(OBJECT_SELF, "PRC_HATHRAN_COHORT_TYPE");
|
||||
|
||||
if(nCohortType == 1) // Ethran
|
||||
{
|
||||
if (nClass > 27) sSummon = "prc_hath_ethrn10";
|
||||
else if (nClass > 24) sSummon = "prc_hath_ethrn09";
|
||||
else if (nClass > 21) sSummon = "prc_hath_ethrn08";
|
||||
else if (nClass > 18) sSummon = "prc_hath_ethrn07";
|
||||
else if (nClass > 15) sSummon = "prc_hath_ethrn06";
|
||||
else if (nClass > 12) sSummon = "prc_hath_ethrn05";
|
||||
else if (nClass > 9) sSummon = "prc_hath_ethrn04";
|
||||
else if (nClass > 6) sSummon = "prc_hath_ethrn03";
|
||||
else if (nClass > 3) sSummon = "prc_hath_ethrn02";
|
||||
else if (nClass > 0) sSummon = "prc_hath_ethrn01";
|
||||
}
|
||||
else // Default Rashemi Barbarian
|
||||
{
|
||||
if (nClass > 27) sSummon = "prc_hath_rash10";
|
||||
else if (nClass > 24) sSummon = "prc_hath_rash9";
|
||||
else if (nClass > 21) sSummon = "prc_hath_rash8";
|
||||
else if (nClass > 18) sSummon = "prc_hath_rash7";
|
||||
else if (nClass > 15) sSummon = "prc_hath_rash6";
|
||||
else if (nClass > 12) sSummon = "prc_hath_rash5";
|
||||
else if (nClass > 9) sSummon = "prc_hath_rash4";
|
||||
else if (nClass > 6) sSummon = "prc_hath_rash3";
|
||||
else if (nClass > 3) sSummon = "prc_hath_rash2";
|
||||
else if (nClass > 0) sSummon = "prc_hath_rash";
|
||||
}
|
||||
|
||||
object oCreature = CreateObject(OBJECT_TYPE_CREATURE, sSummon, GetSpellTargetLocation());
|
||||
int nMaxHenchmen = GetMaxHenchmen();
|
||||
SetMaxHenchmen(99);
|
||||
AddHenchman(OBJECT_SELF, oCreature);
|
||||
SetMaxHenchmen(nMaxHenchmen);
|
||||
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetSpellTargetLocation());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,14 @@ void main()
|
||||
{
|
||||
object oDead = OBJECT_SELF;
|
||||
ExecuteScript("prc_ondeath", oDead);
|
||||
|
||||
// Clear Circle Magic state on death
|
||||
DeleteLocalInt(OBJECT_SELF, "CircleMagicActive");
|
||||
DeleteLocalInt(OBJECT_SELF, "CircleMagicTotal");
|
||||
DeleteLocalString(OBJECT_SELF, "CircleMagicClass");
|
||||
DeleteLocalInt(OBJECT_SELF, "CircleMagicMaxParticipants");
|
||||
DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_ADJUSTMENT);
|
||||
DeleteLocalInt(OBJECT_SELF, "CircleMagicAnimating");
|
||||
|
||||
if (GetIsObjectValid(GetLocalObject(GetModule(), "Necrocarnate")))
|
||||
{
|
||||
|
||||
@@ -63,6 +63,14 @@ void main()
|
||||
|
||||
// Clear a damage tracking variable. Oni's stuff uses this
|
||||
SetLocalInt(oDead, "PC_Damage", 0);
|
||||
|
||||
// Clear Circle Magic state on death
|
||||
DeleteLocalInt(OBJECT_SELF, "CircleMagicActive");
|
||||
DeleteLocalInt(OBJECT_SELF, "CircleMagicTotal");
|
||||
DeleteLocalString(OBJECT_SELF, "CircleMagicClass");
|
||||
DeleteLocalInt(OBJECT_SELF, "CircleMagicMaxParticipants");
|
||||
DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_ADJUSTMENT);
|
||||
DeleteLocalInt(OBJECT_SELF, "CircleMagicAnimating");
|
||||
|
||||
// Do Lolth's Meat for the killer
|
||||
if(GetAbilityScore(oDead, ABILITY_INTELLIGENCE) >= 4
|
||||
|
||||
@@ -78,7 +78,8 @@ void PrcFeats(object oPC)
|
||||
DelayCommand(0.3, DeletePRCLocalIntsT(oPC));
|
||||
DelayCommand(0.4, EvalPRCFeats(oPC));
|
||||
DelayCommand(0.4, DoWeaponsEquip(oPC));
|
||||
DelayCommand(1.0, DeleteLocalInt(oPC,"ONREST"));
|
||||
DelayCommand(0.5, DeleteLocalInt(oPC, "CircleMagicAnimating"));
|
||||
DelayCommand(1.0, DeleteLocalInt(oPC,"ONREST"));
|
||||
}
|
||||
|
||||
void RestCancelled(object oPC)
|
||||
|
||||
@@ -81,6 +81,7 @@ const int STAGE_WIELDING = 52;
|
||||
const int STAGE_WIELDING_ONE = 53;
|
||||
const int STAGE_WIELDING_TWO = 54;
|
||||
const int STAGE_WIELDING_POLEARM = 55;
|
||||
const int STAGE_HATHRAN_COHORT = 56;
|
||||
|
||||
// Confirmation stage for registering cohort
|
||||
const int STAGE_REGISTER_CONFIRM = 200;
|
||||
@@ -485,6 +486,7 @@ void main()
|
||||
AddChoice("Miscellaneous options.", 11);
|
||||
if(DEBUG)//TO DO: add separate switch
|
||||
AddChoice("Wipe PRC Spellbooks", 12);
|
||||
AddChoice("Hathran Cohort Selection", 56);
|
||||
if((!bRanged && oWeaponR != OBJECT_INVALID) && (PRCGetCreatureSize(oPC) > 1 && PRCGetCreatureSize(oPC) < 5))
|
||||
{
|
||||
AddChoice("Set weapon wielding.", 52);
|
||||
@@ -1642,7 +1644,17 @@ void main()
|
||||
|
||||
AddChoice("Back", CHOICE_RETURN_TO_PREVIOUS);
|
||||
MarkStageSetUp(nStage, oPC);
|
||||
}
|
||||
}
|
||||
else if(nStage == STAGE_HATHRAN_COHORT)
|
||||
{
|
||||
SetHeader("Select your Hathran cohort type:");
|
||||
AddChoice("Barbarian", 0);
|
||||
AddChoice("Ethran", 1);
|
||||
AddChoice("Back", STAGE_ENTRY);
|
||||
|
||||
MarkStageSetUp(nStage, oPC);
|
||||
SetDefaultTokens();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1736,6 +1748,8 @@ void main()
|
||||
DelayCommand(1.0, ExecuteScript("prc_wipeNSB", oPC));
|
||||
AllowExit(DYNCONV_EXIT_FORCE_EXIT);
|
||||
}
|
||||
else if(nChoice == 56)
|
||||
nStage = STAGE_HATHRAN_COHORT;
|
||||
|
||||
// Mark the target stage to need building if it was changed (ie, selection was other than ID all)
|
||||
if(nStage != STAGE_ENTRY)
|
||||
@@ -2431,14 +2445,28 @@ void main()
|
||||
MyDestroyObject(GetItemPossessedBy(oPC, "prc_pnp_familiar"));
|
||||
}
|
||||
}
|
||||
else if (nStage == STAGE_CDKEY_ADD)
|
||||
{
|
||||
else if (nStage == STAGE_CDKEY_ADD)
|
||||
{
|
||||
if(nChoice == 1)
|
||||
AddNewCDKey(oPC);
|
||||
|
||||
nStage = STAGE_ENTRY;
|
||||
MarkStageNotSetUp(nStage, oPC);
|
||||
}
|
||||
else if (nStage == STAGE_HATHRAN_COHORT)
|
||||
{
|
||||
if(nChoice == STAGE_ENTRY)
|
||||
{
|
||||
nStage = STAGE_ENTRY;
|
||||
MarkStageNotSetUp(nStage, oPC);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPersistantLocalInt(oPC, "PRC_HATHRAN_COHORT_TYPE", nChoice);
|
||||
nStage = STAGE_ENTRY;
|
||||
MarkStageNotSetUp(nStage, oPC);
|
||||
}
|
||||
}
|
||||
else if (nStage == STAGE_WIELDING)
|
||||
{
|
||||
if(nChoice == CHOICE_RETURN_TO_PREVIOUS)
|
||||
@@ -2556,7 +2584,7 @@ void main()
|
||||
|
||||
//AddChoice("Back", CHOICE_RETURN_TO_PREVIOUS);
|
||||
MarkStageSetUp(nStage, oPC);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Store the stage value. If it has been changed, this clears out the choices
|
||||
SetStage(nStage, oPC);
|
||||
|
||||
Reference in New Issue
Block a user