Jump to content

Help with NPC Looting Bodies Script


Recommended Posts

Hi there.

 

I'm working on a script to make it so npcs loot bodies around the player character. My script compiles and executes in the game but it doesn't work. If someone could give me a hand i would really appreciate it.

 

Here is the code:

Scriptname NPCLootingScript extends ReferenceAlias

Actor Property PlayerRef Auto Const
Actor Property Corpse1 Auto
Actor Property Corpse2 Auto
Actor Property Corpse3 Auto
Actor Property Looter1 Auto
Actor Property Looter2 Auto
Actor Property Looter3 Auto
int Property TimerDuration Auto Const
Keyword Property ActorTypeNPC Auto
Keyword Property RELootedCorpse Auto Const
int Property distance Auto Const
ObjectReference[] Property NPCs Auto

Event OnInit()
	Self.StartTimer(TimerDuration, 0)
EndEvent

Event OnTimer(int aiTimerID)

Debug.Notification("Script running")

If !PlayerRef.IsInCombat()
		NPCs = PlayerRef.FindAllReferencesWithKeyword(ActorTypeNPC, distance)
		int i = 0
		
	While i < NPCs.Length
		Actor MyActor = NPCs[i] as Actor
		ActorBase PlayerBase = MyActor.GetActorBase()
		String name = PlayerBase.GetName() as String
		Debug.Notification("Found " + name + " in" + i as string)
			if MyActor == PlayerRef
				NPCs.Remove(i)
			ElseIf MyActor.IsDead() && !MyActor.HasKeyword(RELootedCorpse)
				if !Corpse1
					Corpse1 = MyActor
				ElseIf !Corpse2
					Corpse2 = MyActor				
				ElseIf !Corpse3
					Corpse3 = MyActor
				EndIf
			Else
				if !Looter1
					Looter1 = MyActor
				ElseIf !Looter2
					Looter2 = MyActor		
				ElseIf !Looter3
					Looter3 = MyActor
				EndIf
			EndIf
		i+=1
	EndWHile

	If Looter1 && Corpse1
		Looter1.PathToReference(Corpse1, 1.0)
	EndIf

	If Looter2 && Corpse2
		Looter2.PathToReference(Corpse2, 1.0)
	EndIf

	If Looter3 && Corpse3
		Looter1.PathToReference(Corpse3, 1.0)
	EndIf

	If Looter1 && Corpse1
		Corpse1.RemoveAllItems(Looter1)
		Corpse1.AddKeyword(RELootedCorpse)
	EndIf

	If Looter2 && Corpse2
		Corpse2.RemoveAllItems(Looter2)
		Corpse2.AddKeyword(RELootedCorpse)
	EndIf

	If Looter3 && Corpse3
		Corpse3.RemoveAllItems(Looter3)
		Corpse3.AddKeyword(RELootedCorpse)
	EndIf
EndIf
Self.StartTimer(TimerDuration, 0)
EndEvent

This is attached into the player reference alias in a quest.

Link to comment
Share on other sites

  • Replies 40
  • Created
  • Last Reply

Top Posters In This Topic

If I had to guess I would say that the OnInit runs on the reference, and the player's reference is already initialized when this script runs, so the timer never happens.

Link to comment
Share on other sites

I wouldn't use 0 as Timer ID and what is the value of TimerDuration?

int Property TimerDuration = X Auto Const
int Property TimerID = Y Auto Const   ;Y!=0

Event OnInit()
   StartTimer(TimerDuration, TimerID)
   debug.notification("My Alias is initialized.")
EndEvent

OnTimer(int aiTimerID)
   if aiTimerID == TimerID
       StartTimer(TimerDuration, TimerID)
       debug.notification("My Timer is expired.")
   endif
EndEvent

What reference does the Alias points at? The Player? If not, does the Alias get cleared?

Edited by LarannKiar
Link to comment
Share on other sites

I wouldn't use 0 as Timer ID and what is the value of TimerDuration?

int Property TimerDuration = X Auto Const
int Property TimerID = Y Auto Const   ;Y!=0

Event OnInit()
   StartTimer(TimerDuration, TimerID)
   debug.notification("My Alias is initialized.")
EndEvent

OnTimer(int aiTimerID)
   if aiTimerID == TimerID
       StartTimer(TimerDuration, TimerID)
       debug.notification("My Timer is expired.")
   endif
EndEvent

What reference does the Alias points at? The Player? If not, does the Alias get cleared?

Here is a screenshot: Script01.jpg

 

Now about the timer id, it doesn't matter outside the script, it could be any number. Still at this point i'm desperated so i changed it to 5 (both at the beginning and the end) and it's the same. Damn i could be doing some real work if i were not stuck with this :/

 

