PDA

View Full Version : Heartbeat script troubles


Pavlos
05-05-2006, 03:58 PM
Now... this is going to be a long post so please bear with me! Also, I apologise for posting so many questions in a row. It is not something I would normally do, but I want to get this done!

I am working on improving the major fights of the endgame and everything is going brilliantly for the Nihilus fight aside from the drain feature. I wanted Nihilus to drain x amount of VP from the Exile and his Party on the Ravager... I accounted for any cheaters by not simply grabbing hold of the PC, Visas, and Mandalore (Though it may be that I have to do that to get this to work!)

Now these come from my include file:

//:://////////////////////////////////////////////
//:: IS FORCE USER (w00t!)
//:://////////////////////////////////////////////
//::int GetIsForceSensitive(object oToCheck);
//:://////////////////////////////////////////////
//:: LAST EDIT: 05/05/06
//:://////////////////////////////////////////////
/*
{Snipped my novel}
*/
//:://////////////////////////////////////////////

int GetIsForceSensitive(object oToCheck)

{

int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == 3) //Jedi Guardian
||(nClass1 == 4) //Jedi Consular
||(nClass1 == 5) //Jedi Sentinel
||(nClass1 == 11) //Jedi Weapon Master
||(nClass1 == 12) //Jedi Master
||(nClass1 == 13) //Jedi Watchman
||(nClass1 == 14) //Sith Marauder
||(nClass1 == 15) //Sith Lord
||(nClass1 == 16) //Sith Assassin
||(nClass2 == 3)
||(nClass2 == 4)
||(nClass2 == 5)
||(nClass2 == 11)
||(nClass2 == 12)
||(nClass2 == 13)
||(nClass2 == 14)
||(nClass2 == 14)
||(nClass2 == 15)
||(nClass2 == 16))
{
return TRUE;
}
else {
return FALSE;
}
} I am aware that the generic include has a function like this (now)... but I wasn't at the time of making this :).

//:://////////////////////////////////////////////
//:: IS COMBAT FORCE USER
//:://////////////////////////////////////////////
//::int GetIsCombatBased(object oToCheck);
//:://////////////////////////////////////////////
//:: LAST EDIT: 05/05/06
//:://////////////////////////////////////////////
/*
Checks if the object oToCheck is a combat based class
i.e. Jedi Guardian
Jedi Weapon Master
Sith Marauder

Returns TRUE if they are!
FOR USE ONLY WITH FORCE USERS!
*/
//:://////////////////////////////////////////////

int GetIsCombatBased(object oToCheck)

{

int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == 3)
||(nClass1 == 11)
||(nClass1 == 14)
||(nClass2 == 3)
||(nClass2 == 11)
||(nClass2 == 14))
{
return TRUE;
}
else {
return FALSE;
}

}

//:://////////////////////////////////////////////
//:: IS SPELL FORCE USER
//:://////////////////////////////////////////////
//::int GetIsSpellBased(object oToCheck);
//:://////////////////////////////////////////////
//:: LAST EDIT: 05/05/06
//:://////////////////////////////////////////////
/*
Checks if the object oToCheck is a spell based class
i.e. Jedi Consular
Jedi Master
Sith Lord

Returns TRUE if they are!
FOR USE ONLY WITH FORCE USERS!
*/
//:://////////////////////////////////////////////

int GetIsSpellBased(object oToCheck)

{

int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == 4)
||(nClass1 == 12)
||(nClass1 == 15)
||(nClass2 == 4)
||(nClass2 == 12)
||(nClass2 == 15))
{
return TRUE;
}
else {
return FALSE;
}

}

//:://////////////////////////////////////////////
//:: IS SKILL FORCE USER
//:://////////////////////////////////////////////
//::int GetIsSkillBased(object oToCheck);
//:://////////////////////////////////////////////
//:: LAST EDIT: 05/05/06
//:://////////////////////////////////////////////
/*
Checks if the object oToCheck is a spell based class
i.e. Jedi Sentinel
Jedi Watchman
Sith Assassin

Returns TRUE if they are!
FOR USE ONLY WITH FORCE USERS!
*/
//:://////////////////////////////////////////////

int GetIsSkillBased(object oToCheck)

