Jump to content

help with onItemAdded, detection and cycling through containers


lasere200

Recommended Posts

Hi,

 

I've come across some roadblocks with my mod and I am looking to solve them but I just can't on my own. Help would be greatly appreciated.

 

1. I currently handle player detection by keywording all NPCs in the game, finding them via FindAllReferencesWithKeyword with a 4096 range and using IsDetectedBy on each of them. Obviously this leads to a lot of conflicts. Is there a way to get if the player is detected or not (for purposes of stealing) without modifying NPC records and if possible without F4SE?

ScriptName ALEnhDetectedEffectScript extends Activemagiceffect

;-- Properties --------------------------------------
Actor Property Player Auto
float Property ALEnhRange Auto
GlobalVariable Property ALEnhDetected Auto
Keyword Property ALEnhDetectedKeyword Auto mandatory
Perk Property ALEnhDetectedPerk Auto

;-- Variables ---------------------------------------

;-- Functions ---------------------------------------

Event OnEffectStart(Actor akTarget, Actor akCaster)
	While (Player.HasPerk(ALEnhDetectedPerk))
		ObjectReference[] Actors = Player.FindAllReferencesWithKeyword(ALEnhDetectedKeyword as Form, ALEnhRange)
		If (Actors.length != 0 || Actors != None)
			int x = 0
			int d = 0
			While (x < Actors.length)
				Actor kActor = Actors[x] as Actor
				If (Self.CheckIfaActorCanBeProcessed(Actors[x]))
					If (kActor != None)
						If (kActor.IsDead() == False)
							If (Player.IsDetectedBy(kActor) == True)
								d = 1
							EndIf
						EndIf
					EndIf
				EndIf
				x += 1
			EndWhile
			ALEnhDetected.SetValue(d as float)
		Else
			ALEnhDetected.SetValue(0 as float)
		EndIf
	EndWhile
EndEvent

bool Function CheckIfaActorCanBeProcessed(ObjectReference akItem)
	return akItem.Is3DLoaded() && !akItem.IsDisabled() && !akItem.IsDeleted() && !akItem.IsDestroyed() && !akItem.IsActivationBlocked()
EndFunction

2. I am using this same method to to find containers around the Player. I have keyworded all the containers in the game and I am using FindAllReferencesWithKeyword. Is there a way to do this without without modifying records? Somehow use their type?

 

3. After I get the container this is what I use to loot it. I am looking to modify the function below to remove a certain keyword from the items I am picking up (which as far as I can tell means removing the keyword from the base object and adding it back after having looted). Also if there is some OnItemAdded on the Player solution to this I'd love to know about it, I don't know where to start. Basically it would need to check on every item added if it has a certain keyword and delete it (from the the added form not the base object)

Function LootContainerItems(ObjectReference akContainer, Keyword akKeyword)
    akContainer.RemoveItem(akKeyword as Form, akContainer.GetItemCount(akKeyword as Form), True, Player as ObjectReference)
EndFunction

Thank you.

 

Edit: What would solve 1 and 2 would be something like FindAllReferences. I wouldn't mind filtering all the references via scripting because it wouldn't require to modify npc and container records anymore.That's the dream.

Edited by lasere200
Link to comment
Share on other sites

It looks like you are making a perk to detect unlooted NPCs and containers?

 

Well, I suppose instead of adding your keyword via CK, make a formlist of all containers/npcs that you want to add the keyword to, then run a script when the game loads to add the keyword to the objects in the formlist that don't have the keyword. That will solve the mod conflict issue.

 

But, I don't really see why you need to use keywords in the first place. You could try using FindAllReferencesOfType(Container, ALEnhRange), which should return anything that extends the container script, including actors, I think.

You could then use: "if (myItem is Actor)" to filter npcs if necessary. You could do an item count and assume that non-empty containers haven't been looted instead of using a keyword to track them.

Edited by steve40
Link to comment
Share on other sites

FindAllReferencesOfType only accepts base objects or form lists as arguments, not actual "types", or at least that's what I gather from it's Creation Kit page.

 

How would I go about adding keywords via script as you said? Is it possible to do it through Quest Aliases? I've seen you can assign keywords to them. All I am interested in is keywording actors and containers - and I saw that I can choose to search in the loaded area for actors only and even with GetIsObjectType set to "Container" as a type.

 

 

Here is my mod. It works and it terms of gameplay there are very few quirks but the compatibility is very very low since I am modifying 5000 records just to add a single keyword.

https://bethesda.net/en/mods/fallout4/mod-detail/1922946

Here is the F3NV version which was a lot smoother and is what I am aiming for:

