Jump to content

Help with NPC Looting Bodies Script


Recommended Posts

Thanks, i will look into it, but i'm not done yet. Here is a modification to my script that i attached to EncGunner01Template

Here is the code:

Scriptname IndNPCLootScript extends Actor Const

int Property TimerDuration Auto Const
Keyword Property ActorTypeNPC Auto Const
Keyword Property RELootedCorpse Auto Const
int Property distance Auto Const

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

Event OnTimer(int aiTimerID)
Actor Looter = Self as Actor
Actor Corpse1 = none
Actor Corpse2 = none
Actor Corpse3 = none

If !Looter.IsInCombat()
Debug.Notification("Script running")
		ObjectReference[] NPCs = Looter.FindAllReferencesWithKeyword(ActorTypeNPC, distance)
		int i = 0
		int Size = NPCs.Length as int

	If size > 10
		size = 10
	EndIf
		
	While i <= Size
		Actor MyActor = NPCs[i] as Actor
		String name = MyActor.GetActorBase().GetName()
			if MyActor == Looter
				NPCs.Remove(i)
			ElseIf MyActor.IsDead() && !MyActor.HasKeyword(RELootedCorpse)
				If !Corpse1
					Corpse1 = MyActor as Actor
					Debug.Notification("Corpse1 found")
				ElseIf !Corpse2
					Corpse2 = MyActor as Actor		
					Debug.Notification("Corpse2 found")			
				ElseIf Corpse3
					Corpse3 = MyActor as Actor
					Debug.Notification("Corpse3 found")
				EndIf
				
			EndIf
		i+=1
	EndWHile

	If Looter && Corpse1
		Debug.Notification("Processing looting1")
		Looter.PathToReference(Corpse1 as ObjectReference, 1.0)
		Corpse1.RemoveAllItems(Looter)
		Corpse1.AddKeyword(RELootedCorpse)
		Utility.Wait(5)
	EndIf
	
	If Looter && Corpse2
		Debug.Notification("Processing looting2")
		Looter.PathToReference(Corpse2 as ObjectReference, 1.0)
		Corpse2.RemoveAllItems(Looter)
		Corpse2.AddKeyword(RELootedCorpse)
		Utility.Wait(5)
	EndIf
	
	If Looter && Corpse1
		Debug.Notification("Processing looting3")
		Looter.PathToReference(Corpse3 as ObjectReference, 1.0)
		Corpse3.RemoveAllItems(Looter)
		Corpse3.AddKeyword(RELootedCorpse)
		Utility.Wait(5)
	EndIf

EndIf
Self.StartTimer(TimerDuration, 5)
EndEvent

Attached to the npc:

Script03.jpg

 

But of course, it doesn't work:

Screen-Shot2663.jpg

 

I spawned EncGunner01Template but did nothing. Also it doesn't show any notifications. Any ideas? i wonder if it's even possible to go this way.

Edited by cenarius451
Link to comment
Share on other sites

  • Replies 40
  • Created
  • Last Reply

Top Posters In This Topic

I made some discoveries. What was preventing to my script to keep going was the damn PathToReference. And it never worked, so instead i discovered a different method: AI Packages.

 

So i made 3 AI Packages, one for each body to loot:

Script07.jpg

 

I pointed them to my quest so i could get the corpse aliases as the travel destiny. Then i set a global variable so the package doesn't apply until i change it in my script, making (or forcing most likely) the NPC to move onto the destination (the body):

Script08.jpg

 

The idea is to make 3 globals and change their values to 1 each if, in theory, finds up to 3 bodies. The cute thing about this method is that once the package finishes i can attach a fragment on the "End" state to do the looting (basically removing all items and passing them to the looter aias, and then give the keyword to it that says it's already looted). I can even put an idle animation there. Could be more bodies and cute stuff but i can get fancy later once this works, if it works (which it doesn't).

 

Then in my quest i add several aliases:

Script04.jpg

 

And set them as the following:

 

Script05.jpg

 

Script06.jpg

 

Looks cute and all but thi would work only once. Ingame this doesn't work as it is, so maybe there is a way to make it so this repeats? here is the updated script:

Scriptname NPCLootingScript extends ReferenceAlias

Actor Property PlayerRef Auto Const
int Property TimerDuration Auto Const
Keyword Property ActorTypeNPC Auto
Keyword Property RELootedCorpse Auto Const
Alias Property ACorpse1 Auto
Alias Property ACorpse2 Auto
Alias Property ACorpse3 Auto
Alias Property ALooter1 Auto
Alias Property ALooter2 Auto
Alias Property ALooter3 Auto
GlobalVariable Property NPCLootingGlobal Auto Const
GlobalVariable Property NPCLootingGlobal2 Auto Const
GlobalVariable Property NPCLootingGlobal3 Auto Const

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