{

int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == 5)
||(nClass1 == 13)
||(nClass1 == 16)
||(nClass2 == 5)
||(nClass2 == 13)
||(nClass2 == 16))
{
return TRUE;
}
else {
return FALSE;
}

}

Having done these basic scripts I went on to create some more functions relevant to the mod (Some of which have changed recently due to my testing and I accidentally saved over my backup file :doh:. So I cannot account for any mistakes I may have made recently - But the original script was syntax error free from what I could see and should have worked. These are pretty much the same though, maybe one or two errors).

//:://////////////////////////////////////////////
//:: DAMAGE TABLES
//:://////////////////////////////////////////////
//:: int GetNihilusDrainValue(object oToCheck);
//:://////////////////////////////////////////////
//:: LAST EDIT: 05/05/06
//:://////////////////////////////////////////////
/*
Writing out damages and so forth was getting
somewhat annoying. So I created a lovely generic
variable table, or something similar to that end.

I think it is pretty self explanatory... so I won't
write a novel on this one.

DISTANCE = (2 m) (10m) (Module Effect)
CLASS TYPE \/
Non-Force 6VP/s 4VP/s 2VP/s
Combat Force 10VP/s 6VP/s 2VP/s
Skill Force 8VP/s 4VP/s 0VP/s
Spells Force 12VP/s 8VP/s 4VP/s
//:://////////////////////////////////////////////

int GetNihilusDrainValue(object oToCheck)
{


object oNih = OBJECT_SELF;
int nFar = FloatToInt(GetDistanceBetween(oNih, oToCheck));
int nHurts = 0;

if (nFar <= 2)
{
if (!GetIsForceSensitive(oToCheck))
{
int nHurts = 6;
}
if (GetIsCombatBased(oToCheck))
{
int nHurts = 10;
}
if (GetIsSkillBased(oToCheck))
{
int nHurts = 8;
}
if (GetIsSpellBased(oToCheck))
{
int nHurts = 12;
}
}
if (nFar > 2 && nFar <= 10)
{
if (!GetIsForceSensitive(oToCheck))
{
int nHurts = 4;
}
if (GetIsCombatBased(oToCheck))
{
int nHurts = 6;
}
if (GetIsSkillBased(oToCheck))
{
int nHurts = 4;
}
if (GetIsSpellBased(oToCheck))
{
int nHurts = 8;
}
}
if (nFar > 10)
{
if (!GetIsForceSensitive(oToCheck))
{
int nHurts = 2;
}
if (GetIsCombatBased(oToCheck))
{
int nHurts = 2;
}
if (GetIsSkillBased(oToCheck))
{
int nHurts = 0;
}
if (GetIsSpellBased(oToCheck))
{
int nHurts = 4;
}
}

return nHurts;

}

//:://////////////////////////////////////////////
//:: NIHILUS DRAINING ABILITY
//:://////////////////////////////////////////////
//:: void NihilusDrains();
//:://////////////////////////////////////////////
//:: LAST EDIT: 05/05/06
//:://////////////////////////////////////////////
/*
{Snipped an epic adventure}
*/
//:://////////////////////////////////////////////
void NihilusDrains()
{
object oVict = GetFirstObjectInArea(GetModule(), OBJECT_TYPE_CREATURE);
object oNih = OBJECT_SELF;

while (GetIsObjectValid(oVict))
{
string sName = GetTag(oVict);
if ((!GetIsEnemy(oVict, GetFirstPC())) && (sName != "DarthNihilus") && (sName != "g_sithcomm01")) // Nihilus damn well is neutral until the fight!
{ // We thus have to do this check.

int nValue = GetNihilusDrainValue(oVict);

effect eOuch = EffectDamage(nValue, DAMAGE_TYPE_DARK_SIDE);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eOuch, oVict);
effect eHeal = EffectHeal(nValue/2);
if ((nValue/2) > 0)
{
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oNih);
}
}
}
oVict = GetNextObjectInArea(GetModule(), OBJECT_TYPE_CREATURE);
}

Everything looking Ok so far?

Then I wrote the heartbeat for Nihilus.


