KingsGambit Posted October 8, 2012 Share Posted October 8, 2012 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 More sharing options...
Recommended Posts