TimerDuration is set to 30 seconds, hope its enough time to process the npcs pathing but i can't test this yet as i'm unable to make this to work.

Link to comment
Share on other sites

Looter variables or corpse variables are not being assigned properly. Even if the actor failed to path to their target, the items should still be moved to them. Also, PathToReference is latent meaning it blocks script execution until it's done so your code should be like this:

Scriptname NPCLootingScript extends ReferenceAlias

Actor Property PlayerRef Auto Const
Actor Property Corpse1 Auto
Actor Property Corpse2 Auto
Actor Property Corpse3 Auto
Actor Property Looter1 Auto
Actor Property Looter2 Auto
Actor Property Looter3 Auto
int Property TimerDuration Auto Const
Keyword Property ActorTypeNPC Auto
Keyword Property RELootedCorpse Auto Const
int Property distance Auto Const
ObjectReference[] Property NPCs Auto

Event OnInit()
    Self.StartTimer(TimerDuration, 0)
EndEvent

Event OnTimer(int aiTimerID)

Debug.Notification("Script running")

If !PlayerRef.IsInCombat()
        NPCs = PlayerRef.FindAllReferencesWithKeyword(ActorTypeNPC, distance)
        int i = 0
        
    While i < NPCs.Length
        Actor MyActor = NPCs[i] as Actor
        ActorBase PlayerBase = MyActor.GetActorBase()
        String name = PlayerBase.GetName() as String
        Debug.Notification("Found " + name + " in" + i as string)
            if MyActor == PlayerRef
                NPCs.Remove(i)
            ElseIf MyActor.IsDead() && !MyActor.HasKeyword(RELootedCorpse)
                if !Corpse1
                    Debug.Notification("Corpse1 Assigned")   <----
                    Corpse1 = MyActor
                ElseIf !Corpse2
                    Corpse2 = MyActor                
                ElseIf !Corpse3
                    Corpse3 = MyActor
                EndIf
            Else
                if !Looter1
                    Debug.Notification("Looter1 Assigned")   <----
                    Looter1 = MyActor
                ElseIf !Looter2
                    Looter2 = MyActor        
                ElseIf !Looter3
                    Looter3 = MyActor
                EndIf
            EndIf
        i+=1
    EndWHile

    If Looter1 && Corpse1
       Debug.Notification("Looter1 is moving") <----
       Looter1.PathToReference(Corpse1, 1.0)
       Corpse1.RemoveAllItems(Looter1)
       Corpse1.AddKeyword(RELootedCorpse)
       Debug.Notification("Looter1 is done looting") <----
    EndIf

    If Looter2 && Corpse2
        Debug.Notification("Looter2 is moving")   <----
        Looter2.PathToReference(Corpse2, 1.0)
        Corpse2.RemoveAllItems(Looter2)
        Corpse2.AddKeyword(RELootedCorpse)
        Debug.Notification("Looter2 is done looting") <----
    EndIf

    If Looter3 && Corpse3
        Debug.Notification("Looter3 is moving") <----
        Looter3.PathToReference(Corpse3, 1.0)
        Corpse3.RemoveAllItems(Looter3)
        Corpse3.AddKeyword(RELootedCorpse)
        Debug.Notification("Looter3 is done looting") <----
    EndIf
EndIf
Self.StartTimer(TimerDuration, 0)
EndEvent
Edited by NoCashNoExp
Link to comment
Share on other sites

Do not modify array length while looping on it. Example: Array has 5 ObjectReferences and the 3rd is the player.

 

Demonstration:

 

- [Actor, Actor, Player, Actor, Actor] i=0, len=5

 

At i = 2 (Remove Player)

 

- [Actor,Actor,Actor,Actor] BUT len was resolved to = 5

 

At i = 4

 

[Actor,Actor,Actor,Actor,??? <---- Out of bounds] len is still = 5

Edited by NoCashNoExp
Link to comment
Share on other sites

Thanks

 

Do not modify array length while looping on it. Example: Array has 5 ObjectReferences and the 3rd is the player.

 

Demonstration:

 

- [Actor, Actor, Player, Actor, Actor] i=0, len=5

 

At i = 2 (Remove Player)

 

- [Actor,Actor,Actor,Actor] BUT len was resolved to = 5

 

At i = 4

 

[Actor,Actor,Actor,Actor,??? <---- Out of bounds] len is still = 5

Thanks for the help. I did a similar edit to my script, added several debugging notifications and i realized my script works, not as intended but it does what it does. I still tried yours though it's the same base.

Screenshot: script02.jpg

 

My updated script:

