cenarius451 Posted April 25, 2021 Author Share Posted April 25, 2021 (edited) Thanks, i will look into it, but i'm not done yet. Here is a modification to my script that i attached to EncGunner01TemplateHere 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: But of course, it doesn't work: 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 April 25, 2021 by cenarius451 Link to comment Share on other sites More sharing options...
cenarius451 Posted April 27, 2021 Author Share Posted April 27, 2021 (edited) 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: 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): 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: And set them as the following: 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 April 27, 2021 by cenarius451 Link to comment Share on other sites More sharing options...
LarannKiar Posted April 27, 2021 Share Posted April 27, 2021 OK, I'm creating a working version for you right now.. :) Link to comment Share on other sites More sharing options...
cenarius451 Posted April 27, 2021 Author Share Posted April 27, 2021 Thanks, and please post everything, i want to learn. Link to comment Share on other sites More sharing options...
LarannKiar Posted April 27, 2021 Share Posted April 27, 2021 (edited) 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 April 27, 2021 by LarannKiar Link to comment Share on other sites More sharing options...
cenarius451 Posted April 27, 2021 Author Share Posted April 27, 2021 (edited) 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?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 April 27, 2021 by cenarius451 Link to comment Share on other sites More sharing options...
cenarius451 Posted April 27, 2021 Author Share Posted April 27, 2021 Mm, your method doesn't work on my game, i don't even get the notifications. Link to comment Share on other sites More sharing options...
LarannKiar Posted April 27, 2021 Share Posted April 27, 2021 (edited) 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 April 27, 2021 by LarannKiar Link to comment Share on other sites More sharing options...
cenarius451 Posted April 27, 2021 Author Share Posted April 27, 2021 "Alias_Looter.ForceRefTo(an actor's reference)"I tried that but my script didn't compile, said "incompatible" values or something. Oh well, another day squeezing my brain. Seriously thanks for the help ;) Link to comment Share on other sites More sharing options...
LarannKiar Posted April 27, 2021 Share Posted April 27, 2021 (edited) 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 April 27, 2021 by LarannKiar Link to comment Share on other sites More sharing options...
Recommended Posts