Tes4Mod:Cobl/Modding/Inventory Tracking

The UESPWiki – Your source for The Elder Scrolls since 1995
Jump to: navigation, search


Background[edit]

COBL's Inventory Tracking provides an easy way for modders to monitor changes in the player's inventory, essentially creating OnPickup/Drop Item type blocks. This is currently used by the Keychain mod to move any keys the player picks up to the keychain (a remote container). It could also be used to automatically determine whether the loot the player just picked up is worth keeping or should be trashed, automatically equip better armour, or continuously supply arrows as the player runs out.

Requirements[edit]

The player will need OBSE (>= v.0012) and Pluggy (>= v56) to use the Inventory Tracker.

Specifics[edit]

The Inventory Tracker scans the player's inventory every 2 seconds. If the contents have changed since the last scan, the change (item's base reference, count, etc.) is marked on the appropriate list. There are, currently, 5 lists that can be broken down into 2 categories:

  • Gains
    • Increased items - Player has more of these items since the last scan. Marks the items' base references and changes in count.
    • Added items - Items that the player didn't have in the last scan. Marks the items' base references.
    • New items - Items that the player has never had before (based on BaseID). Marks the items' base references.
  • Loses
    • Decreased items - Player has fewer of these items since the last scan. Marks the items' base references and changes in count.
    • Removed items - Player has lost the entire stack of these items. Marks the items' base references.

Example[edit]

The player has 5 Iron Arrows, 1 Iron Cuirass, and 50 Gold. Inventory tracking is initialized, makes the first scan, and the lists are updated:

Increased - 5 Iron Arrows, 1 Iron Cuirass, 50 Gold
Added - Iron Arrows, Iron Cuirass, Gold
New - Iron Arrows, Iron Cuirass, Gold

The player doesn't do anything for the next 2 seconds, IT makes the second scan, and updates the lists:

No changes

The player picks up 20 Gold, 1 Leather Cuirass, drops the Iron Cuirass and 2 Iron Arrows, IT makes the third scan and updates the lists:

Increased - 20 Gold, 1 Leather Cuirass
Added - Leather Cuirass
New - Leather Cuirass
Decreased - 2 Iron Arrows, 1 Iron Cuirass
Added - Iron Cuirass

Finally, the player picks up the Iron Cuirass again, IT makes the fourth scan and updates the lists:

Increased - 1 Iron Cuirass
Added - Iron Cuirass

Taking requests[edit]

If you'd like the Inventory Tracking to do something else, post it in this section and I'll see what I can do.

Eat/Drink list[edit]

A list of ingredients and potions that the player has eaten or drunk. As of now, the Remove list is indiscriminate about how the item was removed and all of the various methods (via script, dropping the item, selling the item, or eating the item, etc.) are treated the same way.

As of now, I'm thinking this would be a single list and you'd need to use IsFood, IsIngredient, and IsAlchemyItem to determine whether it's food or drink.

Quick Guide[edit]