https://www.nexusmods.com/newvegas/mods/42631/?

Link to comment
Share on other sites

This is my main script and along with the detection script from above, my entire mod. I use two keywords one for looting, one for detection.

The looting keyword is on all the items I want to loot, all containers and NPCs while the detection keyword is on all the NPCs.

The only in game issue that I haven't been able to fix is removing the keyword from the items I loot from containers. Because of that when I drop them I automatically pick them back up once. Since they are being looted from the open world the second time the keyword actually gets removed.

 

Being able to keyword cotnainers and NPC some other way could make a big differece.

ScriptName ALEnhEffectScript extends Activemagiceffect

;-- PROPERTIES ----------------------------------
Actor Property Player Auto
float Property ALEnhRange Auto
Form Property AmmoFusionCore Auto
Form Property PowerGenerator01 Auto
Form Property WaterPurified Auto
FormList Property ALEnhActivatorList Auto
FormList Property ALEnhBottleList Auto
Keyword Property ALEnhKeyword Auto mandatory
Keyword Property BobbleheadKeyword Auto mandatory
Keyword Property PerkMagKeyword Auto mandatory
Perk Property ALEnhPerk Auto

;-- VARIABLES -----------------------------------
GlobalVariable Property ALEnhDetected Auto
GlobalVariable Property ALEnhLockpicking Auto
GlobalVariable Property ALEnhStealing Auto

;-- EVENTS --------------------------------------
;---- Start -------------------------------------
Event OnEffectStart(Actor akTarget, Actor akCaster)
	While (Player.HasPerk(ALEnhPerk))
		ObjectReference[] Loot = Player.FindAllReferencesWithKeyword(ALEnhKeyword as Form, ALEnhRange)
		If (Loot.length != 0 || Loot != None)
			int x = 0
			While (x < Loot.length)
			If (Self.CheckIfReferenceCanBeProcessed(Loot[x]))

;------ Bodies and Corpses ----------------------
				Actor kActor = Loot[x] as Actor
				If (kActor != None) 
					If (kActor.IsDead() == True)
						If (!Loot[x].IsActivationBlocked())
							LootContainerItems(Loot[x], ALEnhKeyword)
						EndIf
					EndIf
				EndIf

;------ Activators ------------------------------
				ElseIf (ALEnhActivatorList.HasForm(Loot[x].GetBaseObject()) || Loot[x].HasKeyword(BobbleheadKeyword) || Loot[x].HasKeyword(PerkMagKeyword))
					If (Loot[x].Is3DLoaded())
						If (Player.WouldBeStealing(Loot[x]))
							If (ALEnhStealing.GetValueInt() == 1)
								If (ALEnhDetected.GetValueInt() == 0)
									Loot[x].Activate(Player as ObjectReference, False)
								EndIf
							EndIf
						Else
							Loot[x].Activate(Player as ObjectReference, False)
						EndIf
					EndIf

;------------------------------------------------
				ElseIf (!Loot[x].IsActivationBlocked() && Loot[x].Is3DLoaded())

;------ Locked Containers -----------------------
					ObjectReference kContainer = Loot[x].GetContainer()
					If (kContainer != None)
						If (KContainer.IsLocked() == True)
							If (ALEnhLockpicking.GetValue() == 1 as float)
								If (Player.WouldBeStealing(Loot[x]))
									If (ALEnhStealing.GetValueInt() == 1)
										If (ALEnhDetected.GetValueInt() == 0)
											Loot[x].Activate(Player as ObjectReference, False)
											Utility.Wait(2 as float)
										EndIf
									EndIf
								Else
									Loot[x].Activate(Player as ObjectReference, False)
									Utility.Wait(2 as float)
								EndIf
							EndIf
						EndIf

;------ Unlocked Containers ---------------------
						Elseif (Player.WouldBeStealing(Loot[x]))
							If (ALEnhStealing.GetValueInt() == 1)
								If (ALEnhDetected.GetValueInt() == 0)
									LootContainerItems(Loot[x], ALEnhKeyword)
								EndIf
							EndIf
						Else
							LootContainerItems(Loot[x], ALEnhKeyword)
						EndIf

;------ Empty Bottles ---------------------------
					ElseIf (ALEnhBottleList.HasForm(Loot[x].GetBaseObject()))
						If (Player.GetItemCount(WaterPurified) + Player.GetItemCount(ALEnhBottleList as Form) < 10)
							If (Player.WouldBeStealing(Loot[x]))
								If (ALEnhStealing.GetValueInt() == 1)
									If (ALEnhDetected.GetValueInt() == 0)
										LootOpenWorldItem(Loot[x], ALEnhKeyword)
									EndIf
								EndIf
							Else
								LootOpenWorldItem(Loot[x], ALEnhKeyword)
							EndIf
						EndIf

