While searching the forums for a nice cold damage script that kicks in when entering a cold area, I found one that TheBarbarian made almost a year ago for NWN1 for QuenGalad. However, in that thread they discussed that familiars and animal companions shouldn’t be affected and that sounds good to me…
When trying his script in my NWN2 module the PCs companions aren’t affected by the cold. I think that might have to do with the GetAssociate function and that the companions are companions and not henchmen, like in NWN1. However, when searching functions in the script assist I couldn’t find anynthing like ASSOCIATE_TYPE_COMPANION instead of ASSOCIATE_TYPE_HENCHMAN. How should I change this script for it to work properly in NWN2? I know I could most certainly do it in a very convoluted way by checking all the companions’ tags or something like that, but that seems a bit clunky. Here is the script (I made some small changes with putting a local int in the script associated with the area that is cold (and that’s actually what TheBarbarian suggested), and I changed GetEnteringObject to GetFirstPC):
//Script by TheBarbarian
//If you want to avoid unnecessary area heartbeat scripts running, you could use a pseudoheartbeat. Start it once in the OnEnter event of the area, then keep checking whether the PC is in a freezing area.
//In this example, set a local integer called FREEZING to anything non-zero on the area itself, and place the above script in the OnEnter event of cold areas.
void FreezingAreaHB(object oPC)
{
// Abort if the player is no longer in a freezing area.
if (!GetLocalInt(GetArea(oPC), "FREEZING"))
{
DeleteLocalInt(oPC, "IS_FREEZING");
return;
}
// PCs do not take freezing damage while resting.
if (!GetIsResting(oPC))
{
// Cycle through faction members.
object oTarget = GetFirstFactionMember(oPC, FALSE);
while (oTarget != OBJECT_INVALID)
{
// Deal cold damage to the PC and to henchman associates of the PC.
if (oTarget == oPC || (GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN && GetMaster(oTarget) == oPC))
{
int iDamage = Random(6) + 1;
effect eAreaCold = EffectDamage(iDamage,DAMAGE_TYPE_COLD);
effect eVis = EffectVisualEffect(VFX_COM_HIT_FROST);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eAreaCold, oTarget);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
}
oTarget = GetNextFactionMember(oPC, FALSE);
}
}
DelayCommand(5.0, FreezingAreaHB(oPC));
}
void main()
{
object oPC = GetFirstPC();
object oArea = GetObjectByTag("mistymountains");
SetLocalInt(oArea,"FREEZING",1);
if (!GetIsPC(oPC))
return;
// Only start the freezing pseudoheartbeat if it isn't already running.
if (!GetLocalInt(oPC, "IS_FREEZING"))
{
SetLocalInt(oPC, "IS_FREEZING", TRUE);
AssignCommand(oPC, FreezingAreaHB(oPC));
}
}
``
Alright, whatever…I’ll just cycle through the companions, I guess. If they all have winter coates they won’t get damaged, so I’ll add that code later I think…
//Script by TheBarbarian
//If you want to avoid unnecessary area heartbeat scripts running, you could use a pseudoheartbeat. Start it once in the OnEnter event of the area, then keep checking whether the PC is in a freezing area.
//In this example, set a local integer called FREEZING to anything non-zero on the area itself, and place the above script in the OnEnter event of cold areas.
void FreezingAreaHB(object oPC)
{
// Abort if the player is no longer in a freezing area.
if (!GetLocalInt(GetArea(oPC), "FREEZING"))
{
DeleteLocalInt(oPC, "IS_FREEZING");
return;
}
// PCs do not take freezing damage while resting.
if (!GetIsResting(oPC))
{
// Cycle through faction members.
object oTarget = GetFirstFactionMember(oPC, FALSE);
while (oTarget != OBJECT_INVALID)
{
int iDamage = Random(6) + 1;
effect eAreaCold = EffectDamage(iDamage,DAMAGE_TYPE_COLD);
effect eVis = EffectVisualEffect(VFX_COM_HIT_FROST);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eAreaCold, oTarget);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
oTarget = GetNextFactionMember(oPC, FALSE);
}
}
DelayCommand(5.0, FreezingAreaHB(oPC));
}
void main()
{
object oPC = GetFirstPC();
object oArea = GetObjectByTag("mistymountains");
SetLocalInt(oArea,"FREEZING",1);
if (!GetIsPC(oPC))
return;
// Only start the freezing pseudoheartbeat if it isn't already running.
if (!GetLocalInt(oPC, "IS_FREEZING"))
{
SetLocalInt(oPC, "IS_FREEZING", TRUE);
AssignCommand(oPC, FreezingAreaHB(oPC));
}
}
Yes, as you see that’s what I did. It’s just that I wanted to make sure that animal companions or familiars wouldn’t get cold damage. But I think I’ve found another way of doing this when checking for coates…
Hmmm. It’s strange. It doesn’t quite work the way I want to. I must be missing something.
My intention with the script: If the PC and the companions move into a cold area and not wearing a certain winter cloak they should be affected by the cold and take damage. And if they take the coat off later, even if they had it on when entering the area, they should be affected by the cold in the cold area.
//Original script by TheBarbarian
//If you want to avoid unnecessary area heartbeat scripts running, you could use a pseudoheartbeat. Start it once in the OnEnter event of the area, then keep checking whether the PC is in a freezing area.
//In this example, set a local integer called FREEZING to anything non-zero on the area itself, and place the above script in the OnEnter event of cold areas.
void FreezingAreaHB(object oPC)
{
// Abort if the player is no longer in a freezing area.
if (!GetLocalInt(GetArea(oPC), "FREEZING"))
{
DeleteLocalInt(oPC, "IS_FREEZING");
return;
}
object oTarget = GetFirstFactionMember(oPC, FALSE);
while (oTarget != OBJECT_INVALID)
{
object oCoat = GetObjectByTag("wintercloak");
if (GetItemInSlot(6,oTarget) == oCoat)
{
SetLocalInt(oTarget,"wearscloak",1);
}
else
{
SetLocalInt(oTarget,"wearscloak",0);
}
oTarget = GetNextFactionMember(oPC, FALSE);
}
// PCs do not take freezing damage while resting.
if (!GetIsResting(oPC))
{
// Cycle through faction members.
object oTarget = GetFirstFactionMember(oPC, FALSE);
while (oTarget != OBJECT_INVALID)
{
//object oCoat = GetObjectByTag("wintercloak");
//if (GetItemInSlot(6,oTarget) == oCoat) return;
if(GetLocalInt(oTarget,"wearscloak"))
{
oTarget = GetNextFactionMember(oPC, FALSE);
}
else
{
int iDamage = Random(6) + 1;
effect eAreaCold = EffectDamage(iDamage,DAMAGE_TYPE_COLD);
effect eVis = EffectVisualEffect(VFX_COM_HIT_FROST);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eAreaCold, oTarget);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
oTarget = GetNextFactionMember(oPC, FALSE);
}
}
}
DelayCommand(5.0, FreezingAreaHB(oPC));
}
void main()
{
object oPC = GetFirstPC();
object oArea = GetObjectByTag("mistymountains");
SetLocalInt(oArea,"FREEZING",1);
if (!GetIsPC(oPC))
return;
// Only start the freezing pseudoheartbeat if it isn't already running.
if (!GetLocalInt(oPC, "IS_FREEZING"))
{
SetLocalInt(oPC, "IS_FREEZING", TRUE);
AssignCommand(oPC, FreezingAreaHB(oPC));
}
}
As it is now, they get cold damage even if they have the cloaks on.
This should do what you’re asking for, I also tested it myself:
To have it work properly, you should slot it on the OnEnter script of the area where the freezing is supposed to happen. You will also have to set a local variable (integer) on freezing areas called FREEZING_AREA equal to 1.
//Original script by TheBarbarian
//If you want to avoid unnecessary area heartbeat scripts running, you could use a pseudoheartbeat.
//Start it once in the OnEnter event of the area, then keep checking whether the PC is in a freezing area.
//In this example, set a local integer called FREEZING_AREA to 1 on the area itself,
//and place the above script in the OnEnter event of cold areas.
int GetHasCold(object oPC)
{
if (GetIsResting(oPC) == TRUE) return FALSE;
object oCLOAK = GetItemInSlot(INVENTORY_SLOT_CLOAK, oPC);
if (GetTag(oCLOAK) == "wintercloak") return FALSE;
return TRUE;
}
void FreezingAreaHB(object oPC)
{
// Abort if the player is no longer in a freezing area.
object oAREA = GetArea(oPC);
if (GetLocalInt(oAREA, "FREEZING_AREA") != 1)
{
DeleteLocalInt(oPC, "FREEZING_PC");
return;
}
if (GetHasCold(oPC) == TRUE)
{
effect eDMG = EffectDamage(1 + Random(6), DAMAGE_TYPE_COLD);
effect eVFX = EffectVisualEffect(VFX_COM_HIT_FROST);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDMG, oPC);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX, oPC);
}
DelayCommand(6.0, FreezingAreaHB(oPC));
}
void main()
{
object oPC = GetEnteringObject();
if (GetObjectType(oPC) != OBJECT_TYPE_CREATURE) return;
int nASSO = GetAssociateType(oPC);
if (nASSO == ASSOCIATE_TYPE_FAMILIAR) return;
if (nASSO == ASSOCIATE_TYPE_ANIMALCOMPANION) return;
if (nASSO == ASSOCIATE_TYPE_SUMMONED) return;
if (GetLocalInt(oPC, "FREEZING_PC") == TRUE) return;
SetLocalInt(oPC, "FREEZING_PC", TRUE);
DelayCommand(0.1, FreezingAreaHB(oPC));
}
If you decide to use the area’s OnHeartbeat handler then this script will do you well:
/*
Place this script in the area's OnHeartbeat handler.
This script will only effect non-associate party members who are not wearing the cloak and not resting.
*/
#include "ginc_item"
void main()
{
object oPC = GetFirstPC();
object oFM = GetFirstFactionMember(oPC, FALSE);
while (GetIsObjectValid(oFM))
{
if (!GetAssociateType(oFM) && !GetIsItemEquipped("wintercloak", oFM) && !GetIsResting(oFM))
{
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(Random(6) + 1, DAMAGE_TYPE_COLD), oFM);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectNWN2SpecialEffectFile("sp_ice_hit"), oFM);
}
oFM = GetNextFactionMember(oPC, FALSE);
}
}
Thanks for the reply. I already have my script on OnEnter and a local variable called FREEZING in my script. I wish I knew what was wrong with my script in the first place, but I guess I’ll try yours Clangeddin.
@travus - I was a bit reluctant to use the area’s OnHeartbeat 'cause I thought that that maybe would slow the computer and game down (think I’ve seen some discussions on that) otherwise your script was very neat and compact.
I’m not that good at scripting but…If the PC or one of the companions have a familiar or animal companion, then the script won’t fire at all as far as I can see. Or am I wrong? The point is that the animal companion shouldn’t get cold damage but the companions and the PC should be able to get cold damage, shouldn’t they, if they don’t have a cloak. In your script it seems to me that the script won’t run if there are animal companions there…Am I wrong?
Yes, you are wrong.
From the toolset:
// Returns the associate type of the specified creature.
// - Returns ASSOCIATE_TYPE_NONE if the creature is not the associate of anyone.
Also in-game screenshot to prove the thing actually works. I also extended it to summoned creatures since they dont get the luxury of choosing a cloak to wear, like familiars and animal companions.
Yes, I noticed your script works in game (even if I haven’t checked with companions that have familiars), but I’m even more confused than before as I don’t understand your answer. Sorry.
object oPC = GetEnteringObject();
if (GetObjectType(oPC) != OBJECT_TYPE_CREATURE) return;
int nASSO = GetAssociateType(oPC);
if (nASSO == ASSOCIATE_TYPE_FAMILIAR) return;
if (nASSO == ASSOCIATE_TYPE_ANIMALCOMPANION) return;
if (nASSO == ASSOCIATE_TYPE_SUMMONED) return;
if (GetLocalInt(oPC, "FREEZING_PC") == TRUE) return;
This is the part I don’t get. If you get to ASSOCIATE_TYPE_FAMILIAR the script returns to void and doesn’t continue to the FreezingAreaHB function even if the PC hasn’t got a cloak. But I’m probably wrong again, as always. I’m just tired of not understanding scripting fully, 'cause a lot of times it seems illogical to me. And I’m at point with my modules now that I actually want to understand most of my scripting. But, ok, I guess I’ll have to give up on this…
The check for the cloak is inside the GetHasCold subroutine, the reason that one is not here is that familiars, companions, ecc… are always immune, by definition, so they don’t need to run the pseudo heartbeat at all.
For normal characters, companions, ecc… instead, you need to check if they are wearing the cloak every 6 seconds or so, that’s why you dont see it in the part you pasted, it’s at the beginning of the script.
Ok, so the script continues for the other characters so to speak but returns to void for the familiar, animal companion and summoned creature…
I still wish someone could explain what was wrong with my script. The way I see it I seem to have done something wrong with one of the while loops…I guess I’ll have to read and reread and see if I can find out what’s wrong on my own.
If you are confused about the fact that despite all those returns and the presence of a familiar, ecc… the script runs for those characters, it’s because I defined oPC differently than how it was defined in the original script.
First oPC was GetFirstPC(), the first player character in the game.
I changed it to GetEnteringObject(), which is EVERY object that enters that area and runs that script.
So, a familiar will run the script, check his status and return immediately.
This may seem more inefficient at first, but if you watch closely, I eliminated the faction loop inside the pseudo heartbeat, this actually saves a lot calculations later on.
If we wanted to optimize this even further we could exclude enemies from it, although I would expect creatures that live there to have ice immunity or very high resistances to it.
Looking at it, I think that the two issues were the definition of oPC as GetFirstPC() all the time and the definition of the cloak as GetObjectByTag instead of retrieving it from the inventory slot first and then checking its tag.
Thanks for all the help Clangeddin! I tried with changing that and it worked better, even if it didn’t work fully.
I’ll use your script instead. One thing I noticed with your script now, when testing again, that was weird, is that if you enter the area with everyone wearing the cloaks, you don’t get damaged, and that is as it should be, but if you remove the cloak from one of the characters in the cold area he/she still doesn’t get damaged. Is there anyway to remedy that? It is a heartbeatscript (or psuedoheartbeatscript) so it should damage the character if they remove the cloak shouldn’t it? I can see no wrong in the script when it comes to this…
Or maybe it would be better to use travus’ script for this to work?
If you are talking about my script, that issue was fixed too.
Wait 6 seconds for the next delayed command to return and you should see the characters getting damaged again, it’s like with resting, it’s checked inside the same subroutine.