Files
HeroesStone_PRC8/_module/nss/cz_realtroll_ud.nss
Jaysyn904 1eefc84201 Initial Commit
Initial Commit.
2025-09-14 15:40:46 -04:00

376 lines
17 KiB
Plaintext

/*
Tz-Auber's Perfect Troll Regeneration Script 1.3
By: Tz-Auber
Last Modified: 9/23/03
Developed for: 1.30/SoU
Description
-----------
This small script properly implements 3rd Edition subdual damage rules as it relates
to regeneration. Some of the highlighted features are:
- By using the SetImmortal function, feedback for the Troll's health is properly
displayed.
- Only uses OnHeartbeat and OnDamaged events. No messing with OnDeath and XP
calculations.
- Allows for quickly killing a downed Troll through a specified item (by default,
a torch item type equipped in the off-hand) or through SoU grenade type
weapons (Acid Flask and Alchemist Fire).
- A knockdown effect occurs when actual damage exceeds subdual damage, and it
also simulates attacking a prone opponent.
- Allows for automatic 3E Coup de Grace attempts if the Troll is down and the equipped
weapon is capable of doing permanent damage.
Modifications Required
----------------------
- This script must be placed in a creature's OnUserDefined event.
- The OnSpawn script must have the following SetSpawnInConditions uncommented/added:
- NW_FLAG_HEARTBEAT_EVENT
- NW_FLAG_DAMAGED_EVENT
- NW_FLAG_ATTACK_EVENT
- NW_FLAG_SPELL_CAST_AT_EVENT
- The following line must be added in the OnSpawn script at the end
- SetImmortal(OBJECT_SELF,TRUE);
- Remove the default Troll's regeneration property by removing it's hide from the
creature inventory (or remove the regeneration property if you want to keep
other hide properties).
Credits
-------
I want to thank U'lias, as I used his code for a basis and starting point
(creation date 12/29/02). His code can be found by searching the vault under
"Ulias' 3eMM D&D Style Trolls v1.3"
I would also like to thank El Magnifico Uno, one of the pioneers of Troll
regeneration code, who also provided useful critique in the development of this
work.
Version History
---------------
1.3 (9/23/03) - Found out that my code has just been released into the wilds of
a PW and have received a lot of useful feedback. New features added: support
for 3E Coup de Grace attempts and the use of Alchemist Fire and Acid Flasks
to fry a Troll in addition to the torch.
1.2 (9/22/03) - Hmm.. this was supposed to come sooner, but here it is. Changed
from a resref dependency of the burning item, to a base item type dependency.
So any BASE_ITEM_TORCH equipped in the off-hand should work regardless of
tags and resrefs. Finally added debug message considerations. I'll save
the final spell modifications when HotU comes out, but a small placeholder
is there in the meantime.
1.1 (7/24/03) - My implementation of subdual rules were a little messed up. I'm now
considering damage in excess of the limit of SetImmortal function. Also
added visual effect support for acid damaging weapons and tweaked with the
fire visual effects a little.
1.0 (7/21/03) - Initial Release
*/
// Included for the RemoveSpecificEffect function
#include "nw_i0_spells"
// It will be assumed that there is only one item used for a full-round troll burning
const int BURNING_ITEM_TYPE = BASE_ITEM_TORCH;
// The amount of hitpoints (subdual damage) regenerated per round
const int REGENERATION_VALUE = 5;
// The time it takes (in full rounds) to burn a Troll
const int BURNING_TIME = 1;
// Switching debug messages on/off
const int DEBUG_OUTPUT = 0;
void BurnEffect()
{
CreateObject(OBJECT_TYPE_PLACEABLE, "plc_weathmark", GetLocation(OBJECT_SELF), TRUE);
}
void main()
{
int nUser = GetUserDefinedEventNumber();
if(nUser == 1001) // OnHeartbeat Event
{
// the Troll's cumulative Fire and Acid damage
int nPermanentDamage = GetLocalInt(OBJECT_SELF, "nPermanentDamage");;
// the Troll's original maximum hitpoints
int nOriginalHPs = GetMaxHitPoints(OBJECT_SELF);
// the Troll's current hitpoints
int nCurrentHPs = GetCurrentHitPoints(OBJECT_SELF);
// the Troll's excess damage
int nExcessDamage = GetLocalInt(OBJECT_SELF, "nExcessDamage");
// the maximum number of HPs that the Troll can possibly regenerate due
// to permanent fire or acid damage
int nMaxHPsPossible = nOriginalHPs - nPermanentDamage;
// Debug Stuff
if(DEBUG_OUTPUT)
{
SendMessageToPC(GetFirstPC(),"Current HPs = " + IntToString(nCurrentHPs));
SendMessageToPC(GetFirstPC(),"Subdual Damage = " + IntToString(nMaxHPsPossible - nCurrentHPs));
SendMessageToPC(GetFirstPC(),"Excess Damage = " + IntToString(nExcessDamage));
SendMessageToPC(GetFirstPC(),"Effective HPs = " + IntToString(nCurrentHPs - nExcessDamage));
SendMessageToPC(GetFirstPC(),"Max Possible HPs = " + IntToString(nMaxHPsPossible) + "/" + IntToString(nOriginalHPs));
SendMessageToPC(GetFirstPC(),"-----");
}
// the Troll may only regenerate if its current HPs are less than its
// maximum possible HPs left after fire and acid damage
if(nExcessDamage >= REGENERATION_VALUE)
{
nExcessDamage -= REGENERATION_VALUE;
SetLocalInt(OBJECT_SELF, "nExcessDamage", nExcessDamage);
}
else if(nExcessDamage > 0)
{
// regeneration effect (the excess difference)
effect eHeal = EffectHeal(REGENERATION_VALUE - nExcessDamage);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, OBJECT_SELF);
nExcessDamage = 0;
SetLocalInt(OBJECT_SELF, "nExcessDamage", nExcessDamage);
}
else if (nCurrentHPs <= (nMaxHPsPossible - REGENERATION_VALUE))
{
// regeneration effect (5 HPs every round)
effect eHeal = EffectHeal(REGENERATION_VALUE);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, OBJECT_SELF);
}
else if (nCurrentHPs <= nMaxHPsPossible)
{
// regeneration effect (the remainder of hitpoints)
effect eHeal = EffectHeal(nMaxHPsPossible - nCurrentHPs);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, OBJECT_SELF);
}
// the Troll will now see if it can get up after it's latest beating
// provided that the Troll is grounded
effect eKnockdown = EffectKnockdown();
int bDowned = GetLocalInt(OBJECT_SELF,"bDowned");
// When current subdual damage is lower than current hit points
if (((nMaxHPsPossible - nCurrentHPs + nExcessDamage) < nMaxHPsPossible)&&(bDowned))
{
RemoveSpecificEffect(GetEffectType(eKnockdown),OBJECT_SELF);
SetLocalInt(OBJECT_SELF,"bDowned",0);
}
}
else if(nUser == 1006) // OnDamaged Event
{
object oDamager = GetLastDamager();
// the Troll's cumulative Permanent damage
int nPermanentDamage = GetLocalInt(OBJECT_SELF, "nPermanentDamage");
// the Troll's current Fire damage newly received
int nFireDamage = GetDamageDealtByType(DAMAGE_TYPE_FIRE);
// the Troll's current Acid damage newly received
int nAcidDamage = GetDamageDealtByType(DAMAGE_TYPE_ACID);
// the rest of the damage inflicted on the Troll
int nOtherDamage = GetTotalDamageDealt() - nFireDamage - nAcidDamage;
// the Troll's excess damage
int nExcessDamage = GetLocalInt(OBJECT_SELF, "nExcessDamage");
// the Troll's current hitpoints
int nCurrentHPs = GetCurrentHitPoints(OBJECT_SELF);
// the Troll's previous (newly inflicted HP value)
int nPrevHPs = GetLocalInt(OBJECT_SELF, "nPrevHPs");
// in case this is the first time the Troll is damaged
if(nPrevHPs == 0)
nPrevHPs = GetMaxHitPoints(OBJECT_SELF);
// if there is damage in excess of the previous hp value,
// excess damage will be updated
if(nOtherDamage > nPrevHPs)
nExcessDamage += (nOtherDamage - nPrevHPs);
// Now the excess damage and previous hit points will be updated
SetLocalInt(OBJECT_SELF,"nExcessDamage",nExcessDamage);
SetLocalInt(OBJECT_SELF,"nPrevHPs",nCurrentHPs);
// make sure Fire damage is not less than 0
if (nFireDamage < 0) nFireDamage = 0;
// make sure Acid damage is not less than 0
if (nAcidDamage < 0) nAcidDamage = 0;
// if the Troll suffered Fire damage
if (nFireDamage > 0)
{
// keep track of cumulative Fire damage
nPermanentDamage += nFireDamage;
// won't be counted as subdual damage
effect eHeal = EffectHeal(nFireDamage);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, OBJECT_SELF);
}
// if the Troll suffered Acid damage
if (nAcidDamage > 0)
{
// keep track of cumulative Acid damage
nPermanentDamage += nAcidDamage;
// won't be counted as subdual damage
effect eHeal = EffectHeal(nAcidDamage);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, OBJECT_SELF);
}
// Apply the Permanent Damage
SetLocalInt(OBJECT_SELF,"nPermanentDamage",nPermanentDamage);
// the Troll's original maximum hitpoints
int nOriginalHPs = GetMaxHitPoints(OBJECT_SELF);
// the maximum number of HPs that the Troll can possibly regenerate due
// to permanent fire or acid damage
int nMaxHPsPossible = nOriginalHPs - nPermanentDamage;
// This part will simulate an unconscience effect via knockdown
// If the troll's subdual damage exceeds it's current hitpoints ...
effect eKnockdown = EffectKnockdown();
int bDowned = GetLocalInt(OBJECT_SELF,"bDowned");
// When current subdual damage is higher than current hit points
if ((nMaxHPsPossible < (nMaxHPsPossible - nCurrentHPs + nExcessDamage))&&(!bDowned))
{
ApplyEffectToObject(DURATION_TYPE_PERMANENT,eKnockdown,OBJECT_SELF);
bDowned = 1;
SetLocalInt(OBJECT_SELF,"bDowned",bDowned);
}
// Now let's check and see if the troll has attained final death
// First we'll check whether someone is carefully burning the body
// (will take a full round to perform)
if ((GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oDamager)) == BURNING_ITEM_TYPE)
&& bDowned)
{
DelayCommand(0.2,SetCommandable(FALSE,oDamager));
AssignCommand(oDamager, ClearAllActions());
AssignCommand(oDamager, ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW, 1.0, RoundsToSeconds(BURNING_TIME)));
AssignCommand(oDamager, ActionDoCommand(SetCommandable(TRUE,oDamager)));
effect eFlame = EffectVisualEffect(VFX_DUR_INFERNO_CHEST); //VFX_IMP_FLAME_M
DelayCommand(2.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFlame, OBJECT_SELF, 5.0));
DelayCommand(1.5, BurnEffect());
effect eDeath = EffectDeath();
SetImmortal(OBJECT_SELF,FALSE);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, OBJECT_SELF);
}
// Now let's check and see if the damager is capable of performing a
// Coup de Grace attempt, and if so they will automatically perform one.
// The Troll's save will be Fortitude of DC 10 + Permanent Damage dealt.
else if (GetLocalInt(OBJECT_SELF,"bCoupdeGrace") && !GetLocalInt(OBJECT_SELF,"bGrenade")
&& (nFireDamage || nAcidDamage) && bDowned)
{
DeleteLocalInt(OBJECT_SELF,"bCoupdeGrace");
object eSelf = OBJECT_SELF;
location lSelf = GetLocation(eSelf);
DelayCommand(0.2,SetCommandable(FALSE,oDamager));
AssignCommand(oDamager, ClearAllActions());
AssignCommand(oDamager, ActionMoveToObject(eSelf,TRUE,0.1));
AssignCommand(oDamager, ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW, 1.0, 4.5));
//AssignCommand(oDamager, ActionDoCommand(SetCommandable(TRUE,oDamager)));
if(!FortitudeSave(OBJECT_SELF,10 + nFireDamage + nAcidDamage,SAVING_THROW_TYPE_ALL,oDamager))
{
effect eBlood = EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL);
AssignCommand(oDamager, ActionDoCommand(ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eBlood, lSelf)));
AssignCommand(oDamager, ActionDoCommand(SetCommandable(TRUE,oDamager)));
FloatingTextStringOnCreature("Coup de Grace (success)",oDamager);
// Fire will be favored over acid in the event they are equal
if(nFireDamage >= nAcidDamage)
{
effect eFlame = EffectVisualEffect(VFX_DUR_INFERNO_CHEST); //VFX_IMP_FLAME_M
DelayCommand(2.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFlame, OBJECT_SELF, 3.0));
DelayCommand(1.5, BurnEffect());
//object oFlame = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_flamemedium", GetLocation(OBJECT_SELF), TRUE);
//DestroyObject(oFlame, 3.25);
}
else
{
effect eAcid = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_ACID);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eAcid, OBJECT_SELF);
}
effect eDeath = EffectDeath();
SetImmortal(OBJECT_SELF,FALSE);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, OBJECT_SELF);
}
else
{
AssignCommand(oDamager, ActionDoCommand(SetCommandable(TRUE,oDamager)));
FloatingTextStringOnCreature("Coup de Grace (failure)",oDamager);
}
}
// ... or if it took damage the old fashioned way ...
// (i.e. all hitpoints exhausted due to fire and/or acid)
else if (nMaxHPsPossible <= 0)
{
SetImmortal(OBJECT_SELF,FALSE);
// Fire will be favored over acid in the event they are equal
if(nFireDamage >= nAcidDamage)
{
effect eFlame = EffectVisualEffect(VFX_DUR_INFERNO_CHEST); //VFX_IMP_FLAME_M
DelayCommand(2.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFlame, OBJECT_SELF, 3.0));
DelayCommand(1.5, BurnEffect());
//object oFlame = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_flamemedium", GetLocation(OBJECT_SELF), TRUE);
//DestroyObject(oFlame, 3.25);
}
else
{
effect eAcid = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_ACID);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eAcid, OBJECT_SELF);
}
effect eDeath = EffectDeath();
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, OBJECT_SELF);
}
}
else if(nUser == 1011) // OnSpellCastAt Event
{
int nSpellID = GetLastSpell();
object oCaster = GetLastSpellCaster();
// Will consider all instant death effects and grenade weapon burnings
if(GetLastSpellHarmful())
{
if(GetLocalInt(OBJECT_SELF,"bDowned") &&
((nSpellID == SPELL_GRENADE_ACID) || (nSpellID == SPELL_GRENADE_FIRE)))
{
SetLocalInt(OBJECT_SELF,"bGrenade",1);
object eSelf = OBJECT_SELF;
DelayCommand(0.2,SetCommandable(FALSE,oCaster));
AssignCommand(oCaster, ClearAllActions());
AssignCommand(oCaster, ActionMoveToObject(eSelf,TRUE,0.1));
AssignCommand(oCaster, ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW, 1.0, RoundsToSeconds(BURNING_TIME)));
AssignCommand(oCaster, ActionDoCommand(SetCommandable(TRUE,oCaster)));
if(nSpellID == SPELL_GRENADE_FIRE)
{
effect eFlame = EffectVisualEffect(VFX_DUR_INFERNO_CHEST); //VFX_IMP_FLAME_M
DelayCommand(2.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFlame, OBJECT_SELF, 3.0));
DelayCommand(1.5, BurnEffect());
//object oFlame = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_flamemedium", GetLocation(OBJECT_SELF), TRUE);
//DestroyObject(oFlame, 3.25);
}
else if(nSpellID == SPELL_GRENADE_ACID)
{
effect eAcid = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_ACID);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eAcid, OBJECT_SELF);
}
effect eDeath = EffectDeath();
SetImmortal(OBJECT_SELF,FALSE);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, OBJECT_SELF);
}
}
// Will consider all healing effects
else
{
}
}
else if(nUser == 1005) // OnPhysicalAttacked Event
{
if(GetLocalInt(OBJECT_SELF,"bDowned"))
{
SetLocalInt(OBJECT_SELF,"bCoupdeGrace",1);
}
}
}