;------ Fusion Cores ----------------------------
					ElseIf (Loot[x].GetBaseObject() == AmmoFusionCore)
						ObjectReference PowerGenerator = Game.FindClosestReferenceOfTypeFromRef(PowerGenerator01, Loot[x], 128 as float)
						If (Player.WouldBeStealing(Loot[x]))
							If (ALEnhStealing.GetValueInt() == 1)
								If (ALEnhDetected.GetValueInt() == 0)
									If (PowerGenerator != None)
										Loot[x].RemoveKeyword(ALEnhKeyword)
										Loot[x].Activate(Player as ObjectReference, False)
									Else
										LootOpenWorldItem(Loot[x], ALEnhKeyword)
									EndIf
							ElseIf (PowerGenerator != None)
								Loot[x].RemoveKeyword(ALEnhKeyword)
								Loot[x].Activate(Player as ObjectReference, False)
							Else
								LootOpenWorldItem(Loot[x], ALEnhKeyword)
							EndIf
						EndIf

;------ Open World Items ------------------------
					ElseIf (Player.WouldBeStealing(Loot[x]))
						If (ALEnhStealing.GetValueInt() == 1)
							If (ALEnhDetected.GetValueInt() == 0)
								LootOpenWorldItem(Loot[x], ALEnhKeyword)
							EndIf
						EndIf
					Else
						LootOpenWorldItem(Loot[x], ALEnhKeyword)
					EndIf

;---- End ---------------------------------------
				EndIf
				x += 1
			EndWhile
		EndIf
	EndWhile
EndEvent

;-- FUNCTIONS -----------------------------------
bool Function CheckIfReferenceCanBeProcessed(ObjectReference akItemReference)
	return !akItemReference.IsDisabled() && !akItemReference.IsDeleted() && !akItemReference.IsDestroyed()
EndFunction

Function LootContainerItems(ObjectReference akContainer, Keyword akKeyword)
	akContainer.RemoveKeyword(akKeyword)
	akContainer.RemoveItem(akKeyword as Form, akContainer.GetItemCount(akKeyword as Form), True, Player as ObjectReference)
EndFunction

Function LootOpenWorldItem(ObjectReference akItemReference, Keyword akKeyword)
	akItemReference.RemoveKeyword(akKeyword)
	Player.AddItem(akItemReference as Form, 1, True)
EndFunction
Edited by lasere200
Link to comment
Share on other sites

Is it possible to use the ApplyingEffect part of a cloak effect to get all containers in radius of player? In conditions there is [GetIsObjectType Container == 1], ** but I don't know if that just returns chests etc or the fact that everything Object Oriented is technically a container.

 

[**Edit: Just saw OP mentioned this in post #3. Nothing to see here, sorry.]

 

If it does restrict "container" to storage containers, then you could have the cloak effect place the keyword, and remove the keyword when activated/looted by player. But then you might also have a problem losing keywords with container respawn.

 

Idk if that would work. Just thinking out loud.

 

DOvPNXiXcAE_Kt1.jpg

Link to comment
Share on other sites

So I made it work using your suggestion but it has some big shortcomings right now but it's still a big step in the right direction.

 

Made a perk that adds an ability with a cloak/continuous/self magic effect to the player. The cloak applying effect is a spell (script/concentration/contact) that looks like this:

Scriptname ALEnhKeywordEffectScript extends activemagiceffect

Actor Property Player Auto
Keyword Property ALEnhKeyword Auto mandatory
Perk Property ALEnhCloakPerk Auto mandatory

Event OnEffectStart(Actor akTarget, Actor akCaster)
	While (Player.HasPerk(ALEnhCloakPerk))
		debug.trace("hep")
		akTarget.AddKeyword(ALEnhKeyword)
	EndWhile
EndEvent

The only thing I change from the default value is the magnitude of the cloak effect in the ability window (not the magic effect window). It seems to control the range (not any Area setting)

 

Right now my conclusion is that the cloak applying effect only works on enemies that are alive. So if I kill a rat, out of effect range, it works. The keyword is applied to the corpse when I come in range and the corpse is automatically looted. It doesn't work on containers and corpses that were already dead (like the dog and raider dead near the sanctuary bridge).

 

I need something like this to keyword containers too.

Edited by lasere200
Link to comment
Share on other sites

  • 6 months later...
  • Recently Browsing   0 members

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