RATDOG/_module/nss/habd_onpcdying.nss
Jaysyn904 ae152d0814 Added BESIE, MS Moneclature
Added BESIE, Markshire's Nomeclature & started initial setup for commoners in Baleas.
2021-09-01 23:42:36 -04:00

362 lines
15 KiB
Plaintext

// Hemophiliacs Always Bleed to Death
// By Demtrious and OldManWhistler
//
// PLEASE READ "habd_include" FOR MORE INFORMATION.
//
// OnPlayerDying event handler.
#include "habd_include"
// ****************************************************************************
// This function plays a random bleeding VoiceChat on a player.
// oPC - the player to make play a bleed voice.
void PlayBleedVoice (object oPC);
void PlayBleedVoice (object oPC)
{
switch (d6())
{
case 1: PlayVoiceChat (VOICE_CHAT_PAIN1, oPC); break;
case 2: PlayVoiceChat (VOICE_CHAT_PAIN2, oPC); break;
case 3: PlayVoiceChat (VOICE_CHAT_PAIN3, oPC); break;
case 4: PlayVoiceChat (VOICE_CHAT_HEALME, oPC); break;
case 5: PlayVoiceChat (VOICE_CHAT_NEARDEATH, oPC); break;
case 6: PlayVoiceChat (VOICE_CHAT_HELP, oPC); break;
}
return;
}
// ****************************************************************************
// Heals players to 1 hp and to removes negative effects. In also calls the
// user defined bleed stabilization function.
// oPC - the player to heal.
void HealTo1HP(object oPC);
void HealTo1HP(object oPC)
{
object oMod = GetModule();
string sID = GetPCPlayerName(oPC)+GetName(oPC);
// If player is already alive then abort.
if (GetLocalInt(oMod, HABD_PLAYER_STATE+sID) == HABD_STATE_PLAYER_ALIVE) return;
int iNPC = GetLocalInt(OBJECT_SELF, HABD_NPC_BLEED);
// Give the player a chance to run away
if (HABD_POST_BLEED_INVIS_DUR > 0.0) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectInvisibility(INVISIBILITY_TYPE_NORMAL), oPC, HABD_POST_BLEED_INVIS_DUR);
// Turn the plot flag off after a specific period of time.
SetPlotFlag(oPC, FALSE);
// Raises the player to 1 hp.
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(1 - (GetCurrentHitPoints(oPC))), oPC);
SetLocalInt(oMod,HABD_PLAYER_STATE+sID, HABD_STATE_PLAYER_ALIVE); //set player state to alive
// If this is a henchmen, then take them out of the busy state.
if (iNPC)
{
HABDAssociateNotBusy();
}
// Keep the player from being attacked, stop nearby attackers
AssignCommand(oPC, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectInvisibility(INVISIBILITY_TYPE_NORMAL), oPC, 6.0));
// Make the player hostile again.
SetStandardFactionReputation(STANDARD_FACTION_HOSTILE, GetLocalInt(oPC, HABD_OLD_FACTION), oPC);
DeleteLocalInt(oPC, HABD_OLD_FACTION_SET);
AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_DRUNK, 1.0, 5.0));
// Notify the player that they were healed.
DelayCommand(0.5, SendMessageToPC(oPC, "You have healed."));
// Apply user defined penalties.
AssignCommand(oPC, HABDUserDefinedBleed());
//Give a little visual effect for flare.
effect eVisual = EffectVisualEffect(VFX_IMP_RESTORATION);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVisual, oPC);
// If regeneration items were removed then reequip them.
if (HABD_NERF_REGENERATION_ITEMS)
{
AssignCommand(oPC, HABDRegenerationItemsReEquip(oPC));
}
// Fixes the inital respawn issue with monsters not reattacking.
//object oMonster = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oPC, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
//DelayCommand(9.0, AssignCommand(oMonster, ActionAttack(oPC)));
}
// ****************************************************************************
// Returns TRUE if the player has stabilized by gaining any HP since the last
// time they bled.
int CheckForStabilization(object oPC);
int CheckForStabilization(object oPC)
{
object oMod = GetModule();
string sID = GetPCPlayerName(oPC)+GetName(oPC);
//Section deals with possiblity for healing by other players
if (GetCurrentHitPoints(oPC) > GetLocalInt(oMod, HABD_LAST_HP+sID)) //if hitpoint have increased
{
DelayCommand(1.0, HealTo1HP(oPC));
return TRUE;
}
return FALSE;
}
// ****************************************************************************
// Report the bleed count for OBJECT_SELF.
void ReportPlayerBleed();
void ReportPlayerBleed()
{
object oPC = OBJECT_SELF;
object oMod = GetModule();
string sID = GetPCPlayerName(oPC)+GetName(oPC);
int iHPs = GetCurrentHitPoints(oPC);
int iNPC = GetLocalInt(OBJECT_SELF, HABD_NPC_BLEED);
if (iNPC) iHPs = iHPs - 10;
if (
(GetLocalInt(oMod, HABD_PLAYER_STATE+sID) != HABD_STATE_PLAYER_BLEEDING) || // check if player is still bleeding
(iHPs > 0) || // player has healed
(iHPs <= -10) || // player is a goner, let the death script kick in
(CheckForStabilization(oPC)) // check if player has gained any HP
)
{
DeleteLocalInt(oPC, HABD_REPORT_BLEED_RUNNING);
return;
}
// The delay will effect how often players are vocal about bleeding.
DelayCommand(6.0, AssignCommand(oPC, ReportPlayerBleed()));
// Prevent calling this function multiple times
SetLocalInt(oPC, HABD_REPORT_BLEED_RUNNING, 1);
DelayCommand(0.1, FloatingTextStringOnCreature(GetName(oPC)+" is bleeding to death! At "+IntToString(iHPs)+" hitpoints.", oPC));
if (HABD_DM_NOTIFICATION_ON_BLEED) SendMessageToAllDMs(GetName(oPC)+" is bleeding to death! At "+IntToString(iHPs)+" hitpoints.");
PlayBleedVoice(oPC);
AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_DEAD_BACK, 1.0, 6.0));
}
// ****************************************************************************
// This function exists to fix the problem that occurs in bleeding scripts
// when the summoned familiar is being possessed by the player (sorc or wiz).
// That creates a condition where GetIsPC returns true for the familiar.
// What usually happens is that when the possessed familiar dies, the player
// is trapped in its body until the DM manually kills the player.
// While stuck in the dead familiar, the player is unable to run the unpossess
// action and the bleed count on the familiar usually does not work properly.
// This function DOES NOT kill the familiar when the player is bleeding if the player
// is not possessing the familiar. The familiar will be able to continue fighting
// for its unconcious and bleeding master.
// oTarget - the possibly "possessed" player.
int KillPet(object oTarget, int nEffect = TRUE, int nVisualEffectId = VFX_IMP_UNSUMMON);
int KillPet(object oTarget, int nEffect = TRUE, int nVisualEffectId = VFX_IMP_UNSUMMON)
{
// Usage: place in your bleeding script with a call that looks something like
// if (KillPet(oPC)) return; // abort from the bleed script, oPC no longer exists
effect eDeath = EffectDeath(FALSE, FALSE);
effect eVis = EffectVisualEffect(nVisualEffectId);
object oCreature = oTarget;
if(GetIsObjectValid(oCreature))
{
object oMaster = GetMaster(oCreature);
if(GetIsObjectValid(oMaster))
{
//Is the creature a summoned associate
if(GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oMaster) == oCreature)
{
//Apply the VFX and delay the destruction of the summoned monster so
//that the script and VFX can play.
if(nEffect)
DelayCommand(0.001,ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY,eVis,GetLocation(oCreature),1.0f));
SetPlotFlag(oCreature, FALSE);
DelayCommand(0.002,FloatingTextStringOnCreature(GetName(oMaster)+" HAS LOST FAMILIAR '"+GetName(oCreature)+"'", oCreature));
if (HABD_DM_NOTIFICATION_ON_BLEED) SendMessageToAllDMs(GetName(oMaster)+" HAS LOST FAMILIAR '"+GetName(oCreature)+"'");
DelayCommand(0.003, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDeath, oCreature));
return TRUE;
}
}
}
return FALSE;
}
// ****************************************************************************
// Applies -1 HP to the player and checks for stabilization.
// fBleedTimer - the time duration between bleeding -1 HP.
void BleedToDeath(float fBleedTimer)
{
object oMod = GetModule();
object oPC = OBJECT_SELF;
string sID = GetPCPlayerName(oPC)+GetName(oPC);
int iNPC = GetLocalInt(OBJECT_SELF, HABD_NPC_BLEED);
if (HABD_DEBUG) SpeakString("DEBUG: HABD OnBleed, "+GetName(oPC)+", HP: "+IntToString(GetCurrentHitPoints(oPC))+", master: "+GetName(GetMaster(oPC))+", state:"+HABDGetPlayerStateName(oPC), TALKVOLUME_SHOUT);
int iPlayerState = GetLocalInt(oMod, HABD_PLAYER_STATE+sID);
if (iPlayerState != HABD_STATE_PLAYER_BLEEDING) return;
if (CheckForStabilization(oPC)) return;
// if you get here - you are dying and have not been healed
// so you need to roll to see if you stablize
int nSavingRoll = d10();
// Death can be disabled before a certain level by "faking" the stabilization
// check. Do not let the players know that death is disabled because it will
// only encourage them to be idiots.
if ((HABD_NO_DEATH_UNTIL_LEVEL) && (GetHitDice(oPC) < HABD_NO_DEATH_UNTIL_LEVEL))
{
switch (GetCurrentHitPoints(oPC))
{
case 10:
case -1: nSavingRoll = nSavingRoll + 2; break;
case 9:
case -2: nSavingRoll = nSavingRoll + 3; break;
case 8:
case -3: nSavingRoll = nSavingRoll + 4; break;
case 7:
case -4: nSavingRoll = nSavingRoll + 5; break;
case 6:
case -5: nSavingRoll = nSavingRoll + 6; break;
case 5:
case -6: nSavingRoll = nSavingRoll + 7; break;
case 4:
case -7: nSavingRoll = nSavingRoll + 8; break;
case 3:
case -8: nSavingRoll = nSavingRoll + 9; break;
case 2:
case -9:
default: nSavingRoll = nSavingRoll + 10; break;
}
}
if (nSavingRoll > 9) //set to 9 for 3E - lower for easier stabilization
{
DelayCommand(1.0, HealTo1HP(oPC)); //call heal subroutine
// Always make it look like they rolled a 10 to stabilize
SendMessageToPC(oPC,"Saving Roll to stop bleeding (at "+IntToString(GetCurrentHitPoints(oPC))+") = 10");
FloatingTextStringOnCreature(GetName(oPC)+" has self-stabilized.", oPC);
DelayCommand(6.0, SendMessageToPC(oPC, "In a life or death effort you have survived, alive but barely."));
return;
}
//if you get here, you have not been healed and did not successfully stabilize
else
{
// Most important, keep the bleeding chain going.
DelayCommand(fBleedTimer, AssignCommand(oPC, BleedToDeath(fBleedTimer)));
SendMessageToPC(oPC,"Saving Roll to stop bleeding (at "+IntToString(GetCurrentHitPoints(oPC))+") = " +IntToString(nSavingRoll));
SetPlotFlag(oPC, FALSE);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamage(1,DAMAGE_TYPE_MAGICAL,DAMAGE_POWER_PLUS_FIVE), oPC);
SetPlotFlag(oPC, TRUE);
// Update local variable with hitpoints for healing option.
SetLocalInt(oMod,HABD_LAST_HP+sID, GetCurrentHitPoints(oPC));
// if this is true then the player has died.
if (GetCurrentHitPoints(oPC) <= -10)
{
SendMessageToPC(oPC,"You have died.");
// Ensure that plot is not still set.
SetPlotFlag(oPC, FALSE);
// Set up the hostile faction again.
SetStandardFactionReputation(STANDARD_FACTION_HOSTILE, GetLocalInt(oPC, HABD_OLD_FACTION), oPC);
DeleteLocalInt(oPC, HABD_OLD_FACTION_SET);
// Set playerstate to dead not dying
SetLocalInt(oMod,HABD_PLAYER_STATE+sID, HABD_STATE_PLAYER_DEAD);
// OnPlayerDead script will be called after this.
// BleedToDeath will be called one more time, but it will instantly
// abort because the player is not in the bleeding state.
return;
}
}
}
// ****************************************************************************
// OnPlayerDying event handler.
void main()
{
object oMod = GetModule();
object oPC = GetLastPlayerDying();
int iNPC = GetLocalInt(OBJECT_SELF, HABD_NPC_BLEED);
if (iNPC == 1) oPC = OBJECT_SELF;
string sID = GetPCPlayerName(oPC)+GetName(oPC);
// If an NPC is running this script, then set up its master. The master was
// automatically wiped out when the henchman died.
if (iNPC)
{
if (!GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_HENCHMAN, GetLocalObject(OBJECT_SELF, HABD_NPC_MASTER))))
AddHenchman(GetLocalObject(OBJECT_SELF, HABD_NPC_MASTER), oPC);
}
if (HABD_DEBUG) SpeakString("DEBUG: HABD OnDying, "+GetName(oPC)+", HP: "+IntToString(GetCurrentHitPoints(oPC))+", master: "+GetName(GetMaster(oPC))+", state:"+HABDGetPlayerStateName(oPC), TALKVOLUME_SHOUT);
// Check if bleeding is running on DM or DM possessed, then abort.
if(GetIsDM(oPC) || GetIsDM(GetMaster(oPC))) return;
// whistler: if this is a player in a possessed familiar, then just kill it.
// Familiar penalties will kick in when familiar dies.
if (KillPet(oPC)) return;
int iState = GetLocalInt(oMod,HABD_PLAYER_STATE+sID);
if ((iState == HABD_STATE_PLAYER_DEAD) || (iState == HABD_STATE_RESPAWNED_GHOST)) return;
// Most important, issue the commands to start the bleeding chain.
float fBleedTimer = HABDGetBleedTimer(oPC);
AssignCommand(oPC, DelayCommand(fBleedTimer, BleedToDeath(fBleedTimer)));
if (GetLocalInt(oPC, HABD_REPORT_BLEED_RUNNING) == 0) DelayCommand(6.0, AssignCommand(oPC, ReportPlayerBleed()));
int iHPs = GetCurrentHitPoints(oPC);
SetPlotFlag(oPC, TRUE);
// Force friendly to hostile faction.
if (!GetLocalInt(oPC, HABD_OLD_FACTION_SET))
{
SetLocalInt(oPC, HABD_OLD_FACTION, GetStandardFactionReputation(STANDARD_FACTION_HOSTILE, oPC));
SetLocalInt(oPC, HABD_OLD_FACTION_SET, 1);
}
SetStandardFactionReputation(STANDARD_FACTION_HOSTILE, 100, oPC);
// Keep the player from being attacked, stop nearby attackers
AssignCommand(oPC, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectInvisibility(INVISIBILITY_TYPE_NORMAL), oPC, 6.0));
// Allow a good chance for healing - will limit HP to -5 on a bleed level hit.
if (
(iHPs<-5) &&
(iState == HABD_STATE_PLAYER_ALIVE)
)
{
int nHeal = -5 - iHPs; //should heal player to -5
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nHeal), oPC);
}
// Set the state variables.
iHPs = GetCurrentHitPoints(oPC);
SetLocalInt(oMod,HABD_PLAYER_STATE+sID, HABD_STATE_PLAYER_BLEEDING);
SetLocalInt(oMod,HABD_LAST_HP+sID, GetCurrentHitPoints(oPC));
// Check if we are re-entering this state from persistence.
if (GetLocalInt(oPC, HABD_PERSISTANT_REAPPLY) != 1)
{
// Increment the counters.
SetLocalInt(oMod, HABD_CURRENT_BLEED_COUNT+sID, GetLocalInt(oMod, HABD_CURRENT_BLEED_COUNT+sID) + 1);
SetLocalInt(oMod, HABD_BLEED_COUNT+sID, GetLocalInt(oMod, HABD_BLEED_COUNT+sID) + 1);
} else {
DeleteLocalInt(oPC, HABD_PERSISTANT_REAPPLY);
}
// Nerf regeneration items.
if (HABD_NERF_REGENERATION_ITEMS)
{
AssignCommand(oPC, HABDRegenerationItemsUnequip(oPC));
}
// Notify that bleeding has started.
if (iNPC) iHPs = iHPs - 10;
string sMsg = GetName(oPC)+" is bleeding to death! At "+IntToString(iHPs)+" hitpoints. Will die in "+FloatToString((10 + iHPs)*fBleedTimer, 3, 0)+" seconds.";
if (HABD_DM_NOTIFICATION_ON_BLEED) SendMessageToAllDMs(sMsg);
FloatingTextStringOnCreature(sMsg, oPC);
}