296 lines
12 KiB
Plaintext
296 lines
12 KiB
Plaintext
//::///////////////////////////////////////////////
|
|
//:: Demilich constants, includes, and scripts for
|
|
//:: use with CEP adaptation of Demigog's demilich.
|
|
//::///////////////////////////////////////////////
|
|
|
|
//::--------------------------------------------------------------
|
|
//:: Begin Demilich Constants and functions added by Loki Hakanin
|
|
//::--------------------------------------------------------------
|
|
|
|
#include "zep_inc_scrptdlg"
|
|
|
|
//First is RESREF of demilich skull placable to be created
|
|
//when the demilich goes into resting mode.
|
|
const string ZEP_DEMI_SKULL_RESREF = "zep_demi_skull";
|
|
//Second is RESREF of matching dust plume.
|
|
const string ZEP_DEMI_DUST_RESREF = "zep_demi_dust";
|
|
|
|
//Also have matching TAGs for the above two:
|
|
const string ZEP_DEMI_SKULL_TAG = "zep_demi_skull";
|
|
const string ZEP_DEMI_DUST_TAG = "zep_demi_dust";
|
|
|
|
//Following sets the save DC of the demilich's attempt to
|
|
//slay arcane spellcaters and trap their souls.
|
|
const int DEMILICH_SOUL_EAT_SAVE_DC = 15;
|
|
|
|
//Following sets the maximum number of soulgem victims the
|
|
//demilich will take on at a time. Recommend this not be
|
|
//set higher than 8 or so, since LocalObject varibles are
|
|
//used to keep track of the victims.
|
|
const int DEMILICH_NUM_SOULGEMS = 8;
|
|
|
|
//Sets the minimum level an arcane caster must be in order
|
|
//for the demilich to attampt to pull out their soul upon
|
|
//a spell being cast at it.
|
|
const int DEMILICH_POWER_THRESHOLD = 15;
|
|
|
|
//Last we have the new tag which the demliich will use to
|
|
//differentiate a regenerating demilich from a normal one.
|
|
const string ZEP_DEMI_REGEN_SKULL = "zep_demi_skull_regen";
|
|
|
|
//Time, in seconds, for Demilich to regenerate from battle
|
|
//injuries. Defaults to 5 minutes. Note that PCs must use
|
|
//Holy Water on the demilich's bones in the span of that
|
|
//time in order to destroy it once and for all.
|
|
const int ZEP_DEMI_REGEN_TIME = 300;
|
|
|
|
//Messages Displayed over head of victims of soul-geming just
|
|
//before they are killed again by the Demilich's heartbeat
|
|
//script.
|
|
//Note that messages are concatenated with the name of the
|
|
//slain creature for a personalized effect.
|
|
|
|
string ZEP_DEMI_RESLAY_MSG = GetStringByStrRef(nZEPCantBeRaised ,GENDER_MALE);
|
|
//" jerks upright and spasms for a few moments before collapsing again.";
|
|
string ZEP_DEMI_RESLAY_MSG2 = GetStringByStrRef(nZEPNoRaiseExplan ,GENDER_MALE);
|
|
//"Until the demilich's captive souls are freed, its victims cannot be raised.";
|
|
|
|
//Next float variable defines the demilich's perception range,
|
|
//while resting or regenerating, in meters.
|
|
const float ZEP_DEMI_PERC_RANGE = 5.0;
|
|
|
|
//Following messages are used for demilich's respawn scripts.
|
|
//First displayed by a just-respawned demilich that was
|
|
//regenerating.
|
|
//Latter displayed by a disturbed demilich that is responding
|
|
//to intruders.
|
|
string ZEP_DEMI_REGEN_MSG = GetStringByStrRef(nZEPDemiRestored,GENDER_MALE);
|
|
//"At last, I am restored...";
|
|
string ZEP_DEMI_DIST_MSG = GetStringByStrRef(nZEPDemiDisturbed ,GENDER_MALE);
|
|
//"You disturb my work!";
|
|
|
|
//Following is message spoken in Demilich's OnSpellCastAt
|
|
//script if he's hit by a spell from a high level caster
|
|
string ZEP_DEMI_ONSPELL_MSG = GetStringByStrRef(nZEPDemiHavePower ,GENDER_MALE);
|
|
//"Yes, I sense you have power...your potential shall be mine!";
|
|
|
|
//Following defines RESREF and TAG of item that is needed
|
|
//to destroy a regenerating demilich.
|
|
const string ZEP_DEMI_DEST_RESREF = "zep_holy_water";
|
|
const string ZEP_DEMI_DEST_TAG = "zep_holy_water";
|
|
|
|
//Following is name of conversation to start if someone uses
|
|
//the demilich's bone pile. First is for a default bone pile
|
|
//and second is for a regenerating bone pile that has already
|
|
//been defeated once.
|
|
const string ZEP_DEMI_ONUSE_CONV = "zep_demi_bones";
|
|
const string ZEP_DEMI_ONUSE_REGEN = "zep_demi_regen_c";
|
|
|
|
//Following string is floating text displayed over the
|
|
//demilich upon it's final destruction.
|
|
string ZEP_DEMI_FINAL_DEST = GetStringByStrRef(nZEPDemiVictFree ,GENDER_MALE);
|
|
//"With the demilich destroyed, the souls of its victims are released to their bodies.";
|
|
|
|
//Following flag sets whether to automatically resurrect
|
|
//victims of soul-gem entrappment upon the demilich's
|
|
//destruction.
|
|
|
|
const int ZEP_DEMI_RESS_VICTIMS = FALSE;
|
|
|
|
//::-------------------------------------------------
|
|
//:: Begin Demilich Functions added by Loki Hakanin
|
|
//::-------------------------------------------------
|
|
|
|
//Function will spawn appropriate bones and dust plume
|
|
//for demilich. Will also copy over variables associated
|
|
//with any soultrapped victims, and change tag to flag bone
|
|
//pile as regenerating as necessary.
|
|
// Arguments:
|
|
// object oDemilichToReplace = The demilich that we're going
|
|
// to despawn
|
|
// int nWasDestroyed = Are we replacing the demilich with a
|
|
// rest-mode bone pile (FALSE), or a
|
|
// regenerating bone pile (TRUE)?
|
|
void ZEPDemilichSpawnBones(object oDemilichToReplace,int nWasDestroyed);
|
|
|
|
void ZEPDemilichSpawnBones(object oDemilichToReplace,int nWasDestroyed)
|
|
{
|
|
//So we'll first get the Demilich's location, and then create
|
|
//a pair of the usual demilich placables there. However,
|
|
//if the demilich is regenerating from wounds, we'll change
|
|
//the tag of the skull pile to the ZEP_DEMI_REGEN_SKULL
|
|
//constant value.
|
|
//The scripts on the skull will use this tag to determine
|
|
//if it is a fresh (i.e. undestroyed) demilich, or a crippled,
|
|
//regenerating one.
|
|
//We will also copy over the NumSouls local variable and the
|
|
//set of LocalObjects pointing to the Demlich's set of soul-
|
|
//gem'em victims, if any.
|
|
location lDestroyedLocation = GetLocation(oDemilichToReplace);
|
|
object oSkullPile;
|
|
|
|
//If Demliich in question was destroyed, we create the skull
|
|
//pile with the variant tag defined above, allowing the
|
|
//skull pile to differentiate itself from a "healthy"
|
|
//demilich. This saves us a local variable.
|
|
if (nWasDestroyed)
|
|
oSkullPile=CreateObject(OBJECT_TYPE_PLACEABLE,ZEP_DEMI_SKULL_RESREF,lDestroyedLocation,FALSE,ZEP_DEMI_REGEN_SKULL);
|
|
else oSkullPile=CreateObject(OBJECT_TYPE_PLACEABLE,ZEP_DEMI_SKULL_RESREF,lDestroyedLocation,FALSE);
|
|
|
|
object oDustPlume=CreateObject(OBJECT_TYPE_PLACEABLE,ZEP_DEMI_DUST_RESREF,lDestroyedLocation,FALSE);
|
|
|
|
//Now for the copying. Loop through all GemVictim objects,
|
|
//based on NumSouls. Remember that GemVictim indexes start
|
|
//from 0 (like an array in many programming languages),
|
|
//though the Numsouls variable is a positive int.
|
|
int nCounter=0;
|
|
int nNumSouls=GetLocalInt(oDemilichToReplace,"NumSouls");
|
|
//SendMessageToAllDMs(("NumSouls(SpawnBones):"+(IntToString(nNumSouls))));
|
|
//Set NumSouls counter on skull pile
|
|
SetLocalInt(oSkullPile,"NumSouls",nNumSouls);
|
|
string sVictimCounterName="GemVictim";
|
|
object oVictimCounter;
|
|
|
|
for (nCounter=0; nCounter < nNumSouls; nCounter++)
|
|
{
|
|
//Get object variable name, then set a variable on the
|
|
//skull pile with the same name, pointing to same
|
|
//object.
|
|
//Note we don't delete the local objects of the Demlich
|
|
//here as they will be cleaned up upon the final destruction
|
|
//of the demilich, which always follows this function's call.
|
|
sVictimCounterName=("GemVictim"+(IntToString(nCounter)));
|
|
oVictimCounter=GetLocalObject(oDemilichToReplace,sVictimCounterName);
|
|
// if (oVictimCounter==OBJECT_INVALID)
|
|
// SendMessageToAllDMs(("VictimCount("+(IntToString(nCounter))+") invalid."));
|
|
SetLocalObject(oSkullPile,sVictimCounterName,oVictimCounter);
|
|
}
|
|
|
|
}
|
|
|
|
//Function will play some dazed effects for a second,
|
|
//display some text explaining that the resurrection spell
|
|
//fails due to the demilich having oCreatureToSlay's soul,
|
|
//and then re-kills them.
|
|
void ZEPDemilichReSlay(object oCreatureToSlay);
|
|
|
|
void ZEPDemilichReSlay(object oCreatureToSlay)
|
|
{
|
|
//We'll get the name of our victim and concatenate it with
|
|
//our first message. This personalizes things a bit.
|
|
string sVictimName=GetName(oCreatureToSlay);
|
|
|
|
AssignCommand(oCreatureToSlay,ClearAllActions());
|
|
//Play spasm animation:
|
|
AssignCommand(oCreatureToSlay,ActionPlayAnimation(ANIMATION_FIREFORGET_SPASM));
|
|
//At the same time, display a descriptive string about the
|
|
//creature's state.
|
|
FloatingTextStringOnCreature((sVictimName+ZEP_DEMI_RESLAY_MSG),oCreatureToSlay,FALSE);
|
|
//Kill the creature again.
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectDeath(FALSE),oCreatureToSlay);
|
|
//Further descriptive string.
|
|
DelayCommand(6.0,FloatingTextStringOnCreature(ZEP_DEMI_RESLAY_MSG2,oCreatureToSlay,FALSE));
|
|
}
|
|
|
|
//Function will kill all soulgem victims of the Demilich again
|
|
//if they have been raised, playing some appriopriate animations
|
|
//and VFX, and displaying some text.
|
|
//Essentially, we'll check against the NumSouls variable
|
|
//on our demilich, and then cycle through all GemVictim
|
|
//local objects, running a function to display effects and
|
|
//kill them as necessary.
|
|
void ZEPDemilichReSlaySoulGemVictims(object oDemilich);
|
|
|
|
void ZEPDemilichReSlaySoulGemVictims(object oDemilich)
|
|
{
|
|
int nCounter=0; //Counter for variable names
|
|
int nNumSouls=GetLocalInt(oDemilich,"NumSouls"); //Number of consumed souls
|
|
string sVictimCounterName; //Used with nCounter to create proper local variable name
|
|
object oVictimCounter; //Counter for actual objects pointed to by demilich's victim list.
|
|
|
|
//Don't forget, Counter (i.e., the "GemVictim" variable
|
|
//name index) starts from 0, like an array index.
|
|
//But nNumSouls is a positive int, so this for loop should be
|
|
//logically consistent.
|
|
for (nCounter=0; nCounter < nNumSouls; nCounter++)
|
|
{
|
|
sVictimCounterName=("GemVictim"+(IntToString(nCounter)));
|
|
oVictimCounter=GetLocalObject(oDemilich,sVictimCounterName);
|
|
//If the victim has more than 0 HPs... (i.e. is alive)
|
|
//we'll kill them again, with explanatory text.
|
|
if (GetCurrentHitPoints(oVictimCounter) > 0)
|
|
ZEPDemilichReSlay(oVictimCounter);
|
|
}
|
|
}
|
|
|
|
|
|
//Following function spawns a demilich from the bones
|
|
//that it left behind, either when resting or regenerating.
|
|
//Note that the soulgem victim list is copied over, and
|
|
//therefore preserved.
|
|
//Arguments:
|
|
// oSourceBones- the bone placable that relevant variables
|
|
// will be copied over from.
|
|
// nDestroyBones- destroy object oSourceBones upon completion
|
|
// of the function...TRUE= destroy it, FALSE = do nothing.
|
|
object ZEPDemilichFromBones(object oSourceBones, int nDestroyBones);
|
|
|
|
object ZEPDemilichFromBones(object oSourceBones, int nDestroyBones)
|
|
{
|
|
//First we create the Demilich.
|
|
object oReturnDemilich=CreateObject(OBJECT_TYPE_CREATURE,"zep_demi_lich",GetLocation(oSourceBones));
|
|
//Now we'll use a loop to copy over the soulgem victim list.
|
|
int nNumSouls = GetLocalInt(oSourceBones,"NumSouls");
|
|
int nCounter=0;
|
|
string sVarNameCounter;
|
|
object oVictimCounter;
|
|
|
|
//For loop will use int counter, concatenated with "GemVictim"
|
|
//to generate correct variable names. Will then retrieve
|
|
//these variables from the oSourceBones object and copy
|
|
//them over to our new Demilich.
|
|
for (nCounter=0; nCounter < nNumSouls; nCounter++)
|
|
{
|
|
sVarNameCounter = ("GemVictim"+IntToString(nCounter));
|
|
oVictimCounter = GetLocalObject(oSourceBones,sVarNameCounter);
|
|
SetLocalObject(oReturnDemilich,sVarNameCounter,oVictimCounter);
|
|
}
|
|
//Now we set the "NumSouls" counter on our newsly-spawned
|
|
//Demilich.
|
|
SetLocalInt(oReturnDemilich,"NumSouls",nNumSouls);
|
|
|
|
//Now, we're going to compare our bone pile's resref against
|
|
//the regenerating resref. If it's the same, we display the
|
|
//regeneration message (defined above).
|
|
//Else we play the default "disturbed" message, since this
|
|
//function is called in two ways...a regenerating demilich
|
|
//or a disturbed Demilich.
|
|
string sBonePileTag=GetTag(oSourceBones);
|
|
if (sBonePileTag == ZEP_DEMI_REGEN_SKULL)
|
|
{
|
|
AssignCommand(oReturnDemilich,ActionSpeakString(ZEP_DEMI_REGEN_MSG,TALKVOLUME_SHOUT));
|
|
FloatingTextStringOnCreature(ZEP_DEMI_REGEN_MSG,oReturnDemilich);
|
|
}
|
|
else
|
|
{
|
|
AssignCommand(oReturnDemilich,ActionSpeakString(ZEP_DEMI_DIST_MSG,TALKVOLUME_SHOUT));
|
|
FloatingTextStringOnCreature(ZEP_DEMI_DIST_MSG,oReturnDemilich);
|
|
}
|
|
|
|
//Last, if the user has specified that we should destroy the
|
|
//bones and dust plume, we do so.
|
|
if (nDestroyBones == TRUE)
|
|
{
|
|
object oDustPlume=GetNearestObjectByTag(ZEP_DEMI_DUST_TAG);
|
|
// if (oDustPlume==OBJECT_INVALID)
|
|
// SendMessageToAllDMs("Bad Dust plume!");
|
|
DestroyObject(oDustPlume);
|
|
DestroyObject(oSourceBones);
|
|
}
|
|
return oReturnDemilich;
|
|
}
|
|
|
|
//:: ---------------------------------------
|
|
//:: End Demilich functions and constants
|
|
//:: ---------------------------------------
|