/*

--------------------------------------------------
1) If the player gets too close then you lose VP!
SHOCK HORROR! There are three levels to this loss.

DISTANCE = (2 m) (10m) (Module Effect)
CLASS TYPE \/
Non-Force 6VP/s 4VP/s 2VP/s
Combat Force 10VP/s 6VP/s 2VP/s
Skill Force 8VP/s 4VP/s 0VP/s
Spells Force 12VP/s 8VP/s 4VP/s

Nihilus then receives half of these values.
This will not happen while in a dialogue.
That would be mean.

N.B. To stoffe: I did not actually realise that
you also had something similar to this implemented
in your Combat Arena Mod. I apologise if this seems
like a copied idea... I can assure you that it
is not!
--------------------------------------------------

--------------------------------------------------
2) Nihilus will now cross his arms and so forth
every so often so he doesn't look so idiotic just
standing there...
--------------------------------------------------
*/

#include "pav_gen_inc"
#include "k_inc_switch"

void main()
{
object oNih = OBJECT_SELF;

if (!GetIsInConversation(GetFirstPC())) //The PC won't see Nihilus if he is in a conversation and not speaking to him. This just keeps the script a bit smaller.
{
int nRoll1 = d4();
int nRoll2 = d2();

if (!GetIsInCombat(oNih))
{
if ((nRoll1 >=1) && (nRoll1 <= 3))
{
AssignCommand(oNih, ActionPlayAnimation(10033, 1.0, -1.0));
}
if (nRoll1 == 4)
{
if (nRoll2 == 1)
{
AssignCommand(oNih, ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN _LEFT));
}
if (nRoll2 == 2)
{
AssignCommand(oNih, ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN _RIGHT));
}
}
}
NihilusDrains();
}

if (GetIsInConversation(GetFirstPC()))
{
return;
}
ExecuteScript("k_ai_master", OBJECT_SELF, KOTOR_DEFAULT_EVENT_ON_HEARTBEAT);
}

Debugging this monster doesn't seem to help me find out the problem and I am pulling my hair out trying to understand where I have gone wrong! It is probably one little tiny thing I am missing...

stoffe
05-05-2006, 04:12 PM
object oVict = GetFirstObjectInArea(GetModule(), OBJECT_TYPE_CREATURE);
(snip)
oVict = GetNextObjectInArea(GetModule(), OBJECT_TYPE_CREATURE);



Though I haven't actually tested it, logically this shouldn't work. The module is not an Area. Use GetArea(OBJECT_SELF) instead of GetModule() and perhaps it may work better.

Unless you want the party to be affected anywhere in the Bridge level it may also be better to use GetFirstObjectInShape() instead of GetFirstObjectInArea(), that way you can set the max area of effect more directly. But that's not a bug. :)

Pavlos
05-05-2006, 05:51 PM
The GetArea(OBJECT_SELF) didn't seem to solve the problem I had. I considered using GetFirstObjectInShape(), however I wasn't entirely sure how to get the tiered draining effect working with it. I may have to just have a script for draining Visas, the Exile, and Mandalore... which would mean cheaters might not be able to use it. Then again, the actual plot wouldn't work with that.

stoffe
05-05-2006, 06:46 PM
The GetArea(OBJECT_SELF) didn't seem to solve the problem I had. I considered using GetFirstObjectInShape(), however I wasn't entirely sure how to get the tiered draining effect working with it. I may have to just have a script for draining Visas, the Exile, and Mandalore... which would mean cheaters might not be able to use it. Then again, the actual plot wouldn't work with that.

What, exactly is it that isn't working? Is nothing happening at all? Is Nihilus added idle-animations playing but the drain power not activating?

As for Get*ObjectInShape(), you'd use it pretty much like you did the Get*ObjectInArea() functions, just with a limited max radius, preventing the loop from having to iterate through all of Nihilus goons to find your party members.

Hard to say at a quick glance what else could be wrong without knowing how you've set things up. Is the above script attached to the OnHeartbeat slot event slot on Nihilus character? If you modified Nihilus character template, make sure you use a savegame from before first entering the area when you test things, or the changes will not take effect and your custom script won't be attached to the in-game Nihilus.

I suggest adding debug printouts like:
SendMessageToPC(GetPartyLeader(), "I got this far...");
...at keys areas in your scripts and then check the Feedback screen in-game to see how far you get.