Initialize the system[edit]

  1. Write down an EditorID from your mod - anything except Globals will work
  2. Create a quest with the name of your choice
  3. Mark it as "Start Game Enabled"
  4. Create this quest script (replace the ---s with the name of your quest)
    scn ---Script
    long  Warning
    
    begin GameMode
            SetStage --- 0
            if (Warning == 0)
                    set Warning to 1
                    messagebox "You need COBL v1.35 or later in order to use this mod. Please visit http://planetelderscrolls.gamespy.com/View.php?view=OblivionMods.Detail&id=3508 for more information and the download."
                    return
            endif
            if (Warning == -1)
                    ;Start a quest (see next step), add items, enable objects, etc. to get the player started with your mod
                    StopQuest ---
            endif
    end
    
  5. Attach the quest script to "YourQuest"
  6. Goto the Quest Stages tab
  7. Create a new stage, leave the index as 0
  8. Create a new Log Entry (don't worry - it won't pop up, it's just needed for the Result Script)
    set cobInvTrackingInitFAR.rInUse to AnyRefFromYourMod ;Use the EditorID that you wrote down earlier
    ;set cobInvTrackingFAR.RunInCombat to 1 ;If you'd like Inventory Tracking to run during combat
    cobInvTrackingInitFAR.Activate cobGenActRef, 1
    if (cobInvTrackingInit > 1)
            set Warning to -1
    endif
    
  9. Paste this in the Result Script box

Using the lists[edit]

I suggest using a quest as it will run all the time. You can use an object, but to be safe make sure it's always running (use the "Set" trick - set a variable on itself in GameMode and MenuMode).

  1. Create a quest with the name of your choice
  2. Choose which list you want to track
  3. Create this script: (where you see ---s, replace them with the appropriate name of the list - New, Add, Remove, Inc, or Dec)
    scn YourQuestScript
    
    float fQuestDelayTime
    long  ScanNumber
    long  iChange
    ref   rItem
    
    begin GameMode
            set fQuestDelayTime to 1.5
            if (cobInvTracker.iScan != ScanNumber)
                    set iChange to 0
                    Label
                    if (iChange < cobInvTracker.---Changes)
                            set rItem to (GetInArray cobInvTracker.a--- iChange)
                            ;if you're using a list with a count, such as Increased or Decreased, check the Count array for the count
                                    ;set ItemCount to (GetInArray cobInvTracker.aIncCount iChange)
                            ;Run your code, make checks, etc.
                            set iChange to (iChange + 1)
                            Goto
                    endif
                    set ScanNumber to cobInvTracker.iScan
            endif
    end
    
    ;Copy the GameMode block for MenuMode, with one special check to keep it from running while the Main Menu is open
    begin MenuMode
            if (MenuMode 1044)
                    return
            endif
    
            set fQuestDelayTime to 1.5
            if (cobInvTracker.iScan != ScanNumber)
    ...
    

    Example from Keychain[edit]

    Initialization script

    scn P1DkeyInitScript
    
    ;Checks and requirements
    
    ;COBL-ized
    ;need to activate using dummy ref instead of player
    
    
    long  Warning
    long  Warning2
    
    
    begin GameMode
            SetStage P1DkeyInit 0
            if (Warning == 0)
                    set Warning to 1
                    messagebox "You need OBSE v0014a or higher in order to use the Keychain. Please visit http://obse.silverlock.org/ for more information and the download."
                    return
            endif
            if (Warning2 == 0)
                    set Warning2 to 1
                    messagebox "You need COBL v1.36 or later in order to use the Keychain. Please visit http://planetelderscrolls.gamespy.com/View.php?view=OblivionMods.Detail&id=3508 for more information and the download."
                    return
            endif
            if (Warning == -1) && (Warning2 == -1)
                    StartQuest P1DkeyAdd
                    StopQuest P1DkeyInit
            endif
    end
    
    ;Result Script
    ;Stage 0 - Make sure the player has installed and is using OBSE >= 14 and COBL >= v1.36
    ;SetStage cobSigSE 0
    ;if (cobSigSE.Version > 13)
    ;       set P1DkeyInit.Warning to -1
    ;endif
    ;
    ;set cobInvTrackingInitFAR.rInUse to P1DkeyChain
    ;cobInvTrackingInitFAR.Activate cobGenActRef, 1
    ;if (cobInvTrackingInit > 1)
    ;       set P1DkeyInit.Warning2 to -1
    ;endif
    

    Keychain script (The tracking is split across this and the following Hooking script)

    scn P1DkeyChainScript
    
    long  IsInPlayerPossesion ;If not in player possesion, won't do anything
    long  ScanNumber ;Keeps track of the last inventory tracking scan
    ...
    ;Pick up new keys
    begin MenuMode
            if IsInPlayerPossesion
                    if (cobInvTracker.iScan != ScanNumber)
                            if (P1DkeyContainer02.GetScale == 1) ;Keychain is "Turned On"
                                    if cobInvTracker.AddChanges
                                            set P1DkeyHook.UseIT to 1
                                            P1DkeyHook.Activate cobGenActRef, 1
                                    endif
                            ...
                            endif
                            set ScanNumber to cobInvTracker.iScan
                    endif
            endif
    end
    
    
    begin GameMode
            if IsInPlayerPossesion
                    if (cobInvTracker.iScan != ScanNumber)
                            if (P1DkeyContainer02.GetScale == 1) ;Keychain is "Turned On"
                                    if cobInvTracker.AddChanges
                                            set P1DkeyHook.UseIT to 1
                                            P1DkeyHook.Activate cobGenActRef, 1
                                    endif
                            ...
                            endif
                            set ScanNumber to cobInvTracker.iScan
                    endif
            endif
    end
    

    Hooking script

    scn P1DkeyHookScript
    
    long  UseIT ;Use the inventory tracker
    long  iInv
    ref   rItem
    long  KeyCount
    long  StopSpam ;Used to stop message spam as the keys are taken from the player
    
    begin onActivate
            set iInv to 0
            set StopSpam to 1
            Label
            if UseIT
                    if (iInv < cobInvTracker.AddChanges)
                            set rItem to (GetInArray cobInvTracker.aAdd iInv)
                    else
                            set rItem to 0
                    endif
            else
                    set rItem to (player.GetInventoryObject iInv)
            endif
            if rItem
                    if (IsKey rItem)
                            if (rItem != P1DKeychain) ;...
                                    set KeyCount to (player.GetItemCount rItem)
                                    P1DkeyContainer02.AddItem rItem KeyCount
                                    if StopSpam
                                            set StopSpam to 0
                                            if UseIT
                                                    Message " "
                                                    Message " "
                                            else
                                                    message "Added loose keys to Keychain"
                                                    message "Added loose keys to Keychain"
                                            endif
                                            if MenuMode
                                                    con_ToggleMenus
                                            endif
                                    endif
                                    player.RemoveItem rItem KeyCount
                                    Label 1 ;An engine bug may require multiple RemoveItem calls
                                    set KeyCount to (player.GetItemCount rItem)
                                    if KeyCount
                                            player.RemoveItem rItem KeyCount
                                            Goto 1
                                    endif
                                    if UseIT
                                            set iInv to (iInv + 1)
                                    endif
    ...
                    endif
                    Goto
            endif
            if MenuMode
                    if (StopSpam == 0)
                            con_ToggleMenus
                            ;Refresh inventory menu if necessary
                            player.EquipItem P1DKeychain
                            player.UnEquipItem P1DKeychain
                    endif
            endif
            
            ;Reset inputs
            if UseIT
                    set UseIT to 0
            endif
    end
    

    Advanced[edit]

    Pluggy[edit]

    Pluggy is fairly unique, and I would suggest you read the Intro to Pluggy Arrays and keep the list of functions available as you read this.

    Initializing the system[edit]

    Use the script as in the Quick Guide. Here's a description of the various parts and options:

    Registration System[edit]

    Inventory Tracking uses a registration system so it can shut itself down if the player de-activates your mod. Before you activate the Initializer, make sure to set its rInUse variable to a reference from your mod. The reference can be anything new in your mod: base object of an item, specific reference of an object or item that your mod places in the world, a quest, etc. (A specific example that won't work - if you edit a vanilla item, i.e., reduce the weight of a Silver Shortsword, the item is still from "Oblivion.esm" and won't work. If you make a new Silver Shortsword base object, that will work.)

    Running in Combat[edit]

    By default, Inventory Tracking will stop while the player is in combat. If you need to track items in combat (i.e., lost arrows) then set the RunInCombat variable to 1 before activating the Initializer.

    Version Number[edit]

    cobInvTrackingInit can be thought of as a version number. Use the following guide to determine which version you need.

    Version COBL version Changes
    1 v1.35 First version - do not use
    2 v1.36 Fixed a MenuMode bug, first usable version

    Some Background info[edit]

    Variables[edit]

    All of the variables that you'll be using are on the quest "cobInvTracker". They're on the script "cobInvTrackerQS" under the ";Global" section. You will need to refer to them as any other external quest variable and prefix the variable with "cobInvTracker.".

    When does it run?[edit]

    Inventory Tracking runs during most of GameMode and MenuMode. It won't run while the player is in combat (unless it's been set to run during combat) and won't run during the Options menus or Messageboxes.

    Using the lists[edit]

    Scan Index (cobInvTracker.iScan)[edit]

    The scan index is changed after every new scan. Note that it's changed, not necessarily increased. So the check should be

    long ScanNumber
    ...
    begin GameMode
            if (cobInvTracker.iScan != ScanNumber)
                    ;Check the lists for changes
                    set ScanNumber to cobInvTracker.iScan
            endif
    end
    
    ;Need the same for MenuMode as it continues to run
    begin MenuMode
            if (cobInvTracker.iScan != ScanNumber)
                    ;Check the lists for changes
                    set ScanNumber to cobInvTracker.iScan
            endif
    end
    
    • If this is on a quest, you can set "fQuestTimeDelay" to 1 or 1.5.
      • You should also add a special part to the top of the MenuMode block to make sure it won't run during the Main Menu
    ...
    begin MenuMode
            if (MenuMode 1044)
                    return
            endif
    ...
    

    List Updates[edit]

    After every scan a "Changes" variable is updated for each list (AddChanges, RemoveChanges, etc.). This is the number of changes that occurred for each list. For the examples above

    Scan 1

    List Changes
    Increased 3
    Added 3
    New 3
    Decreased 0
    Removed 0

    Scan 2

    List Changes
    Increased 0
    Added 0
    New 0
    Decreased 0
    Removed 0

    Scan 3

    List Changes
    Increased 2
    Added 1
    New 1
    Decreased 2
    Removed 1

    Scan 1

    List Changes
    Increased 1
    Added 1
    New 0
    Decreased 0
    Removed 0

    This number is very important! The arrays will not be "shrunk" after each scan. For instance, on the second scan (inventory doesn't change) the lists actually look like the first:

    Increased - 5 Iron Arrows, 1 Iron Cuirass, 50 Gold
    Added - Iron Arrows, Iron Cuirass, Gold
    New - Iron Arrows, Iron Cuirass, Gold
    

    However, the Changes variables will all be 0 so you should know that the lists should be ignored.

    Use a check, such as

    if (iInv < cobInvTracker.AddChanges)
    

    to make sure you don't use information from old scans.

    The lists[edit]

    The lists themselves are arrays. For instance, on the first scan they will look like this

    Increased List

    Index Item (from array aInc) Count (from array aIncCount)
    0 Iron Arrow 5
    1 Iron Cuirass 1
    2 Gold 50

    Added List

    Index Item (from array aAdd)
    0 Iron Arrow
    1 Iron Cuirass
    2 Gold

    New List

    Index Item (from array aNew)
    0 Iron Arrow
    1 Iron Cuirass
    2 Gold

    Use the function GetInArray to retrieve the information.

  4. Attach the script to the quest