jple Posted January 24, 2021 Share Posted January 24, 2021 (edited) Hey folks. New here, working on a mod that overhauls crime gameplay and the Thieves Guild. I'm looking for a way to track and save to a global variable the total value of stolen goods sold to fences (or to other vendors with the perk that allows to sell stolen goods to normal merchants).The first thing I tried is to look for the way it was done in Oblivion. The Thieves Guild in Oblivion has a TGStolenGoods quest that tracks this exact value and sets the quest's stages according to that value in the quest's script: ScriptName TGStolenGoodsScript Float TotalStolen ; TotalStolen is updated in the Generic quest in the Barter topic. Begin GameMode If GetAmountSoldStolen >= 50 && GetQuestRunning TG02Taxes == 1 SetStage TGStolenGoods 25 ElseIf GetAmountSoldStolen >= 100 && GetQuestRunning TG03Elven == 1 SetStage TGStolenGoods 35 ElseIf GetAmountSoldStolen >= 200 && GetQuestRunning TG04Mistake == 1 SetStage TGStolenGoods 45 ElseIf GetAmountSoldStolen >= 300 && GetQuestRunning TG05Misdirection == 1 SetStage TGStolenGoods 55 ElseIf GetAmountSoldStolen >= 400 && GetQuestRunning TG06Atonement == 1 SetStage TGStolenGoods 65 ElseIf GetAmountSoldStolen >= 500 && GetQuestRunning TG07Lex == 1 SetStage TGStolenGoods 75 ElseIf GetAmountSoldStolen >= 600 && GetStage TG08Blind == 1 SetStage TGStolenGoods 85 ElseIf GetAmountSoldStolen >= 700 && GetStage TG09Arrow == 1 SetStage TGStolenGoods 95 ElseIf GetAmountSoldStolen >= 800 && GetStage TG10Boots == 1 SetStage TGStolenGoods 105 ElseIf GetAmountSoldStolen >= 1000 && GetStage TG11Heist == 1 SetStage TGStolenGoods 115 EndIf End The GetAmountSoldStolen function tracks the total value of stolen goods sold and it works as intended in Oblivion.I've found that this exact function still exists in Skyrim under the same name and is a condition function, so you can't execute it in Papyrus, you can only use it via conditions in the CK or the game's console.What I planned to do was to use ConsoleUtil (adds Papyrus functions that allow to execute console commands and read the output to a string) to get the desired value. Unfortunately, the function never seems to be modified by selling stolen items, it always stays at 0 value. I figured that the function that modifies the value just never gets called by the engine, so to use that was off the table. Next thing I tried was adding a script to a ReferenceAlias of the fence's merchant chest. In short the script listens for the OnItemAdded event and checks if the item is stolen using GetActorOwner (owner is not the player and is not None). Unfortunately I quickly realized that the owner is None most of the time (figured that the ObjectReference of the item is probably non-persistent). So here's what I ended up with at the end: Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) Debug.Trace("LevelingFenceScript: Item " + aiItemCount + "x " + akBaseItem) if akBaseItem != Gold001 as Form ;; ignore gold AdvanceThievesGuildSkill(akBaseItem.GetGoldValue(), aiItemCount) endIf endEvent This allows me to only track the value of any item(s) sold to a specific fence, regardless of the stolen tag. At this point honestly I don't mind if i just track the amount of stolen items sold, but I have no idea if that's possible either. I'd appreciate any help. Edited January 24, 2021 by CzechHero Link to comment Share on other sites More sharing options...
dylbill Posted January 24, 2021 Share Posted January 24, 2021 There's an ObjectReference SKSE function IsOffLimits() so you could try, If akItemReference.IsOffLimits() to check for stolen items, although this might not work because when items enter a container they lose their reference, unless they are persistent. It might work though, you'd have to test it. Link to comment Share on other sites More sharing options...
jple Posted January 24, 2021 Author Share Posted January 24, 2021 (edited) Thanks for the tip, had no idea that function exists. It works perfectly when I add a stolen item to the inventory or drop the item in the world.Unfortunately it doesn't work for items added to containers or items removed from the inventory during bartering, so it seems like you were right with the fact that items lose their reference on entering a container.Here's what I tried: ReferenceAlias script referencing the Player: Scriptname FO_TG_LevelingQuestScript extends ReferenceAlias Actor Property PlayerRef Auto GlobalVariable Property FO_TG_StolenTotal Auto Bool isBartering = false Event OnInit() RegisterForMenu("BarterMenu") EndEvent Event OnPlayerLoadGame() RegisterForMenu("BarterMenu") EndEvent Event OnMenuOpen(String MenuName) if MenuName == "BarterMenu" Debug.Trace("BarterMenu opened, starting barter tracking") isBartering = true endIf EndEvent Event OnMenuClose(String MenuName) if MenuName == "BarterMenu" Debug.Trace("BarterMenu closed, stopping barter tracking") isBartering = false endIf EndEvent Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer) if isBartering == true if akItemReference.IsOffLimits() Debug.Trace("A stolen item was sold to a fence.") endIf endIf EndEvent Papyrus log: [01/24/2021 - 11:26:57AM] Error: Cannot call IsOffLimits() on a None object, aborting function call stack: [alias PlayerAlias on quest FO_TG_LevelingQuest (08019D2E)].FO_TG_LevelingQuestScript.OnItemRemoved() - "FO_TG_LevelingQuestScript.psc" Line ? [01/24/2021 - 11:26:57AM] warning: Assigning None to a non-object variable named "::temp7" stack: [alias PlayerAlias on quest FO_TG_LevelingQuest (08019D2E)].FO_TG_LevelingQuestScript.OnItemRemoved() - "FO_TG_LevelingQuestScript.psc" Line ? I'll see if I can think of another solution in the meantime. EDIT: So I recalled that the OnStoryRemoveFromPlayer quest event exists and has an ObjectReference parameter. I'll see if that ObjectReference is persistent.EDIT2: So I tested that with the quest event then with an alias script which finds the matching reference from the event and takes the ItemRef data, and both work only when dropping the item, not when it's moved to a container. Too bad. Edited January 24, 2021 by CzechHero Link to comment Share on other sites More sharing options...
dylbill Posted January 24, 2021 Share Posted January 24, 2021 Hmm, in that case, you can maybe force your fence to drop items added by the player so that they have an object ref, then pick them up again. Something like this: Actor Property PlayerRef Auto Event OnItemAdded(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) If akSourceContainer == PlayerRef ObjectReference Item = Self.DropObject(akBaseItem, 1) If Item.IsOffLimits() Debug.Trace(Item.GetDisplayName() + " Is Stolen") Endif Self.AddItem(Item) Endif EndEvent Link to comment Share on other sites More sharing options...
jple Posted January 24, 2021 Author Share Posted January 24, 2021 (edited) Just tested it and it works perfectly when adding an item to the fence's container from the player's inventory. However when bartering with the fence all items get tagged as stolen. Might be because through bartering the ownership of the item changes to the ActorBase of the fence. Tried changing the ownership of the container to None, that didn't change anything. Well, it's starting to seem like something like this will only be possible with an SKSE plugin. Might have to look up how to make one or abandon this feature. Edited January 24, 2021 by CzechHero Link to comment Share on other sites More sharing options...
jple Posted January 24, 2021 Author Share Posted January 24, 2021 (edited) So I finally figured it out! Thanks a lot for the help dylbill. Here's a complete solution on how to check if an item was stolen during bartering/adding to a container: Prepare a custom cell (no owner npc/faction) with a helper container inside it. Create a quest with a ReferenceAlias that references the merchant's/fence's trade chest. Add a script to the ReferenceAlias Actor Property PlayerRef Auto MiscObject Property Gold001 Auto ObjectReference Property FenceChestRef Auto Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) if akSourceContainer == PlayerRef && akBaseItem != Gold001 as Form ;; ensure that it was the player that added the item(s) and the item is not gold Debug.Trace("LevelingFenceScript: Item " + aiItemCount + "x " + akBaseItem) ObjectReference ref = GetReference() ref.RemoveItem(akBaseItem, 1, true, FenceChestRef) ;; move the item to a helper container in a custom neutral cell ObjectReference item = FenceChestRef.DropObject(akBaseItem, 1) ;; drop the item in the world so we can check if it was stolen if item.IsOffLimits() ;; check if the item was stolen Debug.Trace(item.GetDisplayName() + " was stolen") FO_TG_StolenTotal.Mod(aiItemCount * akBaseItem.GetGoldValue()) ;; add to the total value of stolen items sold, TODO: implement a custom function to calc enchanted items value Debug.Trace("Total value of stolen items sold: " + FO_TG_StolenTotal.GetValue()) endIf ref.AddItem(item) ;; add the item back endIf EndEvent Auto-Fill all properties and pick the reference for the helper container in the custom cell. Edited January 26, 2021 by CzechHero Link to comment Share on other sites More sharing options...
dylbill Posted January 25, 2021 Share Posted January 25, 2021 Cool no problem glad you got it working! That's good to know, I'll save that for future reference. Link to comment Share on other sites More sharing options...
foamyesque Posted February 5, 2021 Share Posted February 5, 2021 So I finally figured it out! Thanks a lot for the help dylbill. Here's a complete solution on how to check if an item was stolen during bartering/adding to a container: Prepare a custom cell (no owner npc/faction) with a helper container inside it. Create a quest with a ReferenceAlias that references the merchant's/fence's trade chest. Add a script to the ReferenceAlias Actor Property PlayerRef Auto MiscObject Property Gold001 Auto ObjectReference Property FenceChestRef Auto Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) if akSourceContainer == PlayerRef && akBaseItem != Gold001 as Form ;; ensure that it was the player that added the item(s) and the item is not gold Debug.Trace("LevelingFenceScript: Item " + aiItemCount + "x " + akBaseItem) ObjectReference ref = GetReference() ref.RemoveItem(akBaseItem, 1, true, FenceChestRef) ;; move the item to a helper container in a custom neutral cell ObjectReference item = FenceChestRef.DropObject(akBaseItem, 1) ;; drop the item in the world so we can check if it was stolen if item.IsOffLimits() ;; check if the item was stolen Debug.Trace(item.GetDisplayName() + " was stolen") FO_TG_StolenTotal.Mod(aiItemCount * akBaseItem.GetGoldValue()) ;; add to the total value of stolen items sold, TODO: implement a custom function to calc enchanted items value Debug.Trace("Total value of stolen items sold: " + FO_TG_StolenTotal.GetValue()) endIf ref.AddItem(item) ;; add the item back endIf EndEvent Auto-Fill all properties and pick the reference for the helper container in the custom cell. How reliable is this if there is a duplicate item already in the container? Link to comment Share on other sites More sharing options...
NexusComa2 Posted February 5, 2021 Share Posted February 5, 2021 (edited) I'm talking to a store owner. Triggers a script to duplicate all stolen in your backpack.I'm done talking to the store owner. Script compares what stolen items you still have verse what's missing.This could be done with a hidden chest like you said or an array within the script. The array method would be sweet and clean.No bonus to your created skill unless the scripts completes (as that is the last few line of the script). Just incase they exit the game mid transaction. Routines: Inventory count and find stolen items. Push list to array or chest, Inventory count and compare. Skill points added. Chest or array cleared.Triggers: I'm talking to a shop owner, I'm finished talking to a store owner. Edited February 5, 2021 by NexusComa2 Link to comment Share on other sites More sharing options...
Recommended Posts