Pavlos
05-05-2006, 06:59 PM
Thanks for a reply, stoffe.

Basically (I can't believe I missed this out :p) the drain power isn't working. However, given that using anyone other than Mandalore and Visas in your party on the Ravager breaks the game... I have opted for a much simpler route :).

I just threw this togther (Haven't tested yet - but it should work!)

void NihilusDrains()
{

object oMand = GetObjectByTag("Mand");
object oVisas = GetObjectByTag("VisasMarr");
object oPC = GetFirstPC();

object oNih = OBJECT_SELF;

int nPainV = GetNihilusDrainValue(oVisas); //Visas pain value
int nPainM = GetNihilusDrainValue(oMand); //Mandalore pain value
int nPainP = GetNihilusDrainValue(oPC); //The Exile's pain value

effect eOuch1 = EffectDamage(nPainV, DAMAGE_TYPE_DARK_SIDE);
effect eOuch2 = EffectDamage(nPainM, DAMAGE_TYPE_DARK_SIDE);
effect eOuch3 = EffectDamage(nPainP, DAMAGE_TYPE_DARK_SIDE);

ApplyEffectToObject(DURATION_TYPE_INSTANT, eOuch1, oVisas);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eOuch2, oMand);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eOuch3, oPC);

int nHeal = ((nPainV/2)+(nPainM/2)+(nPainP/2));

if (nHeal > 0)
{
effect eHeal = EffectHeal(nHeal);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oNih);
}

}

This simplifies things somewhat :) so I happen to be quite fond of it. In the future I may go back and try and solve the problem, but right now there is no real point in trying to make the script universal.

I can see the advantages of using a GetObjectInShape() function now... it requires less writing (For one thing). But I wont be using that due to my current use of GetObjectByTag(). Now all I have to do is balance this thing (Sighs).

Regarding the debugging printouts: that is exactly what I do, place them at key points in my scripts :). I just remove them for the finished (Or publicly displayed) copy as it makes things a little tidier!

Thank you for your help anyway.

stoffe
05-05-2006, 07:10 PM
I just threw this togther (Haven't tested yet - but it should work!)
(code snipped)
This simplifies things somewhat :) so I happen to be quite fond of it. In the future I may go back and try and solve the problem, but right now there is no real point in trying to make the script universal.


If you only want to affect the members of the party, no matter who they are, you could just do like this. It's even shorter. :)


void NihilusDrains() {
int i;
int nHeal = 0;

for (i = 0; i < GetPartyMemberCount(); i++) {
object oParty = GetPartyMemberByIndex(i);
int nPain = GetNihilusDrainValue(oParty);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nPain, DAMAGE_TYPE_DARK_SIDE), oParty);
nHeal += nPain / 2;
}

if (nHeal > 0)
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nHeal), OBJECT_SELF);
}

Pavlos
05-06-2006, 03:32 AM
AGH! *Slaps forehead* I forgot about the party member index function! That makes it far simpler. There always seems to be a better way to do it than the way I have done :(. Thank you.

Edit: And yet it still doesn't work! AGH!

Edit 2: Now this is verrry odd. Nihilus doesn't seem to even be giving off a heartbeat. In a mad quest to understand what on Earth was wrong I placed your "SendMessageToPC(GetPartyLeader(), "I got this far...");" in a heartbeat script for Nihilus... and nothing happens. This is getting slightly confusing. The animations play, he has no heartbeat, and the players wont get drained of their VP! AGH!

stoffe
05-06-2006, 05:35 AM
Edit: And yet it still doesn't work! AGH!

As far as I can see you have a bug in the GetNihilusDrainValue() function that makes it always return 0 no matter what. Perhaps that's what causing your trouble?

Essentially you are re-declaring the nHurts variable in the local scope each time you assign a value to it. The local variable will override the function-wide one, and will disappear as soon as its if block is exited, and only the initial assignment of 0 to nHurts would be returned.

Also, it's unnessecary to convert the distance to an integer before checking it. It's probably better to just use a float variable instead.

Try this fixed version of your script and it'll probably work:


int GetIsCombatBased(object oToCheck) {
int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == CLASS_TYPE_JEDIGUARDIAN)
||(nClass1 == CLASS_TYPE_JEDIWEAPONMASTER)
||(nClass1 == CLASS_TYPE_SITHMARAUDER)
||(nClass2 == CLASS_TYPE_JEDIGUARDIAN)
||(nClass2 == CLASS_TYPE_JEDIWEAPONMASTER)
||(nClass2 == CLASS_TYPE_SITHMARAUDER))
{
return TRUE;
}
return FALSE;
}

