-
Notifications
You must be signed in to change notification settings - Fork 2
New Papyrus Events
New events exposed by this plugin are automatically called when appropriate. There is no need to write any import
statements. It is sufficient to simply write an implementation for any event you would like to respond to in your scripts.
The following events are closely related to the standard OnHit event that is available in Papyrus by default.
Event OnImpact(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
- This event can be implemented in ObjectReference scripts, or in ActiveMagicEffect scripts (where the Active Magic Effect would be running on the target that gets hit) or ReferenceAlias scripts (where the alias would be pointing to the target that gets hit).
- This event triggers in the same situations as the standard OnHit event, except:
- It only triggers on Actors (i.e. NPCs / creatures).
- It tries to avoid triggering more than once per frame for the same pairing of
akAggressor
+ target (self
).- For example, the regular
OnHit
event may trigger many times within a single frame for a single hit by a poisoned/enchanted weapon, which can cause numerous performance issues.OnImpact
will only trigger once. - Note: while the intention is to mitigate the performance issues resulting from multiple simultaneous triggers in the regular OnHit event, the solution is still not perfect. For example, when throwing a Fireball at someone, there may still be at least two triggers in rapid succession (but not in exactly the same frame): one from the spell, and one from the subsequent Explosion. So, it is still good to take steps and try to avoid running too much simultaneously in your own script (e.g., by jumping to a state that does not implement the event while processing the first trigger).
- For example, the regular
- It does not trigger if there is no Source (
akSource == None
), no Projectile (akProjectile == None
), and no bash attack (abBashAttack == False
). - It does not trigger for hits by Concentration spells (because they are notorious for producing way too many event triggers).
- It does not trigger for Touch or Self-cast spells (because I would typically not associate those with someone receiving an "impact").
- It does not trigger for spells that are not hostile.
- All the parameters have the same meaning as in the normal OnHit event.
- Included as of version 1.0.0.
The following events are related to (un)equipping things in the game.
Event OnSpellEquipped(Spell akSpell, ObjectReference akReference)
- This event can be implemented in ObjectReference scripts, or in ActiveMagicEffect scripts (where the Active Magic Effect would be running on the target that gets hit) or ReferenceAlias scripts (where the alias would be pointing to the target that gets hit).
- This event triggers whenever the corresponding
ObjectReference
(typicallyActor
), which is also passed into the event as the second parameter, equips aSpell
. - The first parameter is the
Spell
that was equipped. - Included as of version 2.0.0.
Event OnSpellUnequipped(Spell akSpell, ObjectReference akReference)
- This event can be implemented in ObjectReference scripts, or in ActiveMagicEffect scripts (where the Active Magic Effect would be running on the target that gets hit) or ReferenceAlias scripts (where the alias would be pointing to the target that gets hit).
- This event triggers whenever the corresponding
ObjectReference
(typicallyActor
), which is also passed into the event as the second parameter, unequips aSpell
. - The first parameter is the
Spell
that was unequipped. - Included as of version 2.0.0.
Event OnShoutEquipped(Shout akShout, ObjectReference akReference)
- This event can be implemented in ObjectReference scripts, or in ActiveMagicEffect scripts (where the Active Magic Effect would be running on the target that gets hit) or ReferenceAlias scripts (where the alias would be pointing to the target that gets hit).
- This event triggers whenever the corresponding
ObjectReference
(typicallyActor
), which is also passed into the event as the second parameter, equips aShout
. - The first parameter is the
Shout
that was equipped. - Included as of version 2.0.0.
Event OnShoutUnequipped(Shout akShout, ObjectReference akReference)
- This event can be implemented in ObjectReference scripts, or in ActiveMagicEffect scripts (where the Active Magic Effect would be running on the target that gets hit) or ReferenceAlias scripts (where the alias would be pointing to the target that gets hit).
- This event triggers whenever the corresponding
ObjectReference
(typicallyActor
), which is also passed into the event as the second parameter, unequips aShout
. - The first parameter is the
Shout
that was unequipped. - Included as of version 2.0.0.
The following events are related to items getting added to or removed from inventories.
Event OnBatchItemsAdded(Form[] akBaseItems, Int[] aiItemCounts, ObjectReference[] akSourceContainers)
- This event can be implemented in ObjectReference scripts, or in ActiveMagicEffect scripts (where the Active Magic Effect would be running on the target that gets hit) or ReferenceAlias scripts (where the alias would be pointing to the target that gets hit).
- This event triggers in similar situations as the regular game's
OnItemAdded
, i.e. when items get added to an inventory/container. However, whereas the standard event always corresponds to a single item (or stack of items of the same type) being added, this newOnBatchItemsAdded
event collects item additions that occur in an extremely short timespan (e.g., within a single frame), and puts them all together in a single Papyrus event call. - Using this event instead of the regular
OnItemAdded
event can help to avoid stressing the game's scripting engine too much in cases where the player may take a huge number of items simultaneously (for example, during the Diplomatic Immunity quest, or when re-gaining their items after getting jailed). - The three arrays passed into this event always all three have the same length (equal to the number of items that were added: this will very often just be 1!).
-
akBaseItems
is an array containing the base objects for the items that were added (analogous to the firstakBaseItem
parameter ofOnItemAdded
). -
aiItemCounts
is an array containing the counts (stack sizes) for every item that was added (analogous to the secondaiItemCount
parameter ofOnItemAdded
). -
akSourceContainers
is an array containing the source containers that the added items came from (analogous to the fourthakSourceContainer
parameter ofOnItemAdded
). - Note that there is no analogous parameter for the third (
akItemReference
) parameter ofOnItemAdded
. - This event partially respects filters defined using
AddInventoryEventFilter
. If a batch of added items does not contain include any items that match the filters, the event will not run. However, if a batch of added items contains even just a single item that matches the filters, the event will be called and the entire batch will be passed into the event. - Because, in practice, the vast majority of (potential) event calls are for only a single added item, it is still very much worthwhile to use any appropriate filters.
-
Note: for some NPCs (if they have a script with the event), it appears that the event can get called when the NPC first gets loaded (on game or cell load). I am not sure whether or not this also happens with the game's regular
OnItemAdded
event. Anyway, it is good to be aware of. - Included as of version 2.2.0.
Example 1: the following example shows a first, basic example of two script implementations that are functionally equivalent, but where the second is less likely to overburden the game's scripting engine because it uses the new event instead of the game's regular event.
; This would be a normal implementation, but is likely to cause stack dumps and dramatically reduce the
; performance of the game's scripting engine when looting a huge number of items at once.
Event OnItemAdded(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
DoSomething(akBaseItem, aiItemCount, akSourceContainer)
EndEvent
; Using the following event (INSTEAD of the one above, not in addition to it) is functionally equivalent,
; but this will not cause issues for the game's scripting engine.
Event OnBatchItemsAdded(Form[] akBaseItems, Int[] aiItemCounts, ObjectReference[] akSourceContainers)
int i = 0
While (i < akBaseItems.Length)
DoSomething(akBaseItems[i], aiItemCounts[i], akSourceContainers[i])
i = i + 1
EndWhile
EndEvent
Example 2: this second, more advanced example uses filters to further improve performance. In cases where the game's regular event is used, this can already often help a lot to reduce the risks of overburdening the scripting engine, but the risk is not entirely eliminated.
State SomeState
; Regardless of which event we decide to use down below to process added items, it is always good to add
; one or more filters (if appropriate)
Form Property SomeFormFilter auto
FormList Property SomeFormListFilter auto
Event OnBeginState()
AddInventoryEventFilter(SomeFormFilter)
AddInventoryEventFilter(SomeFormListFilter)
EndEvent
; This would be a normal implementation, but is likely to cause stack dumps and dramatically reduce the
; performance of the game's scripting engine when looting a huge number of items at once.
Event OnItemAdded(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
DoSomething(akBaseItem, aiItemCount, akSourceContainer)
EndEvent
; Using the following event (INSTEAD of the one above, not in addition to it) is functionally equivalent,
; but this will not cause issues for the game's scripting engine.
Event OnBatchItemsAdded(Form[] akBaseItems, Int[] aiItemCounts, ObjectReference[] akSourceContainers)
If (akBaseItems.Length > 1)
; If we have more than one item, the arrays we got may include items
; that don't match our filters. We can correct for that here.
int[] keepIndices = PAPER_SKSEFunctions.GetInventoryEventFilterIndices(akBaseItems, SomeFormFilter)
keepIndices = PAPER_SKSEFunctions.UpdateInventoryEventFilterIndices(akBaseItems, SomeFormListFilter, keepIndices)
akBaseItems = PAPER_SKSEFunctions.ApplyInventoryEventFilterToForms(keepIndices, akBaseItems)
aiItemCounts = PAPER_SKSEFunctions.ApplyInventoryEventFilterToInts(keepIndices, aiItemCounts)
akSourceContainers = PAPER_SKSEFunctions.ApplyInventoryEventFilterToObjs(keepIndices, akSourceContainers)
EndIf
int i = 0
While (i < akBaseItems.Length)
DoSomething(akBaseItems[i], aiItemCounts[i], akSourceContainers[i])
i = i + 1
EndWhile
EndEvent
EndState
Event OnBatchItemsRemoved(Form[] akBaseItems, Int[] aiItemCounts, ObjectReference[] akDestContainers)
- This event can be implemented in ObjectReference scripts, or in ActiveMagicEffect scripts (where the Active Magic Effect would be running on the target that gets hit) or ReferenceAlias scripts (where the alias would be pointing to the target that gets hit).
- This event triggers in similar situations as the regular game's
OnItemRemoved
, i.e. when items get removed from an inventory/container. However, whereas the standard event always corresponds to a single item (or stack of items of the same type) being removed, this newOnBatchItemsRemoved
event collects item removals that occur in an extremely short timespan (e.g., within a single frame), and puts them all together in a single Papyrus event call. - Using this event instead of the regular
OnItemRemoved
event can help to avoid stressing the game's scripting engine too much in cases where the player may lose a huge number of items simultaneously (for example, during the Diplomatic Immunity quest, or when getting jailed). - The three arrays passed into this event always all three have the same length (equal to the number of items that were removed: this will very often just be 1!).
-
akBaseItems
is an array containing the base objects for the items that were removed (analogous to the firstakBaseItem
parameter ofOnItemRemoved
). -
aiItemCounts
is an array containing the counts (stack sizes) for every item that was added (analogous to the secondaiItemCount
parameter ofOnItemRemoved
). -
akDestContainers
is an array containing the destination containers that the removed items went to (analogous to the fourthakDestContainer
parameter ofOnItemRemoved
). - Note that there is no analogous parameter for the third (
akItemReference
) parameter ofOnItemRemoved
. - This event partially respects filters defined using
AddInventoryEventFilter
. If a batch of removed items does not contain include any items that match the filters, the event will not run. However, if a batch of removed items contains even just a single item that matches the filters, the event will be called and the entire batch will be passed into the event. - Because, in practice, the vast majority of (potential) event calls are for only a single removed item, it is still very much worthwhile to use any appropriate filters.
- Included as of version 2.2.0.
Example 1: the following example shows a first, basic example of two script implementations that are functionally equivalent, but where the second is less likely to overburden the game's scripting engine because it uses the new event instead of the game's regular event.
; This would be a normal implementation, but is likely to cause stack dumps and dramatically reduce the
; performance of the game's scripting engine when losing a huge number of items at once.
Event OnItemRemoved(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
DoSomething(akBaseItem, aiItemCount, akDestContainer)
EndEvent
; Using the following event (INSTEAD of the one above, not in addition to it) is functionally equivalent,
; but this will not cause issues for the game's scripting engine.
Event OnBatchItemsRemoved(Form[] akBaseItems, Int[] aiItemCounts, ObjectReference[] akDestContainers)
int i = 0
While (i < akBaseItems.Length)
DoSomething(akBaseItems[i], aiItemCounts[i], akDestContainers[i])
i = i + 1
EndWhile
EndEvent
Example 2: this second, more advanced example uses filters to further improve performance. In cases where the game's regular event is used, this can already often help a lot to reduce the risks of overburdening the scripting engine, but the risk is not entirely eliminated.
State SomeState
; Regardless of which event we decide to use down below to process added items, it is always good to add
; one or more filters (if appropriate)
Form Property SomeFormFilter auto
FormList Property SomeFormListFilter auto
Event OnBeginState()
AddInventoryEventFilter(SomeFormFilter)
AddInventoryEventFilter(SomeFormListFilter)
EndEvent
; This would be a normal implementation, but is likely to cause stack dumps and dramatically reduce the
; performance of the game's scripting engine when losing a huge number of items at once.
Event OnItemRemoved(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
DoSomething(akBaseItem, aiItemCount, akDestContainer)
EndEvent
; Using the following event (INSTEAD of the one above, not in addition to it) is functionally equivalent,
; but this will not cause issues for the game's scripting engine.
Event OnBatchItemsRemoved(Form[] akBaseItems, Int[] aiItemCounts, ObjectReference[] akDestContainers)
If (akBaseItems.Length > 1)
; If we have more than one item, the arrays we got may include items
; that don't match our filters. We can correct for that here.
int[] keepIndices = PAPER_SKSEFunctions.GetInventoryEventFilterIndices(akBaseItems, SomeFormFilter)
keepIndices = PAPER_SKSEFunctions.UpdateInventoryEventFilterIndices(akBaseItems, SomeFormListFilter, keepIndices)
akBaseItems = PAPER_SKSEFunctions.ApplyInventoryEventFilterToForms(keepIndices, akBaseItems)
aiItemCounts = PAPER_SKSEFunctions.ApplyInventoryEventFilterToInts(keepIndices, aiItemCounts)
akDestContainers = PAPER_SKSEFunctions.ApplyInventoryEventFilterToObjs(keepIndices, akDestContainers)
EndIf
int i = 0
While (i < akBaseItems.Length)
DoSomething(akBaseItems[i], aiItemCounts[i], akDestContainers[i])
i = i + 1
EndWhile
EndEvent
EndState