I have been getting every single day a little bit more adept with scripting (thanks to very helpful users in this forum) and now I slowly manage to get my scripts to do more complicated things how I want and when I want. This is great, but I'm starting to hit a problem which is more about organisation than scripting itself... I'm getting lost in my script!!!
Although my script is not too long (around 130 lines), I'm getting lost in it. Also, due to the many different conditions inside the same script, changing a simple if function can easily break my script and have me 3 hours trying to repair it...
So, I'm asking for advice on how to organize a script for better use, easier editing, and optimization...
I'm sure there are explanations already flying around, but I'm having a hard time finding them. In my case, my mod is set up like the following:
Two quests, one is not "start game enabled" and contains aliases, scenes, dialogues, etc... The other one is "start game enabled" and contains only a single main script that handles all the info in the other quest. Apart from this, I have several spells and dummy objects for achieving several things (Example: find LOS with nearby NPC's).
Where I'm having real troubles, though, is with my main script. I need to find a way to organise it better before it gets out of hand.
The script right now starts right on game start and contains an OnInit() event with a RegisterForUpdate(3) inside. Then follows an OnUpdate() event, and I have in here absolutely all my code except functions, which are separated at the bottom. This means that I have a bunch of nested if's all working fairly well together that decide what to do with the NPC's, scenes and dialogues.
The thing is that now I want to add more scenes which make different things happen depending on several conditions, and this script will soon be messy. How would be best to organize this? I'm starting to look into states, but can I run OnUpdate() events in each different state? Would it be better to have a script for each situation I want to have playing (would have to create more aliases and properties...)? A single quest for each possible scene, or all scenes in a single quest? Is there a better way?
Sorry for the wall of text, but I hope someone can give me some advice on this...
My main script is the one following. I know it's messy and that there are still checks that aren't defined. This script is controlling at the moment one single scene, alias, and a single dialogue, but I need to add more... If you look at the script, at the moment it fires only, per example, between 6PM and 6AM, but I will want to add conditions so that during the day something different happens, or maybe if in a different location, or depending on player reputation... All in the same script and organized like this??? Hope you get the picture...
Spoiler
Scriptname AAaaDielosRStarter extends Quest ConditionalQuest Property AADielosRQuest AutoQuest Property AADielosStarterQuest Auto Alias Property AliasAttacker AutoScene Property SceneFollow Auto Package Property PackageKeepEye AutoPackage Property PackageForceGreet Autoint Property ApproachToTalk Auto Conditionalint Property SceneStarted Auto Conditionalint Property DropItemsVar Auto ConditionalSPELL Property FindLOSSpell AutoFormList Property AAStalkerFindLOSFormList Autoint SceneUpdateCount = 0EVENT onInit()registerForUpdate(3)gotoState ("Start")endEVENTSTATE Start EVENT onUpdate() ;***********************************************************Things to check every loop if DropItemsVar == 1 DropEquippedItems() ;Test, should end being a steal non equiped equipment function EndIf if SceneStarted == 0 ;***********************************************************Here come conditions to start Stalkers like time of day and location (check keyword locations in wiki) Debug.Notification("Starting quest, scene, and trying to grab aliases...") if AADielosRQuest.IsRunning() == False AADielosRQuest.Start() EndIf if (AliasAttacker As ReferenceAlias).GetRef() == None || (AliasAttacker As ReferenceAlias).GetActorReference().IsDead() || ApproachToTalk != 1 ResetQuest() endif if AADielosRQuest.IsRunning() && SceneStarted == 0 && (AliasAttacker As ReferenceAlias).GetRef() != None && ApproachToTalk != 1 float TimeOfDay = GetCurrentHourOfDay() int RandomChance = Utility.RandomInt() ;Not working or not applying correctly... if TimeOfDay >= 18 || TimeOfDay <= 6 && RandomChance <= 1 Debug.Notification("Alias found during the night and starting scene...") SceneFollow.Start() SceneStarted = 1 Else Debug.Notification("No stalkers during the day at the moment...") ResetQuest() EndIf endif EndIf if SceneStarted == 1 if (AliasAttacker As ReferenceAlias).GetActorReference().GetCurrentPackage() == PackageKeepEye DebugPrintStalkerName() ;Print stalker name for debugging... ;***********************************************************Here come conditions to ForceGreet FindLOSSpell.cast(Game.GetPlayer()) ;Debug.Notification("Firing Spell...") Utility.Wait(1) if AAStalkerFindLOSFormList.GetSize() == 1 && AAStalkerFindLOSFormList.HasForm((AliasAttacker As ReferenceAlias).GetReference()) SceneUpdateCount = AAStalkerFindLOSFormList.GetSize() Debug.Notification("The LOS list has " + SceneUpdateCount + " and the only entry is the Stalker, so it applies...") ApproachToTalk = 1 Debug.Notification("ForceGreet should start now...") ElseIf AAStalkerFindLOSFormList.GetSize() == 0 SceneUpdateCount = AAStalkerFindLOSFormList.GetSize() Debug.Notification("The LOS list has " + SceneUpdateCount + " and the Stalker is still following, so it applies...") ApproachToTalk = 1 Debug.Notification("ForceGreet should start now...") Else ;SceneUpdateCount = AAStalkerFindLOSFormList.GetSize() ;Debug.Notification("The LOS list has " + SceneUpdateCount + " entries, thus doesn't apply for ForceGreet...") EndIf SceneUpdateCount = 0 AAStalkerFindLOSFormList.Revert() ;***********************************************************Here come conditions to abort Scene if Game.GetPlayer().GetDistance((AliasAttacker As ReferenceAlias).GetReference()) > 10000 || (AliasAttacker As ReferenceAlias).GetRef() == None || (AliasAttacker As ReferenceAlias).GetActorReference().IsDead() Debug.Notification("Stalker lost victim...") ResetQuest() EndIf Elseif ApproachToTalk != 1 && SceneStarted == 0 ResetQuest() EndIf EndIfEndEventEndState;*************************** F U N C T I O N S **********************************string Function DebugPrintStalkerName()String AliasName = (AliasAttacker As ReferenceAlias).GetReference().GetBaseObject().GetName()if AliasName == "" Debug.Notification("The stalker is a stranger...")Else Debug.Notification("The stalker is: " + AliasName)EndIfEndFunctionfloat Function GetCurrentHourOfDay()float Time = Utility.GetCurrentGameTime()Time -= Math.Floor(Time) ; Remove "previous in-game days passed" bitTime *= 24 ; Convert from fraction of a day to number of hoursReturn TimeEndFunctionint function ResetQuest();**************Add Cooldown token to alias before clearing!!!(AliasAttacker As ReferenceAlias).Clear()AADielosRQuest.Stop()Utility.Wait(1)AADielosRQuest.Start()SceneFollow.Stop()SceneStarted = 0endFunctionint function DropEquippedItems() ;Testing finding equipped items, can see if result fires if player nakedint slot = 1int iwhile (i < 32) Form TempForm = Game.GetPlayer().GetWornForm(slot) if (TempForm) Game.GetPlayer().DropObject(TempForm) endif slot *= 2 i += 1endwhileDropItemsVar = 0endFunction
Thanks in advance!!!