int GetIsSpellBased(object oToCheck) {
int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == CLASS_TYPE_JEDICONSULAR)
||(nClass1 == CLASS_TYPE_JEDIMASTER)
||(nClass1 == CLASS_TYPE_SITHLORD)
||(nClass2 == CLASS_TYPE_JEDICONSULAR)
||(nClass2 == CLASS_TYPE_JEDIMASTER)
||(nClass2 == CLASS_TYPE_SITHLORD))
{
return TRUE;
}
return FALSE;
}


int GetIsSkillBased(object oToCheck) {
int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == CLASS_TYPE_JEDISENTINEL)
||(nClass1 == CLASS_TYPE_JEDIWATCHMAN)
||(nClass1 == CLASS_TYPE_SITHASSASSIN)
||(nClass2 == CLASS_TYPE_JEDISENTINEL)
||(nClass2 == CLASS_TYPE_JEDIWATCHMAN)
||(nClass2 == CLASS_TYPE_SITHASSASSIN))
{
return TRUE;
}
return FALSE;
}

int GetNihilusDrainValue(object oToCheck) {
float fFar = GetDistanceToObject(oToCheck);
int nHurts = 0;

if (fFar <= 2.0) {
if (!GetIsForceSensitive(oToCheck))
nHurts = 6;
else if (GetIsCombatBased(oToCheck))
nHurts = 10;
else if (GetIsSkillBased(oToCheck))
nHurts = 8;
else if (GetIsSpellBased(oToCheck))
nHurts = 12;
}
else if ((fFar > 2.0) && (fFar <= 10.0)) {
if (!GetIsForceSensitive(oToCheck))
nHurts = 4;
if (GetIsCombatBased(oToCheck))
nHurts = 6;
if (GetIsSkillBased(oToCheck))
nHurts = 4;
if (GetIsSpellBased(oToCheck))
nHurts = 8;
}
else {
if (!GetIsForceSensitive(oToCheck))
nHurts = 2;
if (GetIsCombatBased(oToCheck))
nHurts = 2;
if (GetIsSkillBased(oToCheck))
nHurts = 0;
if (GetIsSpellBased(oToCheck))
nHurts = 4;
}

return nHurts;
}

Pavlos
05-06-2006, 05:41 AM
The FloatToInt was a relic of a previous design, I overlooked it. I will try your amendments.

Edit: Although it may have been a bad bug it doesn't appear to have been the one stopping my progress... I just can't see the problem!

stoffe
05-06-2006, 06:21 AM
Edit: Although it may have been a bad bug it doesn't appear to have been the one stopping my progress... I just can't see the problem!

The problem? As in, it still does not work even with the bugfixed variant of your script that I posted?

Are you sure the NihilusDrains() function is run at all? Insert...

SendMessageToPC(GetPartyLeader(), "!!! NIHILUSDRAINS() ENTERED !!!");

...at the very top of the NihilusDrains() function and see if that text appears in the Feedback log in-game when you try it. If not there is a problem with your heartbeat script instead.

Pavlos
05-06-2006, 06:37 AM
Ok, well it doesn't actually print the message on the Feedback screen. So it must be something wrong with the heartbeat. *Groan* What could it be? I'm going to try and look now.

stoffe
05-06-2006, 06:45 AM
Ok, well it doesn't actually print the message on the Feedback screen. So it must be something wrong with the heartbeat. *Groan* What could it be? I'm going to try and look now.


To eliminate the possibility of using the wrong include files when you compile things, put the below script directly in Nihilus heartbeat script and see if that works.

I removed the call to the standard heartbeat in the script below, since in Nihilus case it pretty much just tries to run idle animations, which you already do manually in yours. Since I don't remember if Nihilus has a user-defined heartbeat I added that call instead to be safe. I've also done some minor optimizations here and there, but those shouldn't have cause any trouble, just been some unnessesary overhead (like doing the same class checks twice when there was no need to do so).


