HiddenTradition_PRC8/_module/nss/i420_s_c_oud_gho.nss
2024-06-20 15:47:42 -04:00

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;
}
}