Scriptname NPCLootingScript extends ReferenceAlias

Actor Property PlayerRef Auto Const
Actor Property Corpse1 Auto
Actor Property Corpse2 Auto
Actor Property Corpse3 Auto
Actor Property Looter1 Auto
Actor Property Looter2 Auto
Actor Property Looter3 Auto
int Property TimerDuration Auto Const
Keyword Property ActorTypeNPC Auto
Keyword Property RELootedCorpse Auto Const
int Property distance Auto Const
ObjectReference[] Property NPCs Auto

Event OnInit()
	Self.StartTimer(TimerDuration, 5)
EndEvent

Event OnTimer(int aiTimerID)

Debug.Notification("Script running")

If !PlayerRef.IsInCombat()
		NPCs = PlayerRef.FindAllReferencesWithKeyword(ActorTypeNPC, distance)
		int i = 0
		int Size = NPCs.Length as int

	If size > 6
		size = 6
	EndIf
		
	While i <= Size
		Actor MyActor = NPCs[i] as Actor
		String name = MyActor.GetActorBase().GetName()
		Debug.Notification("Found " + name + " in" + i as string)

			if MyActor == PlayerRef
				Debug.Notification("Found the player and removed it")
				NPCs.Remove(i)
			ElseIf MyActor.IsDead() && !MyActor.HasKeyword(RELootedCorpse)
				if !Corpse1
					Corpse1 = MyActor
				Debug.Notification("Found body1")
				ElseIf !Corpse2
					Corpse2 = MyActor		
				Debug.Notification("Found body2")		
				ElseIf !Corpse3
					Corpse3 = MyActor
				Debug.Notification("Found body3")
				EndIf
			Else
				if !Looter1
					Looter1 = MyActor
				Debug.Notification("Found looter1")
				ElseIf !Looter2
					Looter2 = MyActor		
				Debug.Notification("Found looter2")
				ElseIf !Looter3
					Looter3 = MyActor
				Debug.Notification("Found looter3")
				EndIf
			EndIf
		i+=1
	EndWHile

	Debug.Notification("While cycle ended")
	If Looter1 && Corpse1
		Looter1.PathToReference(Corpse1 as ObjectReference, 1.0)
		Debug.Notification("Processing looting 1")
	EndIf

	If Looter2 && Corpse2
		Looter2.PathToReference(Corpse2 as ObjectReference, 1.0)
		Debug.Notification("Processing looting 2")
	EndIf

	If Looter3 && Corpse3
		Looter1.PathToReference(Corpse3 as ObjectReference, 1.0)
		Debug.Notification("Processing looting 3")
	EndIf

	If Looter1 && Corpse1
		Corpse1.RemoveAllItems(Looter1)
		Corpse1.AddKeyword(RELootedCorpse)
	EndIf

	If Looter2 && Corpse2
		Corpse2.RemoveAllItems(Looter2)
		Corpse2.AddKeyword(RELootedCorpse)
	EndIf

	If Looter3 && Corpse3
		Corpse3.RemoveAllItems(Looter3)
		Corpse3.AddKeyword(RELootedCorpse)
	EndIf
EndIf
	Corpse1 = None
	Corpse2 = None
	Corpse3 = None
	Looter1 = None
	Looter2 = None
	Looter3 = None

Self.StartTimer(TimerDuration, 5)
Debug.Notification("Timer reset")
EndEvent

30 seconds seemed fine for each cycle to complete, but regardless the npcs don't do the pathing, they just stand in their possitions, but the bodies are "looted". The issue remains as my quest stops working after a while. Maybe a solution would be adding a reduced version of my script to each npc template so they search individually instead of making it around the player character? idk, i don't like to touch so many vanilla records, but some cool stuff could be done if i go this way, for example making a script for animals so they eat ("loot") the npcs they kill or other animals, with animations and all, if possible? just dreaming here :p

Link to comment
Share on other sites

Also forgot to mention, for some reason i can't get the name of the npcs :/

 

Also i realize the size should be set to 7, excluding the player character that would make 6 npcs to process (up to 3 bodies and up to 3 looters).

Edited by cenarius451
Link to comment
Share on other sites

Sadly, pathing is handled by the engine. PathToReference() function can fail due to a couple of reasons. You can check if the pathing worked or not.

bool success = Looter1.PathToReference(Corpse1, 1.0)
Debug.Notification("Did Looter go to corpse? " + success)

You need to test the intricacies of this function because I have no clue what it does. Try a simple straight path first. One more advice for your sanity, enable papyrus logging and start using Debug.Trace() instead. The output should be in Documents/My Games/Fallout4/Logs/Script/Script0.log

Edited by NoCashNoExp
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...