void NihilusDrains();
int GetNihilusDrainValue(object oToCheck);
int GetIsSkillBased(object oToCheck);
int GetIsSpellBased(object oToCheck);
int GetIsCombatBased(object oToCheck);


// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void main() {
//The PC won't see Nihilus if he is in a conversation and not speaking to him.
// This just keeps the script a bit smaller.
if (!GetIsInConversation(GetFirstPC())) {
int nRoll1 = d4();
int nRoll2 = d2();

if (!GetIsInCombat()) {
if ((nRoll1 >=1) && (nRoll1 <= 3)) {
ActionPlayAnimation(10033, 1.0, 6.0);
}
else if (nRoll1 == 4) {
if (nRoll2 == 1) {
ActionPlayAnimation( ANIMATION_FIREFORGET_HEAD_TURN_LEFT );
}
else {
ActionPlayAnimation( ANIMATION_FIREFORGET_HEAD_TURN_RIGHT );
}
}
}
else {
NihilusDrains();
}
}

// ST: Remove call to the standard heartbeat, since all it does is run
// idle animations in this case, which you already do above manually.
// Just trigger user-defined heartbeat, if any is defined.
if (GetLocalBoolean(OBJECT_SELF, 28))
SignalEvent(OBJECT_SELF, EventUserDefined(1001));
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NihilusDrains() {
int i;
int nHeal = 0;

for (i = 0; i < GetPartyMemberCount(); i++) {
object oParty = GetPartyMemberByIndex(i);
int nPain = GetNihilusDrainValue(oParty);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nPain, DAMAGE_TYPE_DARK_SIDE), oParty);
nHeal += nPain / 2;
}