Event OnTimer(int aiTimerID)

Debug.Notification("Script running")

If !PlayerRef.IsInCombat()

	If ALooter1 && ACorpse1
		NPCLootingGlobal.SetValueInt(1)
	EndIf

	If ALooter2 && ACorpse2
		NPCLootingGlobal2.SetValueInt(1)
	EndIf

	If ALooter3 && ACorpse3
		NPCLootingGlobal3.SetValueInt(1)
	EndIf
EndIf

Self.StartTimer(TimerDuration, 5)
EndEvent

Like i mentioned before, this doesn't work, however i think i'm moving into the right direction. The idea for this quest was to let the engine do the searching work, searching for bodies and looters and filling things for me, but i doubt this will work. The other method would be doing the search through script as i did before but when trying to fill the aliases i had some problems that made me doubt.

 

So my question would be if there is a way to make the quest reset their aliases so it searches constantly for npcs to fill them? if no, is there a way to fill the aliases through script?

Edited by cenarius451
Link to comment
Share on other sites

Here's the quest script.. I upload the files (.esp and the scripts) into a Google Drive folder.. I also wrote comments on most script functions.

 

I chose the a Vault 81 resident (V81CitizenF02T2Residence [FormID: 00111F63] as the "looter" NPC. Looted bodies use quest alias fill conditions. (Note that I wouldn't use this method but this was your approach). I'd rather use distance events).

 

Of course it's not a "final" version: the timer times should be edited, this plugin has only one Alias_Body01, it needs more debugging and adjustments (especially the "LootedBody_01_Actor").. (The Ref Collection Alias is discarded, you can delete it). The "retrieve" animation is also not included. But I think it's a good start.. Good luck. :smile:

Scriptname TEMP_S extends Quest

Group Looters
ReferenceAlias Property Alias_Looter Auto Const
endGroup

Group Corpses
ReferenceAlias Property Alias_Body01 Auto Const
endGroup

Group Quests
Quest Property TEMP_Q_for_Aliases Auto Const
Quest Property TEMP_Q Auto Const
endGroup

Group Globals
GlobalVariable Property TEMP_G_01 Auto Const
GlobalVariable Property TEMP_G_PackageFinished Auto Const
endGroup

Group Keywords
Keyword Property TEMP_K_Looted Auto Const
endGroup


Event OnQuestInit()
	StartAliasQuest()		;START the quest TEMP_Q_for_Aliases. Corpse alias fill conditions are there. The Alias_Body01 ref alias on this quest is linked to that ref alias.
EndEvent


Function StartAliasQuest()

	if TEMP_Q_for_Aliases.IsRunning() == 1		;failsafe
		TEMP_Q_for_Aliases.Stop()
	endif

	Utility.Wait(0.5)		;wait for stopping...

	if TEMP_Q_for_Aliases.IsRunning() == 0		;Start the alias quest
		TEMP_Q_for_Aliases.Start()
		debug.notification("Alias quest started")
	endif

	Utility.Wait(0.5)		;wait for the aliases to get filled...

	If Alias_Body01.GetReference() == none
		;Alias has not been filled (fill conditions are not met)
		debug.notification("Alias is NOT filled, start loot timer")
		StartTimer(20, 255)			;try to start the alias quest again. (LOOP timer)
	Else
		debug.notification("Alias is filled, StartLoot()")
		StartLoot()
	EndIf

EndFunction




Event OnTimer(int aiTimerID)
	if aiTimerID == 255		;LOOP timer
		StartAliasQuest()
		debug.notification("LOOP timer expired")
	EndIf
	If aiTimerID == 555		;FALSE TIMER: if the actor can't loot the body in 60 seconds, abort the package
		End_Loot()
		debug.notification("aiTimerID 555 expired")
	Endif
EndEvent






Function StartLoot()

	TEMP_G_01.SetValueInt(1)		;for the packages

	(Alias_Looter.GetReference() as Actor).EvaluatePackage()

	StartTimer(60, 555)		;failsafe: if the looter can't path to the body, abort the package

	;WAIT FOR PACKAGE STOP

EndFunction



Function End_loot()		;called from package "on end" fragment / falsafe timer (ID: 555)

	If TEMP_G_PackageFinished.GetValueInt() == 0					;function was called from the falsafe timer
	
		debug.notification("END loot called from FALSAFE TIMER")
	
		if TEMP_Q_for_Aliases.IsRunning() == 1
			TEMP_Q_for_Aliases.Stop()
			debug.notification("Alias quest stopped")
		endif
	
		TEMP_G_01.SetValueInt(0)

		TEMP_G_PackageFinished.SetValueInt(0)

		StartTimer(2, 255)			;start again LOOP timer. For the failsafe timer, no need for 20 seconds.

	ElseIf TEMP_G_PackageFinished.GetValueInt() == 1		;		;function was called from the package "on end" fragment

		LootProcedure()

		MarkBodyAsLooted()

		debug.notification("END loot called from PACKAGE")
	
		if TEMP_Q_for_Aliases.IsRunning() == 1
			TEMP_Q_for_Aliases.Stop()
			debug.notification("Alias quest stopped")
		endif
	
		TEMP_G_01.SetValueInt(0)

		StartTimer(10, 255)			;start again LOOP timer

	EndIf

EndFunction




Function MarkBodyAsLooted()

	debug.notification("MarkBodyAsLooted() running")

	;AliasColl_LootedBodies.AddRef((Alias_Looter.GetReference() as Actor))		;add looted bodies to a ref coll alias. Ref coll actors have a keyword that prevents the looter from looting them again. Remove the looted bodies one by one with a Game Timer.

	LootedBody_01_Actor = (Alias_Body01.GetReference() as Actor)

	LootedBody_01_Actor.AddKeyword(TEMP_K_Looted)

	StartTimerGameTime(0.5, 355)		;looted body has to have this keyword for at least two hours. (so looter won't loot this body in this period).

EndFunction


Actor LootedBody_01_Actor


Event OnTimerGameTime(int aiTimerID)		
	If aiTimerID == 355

		RemoveLootedBody()

	EndIf
EndEvent

Function RemoveLootedBody()

	debug.notification("RemoveLootedBody() Timer expired")

	LootedBody_01_Actor.RemoveKeyword(TEMP_K_Looted)

EndFunction




Function LootProcedure()		;called rom package EndLoot() function. Thisis the actual "loot" procedure. (Animation functions should be placed here).

	If (Alias_Body01.GetReference()) != none && (Alias_Looter.GetReference()) != none

		(Alias_Body01.GetReference() as Actor).RemoveAllItems((Alias_Looter.GetReference() as actor))

		debug.notification("Looted!")

	;DEBUGGING...

	ElseIf (Alias_Body01.GetReference()) == none && (Alias_Looter.GetReference()) != none

		debug.notification("Alias_Body01 is none!")

	ElseIf (Alias_Body01.GetReference()) != none && (Alias_Looter.GetReference()) == none

		debug.notification("Alias_Looter is none!")

	ElseIf (Alias_Body01.GetReference()) == none && (Alias_Looter.GetReference()) == none

		debug.notification("Alias_Body01 AND Looter are none!")

	EndIf

EndFunction
Edited by LarannKiar
Link to comment
Share on other sites

Ah, thanks for this, but what i'm trying to do is to detect alive npcs near a body and select them as looters. Your method uses a forced vault dweler as the looter which the game already does with quest "REActorAttach02" (raiders stripping bodies). So my guess is that this would work as a new random event then? not what i wanted tho. Will keep looking for this sure, but i guess you can't fill aliases through script? if so then this method is useless, will need to find some other way :/

 

EDIT: Wait, spoke too soon? maybe if i do this?

SCRIPT00.jpg

And i had to uncheck "allow disabled" because some mods put disabled actors and enable them though script. Not cute but it works, i've used the method so disabled npcs shouldn't count towards looters.

Edited by cenarius451
Link to comment
Share on other sites

what i'm trying to do is to detect alive npcs near a body and select them as looters.

 

 

Then it's simple. Create an alias called Alias_Looter on the TEMP_Q_AliasQuest. Then use the "Find Matching Reference" to fill the alias when the quest starts. Add your conditions (distance check, Actors only, In Loaded Area flag, GetDead == 0.). And don't forget to link the Alias_Looter on the TEMP_Q quest to the Alias_Looter on the TEMP_Q_for_Aliases quest. So it would be similar to the Alias_Body01. (You can also see the conditions I added to the Alias_Body01 alias).

 

you can't fill aliases through script?

 

 

You can:

 

Alias_Looter.ForceRefTo(an actor's reference).

Edited by LarannKiar
Link to comment
Share on other sites

Well, the "an actor's reference" isn't the reference.. :smile:

 

It would look like this:

;Actor Property
Actor Property MyActor Auto Const

Alias_Looter.ForceRefTo(MyActor)



;OR YOU CAN USE ReferenceAlias Property (if you've created a Reference Alias)
ReferenceAlias Property MyActorRefAlias Auto Const

Alias_Looter.ForceRefTo((MyActorRefAlias.GetReference() as Actor))

Note that the quest TEMP_Q does not stop.. that's why there's a TEMP_Q_for_Aliases quest. That quest starts, stops and fills its reference aliases depending on their conditions.

Edited by LarannKiar
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...