Dynamically Attaching Scripts
There are times that we may want to dynamically attach scripts to objects and actors. This could be because we don't want to alter the vanilla records, or because we think it's going to be too much work to edit every single vanilla record, or because we want our mod to work on objects added by other mods.
There are many ways to do this, and this tutorial will only cover two of them. The first method is to attach magic effect scripts to actors, and the second is to attach reference alias scripts to objects.
Attaching Scripts to Actors
We're going to attach scripts to actors by making use of magic effects. We're going to give the player an ability, and that ability is going to be used to give nearby actors their own abilities containing scripted magic effects. For the purposes of this tutorial, we will be placing scripts on actors that will track the damage done to them by the player, and display the damage as a message in the upper left hand corner of the screen.
Create a Reference Alias
First we need to create a reference alias. This step is not actually necessary, but it is easier to just load up the mod with the player already having the ability, rather than using the console to add the ability to the player. (Another way this could be done would be to create a quest script using AddSpell.)
In the Object Window, navigate to Quest under the Character category. Create a new quest by right-clicking on the list and choosing 'New'. Make sure that Start Game Enabled in the Quest Data tab is checked. Click on the Quest Alias tab, then right-click in the empty list and choose to create a new reference alias. Make sure that the Fill Type is Specific Reference and choose the reference 'PlayerRef' in the '(any)' cell.
Click OK to create the reference alias, and OK again to close the quest window. Hereafter, we will refer to the quest as DASQuest and the reference alias as PlayerAlias.
Create the First Ability
Next, we create the ability that we're going to put on the player. Navigate to Spells which is under Magic in the Object Window. Right-click in the list and choose to create a new spell. Change the Type to 'Ability', making sure that Casting is 'Constant Effect' and that Delivery is 'Self'. For now, we can leave the list of effects blank.
After closing the spell window, navigate to Magic Effects, under Magic in the Object Window. Right-click in the list and choose to create a new magic effect. Change the Effect Archetype to 'Cloak'. The Casting Type should be 'Constant Effect' and Delivery 'Self'. Check the flag for Hide in UI. Click OK to accept the effect as is for now.
Go back to the ability we just created, and in the effects list, add in the magic effect we just created. There doesn't have to be any conditions or duration, but the Magnitude needs to be changed. For now, give magnitude the value of 192.0.
The magnitude of a Cloak archetype effect is the range of the cloak in feet. So a magnitude of 192.0 is 192 feet, or 4096 units, which is the length of a cell (4096 x 4096).
Hereafter, we will refer to this ability as CloakAbility and the magic effect as CloakEffect.
Create the Second Ability
Now we create the ability that we're going to put on our actors. Create a new ability similarly to how we created the first ability. The difference here is that we're going to use another magic effect.
When creating the magic effect, change its Effect Archetype to 'Script'. Again, make sure that the Casting Type is 'Constant Effect' and that Delivery is 'Self'. We should check the No Death Dispel flag if we want the magic effect to stay active even when the actor is dead.
Click OK to accept the effect as is for now. Once the magic effect window has closed, double-click the magic effect to bring it back up again. This step is needed because when we first create a magic effect, we cannot attach a script to it. Well, now we can. This script is going to be the script that we want to attach to our actor:
Scriptname DamageMonitorScript extends ActiveMagicEffect
Actor MySelf
Float Health
Event OnEffectStart(Actor akTarget, Actor akCaster)
MySelf = akTarget
Health = MySelf.GetActorValue("Health")
RegisterForSingleUpdate(0.25)
EndEvent
Event OnUpdate()
Health = MySelf.GetActorValue("Health")
RegisterForSingleUpdate(0.25)
EndEvent
Event OnEffectFinish(Actor akTarget, Actor akCaster)
UnregisterForUpdate()
EndEvent
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, \
bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
Float Damage = Health - MySelf.GetActorValue("Health")
Health = MySelf.GetActorValue("Health")
if (akAggressor == Game.GetPlayer())
Debug.Notification("You did " + Damage as Int + " points of damage.")
endif
EndEvent
Click OK to close the magic effect window. Now go back to the ability we have just made (the second one) and add the magic effect we just made (again, the second one) as its magic effect. We will leave the conditions blank with the duration and magnitude at 0. Click OK to close the spell window.
Hereafter, we refer to this ability as MonitorAbility and the magic effect as MonitorEffect.
Create the Spell
We need to create another new spell. This is the spell that will be cast on an actor when he/she comes into contact with the cloak from the player's ability.
Navigate to Spells under Magic in the Object Window again. Right-click the list and choose to create a new spell. This time, keep the Type as 'Spell', but change the Casting to 'Concentration' and Delivery to 'Aimed'. Click OK to accept the spell as it is for now.
Now create another new magic effect. For this effect, change the Effect Archetype to 'Script', the Casting Type to 'Concentration' and Delivery to 'Aimed'. If we want this effect to affect dead actors as well, then we should check the flag for No Death Dispel.
Close the window, and then open it again to attach a script to the magic effect:
Scriptname AddAbilityScript extends ActiveMagicEffect
Spell Property MonitorAbility Auto
Event OnEffectStart(Actor akTarget, Actor akCaster)
akTarget.AddSpell(MonitorAbility)
EndEvent
Remember to set the value of the 'MonitorAbility' to the MonitorAbility that we created earlier.
We also need to add a condition to the magic effect. Right click the Target Conditions in the upper right corner of the [Magic Effect] window and select 'New'. The condition is:
HasMagicEffect MagicEffect: 'MonitorEffect' == 0
Make sure that it's set to run on Subject. If we don't want the spell to run on dead actors, we should add another condition run on Subject:
GetDead NONE == 0
Click OK to close the magic effect window. Then open up the spell we created and add the magic effect we just created to its effects list. Magnitude and duration can be left at 0.
Hereafter we will refer to this spell as ApplyingSpell and the magic effect as ApplyingEffect.
Putting It All Together
Go back to CloakEffect and edit the Assoc. Item 1 to point to ApplyingSpell. Click OK to close the magic effect window and save the changes.
Open up DASQuest and edit PlayerAlias. Click and drag CloakAbility into the spell list of PlayerAlias. Click OK twice to close the alias and quest windows, and save the mod.
Now start the game with the mod loaded and attack someone. The damage dealt by the player should be seen as messages on the upper left corner of the screen.
Attaching Scripts to Objects
Unlike with actors, objects cannot run magic effect scripts. To give an object a script at runtime, we need to assign the object a reference alias with a reference alias script. For this tutorial, we will create a mod that gives the player firewood whenever he/she hits a tree with an axe.
Create a FormList
First, we're going to create a FormList of trees that we want to give out firewood. We will be using this later to fill the reference aliases we create.
Navigate to FormList under Miscellaneous in the Object Window. Right-click on the list and select 'New' to create a new formlist. Now navigate to Landscape under Tree which is under World Objects in the Object Window. Highlight everything in the list, then click and drag them into the new formlist.
Click OK to create the formlist. Hereafter we will refer to this formlist as TreesList.
Create the First Quest
This quest is going to be holding the reference aliases that we will be assigning to objects at runtime. Navigate to Quest under Character in the Object Window. Right-click on the list and select 'New' to create a new quest.
Go to the Quest Alias tab, then right-click on the empty list and choose to create a new reference alias. Check the flag for Optional. For Fill Type, choose Find Matching Reference and check the flags for In Loaded Area and Closest. Under Match Conditions, right-click the list and select 'New' to create a new condition:
IsInList 'TreesList' == 1.00
Click OK to close the alias window. Then double-click on the alias in order to open it again. We need to do this step because when we first create an alias, we can't attach a script to it. Attach this script to the reference alias:
Scriptname TreeAliasScript extends ReferenceAlias
Keyword Property WeapTypeBattleAxe Auto
Keyword Property WeapTypeWarAxe Auto
MiscObject Property Firewood01 Auto
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, \
bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
if (akAggressor == Game.GetPlayer())
if (akSource.HasKeyword(WeapTypeBattleAxe) || akSource.HasKeyword(WeapTypeWarAxe))
akAggressor.AddItem(FireWood01)
endif
endif
EndEvent
Set the properties for the script. Since the properties are named exactly the same as the editor IDs of what we want them to represent, just click the Auto-Fill All button to set all the values automatically.
Click OK to close the alias window. Now right-click on the reference alias we just created and select 'Duplicate' to create a duplicate of the alias. You can do this as many times as you like, although five is probably enough for most circumstances.
Click OK to close the quest window. Hereafter we will refer to this quest as DASQuest.
Create the Second Quest
This quest is going to start and stop the first quest. We do this so that the game will constantly refill the aliases in the first quest with the closest trees.
Navigate to Quest under Character in the Object Window. Right-click on the list and select 'New' to create a new quest. Click OK to close the quest window, then double-click the quest to open the window again. This step is necessary because if we try to attach a script to a quest right when we create it, the CK crashes (at least in the author's experience).
Attach this script to the quest:
Scriptname UpdateQuestScript extends Quest
Quest Property DASQuest Auto
Event OnInit()
RegisterForSingleUpdate(5)
EndEvent
Event OnUpdate()
DASQuest.Stop()
DASQuest.Start()
RegisterForSingleUpdate(5)
EndEvent
Remember to set the value of the 'DASQuest' property to the DASQuest we created earlier. Check to make sure that Start Game Enabled is checked in the Quest Data tab, then click OK to close the quest window.
Conclusion
The two quests are all that's necessary for this mod. Save the mod and then load the game with it to test it out. A piece of firewood should be received every time the player hits a tree with an axe.
The method for attaching scripts to objects using reference aliases is much easier than what we did with magic effects and actors. But there are disadvantages to using it:
- The number of actors/objects that we can attach the script to is limited to the number of reference aliases we create.
- The script does not stay on the actor/object because we need to constantly refill the aliases.
So while this method worked for what we did here, it might not be good for doing something else. For example, what if instead we wanted the trees to only give out firewood after being hit three times instead of once?
That would have been much more complicated because then we would have to store the number of times we hit the tree somewhere. Obviously, we cannot store this variable in the reference alias scripts because the object filling the alias is not always going to be the same tree. We might swing our axe twice but then the alias gets refilled before the third strike, which means it could take us 5 strikes to get a piece of firewood.
Known Issues
- When using the second method with constantly refilling aliases, be careful with your alias scripts. If the alias script happens to be running a while loop when its quest is stopped, then it leads to suspended stacks, in turn leading to suspended scripts.
External Links
Porting Kuertee's Sittable Rocks Mod from Oblivion
Dynamically Attaching Scripts to Actors Near the Player
Suspended Stacks Suspends Your Scripts, Sometimes Indefinitely