if (nHeal > 0)
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nHeal), OBJECT_SELF);
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int GetIsCombatBased(object oToCheck) {
int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == CLASS_TYPE_JEDIGUARDIAN)
||(nClass1 == CLASS_TYPE_JEDIWEAPONMASTER)
||(nClass1 == CLASS_TYPE_SITHMARAUDER)
||(nClass2 == CLASS_TYPE_JEDIGUARDIAN)
||(nClass2 == CLASS_TYPE_JEDIWEAPONMASTER)
||(nClass2 == CLASS_TYPE_SITHMARAUDER))
{
return TRUE;
}
return FALSE;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int GetIsSpellBased(object oToCheck) {
int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == CLASS_TYPE_JEDICONSULAR)
||(nClass1 == CLASS_TYPE_JEDIMASTER)
||(nClass1 == CLASS_TYPE_SITHLORD)
||(nClass2 == CLASS_TYPE_JEDICONSULAR)
||(nClass2 == CLASS_TYPE_JEDIMASTER)
||(nClass2 == CLASS_TYPE_SITHLORD))
{
return TRUE;
}
return FALSE;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int GetIsSkillBased(object oToCheck) {
int nClass1 = GetClassByPosition(1, oToCheck);
int nClass2 = GetClassByPosition(2, oToCheck);

if ((nClass1 == CLASS_TYPE_JEDISENTINEL)
||(nClass1 == CLASS_TYPE_JEDIWATCHMAN)
||(nClass1 == CLASS_TYPE_SITHASSASSIN)
||(nClass2 == CLASS_TYPE_JEDISENTINEL)
||(nClass2 == CLASS_TYPE_JEDIWATCHMAN)
||(nClass2 == CLASS_TYPE_SITHASSASSIN))
{
return TRUE;
}
return FALSE;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int GetNihilusDrainValue(object oToCheck) {
float fFar = GetDistanceToObject(oToCheck);
int nHurts = 0;

if (fFar <= 2.0) {
if (GetIsCombatBased(oToCheck))
nHurts = 10;
else if (GetIsSkillBased(oToCheck))
nHurts = 8;
else if (GetIsSpellBased(oToCheck))
nHurts = 12;
else
nHurts = 6;
}
else if ((fFar > 2.0) && (fFar <= 10.0)) {
if (GetIsCombatBased(oToCheck))
nHurts = 6;
else if (GetIsSkillBased(oToCheck))
nHurts = 4;
else if (GetIsSpellBased(oToCheck))
nHurts = 8;
else
nHurts = 4;
}
else {
if (GetIsCombatBased(oToCheck))
nHurts = 2;
else if (GetIsSkillBased(oToCheck))
nHurts = 0;
else if (GetIsSpellBased(oToCheck))
nHurts = 4;
else
nHurts = 2;
}

return nHurts;
}



If this does nothing, then most likely your script is not attached to the in-game Nihilus creature. The only two causes for this I can think of would be if you have edited the wrong UTC template, or if you use a save game where Nihilus has already been spawned (I.e. the module he is in has already been loaded) while testing.

Pavlos
05-06-2006, 06:57 AM
Well... that didn't do anything either. If I can't get this sorted I will settle for a simpler version of it (Where Nihilus just drains everything within 10 metres by say... 2 VP). Where did I check for the class twice, by the way? These scripts have been messed about a bit because of my refusal to perform a complete rewrite and edit them instead (An example being the FloatToInt() usage).

Edit: Ah yes, I forgot the most important thing... again. I am using the proper Nihilus *.utc and I have it in the Override because I am currently testing it (i.e. it isn't a complete version). My save is a fresh enter into the module 852NIH.

stoffe
05-06-2006, 07:02 AM
Well... that didn't do anything either. If I can't get this sorted I will settle for a simpler version of it (Where Nihilus just drains everything within 10 metres by say... 2 VP).

If that did nothing your script most likely doesn't run at all. As for your idea of making it simpler, try this:


void main() {
SendMessageToPC(GetPartyLeader(), "Hello World!");
}


Put that as your custom heartbeat script. If you don't see anything on the Feedback screen this time either, you know for sure your script doesn't even run, and thus your problem would be elsewhere.

Double-check your modified Nihilus template. Check that you have modified the correct one (there are a few different ones used for cutscenes), and verify there are no typos in the script name in the OnHeartbeat field. Also make sure the name of the script is no longer than 16 characters and contain no spaces (standard ResRef rules apply to script names).

Edit as well:

Edit: My save is a fresh enter into the module 852NIH.

Before crossing the transition into 852NIH, or just after the area has loaded? If it's the latter it's probably already too late, since Nihilus likely will have been spawned and stored in your save game.

You should use a save from before the module has been loaded. If you don't have one, go back to 851NIH (in-game), save as a new savegame, open the SAVEGAME.SAV file with an ERF Editor (nwhak.exe will do if you have it) and delete the 852NIH.SAV file inside it and save.

Pavlos
05-06-2006, 07:10 AM
I checked, double checked, and redid the script listing in the *.utc. I made sure it was the correct one by checking the module *.git file. SO Nihilus should be sending that message to my Feedback screen, but he doesn't.

Edit: The save is while I am still in 851NIH, about to enter 852NIH.

stoffe
05-06-2006, 07:31 AM
I checked, double checked, and redid the script listing in the *.utc. I made sure it was the correct one by checking the module *.git file. SO Nihilus should be sending that message to my Feedback screen, but he doesn't.

Edit: The save is while I am still in 851NIH, about to enter 852NIH.

Well... I just tested the full heartbeat script I posted above, and it worked just fine. Nihilus drained some VP from all the party members every 6 seconds or so, and any debug messages I inserted were displayed.

What I did:
Compiled the script posted 4 posts up and saved it as st_nihdrains.ncs and put it in override.
Extracted n_darthnihilu001.utc from the 852NIH module.
Opened the file in a GFF Editor and set the ScriptHeartbeat to st_nihdrains.
Started a game before reaching the Ravager, added Visas and Candalore to the party and used the console to warp to 852NIH.
Went through the cutscene and started the battle. When the battle started Nihilus started to siphon some Vitality from party members every 6 seconds.


Somewhere along the line you must do something different, because that script does work. :)

Pavlos
05-06-2006, 07:41 AM
Eeep! You are right... it does work. Sorry for making you do all that. Thank you for your help.

Mr Maniac
05-06-2006, 05:02 PM
Can you host this somehwere to download?

Pavlos
05-06-2006, 06:06 PM
I am planning on releasing this as part of a mod. Don't hold me to it though :). Until I set up a WIP thread nothing is definite. :).