378 lines
16 KiB
Plaintext
378 lines
16 KiB
Plaintext
/////////////////////////////////////////////////////
|
|
// //
|
|
// Invizible420's Ghost w/ visuals and Possession //
|
|
// //
|
|
// Created 03/11/03 By: Invizible420 //
|
|
// //
|
|
// Send Bug Reports to: Digiddy777@yahoo.com //
|
|
// //
|
|
/////////////////////////////////////////////////////
|
|
/*
|
|
Description: This is the on_heartbeat script
|
|
for a ghost. I tried to make it fairly
|
|
accurate to 3rd edition rules (at least as
|
|
close as NWN will let me).
|
|
|
|
This heartbeat script will give the ghost a
|
|
percent chance to use Malevolence (posses)
|
|
the pc as well as Frightening Moan.
|
|
|
|
This script will only fire if a PC is within
|
|
fDist (See Below) meters of the ghost and if
|
|
a PC is in the area.
|
|
So to not consume unnecessary CPU cycles.
|
|
|
|
To simplify code, the moan and malevolence will
|
|
only affect the PC once if the below ONE_SHOT
|
|
options are TRUE. This goes for all the ghosts,
|
|
if set to TRUE and a PC either saves or is possessed,
|
|
then no ghosts in the module will be able to affect
|
|
the PC.
|
|
|
|
Update 04.29.05: Streamlined code into 2 scripts
|
|
renamed them to better suit a filename format.
|
|
** If you are updating from an older version of
|
|
my ghost, delete i420_ghost* files. Then in
|
|
Custom Creatures > Undead --- Update all instances
|
|
of the Ghost.
|
|
|
|
New features: Starting at line 66 you will find 5
|
|
new control variables... These are effects that
|
|
are applied when the PC is possessed and will
|
|
last until they are unpossessed.
|
|
|
|
Also, fixed a minor bug with the ghost using
|
|
moan if the PC is dead. Last, I changed the
|
|
demo module so you can respawn if you die... oops!
|
|
|
|
*/
|
|
#include "NW_I0_GENERIC"
|
|
#include "nw_i0_plot"
|
|
#include "prc_inc_racial"
|
|
|
|
// Customizable Variables:
|
|
|
|
int MOAN_ONE_SHOT = FALSE; // Change to TRUE to make Moan effect one shot (Default: FALSE)
|
|
int MALEVOLENCE_ONE_SHOT = FALSE; // Change to TRUE to make Malevolence one shot (Default: FALSE)
|
|
|
|
float fDIST = 10.0; // How far the ghost must be away from a PC (Default: 10.0m)
|
|
int POSSESS_CHANCE = 20; // % chance last attacker will become possessed [Malevolence] (Default: 20%)
|
|
int MOAN_CHANCE = 10; // % chance last ghost will use Moan attack (Default: 10%)
|
|
float POSSESS_DUR = IntToFloat(d20(1)+10); // Duration of possession (Default: 1d20+10)
|
|
float POSSESSED_PC_ATTACK_DIST = 15.0; // Maximum range another PC must be get attacked by another possessed PC (Default: 15m)
|
|
int DO_DMG = TRUE; // if TRUE ghost will damage PC when possessed, if FALSE ghost will not damage PC
|
|
float DAMAGE_TIME = IntToFloat(d6(1)+3); // This is the amount of time to wait before damaging the PC when possessed
|
|
float DAMAGE_TIME_2 = IntToFloat(d6(1)+FloatToInt(DAMAGE_TIME)+3); // Same as above for 2nd hit
|
|
float DAMAGE_TIME_3 = IntToFloat(d6(1)+FloatToInt(DAMAGE_TIME_2)+3); // Same as above for 3rd hit
|
|
int GHOST_DAMAGE_TYPE = DAMAGE_TYPE_NEGATIVE; // The type of damage the ghost will do to a PC when possessed
|
|
effect GHOST_DMG_VIS = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY); // The visual effect for the ghost possession damage
|
|
float MOAN_DIST = 10.0; // Maximum range of Frightful Moan (Default: 10m about 30 ft)
|
|
float MOAN_DUR = IntToFloat(d4(2)+3); // Duration of Moan Effect in turns (Default: 2d4 + 3 turns)
|
|
|
|
/********/
|
|
// New variables for 4.39.05 update
|
|
int PC_POSSESS_SIT = TRUE; // TRUE means the below chances will affect the PC, FALSE means they won't
|
|
int ABILITY_LOSS_CHANCE = 10; // chance the a (random) ability will be lowered for possess dur
|
|
int AC_LOSS_CHANCE = 10; // Chance the PC's AC will be decreased
|
|
int BLIND_CHANCE = 10; // Chance the ghost will close the PC's eyes (blind them)
|
|
float BLIND_DUR = POSSESS_DUR-IntToFloat(d4(1)-1); // How long to stay blind
|
|
|
|
object oGHOST_LIMBO_WP = GetObjectByTag("GHOST_LIMBO"); // Get the Ghost Limbo Waypoint
|
|
|
|
// Text Color Stuff
|
|
object oCOLOR_OBJ = GetObjectByTag("i420_COLOR_OBJ");
|
|
string COLOR_RED = GetStringLeft(GetName(oCOLOR_OBJ),6);
|
|
string COLOR_BLUE = GetStringRight(GetName(oCOLOR_OBJ),6);
|
|
|
|
|
|
|
|
|
|
//Debugger
|
|
int DEBUGGER = FALSE;
|
|
void DB(string sDebugStr)
|
|
{
|
|
if (DEBUGGER == TRUE) SendMessageToPC(GetFirstPC(),sDebugStr);
|
|
}
|
|
|
|
|
|
// Begin Function Declarations:
|
|
// Define Function Variables
|
|
// Returns TRUE if there is a PC in oArea else returns FALSE
|
|
int CheckAreaForPCs(object oArea);
|
|
// Returns the Nearest PC to oObject
|
|
object GetNearestPCToObject(object oObject);
|
|
// Applies Ghost Possession Effects to oTarget (1 of 4 Effects)
|
|
void ApplyGhostPossessionEffect(object oTarget,object oGhost);
|
|
|
|
|
|
int CheckAreaForPCs(object oArea)
|
|
{
|
|
object oPC;
|
|
int iCheck = FALSE;
|
|
oPC = GetFirstPC();
|
|
while (oPC != OBJECT_INVALID) {
|
|
if (GetArea(oPC) == oArea) {
|
|
iCheck = TRUE;
|
|
}
|
|
oPC = GetNextPC();
|
|
}
|
|
return iCheck;
|
|
}
|
|
|
|
|
|
object GetNearestPCToObject(object oObject)
|
|
{
|
|
object oNearestPC;
|
|
oNearestPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC,oObject);
|
|
if (GetDistanceBetween(OBJECT_SELF,oNearestPC) <= POSSESSED_PC_ATTACK_DIST) {
|
|
return oNearestPC;
|
|
}
|
|
else {
|
|
oNearestPC = OBJECT_INVALID;
|
|
return oNearestPC;
|
|
}
|
|
}
|
|
|
|
void RecreateGhostAtLocation(object oTarget,object oGhost, location lLoc)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(VFX_IMP_DEATH),oTarget);
|
|
AssignCommand(oGhost,JumpToLocation(lLoc));
|
|
DeleteLocalLocation(oGhost,"GHOST_PC_LAST_LOC");
|
|
ClearAllActions(FALSE);
|
|
}
|
|
|
|
|
|
void RecreateGhost(object oTarget,object oGhost)
|
|
{
|
|
if (GetIsDead(oTarget) == FALSE && GetLocalInt(oGhost,"GHOST_PC_DEAD") != 1 &&
|
|
oTarget != OBJECT_INVALID)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(VFX_IMP_DEATH),oTarget);
|
|
AssignCommand(oGhost,JumpToObject(oTarget,FALSE));
|
|
}
|
|
else if (GetIsDead(oTarget) == TRUE || oTarget == OBJECT_INVALID &&
|
|
GetLocalInt(oGhost, "GHOST_PC_DEAD") != 1)
|
|
{
|
|
RecreateGhostAtLocation(oTarget,oGhost,GetLocalLocation(oGhost,"GHOST_PC_LAST_LOC"));
|
|
DeleteLocalLocation(oGhost,"GHOST_PC_LAST_LOC");
|
|
}
|
|
ClearAllActions(FALSE);
|
|
}
|
|
|
|
void DoGhostDmg(object oPC, object oGhost, int iDmgPos)
|
|
{
|
|
if(GetIsDead(oPC) != TRUE && GetLocalInt(oGhost,"GHOST_PC_DEAD") != 1)
|
|
{
|
|
int DAMAGE_AMOUNT_1 = (Random(GetHitDice(oPC))+1*2)+GetAbilityModifier(ABILITY_STRENGTH,oPC); // Amount to damage the PC when possessed first hit
|
|
int DAMAGE_AMOUNT_2 = (Random(GetHitDice(oPC))+1*2)+GetAbilityModifier(ABILITY_STRENGTH,oPC); // Amount to damage the PC when possessed second hit
|
|
int DAMAGE_AMOUNT_3 = (Random(GetHitDice(oPC))+1*2)+GetAbilityModifier(ABILITY_STRENGTH,oPC); // Amount to damage the PC when possessed third hit
|
|
|
|
int iDmg;
|
|
if (iDmgPos == 1) iDmg = DAMAGE_AMOUNT_1;
|
|
if (iDmgPos == 2) iDmg = DAMAGE_AMOUNT_2;
|
|
if (iDmgPos == 3) iDmg = DAMAGE_AMOUNT_3;
|
|
if (iDmg > 0)
|
|
{
|
|
effect eLink1 = EffectLinkEffects(GHOST_DMG_VIS,EffectDamage(iDmg,GHOST_DAMAGE_TYPE));
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eLink1,oPC);
|
|
}
|
|
ClearAllActions(FALSE);
|
|
}
|
|
if (GetIsDead(oPC) == TRUE && GetLocalInt(oGhost,"GHOST_PC_DEAD") != 1)
|
|
{
|
|
DelayCommand(2.5,RecreateGhostAtLocation(oPC,oGhost,GetLocalLocation(oGhost,"GHOST_PC_LAST_LOC")));
|
|
SetLocalInt(oGhost, "GHOST_PC_DEAD",1);
|
|
DelayCommand(POSSESS_DUR+0.5,DeleteLocalInt(oGhost,"GHOST_PC_DEAD"));
|
|
}
|
|
}
|
|
|
|
void DelayAction(object oTarget, int iSit)
|
|
{
|
|
// Check which situation(s) the PC has and apply the effect
|
|
if (iSit == 1)
|
|
{
|
|
int ABILITY_LOSS_AMT = Random(GetHitDice(oTarget))+1; // amount of ability loss
|
|
int iAbility;
|
|
switch(d6(1))
|
|
{
|
|
case 1: iAbility = ABILITY_CHARISMA; break;
|
|
case 2: iAbility = ABILITY_CONSTITUTION; break;
|
|
case 3: iAbility = ABILITY_DEXTERITY; break;
|
|
case 4: iAbility = ABILITY_INTELLIGENCE; break;
|
|
case 5: iAbility = ABILITY_STRENGTH; break;
|
|
case 6: iAbility = ABILITY_WISDOM; break;
|
|
}
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DISABLED),oTarget,POSSESS_DUR-3.5);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectAbilityDecrease(iAbility,ABILITY_LOSS_AMT),oTarget,POSSESS_DUR-3.5);
|
|
}
|
|
if (iSit == 2)
|
|
{
|
|
int AC_LOSS_AMT = Random(GetHitDice(oTarget))+1; // amount of Ac loss
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE),oTarget,POSSESS_DUR-5.0);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectACDecrease(AC_LOSS_AMT),oTarget,POSSESS_DUR-5.0);
|
|
}
|
|
if (iSit == 3)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectVisualEffect(VFX_DUR_BLIND),oTarget,BLIND_DUR-6.0);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectBlindness(),oTarget,BLIND_DUR-6.0);
|
|
}
|
|
}
|
|
|
|
void GhostEffects2(object oTarget,object oGhost)
|
|
{
|
|
|
|
DB("POSSESS_DUR = "+FloatToString(POSSESS_DUR)); // DEBUG LINE
|
|
|
|
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DOMINATED),oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(VFX_IMP_DEATH),oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD),oTarget);
|
|
|
|
|
|
// Sets an int so the PC can't be dominated multiple times simoltaneously, then deletes the int after the ghost effect
|
|
SetLocalInt(oTarget,"PC_IS_DOMINATED",1);
|
|
DelayCommand(POSSESS_DUR,DeleteLocalInt(oTarget,"PC_IS_DOMINATED"));
|
|
|
|
// Dominate the oTarget
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectCutsceneDominated(),oTarget,POSSESS_DUR);
|
|
|
|
// Do the NEW ossessed situations
|
|
if (PC_POSSESS_SIT == TRUE)
|
|
{
|
|
// Check to see if PC will be affected by a possessed situation
|
|
if (d100(1) < ABILITY_LOSS_CHANCE) DelayCommand(2.5,DelayAction(oTarget,1));
|
|
if (d100(1) < AC_LOSS_CHANCE) DelayCommand(4.0,DelayAction(oTarget,2));
|
|
if (d100(1) < BLIND_CHANCE) DelayCommand(5.5,DelayAction(oTarget,3));
|
|
}
|
|
|
|
if(DO_DMG == TRUE) {
|
|
DelayCommand(DAMAGE_TIME,DoGhostDmg(oTarget, oGhost, 1));
|
|
if (FloatToInt(POSSESS_DUR)-1 > FloatToInt(DAMAGE_TIME_2)) DelayCommand(DAMAGE_TIME_2,DoGhostDmg(oTarget, oGhost, 2));
|
|
if (FloatToInt(POSSESS_DUR)-1 > FloatToInt(DAMAGE_TIME_3)) DelayCommand(DAMAGE_TIME_3,DoGhostDmg(oTarget, oGhost, 3));
|
|
}
|
|
DelayCommand(POSSESS_DUR,RecreateGhost(oTarget,oGhost));
|
|
}
|
|
|
|
|
|
void fFace(object oPC, object oGhost)
|
|
{
|
|
float fFacing = GetFacing(oPC);
|
|
AssignCommand(oGhost,SetFacing(fFacing));
|
|
}
|
|
|
|
void ApplyGhostPossessionEffect(object oTarget,object oGhost)
|
|
{
|
|
// Apply effect to make ghost look like it leaps into the PC and disappears
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD),OBJECT_SELF);
|
|
AssignCommand(oGhost,ClearAllActions());
|
|
AssignCommand(oGhost,JumpToObject(oTarget,FALSE));
|
|
|
|
DelayCommand(0.4,AssignCommand(oGhost,JumpToObject(oGHOST_LIMBO_WP,FALSE)));
|
|
|
|
DelayCommand(0.5,fFace(oTarget,oGhost));
|
|
|
|
GhostEffects2(oTarget,oGhost);
|
|
}
|
|
|
|
|
|
void main()
|
|
{
|
|
switch(GetUserDefinedEventNumber())
|
|
{
|
|
case 1001:
|
|
// This insures that the script is only fired if a PC is
|
|
// within the area.
|
|
if (CheckAreaForPCs(GetArea(OBJECT_SELF)) == TRUE) {
|
|
|
|
object oNearestPC = GetNearestPC();
|
|
|
|
if ( GetIsPC(oNearestPC) == TRUE &&
|
|
GetDistanceBetween(OBJECT_SELF,GetNearestPC()) <= fDIST &&
|
|
GetLocalInt(oNearestPC,"i420_GHOST_MALEVOLENCE") != 1 &&
|
|
GetLocalInt(oNearestPC,"PC_IS_DOMINATED") != 1 &&
|
|
GetIsDead(oNearestPC) != TRUE)
|
|
{
|
|
// Roll a % die against the POSSESS_CHANCE
|
|
if (d100(1) <= POSSESS_CHANCE) {
|
|
|
|
// Get DC and Will Saves
|
|
int DC = GetAbilityModifier(ABILITY_CHARISMA,OBJECT_SELF)+15;
|
|
int Will = GetWillSavingThrow(oNearestPC);
|
|
int id20 = d20(1);
|
|
int SavingThrow = Will+id20;
|
|
// If Save fails
|
|
if (DC > SavingThrow ) {
|
|
//SettheLastlocationofPC
|
|
SetLocalLocation(OBJECT_SELF,"GHOST_PC_LAST_LOC",GetLocation(oNearestPC));
|
|
|
|
// Send Message to PC
|
|
SendMessageToPC(oNearestPC,COLOR_RED+"Ghost uses Malevolence!");
|
|
SendMessageToPC(oNearestPC,COLOR_BLUE+GetName(oNearestPC)+" : Will Save vs. Malevolence : *failure* : ("+IntToString(id20)+" + "+IntToString(Will)+" = "+IntToString(SavingThrow)+" vs. DC: "+IntToString(DC)+")");
|
|
// Apply the 1st Ghost Effects
|
|
ApplyGhostPossessionEffect(oNearestPC,OBJECT_SELF);
|
|
}
|
|
// If Save succeeds
|
|
else {
|
|
// Send Message to PC
|
|
SendMessageToPC(oNearestPC,COLOR_RED+"Ghost uses Malevolence!");
|
|
SendMessageToPC(oNearestPC,COLOR_BLUE+GetName(oNearestPC)+" : Will Save vs. Malevolence : *success* : ("+IntToString(id20)+" + "+IntToString(Will)+" = "+IntToString(SavingThrow)+" vs. DC: "+IntToString(DC)+")");
|
|
// Set the int that ghosts can not affect the PC
|
|
if (MALEVOLENCE_ONE_SHOT == TRUE) {
|
|
SetLocalInt(oNearestPC,"i420_GHOST_MALEVOLENCE",1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Roll a % die against the MOAN_CHANCE
|
|
if (d100(1) <= MOAN_CHANCE && GetLocalInt(oNearestPC,"PC_IS_MOANED") != 1 &&
|
|
GetLocalInt(oNearestPC,"i420_GHOST_MALEVOLENCE") != 1 ) {
|
|
PlayAnimation(ANIMATION_FIREFORGET_TAUNT);
|
|
DelayCommand(0.75,ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(VFX_IMP_SILENCE),OBJECT_SELF));
|
|
// Loop through all the Objects in a MOAN_DIST m radius
|
|
object oObjects;
|
|
oObjects = GetFirstObjectInShape(SHAPE_SPHERE,MOAN_DIST,GetLocation(OBJECT_SELF),TRUE,OBJECT_TYPE_CREATURE);
|
|
while (oObjects != OBJECT_INVALID) {
|
|
if ((MyPRCGetRacialType(oObjects) != RACIAL_TYPE_UNDEAD) &&
|
|
(GetLocalInt(oNearestPC,"i420_GHOST_MOAN") != 1) &&
|
|
GetIsDead(oNearestPC) != TRUE) {
|
|
int iDC = GetAbilityModifier(ABILITY_CHARISMA,OBJECT_SELF)+15;
|
|
int iWill = GetWillSavingThrow(oNearestPC);
|
|
int D20 = d20(1);
|
|
int iSavingThrow = iWill+D20;
|
|
// If Save fails
|
|
if (iDC > iSavingThrow ) {
|
|
// Sets an int to make sure PC can't be moaned multiple times simoltaneously, deletes aftet moan effect
|
|
SetLocalInt(oNearestPC,"PC_IS_MOANED",1);
|
|
DelayCommand(MOAN_DUR,DeleteLocalInt(oNearestPC,"PC_IS_MOANED"));
|
|
|
|
SendMessageToPC(oNearestPC,COLOR_RED+"Ghost uses Frightful Moan!");
|
|
SendMessageToPC(oNearestPC,COLOR_BLUE+GetName(oNearestPC)+" : Will Save vs. Fear : *failure* : ("+IntToString(D20)+" + "+IntToString(iWill)+" = "+IntToString(iSavingThrow)+" vs. DC: "+IntToString(iDC)+")");
|
|
effect eVis = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_FEAR);
|
|
effect eLink = EffectLinkEffects(eVis, EffectFrightened());
|
|
AssignCommand(oNearestPC,ClearAllActions(FALSE));
|
|
AssignCommand(oNearestPC,ActionMoveAwayFromObject(OBJECT_SELF,TRUE));
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eLink,oObjects,MOAN_DUR);
|
|
}
|
|
else {
|
|
SendMessageToPC(oNearestPC,COLOR_RED+"Ghost uses Frightful Moan!");
|
|
// Send Message to PC
|
|
SendMessageToPC(oNearestPC,COLOR_BLUE+GetName(oNearestPC)+" : Will Save vs. Fear : *success* : ("+IntToString(D20)+" + "+IntToString(iWill)+" = "+IntToString(iSavingThrow)+" vs. DC: "+IntToString(iDC)+")");
|
|
// Set the int that ghosts can not affect the PC
|
|
if (MOAN_ONE_SHOT == TRUE) {
|
|
SetLocalInt(oNearestPC,"i420_GHOST_MOAN",1);
|
|
}
|
|
}
|
|
}
|
|
oObjects = GetNextObjectInShape(SHAPE_SPHERE,MOAN_DIST,GetLocation(OBJECT_SELF),TRUE,OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 1007:
|
|
object oPlayer = GetLastPlayerDied();
|
|
SetIsTemporaryEnemy(GetLastDamager(),oPlayer);
|
|
DelayCommand(5.0, PopUpDeathGUIPanel(oPlayer,1,1,0,"To test what ghost does after PC dies, press the 'Wait for help' button. (Must be in Multi-player to see the button!)"));
|
|
break;
|
|
}
|
|
}
|