Yeah, this is quite painful...previously, I was getting my ActiveMagicEffect script to report itself via "Self" in OnEffectStart, but Self started suddenly always returning None when it never has done in the past.
Here are my two tracker scripts:
Spoiler
ScriptName ACGMagicCasterActorScript extends ActiveMagicEffectActor Property RefSelfActor Auto ; Reference to the Actor this script is attached to.ACGQuestMagicSystemScript Property RefMagicSystemQuest Auto ; Reference to the magic system quest script.Int Property SelfQuestIndex Auto ; Tracks this script's location in the quest.String[] Property EffectNameArray Auto ; Array to cache effect names.Int[] Property RefTargetScriptIndexCache Auto ; Array to cache target scripts that have been affect by this caster.Int CacheLength ; Keep track of length, for easy looping.Float[] Property TargetDistanceArray Auto ; Keeps track of target distances (in feet).Float[] Property MEffectBaseTimeCostArray Auto ; Keeps track of magic costs time-wise (per second).Float[] Property MEffectBaseDistanceCostArray Auto ; Keeps track of magic costs distance-wise (per foot).Float[] Property MEffectBaseTimeCostStackArray Auto ; MEffectBaseTimeCost stack tracking array.Float[] Property MEffectBaseDistanceCostStackArray Auto ; MEffectBaseDistanceCost stack tracking array.Float TotalCost ; Total calculated Energy cost.Float HealthPercentOld ; Keeps track of this caster's health percentage.Float HealthPercentCur ; This is the current percentage.Float HealthPercentDiff ; Track this caster's health percentage difference.Float Property UpdateInterval = 0.1 AutoEvent OnEffectStart(Actor akTarget, Actor akCaster) RefMagicSystemQuest.RegisterCasterScript(Self) RefSelfActor = akTarget HealthPercentOld = RefSelfActor.GetActorValuePercentage("Health") HealthPercentDiff = 0.0 Debug.Notification("Caster script added.")EndEventFunction RegisterEffect(Int EffectScriptIndex, String ALName, Float BaseTimeCost, Float BaseDistanceCost) RefTargetScriptIndexCache[CacheLength] = EffectScriptIndex EffectNameArray[CacheLength] = ALName MEffectBaseTimeCostArray[CacheLength] = BaseTimeCost MEffectBaseDistanceCostArray[CacheLength] = BaseDistanceCost ; Update CacheLength with the new length. CacheLength = RefTargetScriptIndexCache.LengthEndFunctionEvent OnUpdate() Int n = 0 While n < CacheLength TargetDistanceArray[n] = RefMagicSystemQuest.GetTargetDistance(n, RefSelfActor) / (64 / 3) ; Evaluates to 21.33333333333333333333. TotalCost += (MEffectBaseDistanceCostStackArray[n] * TargetDistanceArray[n]) * (MEffectBaseTimeCostStackArray[n] * UpdateInterval) n += 1 EndWhile RefSelfActor.DamageActorValue("Magicka", TotalCost) If RefSelfActor.GetActorValue("Magicka") <= 0 Self.Dispel() EndIf RegisterForSingleUpdate(UpdateInterval)EndEventEvent OnControlDown(String Control) If Control == "Shout" ; This will remove either all magic from targets the player has cast spells on, or ; selected targets. EndIfEndEventEvent OnKeyDown(Int KeyCode) ; TODO - this function will track the Minus and Equals keys, and modify the damage of spell ; effects of the player's choosing. ; Depending on the key the player presses, damage is stacked positively or negatively for ; the spells the player has selected. ;If KeyCode == 12 ; Minus key. ; MEffectBaseTimeCostStack -= MEffectBaseTimeCost ; MEffectBaseDistanceCostStack -= MEffectBaseDistanceCost ;ElseIf KeyCode == 13 ; Equals key. ; MEffectBaseTimeCostStack += MEffectBaseTimeCost ; MEffectBaseDistanceCostStack += MEffectBaseDistanceCost ;EndIfEndEvent; TODO - When this event is triggered, we want some event to happen to the caster or the caster's tracked targets.Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked) If abPowerAttack && !RefSelfActor.HasLOS(akAggressor) ; ElseIf abPowerAttack && abSneakAttack ; ElseIf abPowerAttack && abHitBlocked ; ElseIf abBashAttack && abSneakAttack ; EndIf ; Check if the caster has recieved enough damage to affect this effect. HealthPercentCur = RefSelfActor.GetActorValuePercentage("Health") ; If this is a negative value If HealthPercentCur != HealthPercentOld HealthPercentDiff = HealthPercentOld - HealthPercentCur HealthPercentOld = HealthPercentCur Debug.Notification("HealthPercentDiff: " + HealthPercentDiff as String) EndIf ; NOTE - this looks very inefficient. It might cause script engine lag over a long period. ; Only tracks damage for now and doesn't restore base damage yet. If HealthPercentDiff < 0.0 && HealthPercentDiff > -0.6 Debug.Notification("Magnitude weakened.") ;Int n = 0 ;Int ArrayLength = QuestRegistryArray.Length ;While n != ArrayLength ; Float TValue = http://forums.bethsoft.com/topic/1510136-how-to-get-an-activemagiceffect-instance/RefMagicSystemQuest.GetTargetHealthDamageBaseStack(QuestRegistryArray[ArrayLength]) * (1.0 + HealthPercentDiff) ; RefMagicSystemQuest.SetTargetHealthDamageMod(QuestRegistryArray[ArrayLength], TValue) ; ArrayLength += 1 ; Utility.Wait(0.05) ;EndWhile ElseIf HealthPercentDiff <= -0.6 Debug.Notification("Effect dispelled.") ;Int n = 0 ;Int ArrayLength = QuestRegistryArray.Length ;While ArrayLength ; RefMagicSystemQuest.DispelTargetEffect(QuestRegistryArray[ArrayLength]) ; ArrayLength += 1 ; Utility.Wait(0.05) ;EndWhile ;Self.Dispel() EndIfEndEvent; Thread-safe function for getting the Actor this script is attached to.Actor Function GetActor() Return Self.RefSelfActorEndFunction
Spoiler
ScriptName ACGMagicTargetActorScript extends ActiveMagicEffectActor Property RefSelfActor Auto ; Reference to the Actor this script is attached to.MagicEffect Property RefEffectParent Auto ; Reference to the Magic Effect this script is attached to.ACGQuestMagicSystemScript Property RefMagicSystemQuest Auto ; Reference to the magic system quest script.Int Property SelfQuestIndex Auto ; Tracks this script's location in the quest.String[] Property EffectNameArray Auto ; Array to cache effect names.Int[] Property RefCasterScriptIndexCache Auto ; Array to cache caster scripts for casters who have affected this Actor.Int CacheLength ; Tracks the length of RefCasterScriptIndexCache. Useful for easily adding new effect entries to the array.Float[] Property HealthDamageBaseArray Auto ; Base damage per second.Float[] Property HealthDamageModArray Auto ; Helps track changes to HealthDamageBase.Float[] Property HealthDamageBaseStackArray Auto ; HealthDamageBase stacking.Float[] Property HealthDamageModStackArray Auto ; HealthDamageMod stacking.Float TotalDamage ; Total calculated damage to apply.Bool DamageModified = False ; Check for modified damage stack.Bool[] Property DeathDispelArray Auto ; For non-Essential Actors.Bool[] Property BleedoutDispelArray Auto ; For Essential Actors.Float UpdateInterval = 0.1 ; Update interval for RegisterForUpdateSingle()Event OnEffectStart(Actor akTarget, Actor akCaster) RefSelfActor = akTarget ; Register with the control Quest. RefMagicSystemQuest.RegisterTargetScript(Self) ; Only for player use. RegisterForControl("Shout") ; Is this the best key to use? Can it be tapped quickly in the middle of combat? RegisterForKey(12) ; Minus key. RegisterForKey(13) ; Equals key. Debug.Notification("Target script added.")EndEventFunction RegisterEffect(Int EffectScriptIndex, String ALName, Float HealthDamage, Bool DispelOnDeath, Bool DispelOnBleedout) RefCasterScriptIndexCache[CacheLength] = EffectScriptIndex EffectNameArray[CacheLength] = ALName HealthDamageBaseArray[CacheLength] = HealthDamage DeathDispelArray[CacheLength] = DispelOnDeath BleedoutDispelArray[CacheLength] = DispelOnBleedout ; Update CacheLength with the new length. CacheLength = RefCasterScriptIndexCache.LengthEndFunctionEvent OnUpdate() ; TODO - DamageModified is False for now. This will be updated soon. If DamageModified ; TODO - update HealthDamage variables as appropriate. Int n = 0 While n < CacheLength TotalDamage += HealthDamageModStackArray[n] EndWhile EndIf ; Deal a relative amount of damage in relation to the UpdateInterval period. RefSelfActor.DamageActorValue("Health", TotalDamage) RegisterForSingleUpdate(UpdateInterval)EndEventEvent OnKeyDown(Int KeyCode) ; TODO - this function will track the Minus and Equals keys, and modify the damage of spell ; effects of the player's choosing. ; Depending on the key the Player presses, we either stack damage positively or negatively, ; according to the base damage and Caster-health-influenced damage. Time cost and distance ; cost are also stacked. This affects this target only if they on the player's selected target list. ;If ??? ; How will this work? ; If KeyCode == 12 ; Minus key. ; HealthDamageBaseStack -= HealthDamageBase ; HealthDamageModStack -= HealthDamageMod ; If HealthDamageBase <= 0.0 ; Self.Dispel() ; EndIf ; ElseIf KeyCode == 13 ; Equals key. ; HealthDamageBaseStack += HealthDamageBase ; HealthDamageModStack += HealthDamageMod ; EndIf ;EndIfEndEvent; For Essential Actors. Non-Essential Actors enter bleedout too, but we want it to keep; affecting them until the OnDying() event.Event OnEnterBleedout() If RefSelfActor.IsEssential() Int n = 0 While n < BleedoutDispelArray.Length If BleedoutDispelArray[n] == True ; Do something EndIf EndWhile EndIfEndEvent; For non-Essential Actors, as only they can die.; OnDying() used because OnDeath() is buggy, according to various gamesas Forum posters.Event OnDying(Actor akKiller) Int n = 0 While n < DeathDispelArray.Length If DeathDispelArray[n] == True ; Do something EndIf EndWhileEndEvent; This function will, in future, remove effect entries and modify spell damage appropriately.Function SelfDispel() ;EndFunction; Thread-safe function for getting the Actor this script is attached to.Actor Function GetActor() Return Self.RefSelfActorEndFunction
And here's the quest script they report to:
Spoiler
ScriptName ACGQuestMagicSystemScript extends QuestSpell Property RefTargetActorSpell AutoSpell Property RefCasterActorSpell AutoActor[] Property MagicTargetActorRegistry AutoActor[] Property MagicCasterActorRegistry AutoACGMagicTargetActorScript[] Property MagicTargetScriptRegistry AutoACGMagicCasterActorScript[] Property MagicCasterScriptRegistry AutoInt TargetIndex = 0Int CasterIndex = 0; Int[] Property RefPlayerCasterActorArray AutoFunction RegisterTargetScript(ACGMagicTargetActorScript TargetScript) MagicTargetScriptRegistry[TargetIndex] = TargetScript MagicTargetActorRegistry[TargetIndex] = MagicTargetScriptRegistry[TargetIndex].GetActor() TargetIndex += 1EndFunctionFunction RegisterCasterScript(ACGMagicCasterActorScript CasterScript) MagicCasterScriptRegistry[CasterIndex] = CasterScript MagicCasterActorRegistry[CasterIndex] = MagicCasterScriptRegistry[CasterIndex].GetActor() CasterIndex += 1EndFunction; See whether the tracker scripts already exist on the target and caster Actors, and if not, add them. Then, add the effects to; the scripts' effect caches.Function RegisterEffect(Actor TargetActor, Actor CasterActor, String EffectNameAL, Float MEffectBaseDistanceCost , Float MEffectBaseTimeCost, Float HealthDamageBase, Bool DeathDispel, Bool BleedoutDispel) Bool FoundTargetActor = False Bool FoundCasterActor = False Bool loop = True Int ArrayLength = MagicTargetActorRegistry.Length Int n = 0 Int WantedTargetIndex Int WantedCasterIndex While loop && n < ArrayLength If MagicTargetActorRegistry[n] == TargetActor && MagicCasterActorRegistry[n] == CasterActor ; If the target and caster Actor pair are found, let's register straight away. MagicTargetScriptRegistry[n].RegisterEffect(n, EffectNameAL, HealthDamageBase, DeathDispel, BleedoutDispel) MagicCasterScriptRegistry[n].RegisterEffect(n, EffectNameAL, MEffectBaseTimeCost, MEffectBaseDistanceCost) Return EndIf If MagicTargetActorRegistry[n] == TargetActor && FoundTargetActor == False WantedTargetIndex = n FoundTargetActor = True If FoundCasterActor loop = False EndIf EndIf If MagicCasterActorRegistry[n] == CasterActor && FoundCasterActor == False WantedCasterIndex = n FoundCasterActor = True If FoundTargetActor loop = False EndIf EndIf n += 1 EndWhile If FoundTargetActor MagicTargetScriptRegistry[ArrayLength] = MagicTargetScriptRegistry[WantedTargetIndex] Else TargetActor.AddSpell(RefTargetActorSpell) EndIf If FoundCasterActor MagicCasterScriptRegistry[ArrayLength] = MagicCasterScriptRegistry[WantedCasterIndex] Else CasterActor.AddSpell(RefCasterActorSpell) EndIf MagicTargetScriptRegistry[ArrayLength].RegisterEffect(ArrayLength, EffectNameAL, HealthDamageBase, DeathDispel, BleedoutDispel) MagicCasterScriptRegistry[ArrayLength].RegisterEffect(ArrayLength, EffectNameAL, MEffectBaseTimeCost, MEffectBaseDistanceCost)EndFunctionFloat Function GetTargetDistance(Int n, Actor CasterActor) Return MagicTargetScriptRegistry[n].GetActor().GetDistance(CasterActor)EndFunction
If someone could help me find an alternative way of getting a handle for the ActiveMagicEffect scripts I need to access, that would be very much appreciated.