Hex6168 Posted October 1, 2018 Share Posted October 1, 2018 I'm trying to create a generic script I can attach to multiple NPCs that causes them to travel across skyrim with unique actors as their targets. Here is what I have so far, and I was wondering if anyone sees any obvious problems this would cause? I decided to use a script instead of AI packages due to how buggy and messed up I've read that package execution gets when they're less than simple. The big concern I have is how 'PathToReference' stops the scripts execution until it completes. Is this a legitimate concern? Do I need to worry about how RegisterForSingleUpdateGameTime works while the script is paused? Here's the script so far: Scriptname BNC_MerchantScript extends Actor Import Utility ;for travel and switching in/out of sandbox mode (not yet implemented) DestinationMerchant should be initially set to editorloc's target merchant Bool IsTraveling ObjectReference Property DestinationMerchant Auto ;set merchants that this NPC will go between here, point unused to NONE ObjectReference Property Merchant1 Auto ObjectReference Property Merchant2 Auto ObjectReference Property Merchant3 Auto ObjectReference Property Merchant4 Auto ObjectReference Property Merchant5 Auto ObjectReference Property Merchant6 Auto ObjectReference Property Merchant7 Auto ;set unused TravelDays to 8 NPC will travel to next location on each of these days Int Property TravelDay1 Auto Int Property TravelDay2 Auto Int Property TravelDay3 Auto Int Property TravelDay4 Auto Int Property TravelDay5 Auto Int Property TravelDay6 Auto Int Property TravelDay7 Auto ObjectReference NewMerchant ;just sets new target, empty properties should be kept toward end so that all values are equal and resets to Merchant1 Function GetNewTarget() If(DestinationMerchant == Merchant1) NewMerchant = Merchant2 EndIf If(DestinationMerchant == Merchant2) NewMerchant = Merchant3 EndIf If(DestinationMerchant == Merchant3) NewMerchant = Merchant4 EndIf If(DestinationMerchant == Merchant4) NewMerchant = Merchant5 EndIf If(DestinationMerchant == Merchant5) NewMerchant = Merchant6 EndIf If(DestinationMerchant == Merchant6) NewMerchant = Merchant7 EndIf If(DestinationMerchant == Merchant7) NewMerchant = Merchant1 EndIf EndFunction ;walks NPC to target merchant, will be altered in future to produce less mundane behavior Function StartTravel() DestinationMerchant = NewMerchant NewMerchant = NONE PathToReference(DestinationMerchant, 0.5) EndFunction ;checks if NPC is on its travel day, will eventually set a sandbox mode that sends the NPC to the town's inn to poke about, will eventually support multiple travel days Function TravelCheck(int Int1, int Int2, int Int3, int Int4, int Int5, int Int6, int Int7) int CurrentDay = ((GetCurrentGameTime() as int) % 7) If((CurrentDay == Int1 || Int2 || Int3 || Int4 || Int5 || Int6 || Int7) && IsTraveling == False) IsTraveling = True GetNewTarget() StartTravel() Else IsTraveling = False EndIf EndFunction Event OnInit() TravelCheck(TravelDay1, TravelDay2, TravelDay3, TravelDay4, TravelDay5, TravelDay6, TravelDay7) RegisterForSingleUpdateGameTime(24.0) EndEvent Event OnUpdateGameTime() TravelCheck(TravelDay1, TravelDay2, TravelDay3, TravelDay4, TravelDay5, TravelDay6, TravelDay7) RegisterForSingleUpdateGameTime(24.0) EndEvent Link to comment Share on other sites More sharing options...
FrankFamily Posted October 1, 2018 Share Posted October 1, 2018 (edited) I think that in papyrus: If((CurrentDay == Int1 || Int2 || Int3 || Int4 || Int5 || Int6 || Int7) && IsTraveling == False)Is going to do: If ((CurrentDay == Int1 || (Int2 as Bool) == TRUE || etcOnly checking the first int against CurrentDay and then checking that the others are different than zero . Instead of checking all Ints against CurrentDay which I think is what you want to do. If so, do the more explicit: If (CurrentDay == Int1 || CurrentDay == int2 || etc) Edited October 1, 2018 by FrankFamily Link to comment Share on other sites More sharing options...
IsharaMeradin Posted October 1, 2018 Share Posted October 1, 2018 If you just want to correct what you have, FrankFamily should have you covered with what he explained. However, I was looking at your script. I thought to myself what if they might want to add more destinations at some point. I tweaked the script a bit and replaced your explicit lists with some arrays and looping through those arrays. You may try it out or not as you see fit. I left your code that I replaced commented out so you could compare the flow etc... Scriptname BNC_MerchantScript extends Actor Import Utility ;for travel and switching in/out of sandbox mode (not yet implemented) DestinationMerchant should be initially set to editorlocs target merchant Bool IsTraveling ObjectReference Property DestinationMerchant Auto ;set merchants that this NPC will go between here, point unused to NONE ;ObjectReference Property Merchant1 Auto ;ObjectReference Property Merchant2 Auto ;ObjectReference Property Merchant3 Auto ;ObjectReference Property Merchant4 Auto ;ObjectReference Property Merchant5 Auto ;ObjectReference Property Merchant6 Auto ;ObjectReference Property Merchant7 Auto ObjectReference[] Property MerchantArray Auto ;only assign those necessary ;set unused TravelDays to 8 NPC will travel to next location on each of these days ;Int Property TravelDay1 Auto ;Int Property TravelDay2 Auto ;Int Property TravelDay3 Auto ;Int Property TravelDay4 Auto ;Int Property TravelDay5 Auto ;Int Property TravelDay6 Auto ;Int Property TravelDay7 Auto Int[] Property TravelDayArray Auto ;only assign those necessary ;ObjectReference NewMerchant ;just sets new target, empty properties should be kept toward end so that all values are equal and resets to Merchant1 Function GetNewTargetAndTravel() Int size = MerchantArray.Length Int index = 0 Int OneToUse = -1 While index < size If DestinationMerchant == MerchantArray[index] If (index + 1) < size OneToUse = index + 1 ;send to next in list Else OneToUse = 0 ;send to first in list EndIf index = size ; got what we need, get out of loop EndIf index += 1 EndWhile PathToReference(MerchantArray[OneToUse], 0.5) EndFunction ;Function GetNewTarget() ;If(DestinationMerchant == Merchant1) ;NewMerchant = Merchant2 ;EndIf ;If(DestinationMerchant == Merchant2) ;NewMerchant = Merchant3 ;EndIf ;If(DestinationMerchant == Merchant3) ;NewMerchant = Merchant4 ;EndIf ;If(DestinationMerchant == Merchant4) ;NewMerchant = Merchant5 ;EndIf ;If(DestinationMerchant == Merchant5) ;NewMerchant = Merchant6 ;EndIf ;If(DestinationMerchant == Merchant6) ;NewMerchant = Merchant7 ;EndIf ;If(DestinationMerchant == Merchant7) ;NewMerchant = Merchant1 ;EndIf ;EndFunction ;walks NPC to target merchant, will be altered in future to produce less mundane behavior ;Function StartTravel() ;DestinationMerchant = NewMerchant ;NewMerchant = NONE ;PathToReference(DestinationMerchant, 0.5) ;EndFunction ;checks if NPC is on its travel day, will eventually set a sandbox mode that sends the NPC to the towns inn to poke about, will eventually support multiple travel days Function TravelCheck() Int CurrentDay = ((GetCurrentGameTime() as int) % 7) Int size = TravelDayArray.Length Int index = 0 While index < size If CurrentDay == TravelDayArray[index] IsTraveling = true index = size ; got what we need, stop loop Else IsTraveling = false EndIf index += 1 EndWhile If IsTraveling == true GetNewTargetAndTravel() EndIf EndFunction ;Function TravelCheck(int Int1, int Int2, int Int3, int Int4, int Int5, int Int6, int Int7) ;int CurrentDay = ((GetCurrentGameTime() as int) % 7) ;If((CurrentDay == Int1 || Int2 || Int3 || Int4 || Int5 || Int6 || Int7) && IsTraveling == False) ;IsTraveling = True ;GetNewTarget() ;StartTravel() ;Else ;IsTraveling = False ;EndIf ;EndFunction Event OnInit() ;TravelCheck(TravelDay1, TravelDay2, TravelDay3, TravelDay4, TravelDay5, TravelDay6, TravelDay7) TravelCheck() RegisterForSingleUpdateGameTime(24.0) EndEvent Event OnUpdateGameTime() ;TravelCheck(TravelDay1, TravelDay2, TravelDay3, TravelDay4, TravelDay5, TravelDay6, TravelDay7) TravelCheck() RegisterForSingleUpdateGameTime(24.0) EndEvent Link to comment Share on other sites More sharing options...
Hex6168 Posted October 1, 2018 Author Share Posted October 1, 2018 Thank you guys for the input. Frank was correct about the conditions for the TravelCheck function, and looping Ishara suggested reduces a lot of unecessary property settings for the couple of test NPCs I made for this. What I'm doing now is following them around to see how the PathToReference function works to see if this really will end up producing more reliable behavior than packages. The one I'm going to be following right now begins in Whiterun, spends days 1-3 visiting different shops in town, goes to Alvor in Riverwood on day 4, back to whiterun to visit Adrianne on day 5, goes to Rorikstead's inn on day 6, then returns to whiterun to visit Eorlund on day 7. A question regarding Packages for complicated NPCs: Is there a way to add or remove AI packages through script alone, or will I need to push the NPCs into alias' to get "toggled" AI Packages? Link to comment Share on other sites More sharing options...
ReDragon2013 Posted October 1, 2018 Share Posted October 1, 2018 (edited) Try to use ReferenceAlias scripts instead of actor scripts. Maybe next is useful. BNC_MerchantScript Scriptname BNC_MerchantScript extends ReferenceAlias {IsharaMeradin + ReDragon 2018} ; make a quest and use for each of your traveller NPCs an own alias ; https://forums.nexusmods.com/index.php?/topic/7033931-please-review-multi-target-travel-script-for-multiple-actors/ ; Hex6168 wrote: "I'm trying to create a generic script I can attach to multiple NPCs that causes them to travel across Skyrim with unique actors as their targets" ; === only assign those necessary === ;set merchants that this NPC will go between here, point unused to NONE ObjectReference[] PROPERTY MerchantArray auto ; 7 merchants ;set unused TravelDays to 8 NPC will travel to next location on each of these days Int[] PROPERTY TravelDayArray auto ; 7 days (TravelDay1, TravelDay2, TravelDay3, TravelDay4, TravelDay5, TravelDay6, TravelDay7) ; is he/she travelling Bool PROPERTY bTravel auto Hidden ; [default=False] ; -- EVENTs -- 3 EVENT OnInit() RegisterForSingleUpdate(1.0) ; Do not overwhelming the OnInit() event !!! ENDEVENT EVENT OnUpdate() TravelCheck() ENDEVENT EVENT OnUpdateGameTime() TravelCheck() ENDEVENT EVENT OnDeath(Actor akKiller) gotoState("Done") ; is triggered when this actor finishes dying ENDEVENT ;========================== state Done ;========= EVENT OnUpdateGameTime() ENDEVENT ;======= endState ; -- FUNCTIONs -- 2 ;--------------------- FUNCTION TravelCheck() ;--------------------- ;checks if NPC is on its travel day, will eventually set a sandbox mode that sends the NPC to the towns inn to poke about, ;will eventually support multiple travel days int CurrentDay = (Utility.GetCurrentGameTime() as Int) % 7 int n = TravelDayArray.Length ; n should be 7, TravelDay1 .. TravelDay7 int i = 0 WHILE (i < n) IF (CurrentDay == TravelDayArray[i]) IF (i == n) i = 0 ; resets to Merchant1 ELSE i = i + 1 ENDIF SetNewTargetAndTravelTo(i) i = n ; got what we need, stop loop ENDIF i = i + 1 ENDWHILE If ( bTravel ) ELSE Debug.Trace(self + "No travel available!!") ENDIF RegisterForSingleUpdateGameTime(24.0) ENDFUNCTION ;-------------------------------------- FUNCTION SetNewTargetAndTravelTo(Int j) ;-------------------------------------- ;just sets new target objectReference targetRef = MerchantArray[j] IF ( targetRef ) bTravel = self.GetActorReference().PathToReference(targetRef, 0.5) ; This function is latent. ; It will suspend the scripts execution until the path is either complete or failed/was interrupted. RETURN ; - STOP - merchant is a valid target, nothing more to do ENDIF ;--------------------- try to find a valid merchant int n = MerchantArray.Length ; n should be 7, Merchant1 .. Merchant7 int i = Utility.RandomInt(0, n - 1) ; i = 0 WHILE (i < n) && (!targetRef) targetRef = MerchantArray[i] IF ( targetRef ) i = n ; got what we need, stop loop ENDIF i = i + 1 ENDWHILE IF ( targetRef ) bTravel = self.GetActorReference().PathToReference(targetRef, 0.5) ELSE bTravel = False ; something goes horrible wrong ENDIF ENDFUNCTION You asked: "Is there a way to add or remove AI packages through script alone?" NO. Edited October 1, 2018 by ReDragon2013 Link to comment Share on other sites More sharing options...
foamyesque Posted October 1, 2018 Share Posted October 1, 2018 Aliases are basically the way to add packages to actors. The only other one I'm aware of is by editing the NPCs directly, which is a conflict-prone option. As far as toggling packages goes you can apply conditions, which gives you a wide variety of possibilities. Link to comment Share on other sites More sharing options...
Recommended Posts