Papyrus - Advanced Spell Creation
The first part begins with the set up. Here's what we need to do:
- Modify a spell that has an explosion (Fireball for example) to create a custom spell
- Modify the MagicEffect it references, making it your own MagicEffect, with Detrimental flag unchecked and everything else the same
Now for the second part, we need to add a Quest that will act as the controller / receiver for spell hit events. After this we merely need to customize the properties we scripted, as demonstrated in the video. Note that the spell as it is here was designed for demonstration purposes, and I leave it up to the reader to extend it any further. There are a few things that can be done with that in mind, such as maintaining proximity to every NPC that the orb attacks, using TranslateTo and averaging the NPC locations to generate a centroid. You can also improve the robustness of the event handling and so forth.
Here is the script for the Quest
Scriptname AAASpellControlScript extends Quest
;by EB / Xoleras xoleras.com - leave this here please! :)
float Property maxDist = 768.0 Auto
Activator Property AAASphere Auto
ObjectReference[] spheres = None
Actor[] extraActors
bool lock = false
Event OnUpdate()
int i = 0
while (i < extraActors.Length)
if (extraActors[i] && !extraActors[i].IsDead())
int j = 0
while (j < spheres.Length)
if (spheres[j] && spheres[j].GetDistance(extraActors[i]) < maxDist)
(spheres[j] As AAASphereScript).AddActor(extraActors[i])
endIf
j += 1
endWhile
endIf
i += 1
endWhile
endEvent
function HitEvent(Actor A)
if (!lock)
lock = true
if (spheres)
int i = 0
while (i < spheres.Length)
if (spheres[i] && spheres[i].GetDistance(A) < maxDist)
(spheres[i] As AAASphereScript).AddActor(A)
endIf
i += 1
endWhile
if (i == spheres.Length)
int index = GenerateSphere(A)
(spheres[index] As AAASphereScript).AddActor(A)
endIf
else
;create new sphere
spheres = new ObjectReference[16]
int index = GenerateSphere(A)
(spheres[index] As AAASphereScript).AddActor(A)
endIf
lock = false
else
if (!extraActors)
extraActors = new Actor[16]
endIf
int i = 0
while (i < extraActors.Length)
if (!extraActors[i] || extraActors[i].IsDead())
extraActors[i] = A
i = extraActors.Length
endIf
i += 1
endWhile
RegisterForSingleUpdate(0.2)
endIf
endFunction
int function GenerateSphere(ObjectReference spawnPoint)
int i = 0
while (i < spheres.Length)
if (!spheres[i])
spheres[i] = spawnPoint.PlaceAtMe(AAASphere)
spheres[i].SetPosition(spheres[i].X, spheres[i].Y, spheres[i].Z + 384.0)
return i
endIf
i += 1
endWhile
endFunction
And here is the code for the Activator
Scriptname AAASphereScript extends ObjectReference
;by EB / Xoleras xoleras.com - leave this here please! :)
Spell Property streamSpell Auto
int maxHits = 10
float timeDelta = 0.1
float timeDelay= 0.4
Actor[] targetList
int count = 0
Event OnUpdate()
maxHits -= 1
if (targetList)
LaunchStream()
endIf
if (maxHits > 0)
RegisterForSingleUpdate(timeDelta)
else
Disable(true)
utility.wait(2)
Delete()
endIf
endEvent
Event OnLoad()
RegisterForSingleUpdate(timeDelta)
endEvent
function AddActor(Actor A)
if (!targetList)
targetList = new Actor[16]
endIf
if (count < 16)
targetList[count] = A
count += 1
endIf
endFunction
function LaunchStream()
int i = 0
while (i < count)
if (targetList[i] && !targetList[i].IsDead())
streamSpell.Cast(self, targetList[i])
utility.wait(timeDelay)
InterruptCast()
endIf
i += 1
endWhile
endFunction
Finally, add the script to the MagicEffect. We need to add a Quest property and we need to add the OnEffectStart event that fires when the spell hits a target. Before you compile the ActiveMagicEffect script, you must first compile the Activator script then the Quest script.
Quest Property ControlQuest Auto
Event OnEffectStart(Actor victim, Actor caster)
if (victim)
(ControlQuest As AAASpellControlScript).HitEvent(victim)
endIf
endEvent
Make sure that the scripts are actually added to the objects that we intended to add them to, otherwise errors will be given and the spell will not function.