Hi KevL,
Thanks again for stepping in … That looks like I will be able to place it on my PCs heartbeat scripts and have it apply via OBJECT_SELF. (I have adjusted the scriptsets.2da so that the PC and companions can have the same heartbeat script and differentiated within the script.) I will give it a go.
That would then work for every PC a player controls.
Cheers, Lance.
EDIT: Yes. I just tried the following adaptation by placing the code in my own feat include files and calling the function FixLightBlind from the PC heartbeat and it worked fine.
By the way, I worked out why my GetIsDay appeared awry. It is because the “day” did not start on the hour, but the hour after the module setting. I did not know that “dusk” had its own hour. I realised this after allowing time to pass another hour (15 minutes real time in my campaign) with this code when it had not worked at the expected hour. i.e. Dusk started at 1800 hours, but the “night” started at 1900. That explains my other issue I had when timing with GetIsDay.
EDIT 2: I used -1004 for my unique number just because I have used -1000 backwards for my own campaign ones.
Thanks for putting the time into this KevL!
/* Constants */
const int SPELLID_LIGHTPENALTY = -1004; // arbitrary - shall be unique 52873.
const int FEAT_LIGHT_BLINDNESS = 1767; // GrayOrc or Drow
const int FEAT_LIGHT_SENSITIVITY = 1768; // GrayDwarf
const string VAR_PENALTY = "LightPenalty"; // (int) character's current penalty-val/status
const int PENALTY_NOTAPPLICABLE = -1; // * character has neither LightBlindness nor LightSensitivity
const int PENALTY_INIT = 0; // assess or re-assess status
const int PENALTY_BLINDNESS = 1; // * has LightBlindness
const int PENALTY_SENSITIVITY = 2; // * has LightSensitivity
const string VAR_STATE = "LightPenaltyState"; // (int) tracks the change from daytonight/nighttoday
const int STATE_NIGHT = 0; // penalty off
const int STATE_DAY = 1; // penalty on
////////////////////////////////////////////////////////////////////////
// Clears any penalties from Blindness or Sensitivity.
////////////////////////////////////////////////////////////////////////
void ClearPenalty(object oTarget, int iPenalty);
void ClearPenalty(object oTarget, int iPenalty)
{
string sFeat;
switch (iPenalty)
{
case PENALTY_BLINDNESS: sFeat = "Light Blindness"; break;
case PENALTY_SENSITIVITY: sFeat = "Light Sensitivity"; break;
default: return;
}
DelayCommand(0.5f, AssignCommand(oTarget, PlaySound("sfx_portalexplodingsmall", TRUE)));
DelayCommand(0.5f, SetNoticeText(oTarget, "You are no longer experiencing " + sFeat));
SendMessageToPC(oTarget, "<c=limegreen>You are no longer experiencing " + sFeat + ".</c>");
effect eEffect = GetFirstEffect(oTarget);
while (GetIsEffectValid(eEffect))
{
if (GetEffectSpellId(eEffect) == SPELLID_LIGHTPENALTY)
{
RemoveEffect(oTarget, eEffect);
eEffect = GetFirstEffect(oTarget);
}
else
{
eEffect = GetNextEffect(oTarget);
}
}
}
////////////////////////////////////////////////////////////////////////
// Applies penalties for Blindness or Sensitivity.
////////////////////////////////////////////////////////////////////////
void ApplyPenalty(object oTarget, int iPenalty);
void ApplyPenalty(object oTarget, int iPenalty)
{
string sFeat;
switch (iPenalty)
{
case PENALTY_BLINDNESS: sFeat = "Light Blindness !"; break;
case PENALTY_SENSITIVITY: sFeat = "Light Sensitivity !"; break;
default: return;
}
effect eAtkDecr = EffectAttackDecrease(iPenalty);
effect eSaveDecr = EffectSavingThrowDecrease(SAVING_THROW_ALL, iPenalty);
effect eSkillDecr = EffectSkillDecrease(SKILL_ALL_SKILLS, iPenalty);
eAtkDecr = EffectLinkEffects(eAtkDecr, eSaveDecr);
eAtkDecr = EffectLinkEffects(eAtkDecr, eSkillDecr);
eAtkDecr = SupernaturalEffect(eAtkDecr);
eAtkDecr = SetEffectSpellId(eAtkDecr, SPELLID_LIGHTPENALTY);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAtkDecr, oTarget);
DelayCommand(0.5f, AssignCommand(oTarget, PlaySound("sfx_drain", TRUE)));
DelayCommand(0.5f, SetNoticeText(oTarget, "You are experiencing " + sFeat));
SendMessageToPC(oTarget, "<c=firebrick>You are experiencing " + sFeat
+ "\nAttacks, Saves and Skills are reduced by " + IntToString(iPenalty) + "</c>");
}
////////////////////////////////////////////////////////////////////////
// Initializes oTarget's penalty-type and removes the feat itself.
// - returns -1 if not applicable
////////////////////////////////////////////////////////////////////////
int Initialize(object oTarget);
int Initialize(object oTarget)
{
int iPenalty;
if (GetHasFeat(FEAT_LIGHT_SENSITIVITY, oTarget))
{
FeatRemove(oTarget, FEAT_LIGHT_SENSITIVITY);
iPenalty = PENALTY_SENSITIVITY;
}
else if (GetHasFeat(FEAT_LIGHT_BLINDNESS, oTarget))
{
FeatRemove(oTarget, FEAT_LIGHT_BLINDNESS);
iPenalty = PENALTY_BLINDNESS;
}
else
{
iPenalty = PENALTY_NOTAPPLICABLE;
}
SetLocalInt(oTarget, VAR_PENALTY, iPenalty);
return iPenalty;
}
////////////////////////////////////////////////////////////////////////
// Restores the proper feat if oTarget gets DaylightAdaptation.
// - note that delevel could again wipe off DaylightAdaptation
////////////////////////////////////////////////////////////////////////
void RestoreFeat(object oTarget, int iPenalty);
void RestoreFeat(object oTarget, int iPenalty)
{
int iFeatId;
switch (iPenalty)
{
case PENALTY_BLINDNESS: iFeatId = FEAT_LIGHT_BLINDNESS; break;
case PENALTY_SENSITIVITY: iFeatId = FEAT_LIGHT_SENSITIVITY; break;
default: return;
}
FeatAdd(oTarget, iFeatId, FALSE);
if (GetLocalInt(oTarget, VAR_STATE) == STATE_DAY)
ClearPenalty(oTarget, iPenalty);
DeleteLocalInt(oTarget, VAR_STATE);
DeleteLocalInt(oTarget, VAR_PENALTY);
}
////////////////////////////////////////////////////////////////////////////////////////
//'fix_lightblind' travus 2019 feb 11 kevL 2019 feb 12 - modified.
// Fixes the bugs wrt/ LightBlindness (GrayOrc/Drow) or LightSensitivity (GrayDwarf) by
// removing the feat altogether and applying scripted effects. If the character acquires
// DaylightAdaptation the proper feat is restored since DaylightAdaptation not bugged.
// NOTE: Is intended as a heartbeat script and currently is coded only for the FirstPC.
// LightBlindness/LightSensitivity is assumed to be a racial feat that a character will
// have from its creation. (ie, if either feat is granted to a character during play this
// will not work; because VAR_PENALTY has already been set to PENALTY_NOTAPPLICABLE)
////////////////////////////////////////////////////////////////////////////////////////
void FixLightBlind(object oTarget);
void FixLightBlind(object oTarget)
{
int iPenalty = GetLocalInt(oTarget, VAR_PENALTY);
if (iPenalty != PENALTY_NOTAPPLICABLE)
{
if (!GetHasFeat(FEAT_DAYLIGHT_ADAPATION, oTarget))
{
if (iPenalty == PENALTY_INIT){iPenalty = Initialize(oTarget);}
int iState = GetLocalInt(oTarget, VAR_STATE);
if (GetIsNight() || GetIsAreaInterior(GetArea(oTarget)))
{
if (iState == STATE_DAY)
{
SetLocalInt(oTarget, VAR_STATE, STATE_NIGHT);
ClearPenalty(oTarget, iPenalty);
}
}
else if (iState == STATE_NIGHT)
{
SetLocalInt(oTarget, VAR_STATE, STATE_DAY);
ApplyPenalty(oTarget, iPenalty);
}
}
else if (iPenalty != PENALTY_INIT)
{
RestoreFeat(oTarget, iPenalty);
}
}
}