Jump to content

[LE] [Please review] multi-target travel script for multiple actors


Hex6168

Recommended Posts

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

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 || etc

Only 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 by FrankFamily
Link to comment
Share on other sites

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

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

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 by ReDragon2013
Link to comment
Share on other sites

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

  • Recently Browsing   0 members

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