generated from Jaysyn/ModuleTemplate
652 lines
29 KiB
Plaintext
652 lines
29 KiB
Plaintext
// Henchmen Always Bleed to Death
|
|
// By Demtrious, OldManWhistler and IntrepidCW
|
|
//
|
|
// PLEASE READ "habd_include" FOR MORE INFORMATION.
|
|
//
|
|
// OnPlayerDying event handler.
|
|
|
|
#include "habd_include"
|
|
#include "subdual_inc"
|
|
// ****************************************************************************
|
|
|
|
// 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)
|
|
{
|
|
int iplay = FALSE;
|
|
//PlayingVoices activated?
|
|
if ( HABD_PLAY_BLEED_VOICE ==TRUE && HABD_PLAY_BLEED_VOICE<2)
|
|
iplay = TRUE;
|
|
if ( HABD_PLAY_BLEED_VOICE >1 && HABD_PLAY_BLEED_VOICE<=100 )
|
|
if ( d100(1) >= HABD_PLAY_BLEED_VOICE )
|
|
iplay = TRUE;
|
|
|
|
if (iplay == TRUE)
|
|
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;
|
|
}
|
|
|
|
// ****************************************************************************
|
|
|
|
|
|
// Returns the delay in seconds that must be applied to the player healing
|
|
// after estabilization.
|
|
float DelayToHeal(object oPC);
|
|
|
|
float DelayToHeal(object oPC)
|
|
{
|
|
float ftime = 1.0;
|
|
if (HABD_DELAY_TOHEAL == 0.0 || GetCurrentHitPoints(oPC)>=1)
|
|
ftime = 1.0; //the old HABD value
|
|
else
|
|
{
|
|
ftime = HABD_DELAY_TOHEAL * IntToFloat(1- GetCurrentHitPoints(oPC) );
|
|
if (ftime < 1.0) ftime = 1.0;
|
|
}
|
|
return ftime;
|
|
}
|
|
// ****************************************************************************
|
|
|
|
|
|
// 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, int iIgnoreDelayVar = FALSE);
|
|
|
|
void HealTo1HP(object oPC, int iIgnoreDelayVar = FALSE)
|
|
{
|
|
object oMod = GetModule();
|
|
string sID = GetPCPlayerName(oPC)+GetName(oPC);
|
|
|
|
if (GetLocalInt(oMod, HABD_DELAYTOHEAL_CONTROL+sID) == FALSE &
|
|
iIgnoreDelayVar == FALSE)
|
|
return;
|
|
DeleteLocalInt(oMod, HABD_DELAYTOHEAL_CONTROL+sID);
|
|
|
|
// If player is already alive then abort.
|
|
if (HABDGetHABD_PLAYER_STATE(sID, oPC) == 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);
|
|
if (HABD_POST_BLEED_CONCEAL_DUR > 0.0 && HABD_POST_BLEED_INVIS_DUR > 0.0)
|
|
if (HABD_POST_BLEED_CONCEAL_DUR >= HABD_POST_BLEED_INVIS_DUR )
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectConcealment(100,MISS_CHANCE_TYPE_NORMAL), oPC, HABD_POST_BLEED_INVIS_DUR);
|
|
if (HABD_POST_BLEED_INVIS_DUR > HABD_POST_BLEED_CONCEAL_DUR )
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectConcealment(100,MISS_CHANCE_TYPE_NORMAL), oPC, HABD_POST_BLEED_CONCEAL_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);
|
|
HABDSetHABD_PLAYER_STATE(HABD_STATE_PLAYER_ALIVE, sID, oPC); //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));
|
|
}
|
|
|
|
//Save info to DB (and maybe export PC) to avoid server crash problems,
|
|
//now that we are sure the PC is Alive.
|
|
if ( HABD_SMART_SAVETODB == TRUE )
|
|
HABDSetDBString(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 possibility for healing by other players
|
|
int lastHP = 0;
|
|
if (GetIsPC(oPC) && GetIsDMPossessed(oPC) == FALSE)//PC
|
|
lastHP = GetLocalInt(oMod, HABD_LAST_HP+sID);
|
|
else //NPC
|
|
lastHP = GetLocalInt(oPC, HABD_LAST_HP);
|
|
if (GetCurrentHitPoints(oPC) > lastHP) //if hitpoint have increased
|
|
{
|
|
float ftime = DelayToHeal(oPC);
|
|
DelayCommand(ftime, HealTo1HP(oPC));
|
|
if (FloatToInt(ftime)>1)
|
|
{
|
|
if(HABD_USERLANGUAGE==0)
|
|
FloatingTextStringOnCreature(GetName(oPC)+" will heal to 1 HP in: "+IntToString(FloatToInt(ftime))+" seconds.", oPC, TRUE);
|
|
else if (HABD_USERLANGUAGE==1)
|
|
FloatingTextStringOnCreature(GetName(oPC)+" se curara a 1 PG en: "+IntToString(FloatToInt(ftime))+" segundos.", oPC, TRUE);
|
|
}
|
|
SetLocalInt(oMod, HABD_DELAYTOHEAL_CONTROL+sID, TRUE);
|
|
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 (
|
|
(HABDGetHABD_PLAYER_STATE(sID, oPC) != 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);
|
|
|
|
string sbleedmsg = GetName(oPC)+" is bleeding to death! At "+IntToString(iHPs)+" hitpoints.";
|
|
if(HABD_USERLANGUAGE==0)
|
|
sbleedmsg = GetName(oPC)+" is bleeding to death! At "+IntToString(iHPs)+" hitpoints.";
|
|
else if(HABD_USERLANGUAGE==1)
|
|
sbleedmsg = GetName(oPC)+" esta muriendose! A "+IntToString(iHPs)+" puntos de golpe.";
|
|
|
|
DelayCommand(0.1, FloatingTextStringOnCreature(sbleedmsg, oPC));
|
|
if (HABD_DM_NOTIFICATION_ON_BLEED) SendMessageToAllDMs(sbleedmsg);
|
|
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.
|
|
|
|
// Whistler didnt take in mind that a player can possess the familiar even
|
|
// when he is bleeding!!! it ruins all the timers. I havent found a fix that
|
|
// doesnt kill the pet.
|
|
// 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.
|
|
// 1.65 has changed the familiar functions, but none of them seems to affect us
|
|
// 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);
|
|
|
|
string skillpetmessage = GetName(oMaster)+" HAS LOST FAMILIAR '"+GetName(oCreature)+"'";
|
|
if(HABD_USERLANGUAGE==0)
|
|
skillpetmessage = GetName(oMaster)+" HAS LOST FAMILIAR '"+GetName(oCreature)+"'";
|
|
else if(HABD_USERLANGUAGE==1)
|
|
skillpetmessage = GetName(oMaster)+" HA PERDIDO SU FAMILIAR '"+GetName(oCreature)+"'";
|
|
|
|
DelayCommand(0.002,FloatingTextStringOnCreature(skillpetmessage, oCreature));
|
|
if (HABD_DM_NOTIFICATION_ON_BLEED) SendMessageToAllDMs(skillpetmessage);
|
|
DelayCommand(0.003, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDeath, oCreature));
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// ****************************************************************************
|
|
|
|
|
|
// It will kill any familiar of the PC. Take in mind that a player can possess
|
|
// the familiar even when he is bleeding!!! it ruins all the timers. I havent
|
|
// found a fix that doesnt kill the pet.
|
|
// Facts: if you are alone in the server and posses a familiar, GetFirstPC()
|
|
// doesnt seem to return something that is not usefull.
|
|
void DestroyFamiliar(object oPC);
|
|
|
|
void DestroyFamiliar(object oPC)
|
|
{
|
|
int i = 1;
|
|
object oPet = GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oPC,i);
|
|
effect eDeath = EffectDeath(FALSE, FALSE);
|
|
while( oPet != OBJECT_INVALID )
|
|
{
|
|
if(HABD_USERLANGUAGE==0)
|
|
SendMessageToPC(oPC, "HABD: Your familiar died");
|
|
else if(HABD_USERLANGUAGE==1)
|
|
SendMessageToPC(oPC, "HABD: Tu familiar ha muerto");
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDeath, oPet);
|
|
i=i+1;
|
|
oPet = GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oPC,i);
|
|
}
|
|
}
|
|
|
|
|
|
// ****************************************************************************
|
|
|
|
// Applies -1 HP to the player and checks for stabilization.
|
|
// fBleedTimer - the time duration between bleeding -1 HP.
|
|
void BleedToDeath(float fBleedTimer, object oPC, int iCoupDeGrace, object oFamiliar = OBJECT_INVALID)
|
|
{
|
|
object oMod = GetModule();
|
|
//object oPC = OBJECT_SELF;
|
|
|
|
// whistler: if this is a player in a possessed familiar, then just kill it.
|
|
// IntrepidCW: However, calling it in the BleedToDeath function will ruin the
|
|
// bleeding chain. So we also restore the bleeding chain
|
|
// Known Bug: If the plater possess the familiar when he has stabilized
|
|
// but the 1 HP healing has not still been applied, he will remain at ground
|
|
// (logout/login could solve the problem)
|
|
if (HABD_FAMILIAR_EXPLOIT <=1)
|
|
if ( GetIsPC(oFamiliar) || GetIsPossessedFamiliar(oFamiliar) )
|
|
{
|
|
KillPet(oFamiliar);
|
|
AssignCommand(oMod, DelayCommand(fBleedTimer, BleedToDeath(fBleedTimer, oPC, iCoupDeGrace, OBJECT_INVALID )));
|
|
return;
|
|
}
|
|
|
|
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);
|
|
//DEBUG: Not translated to spanish
|
|
int iPlayerState = HABDGetHABD_PLAYER_STATE(sID, oPC);
|
|
if (iPlayerState != HABD_STATE_PLAYER_BLEEDING) return;
|
|
|
|
// Deals with a exploit that happens when you level up while bleeding
|
|
if ( GetLocalInt(oPC, HABD_LAST_LEVEL) != 0 )
|
|
{
|
|
if ( GetHitDice(oPC) > GetLocalInt(oPC, HABD_LAST_LEVEL) )
|
|
{
|
|
SetLocalInt(oPC, HABD_LAST_LEVEL, GetHitDice(oPC) );
|
|
//Kill the PC
|
|
if ( HABD_LEVELUPBLEED_EXPLOIT_KILLS == TRUE )
|
|
{
|
|
if(HABD_USERLANGUAGE==0)
|
|
{
|
|
SendMessageToPC(oPC,"You have died, because you used the exploit to level up when Bleeding. If error, alert DMs with full details.");
|
|
SendMessageToAllDMs(GetName(oPC)+" (Player:"+GetPCPlayerName(oPC)+" has died, because you used the exploit to level up when Bleeding.");
|
|
}
|
|
else if(HABD_USERLANGUAGE==1)
|
|
{
|
|
SendMessageToPC(oPC,"Has muerto, por que usaste la trampa de subir de nivel mientras morias. En caso de error, alerta a los DMs con detalles completos.");
|
|
SendMessageToAllDMs(GetName(oPC)+" (El Jugador:"+GetPCPlayerName(oPC)+" ha muerto, por que uso la trampa de subir de nivel mientras moria.");
|
|
}
|
|
|
|
WriteTimestampedLogEntry(GetName(oPC)+" (Player:"+GetPCPlayerName(oPC)+") has died, because you used the exploit to level up when Bleeding.");
|
|
// Ensure that plot is not still set.
|
|
SetPlotFlag(oPC, FALSE);
|
|
// Drop him to -10 HPs
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamage(GetCurrentHitPoints(oPC)+10,DAMAGE_TYPE_MAGICAL,DAMAGE_POWER_PLUS_FIVE), oPC);
|
|
// 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
|
|
HABDSetHABD_PLAYER_STATE(HABD_STATE_PLAYER_DEAD, sID, oPC);
|
|
// 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;
|
|
}
|
|
else
|
|
{
|
|
if(HABD_USERLANGUAGE==0)
|
|
{
|
|
SendMessageToPC(oPC,"You used the exploit to level up when Bleeding, DMs will know it. If error, alert DMs with full details.");
|
|
SendMessageToAllDMs(GetName(oPC)+" (Player:"+GetPCPlayerName(oPC)+" Used the exploit to level up when Bleeding.");
|
|
}
|
|
else if(HABD_USERLANGUAGE==1)
|
|
{
|
|
SendMessageToPC(oPC,"Has usado la trampa de subir de nivel mientras morias, los DMs han sido avisados y lo sabran leyendo el Log. En caso de error, alerta a los DMs con detalles completos.");
|
|
SendMessageToAllDMs(GetName(oPC)+" (El Jugador:"+GetPCPlayerName(oPC)+" Uso la trampa de subir de nivel mientras moria.");
|
|
}
|
|
WriteTimestampedLogEntry(GetName(oPC)+" (Player:"+GetPCPlayerName(oPC)+" Used the exploit to level up when Bleeding.");
|
|
}
|
|
}
|
|
//else SetLocalInt(oPC, HABD_LAST_LEVEL, GetHitDice(oPC) );
|
|
}
|
|
else SetLocalInt(oPC, HABD_LAST_LEVEL, GetHitDice(oPC) );
|
|
// - end of antiexploit code
|
|
|
|
//Coup de Grace for PCs alone in area feature
|
|
if ( iCoupDeGrace == TRUE)
|
|
if ( GetHitDice(oPC) > HABD_NO_DEATH_UNTIL_LEVEL )
|
|
if ( HABD_GetIsSafeArea(oPC) == FALSE)
|
|
{
|
|
//kill the PC
|
|
if(HABD_USERLANGUAGE==0)
|
|
SendMessageToPC(oPC,"You have died, due to coup of grace,");
|
|
else if(HABD_USERLANGUAGE==1)
|
|
SendMessageToPC(oPC,"Has muerto, debido a un golpe de gracia,");
|
|
// Ensure that plot is not still set.
|
|
SetPlotFlag(oPC, FALSE);
|
|
// Drop him to -10 HPs
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamage(GetCurrentHitPoints(oPC)+10,DAMAGE_TYPE_MAGICAL,DAMAGE_POWER_PLUS_FIVE), oPC);
|
|
// 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
|
|
HABDSetHABD_PLAYER_STATE(HABD_STATE_PLAYER_DEAD, sID, oPC);
|
|
// 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;
|
|
}
|
|
//-
|
|
|
|
//Check if someone has healed at least one hp to the PC.
|
|
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 stabilize. Default d10.
|
|
int nSavingRoll = Random(HABD_STABILIZATION_DICE)+1;
|
|
|
|
// See what you must roll to self-stabilized
|
|
int iNeededSavingRoll = HABD_GLOBAL_STABILIZATION_NUMBER;
|
|
if ( HABDPlayerIsSolo(oPC) )
|
|
iNeededSavingRoll = HABD_SOLO_STABILIZATION_NUMBER;
|
|
|
|
// 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*HABD_STABILIZATION_DICE/10; break;
|
|
case 9:
|
|
case -2: nSavingRoll = nSavingRoll + 3*HABD_STABILIZATION_DICE/10; break;
|
|
case 8:
|
|
case -3: nSavingRoll = nSavingRoll + 4*HABD_STABILIZATION_DICE/10; break;
|
|
case 7:
|
|
case -4: nSavingRoll = nSavingRoll + 5*HABD_STABILIZATION_DICE/10; break;
|
|
case 6:
|
|
case -5: nSavingRoll = nSavingRoll + 6*HABD_STABILIZATION_DICE/10; break;
|
|
case 5:
|
|
case -6: nSavingRoll = nSavingRoll + 7*HABD_STABILIZATION_DICE/10; break;
|
|
case 4:
|
|
case -7: nSavingRoll = nSavingRoll + 8*HABD_STABILIZATION_DICE/10; break;
|
|
case 3:
|
|
case -8: nSavingRoll = nSavingRoll + 9*HABD_STABILIZATION_DICE/10; break;
|
|
case 2:
|
|
case -9:
|
|
default: nSavingRoll = nSavingRoll + HABD_STABILIZATION_DICE; break;
|
|
}
|
|
}
|
|
|
|
//Default 10 for 3E - lower for easier stabilization
|
|
int iStabilized = FALSE;
|
|
if (nSavingRoll >= iNeededSavingRoll)
|
|
iStabilized = TRUE;
|
|
else
|
|
if (HABD_GetIsSafeArea(oPC) == TRUE)
|
|
iStabilized = TRUE;
|
|
|
|
//Stabilized
|
|
if (iStabilized == TRUE)
|
|
{
|
|
float ftime = DelayToHeal(oPC);
|
|
DelayCommand(ftime, HealTo1HP(oPC)); //call heal subroutine
|
|
if (FloatToInt(ftime)>1)
|
|
{
|
|
if(HABD_USERLANGUAGE==0)
|
|
FloatingTextStringOnCreature(GetName(oPC)+" will heal to 1 HP in: "+IntToString(FloatToInt(ftime))+" seconds.", oPC, TRUE);
|
|
else if(HABD_USERLANGUAGE==1)
|
|
FloatingTextStringOnCreature(GetName(oPC)+" se curara a 1 PG en: "+IntToString(FloatToInt(ftime))+" segundos.", oPC, TRUE);
|
|
}
|
|
SetLocalInt(GetModule(), HABD_DELAYTOHEAL_CONTROL+sID, TRUE);
|
|
|
|
// Always make it look like they rolled a 10 to stabilize
|
|
if(HABD_USERLANGUAGE==0)
|
|
{
|
|
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."));
|
|
}
|
|
else if(HABD_USERLANGUAGE==1)
|
|
{
|
|
SendMessageToPC(oPC,"Tirada de Estabilizacion (a "+IntToString(GetCurrentHitPoints(oPC))+") = 10");
|
|
FloatingTextStringOnCreature(GetName(oPC)+" se ha auto-estabilizado.", oPC);
|
|
DelayCommand(6.0, SendMessageToPC(oPC, "En un esfuerzo a vida o muerte has sobrevivido, vivo pero herido."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
//if you get here, you have not been healed and did not successfully stabilize
|
|
else
|
|
{
|
|
// Most important, keep the bleeding chain going.
|
|
AssignCommand(oMod, DelayCommand(fBleedTimer, BleedToDeath(fBleedTimer, oPC, iCoupDeGrace, oFamiliar)));
|
|
if(HABD_USERLANGUAGE==0)
|
|
SendMessageToPC(oPC,"Saving Roll to stop bleeding (at "+IntToString(GetCurrentHitPoints(oPC))+") = " +IntToString(nSavingRoll));
|
|
else if(HABD_USERLANGUAGE==1)
|
|
SendMessageToPC(oPC,"Tirada de Estabilizacion (a "+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.
|
|
if (GetIsPC(oPC) && GetIsDMPossessed(oPC) == FALSE) //PC
|
|
SetLocalInt(oMod,HABD_LAST_HP+sID, GetCurrentHitPoints(oPC));
|
|
else //NPC
|
|
SetLocalInt(oPC,HABD_LAST_HP, GetCurrentHitPoints(oPC));
|
|
|
|
// if this is true then the player has died.
|
|
if (GetCurrentHitPoints(oPC) <= -10)
|
|
{
|
|
if(HABD_USERLANGUAGE==0)
|
|
SendMessageToPC(oPC,"You have died.");
|
|
else if(HABD_USERLANGUAGE==1)
|
|
SendMessageToPC(oPC,"Has muerto.");
|
|
// 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
|
|
HABDSetHABD_PLAYER_STATE(HABD_STATE_PLAYER_DEAD, sID, oPC);
|
|
// 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()
|
|
{
|
|
if (CheckSubdual(GetLastPlayerDying())) return;
|
|
{
|
|
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);
|
|
|
|
//to properly deal with the levelling up while bleeding exploit.
|
|
SetLocalInt(oPC, HABD_LAST_LEVEL, GetHitDice(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 bleeding is disabled for Henchmen
|
|
if ( HABD_HENCHMEN_BLEED == FALSE || HABD_HENCHMEN_BLEED == 2 )
|
|
return;
|
|
}
|
|
//DEBUG: Not translated to Spanish
|
|
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;
|
|
// Intrepid: Damn, player can possess a familiar when they are bleeding, which
|
|
// ruins all the bleeding thing (at least in NWN 1.64 patch). The only fix
|
|
// 100% relliable I have now is to kill all familiars.
|
|
if (HABD_FAMILIAR_EXPLOIT == 0)
|
|
DestroyFamiliar(oPC);
|
|
|
|
int iState = HABDGetHABD_PLAYER_STATE(sID, oPC);
|
|
if ((iState == HABD_STATE_PLAYER_DEAD) || (iState == HABD_STATE_RESPAWNED_GHOST))
|
|
return;
|
|
|
|
if ( iState == HABD_STATE_PC_PERMADEATH )
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(1 - (GetCurrentHitPoints(oPC))), oPC);
|
|
HABDJumpToPermaDeathPoint(oPC, HABD_EVENTDYINGSIGNAL);
|
|
return;
|
|
}
|
|
|
|
//coup de grace feature
|
|
int CoupDeGrace = HABDCoupDeGraceAloneInArea(oPC);
|
|
|
|
// Most important, issue the commands to start the bleeding chain.
|
|
float fBleedTimer = HABDGetBleedTimer(oPC);
|
|
|
|
//start the bleeding chain
|
|
AssignCommand(oMod, DelayCommand(fBleedTimer, BleedToDeath(fBleedTimer, oPC, CoupDeGrace, GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oPC,1) )));
|
|
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
|
|
HABDStopNearbyAttackers(oPC);
|
|
|
|
|
|
// CAP TO NEGATIVE HPs
|
|
// Allow a good chance for healing - will limit HP to -5 on a bleed level hit.
|
|
// Actually configurable via constant HABD_BLEEDING_START_LIMIT
|
|
if ( HABD_BLEEDING_START_LIMIT > -10)
|
|
if ( HABD_GetIsHardArea(oPC) == FALSE)
|
|
if (
|
|
(iHPs < HABD_BLEEDING_START_LIMIT) &&
|
|
(iState == HABD_STATE_PLAYER_ALIVE)
|
|
)
|
|
{
|
|
//should heal player to -5 (if default)
|
|
int nHeal = HABD_BLEEDING_START_LIMIT - iHPs;
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nHeal), oPC);
|
|
}
|
|
|
|
// Set the state variables.
|
|
iHPs = GetCurrentHitPoints(oPC);
|
|
HABDSetHABD_PLAYER_STATE(HABD_STATE_PLAYER_BLEEDING, sID, oPC);
|
|
if (GetIsPC(oPC) && GetIsDMPossessed(oPC) == FALSE) //PC
|
|
SetLocalInt(oMod,HABD_LAST_HP+sID, GetCurrentHitPoints(oPC));
|
|
else //NPC
|
|
SetLocalInt(oPC,HABD_LAST_HP, 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_USERLANGUAGE==0)
|
|
sMsg = GetName(oPC)+" is bleeding to death! At "+IntToString(iHPs)+" hitpoints. Will die in "+FloatToString((10 + iHPs)*fBleedTimer, 3, 0)+" seconds.";
|
|
else if(HABD_USERLANGUAGE==1)
|
|
sMsg = GetName(oPC)+" esta muriendose! A "+IntToString(iHPs)+" puntos de golpe. Morira en "+FloatToString((10 + iHPs)*fBleedTimer, 3, 0)+" segundos.";
|
|
if (HABD_DM_NOTIFICATION_ON_BLEED) SendMessageToAllDMs(sMsg);
|
|
FloatingTextStringOnCreature(sMsg, oPC);
|
|
|
|
//Save info to DB (and maybe export PC) to avoid server crash problems,
|
|
//now that we are sure the PC is bleeding.
|
|
if ( HABD_SMART_SAVETODB == TRUE )
|
|
HABDSetDBString(oPC);
|
|
} }
|