This tutorial assumes you have a basic knowledge of Papyrus, and States.
Scriptname TUT_CastingActivatorScript extends ObjectReference
{Script that causes events to happen when cast on by the player}
Actor Property PlayerREF Auto
{This is the most efficient way to refer to the player, should autofill}
Spell Property ActivateSpell Auto
{This is the spell that should make the script run.}
Auto State Open; default state, how the door usually is (open or closed)
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
If akSource as Spell
If (akSource as Spell) == ActivateSpell
Self.Activate(PlayerREF)
EndIf
EndIf
EndEvent
Event OnActivate(ObjectReference akActionRef)
GoToState("Busy")
Self.SetOpen();Self.SetOpen(false) if the door is initially closed
GoToState("Open")
EndEvent
EndState
State Busy; door still opening/closing, don't want to cause any issues if activated during this time
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
;empty state
EndEvent
Event OnActivate(ObjectReference akActionRef)
;empty state
EndEvent
EndState
The Basic Script
So we add a script to our activator. We've got a property for the player, which should autofill, and a property for our activator spell. I've got a little note about having more than one activator spell, which we'll cover later on. The activator spell is a property you should fill with the spell that you want to cause the script - in this case the opening of the door.
Then we define an Auto state, which is the state that the script will begin in. For our script, I assumed the door would begin closed but pointed out some areas (commented out in the script) where you would change it if it were open to begin with. I'll continue with that assumption from here on out - adjust if necessary. In this Auto state, if the door is hit, it'll call the OnHit event. We use the parameter akSource, which will give us what hit our activator (specifically what the script is on, which in this case is our activator). We compare akSource to our ActivatorSpell, to see if akSource is equal to ActivatorSpell. If the source of the hit was our spell, that means that the spell was cast at our activator. (We must Cast akSource to Spell in the first place.)
Then, we activate the ObjectReference the script is on - our activator. We use the PlayerREF parameter to tell the door that the player opened it. It's basically just faking who opened the door - that doesn't mean the player has to cast the spell in-game to get the script to run, though. This will call the OnActivate event, so we can go to that event within the Auto state.
OnActivate, we'll go to the next state. This means that after this happens, any time you "hit" the activator while the script is in the Busy state, nothing will happen because we haven't defined any code within the events for the Busy state. It's basically making sure nothing happens while in that state. Then the script continues in the Auto state. So it opens our activator (this script only works for an opening activator, obviously), and when it's finished switches to the state Open.
In the Open state, if hit, it will activate itself, go to the Busy state while closing the door and then go to the Closed Auto state when the door is finished closing.
Shouts and Non-Door Activators
Scriptname ActivatorSpellScript Extends ObjectReference
{This script should be on your activator - be it door or shrine or anything other activator}
Actor Property PlayerREF Auto
{This is the most efficient way to refer to the player, should autofill}
Spell Property ActivateSpell Auto
{This is the shout that should make the script run - yes it's also a spell.}
Actor Property EnemyToSpawn Auto
{The enemy we're going to be spawning}
Auto State Ready; default state, activator is ready to spawn enemy
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
If (akSource as Spell)
If (akSource as Spell) == ActivateSpell
Self.Activate(PlayerREF)
EndIf
EndIf
EndEvent
Event OnActivate(ObjectReference akActionRef)
GoToState("Busy")
PlayerREF.PlaceAtMe(EnemyToSpawn)
GoToState("Ready")
EndEvent
EndState
State Busy; spawning enemy
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
;empty state
EndEvent
Event OnActivate(ObjectReference akActionRef)
;empty state
EndEvent
EndState
We have the same properties, since a shout actually uses a spell property here. We'll use this like we did in the basic script. When the pillar is hit, we go to the busy state, then we do what we want to do (in this case placing our enemy actor at the player) and then once we're done with that we go back to the ready state so the pillar is ready to be shouted at again.
Spells OR Shouts
Then we'll utilize what is called an OR. It says that If/ElseIf A OR B, run X. That's very arbitrary so I'll clarify:
"If MyQuestProperty Stage is 50 OR Player is Female" would look like this in Papyrus:
If (MyQuestProperty.GetStage() == 50 || Game.GetPlayer().GetActorBase().GetSex() == 1)
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
If akSpell == ActivateSpell || akSpell == ActivateSpellTwo
Self.Activate(PlayerREF)
EndIf
EndEvent
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
If akSpell == ActivateSpell || akSpell == ActivateSpellTwo || akSpell == ActivateSpellThree
Self.Activate(PlayerREF)
EndIf
EndEvent