Jump to content

My custom summon scripts


Recommended Posts

Having finally released my Golden Saints mod, I thought I would share my scripts in case another modder struggles to find much info on similar challlenges to those I faced. My main difficulty stemmed from the simple fact that I wanted my spell to be able to summon different Actors (of equal level) from a list at random. The standard conjuration spell will only ever summon a single (levelled) NPC, though it obviously has the benefits of handling duration, perks/skill/dual casting modifiers, stacking and VFX transparently.

 

To get my custom Summon spell working, in the CK I had to create a custom projectile. The projectile impact placed a custom explosion and the explosion in turn placed a custom activator. The activator had the following script:

 

FormList Property SummonNPCsList auto

Event OnLoad()

int RandomNPC = Utility.RandomInt(0, (SummonNPCsList.GetSize() - 1))
ActorBase NPC = (SummonNPCsList.GetAt(RandomNPC) as ActorBase)
Self.PlaceActorAtMe(NPC)
Utility.Wait(4)
Self.DisableNoWait()
Self.Delete()

EndEvent

I have a custom FormList with all my Actors in it. I generated a random number using Utility.RandomInt between 0 and the last index in the list and spawned a new copy of the random actor.

 

On all my actors, is attached a script as follows:

GlobalVariable Property SummonedNPC auto
Explosion Property ExplosionNPCDeath auto

Event OnLoad()

Self.SetRelationshipRank(Game.GetPlayer(), 3)
Self.SetPlayerTeammate()
SummonedNPC.SetValue(1)
RegisterForUpdate(90)

EndEvent


Event OnDeath(Actor akKiller)

SummonedNPC.SetValue(0)
NPCDeath(Self)

EndEvent

Event OnUpdate()

SummonedNPC.SetValue(0)
NPCDeath(Self)

endEvent

function NPCDeath(Actor NPCToKill)

UnregisterForUpdate()
NPCToKill.PlaceAtMe(ExplosionNPCDeath)
NPCToKill.Disable(True)
Utility.Wait(1)
NPCToKill.Delete()

endfunction

With the above, OnLoad sets my NPC to be a teammate and ally of the player, changes a global variable so i could track if one already existed should the spell be cast again and registered for an update after 90 seconds (the spell duration) to "banish" the NPC if the spell timer expires, or if they are killed, with a second custom explosion (purely cosmetic) when they disappear.

 

This only worked as long as the player didn't cast the spell a second time before the first had run out. My major problem was how to banish the actor if the spell is cast again. Using OnDeath didn't help because then, my global variable got set to zero, despite another NPC being alive. In the end, it took a second global variable and a 1 second update time to fix. I needed another state to check for, in addition to "dying" and "time ran out". I managed it as follows. In the activator script:

GlobalVariable Property SummonedNPC auto
GlobalVariable Property SecondNPC auto
FormList Property SummonNPCsList auto

Event OnLoad()

if (SummonedNPC.GetValue() == 1)
	SecondNPC.SetValue(1)
	Utility.Wait(1)
endif

int RandomNPC = Utility.RandomInt(0, (SummonNPCsList.GetSize() - 1))
ActorBase NPC = (SummonNPCsList.GetAt(RandomNPC) as ActorBase)
Self.PlaceActorAtMe(NPC)
Utility.Wait(4)
Self.DisableNoWait()
Self.Delete()

EndEvent

If the first global variable returns true, it means I have an NPC active already. So I change my second global variable. Also, note the one second pause. And on the Actor script:

 

GlobalVariable Property SummonedNPC auto
GlobalVariable Property SecondNPC auto
Explosion Property ExplosionNPCDeath auto
int Count

Event OnLoad()

Self.SetRelationshipRank(Game.GetPlayer(), 3)
Self.SetPlayerTeammate()
SummonedNPC.SetValue(1)
Count = 0
RegisterForUpdate(1)

EndEvent


Event OnDeath(Actor akKiller)

SummonedNPC.SetValue(0)
NPCDeath(Self)

EndEvent

Event OnUpdate()

if (SecondNPC.GetValue() == 1)
	SecondNPC.SetValue(0)
	NPCDeath(Self)
elseif (Count < 90)
	Count +=1
else
	SummonedNPC.SetValue(0)
	NPCDeath(Self)
endif

endEvent

function NPCDeath(Actor NPCToKill)

UnregisterForUpdate()
NPCToKill.PlaceAtMe(ExplosionNPCDeath)
NPCToKill.Disable(True)
Utility.Wait(1)
NPCToKill.Delete()

endfunction

The OnLoad now initialises a timer for the spell and registers for an update every second. OnDeath remains unchanged. OnUpdate however, every second the actor checks my second global variable. If it returns true at any point, it resets it to 0, and crucially leaving the first global alone, and banishes himself. Otherwise, the count will continue to the spell duration, at which point the NPC will disappear. The one second pause in the Activator gives the actor script the time it needs to resolve itself. The other two causes of "death", namely dying and duration reached, are unchanged and reset the first global to 0. It will always resolve itself in the correct order.

 

A big positive of this way is that I can summon randomly from a list and the NPCs aren't flagged as a CommandedActor. This could be used for example, to summon an NPC with dialogue (instead of that incessant zombie-like moaning!), a merchant, packmule, an "arena" or something else. The downside is that as it is now, these stack with vanilla summons as I'm not checking if one already exists. It may be relatively straightforward to do, along the lines of:

 

- Check for Twin Souls Summon active? -> Banish it.

- Check for Second Summon active? -> Mark it as Twin Souls Summon

- Summon my Actor -> Mark as Second Summon if step 2 was true. Else mark as Twin Souls Summon.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...