Jump to content

[Tutorial] Adding Items to a Store


Recommended Posts

Hello there!

It's two years in the future here, how's thing way back when?

 

Thanks for this info.

 

I wanted to ask some advice - I'm trying to add something to the starting cupboard.

I've identified it as : 546e5d97-c234-46dc-9439-b23aafc02198

... and the thing I want to add is my custom item, so that guid goes into a custom LL (LootList) with just one item in it.

 

I only want the item to appear in the cupboard once, so I did this;

                    "RunOnlyOnce": "true",
                    "SucceedOnlyOnce": "true",

 

and I put the lootlist GUID into the CGenerateLootListInto value

 

What makes the MoScriptHook get run by the game? Does it go in a special folder?

Link to comment
Share on other sites

18 hours ago, Svartypops said:

What makes the MoScriptHook get run by the game? Does it go in a special folder?

It just needs to be anywhere game data can be loaded from (e.g. anywhere in a mod folder). If the game data object is loaded, it will be run when appropriate.

  • Thanks 1
Link to comment
Share on other sites

On 6/14/2021 at 7:35 PM, BMac said:

It just needs to be anywhere game data can be loaded from (e.g. anywhere in a mod folder). If the game data object is loaded, it will be run when appropriate.

Thank you very much for this.

 

I've been putting items in existing lootlists by just putting one item under the existing header - is that possible, or does it overwrite the existing lootlist with mine?
I'm rather confused how this "put what you want to change in the mod" thing works because ... how can you remove things from an existing list? So my guess is that I overwrite the whole of whatever currently exists with whatever I put in brackets?  But that can't be true either ...  my mind is tying itself in knots.

Edited by Svartypops
Link to comment
Share on other sites

Your last sentence is correct.  If you put any object in a bundle file in your mod folder, it throws out any already-existing object with that "ID" and replaces it with what you have in your file.  Putting only the new entries you want to add in your override loot list will actually replace the entire loot list with only those.  You'd have to copy over the existing data and then add or remove from that.  That's part of why ModScriptHookGameData is useful; it lets you make changes to store loot without copying and overriding entire loot lists.

  • Like 2
Link to comment
Share on other sites

4 hours ago, BMac said:

Your last sentence is correct.  If you put any object in a bundle file in your mod folder, it throws out any already-existing object with that "ID" and replaces it with what you have in your file.  Putting only the new entries you want to add in your override loot list will actually replace the entire loot list with only those.  You'd have to copy over the existing data and then add or remove from that.  That's part of why ModScriptHookGameData is useful; it lets you make changes to store loot without copying and overriding entire loot lists.

Perfect, this completely clears this up for me.

I'll see what I can do about tweaking my mod to adjust the existing loot lists, because just overwriting the original one is bad for compatibility.

 

Thank you very much BMac ❤️

 

Ah... seems I can't substitute the LootList for the container in the first post. That's a shame. I'll have to be the overwriting method, after all.

Edited by Svartypops
  • Thanks 1
Link to comment
Share on other sites

  • 6 months later...

Hello and a happy new year!

 

I tried everything possible to add my custom item to a crate / vendor / body, but unfortunately it will not appear. I hope someone can help me out...

The folder structure is like in the tutorial; the item itself works fine, itemmod and abilities are linked together. The following code is in one file; abilities and statuseffects in 2 seperate ones. (I think this is only for formatting; the variables handle everthing else. I saw mods where everything is in one file and it works just fine. But for educational reasons I wanted to stay with the original file structure).

Spoiler
{
	"GameDataObjects": [
	{
		"$type": "Game.GameData.EquippableGameData, Assembly-CSharp",
		"DebugName": "MOD_Abydons_Ring_Item",
		"ID": "f77813b4-0cee-437f-8128-07391fea2e05",
		"Components": [{
			"$type": "Game.GameData.ItemComponent, Assembly-CSharp",
			"DisplayName": 20525,
			"DescriptionText": 20526,
			"FilterType": "Clothing",
			"InventoryAudioEventListID": "c607cfb4-6130-4827-87f4-fac4c95a3e02",
			"IsQuestItem": "false",
			"IsIngredient": "false",
			"IsCurrency": "false",
			"IsAdventuringItem": "false",
			"IsJunk": "false",
			"CanSellForFullValue": "true",
			"MaxStackSize": 1,
			"NeverDropAsLoot": "false",
			"CanBePickpocketed": "true",
			"IsUnique": "true",
			"Value": 37500,
			"IconTextureSmall": "gui/icons/items/jewelry/ring_harmony_s.png",
			"IconTextureLarge": "gui/icons/items/jewelry/ring_harmony_l.png",
			"PencilSketchTexture": "",
			"InspectOnUseButton": [],
			"IsPlaceholder": "false"
		},
		{
			"$type": "Game.GameData.EquippableComponent, Assembly-CSharp",
			"EquipmentType": "None",
			"EquipmentSlot": "RingAnyHand",
			"AppearancePiece": {
				"ModelVisualDataPath": ""
			},

			"ItemModsIDs": ["9fe3c993-dbd0-4781-af66-d8333b49f903"],

			"OnEquipVisualEffects": [],
			"RestrictedToClassIDs": [],
			"RestrictedToPlayer": "false",
			"EquipConditionals": {
				"Operator": 0,
				"Components": []
			},
			"ProficientAbilityID": "00000000-0000-0000-0000-000000000000",
			"CannotUnequip": "false",
			"ItemRendererPrefab": "",
			"ItemModel": "",
			"AnimationController": "",
			"PaperdollOverrideRenderer": "",
			"AttackSummonID": "00000000-0000-0000-0000-000000000000",
			"CannotSheathe": "false",
			"PropVisualEffects": []
		}]
	},
	{
		"$type": "Game.GameData.ItemModGameData, Assembly-CSharp",
		"DebugName": "MOD_Abydons_Ring_Mod",
		"ID": "9fe3c993-dbd0-4781-af66-d8333b49f903",
		"Components": [{
			"$type": "Game.GameData.ItemModComponent, Assembly-CSharp",
			"DisplayName": 20525,
			"HideFromUI": "false",
			"EnchantCategory": "Attributes",
			"Cost": 1,
			"CursesItem": "false",
			"DurabilityDamage": 0,
			"StatusEffectsOnEquipIDs": [],
			"StatusEffectsOnLaunchIDs": [],
			"StatusEffectsOnAttackIDs": [],
			"AbilityModsOnEquipIDs": [],
			"OnEquipVisualEffects": [],
			"DamageProcs": [],
			"AbilitiesOnEquipIDs": ["285ed8bd-d40a-4709-b79e-1f5740d2916e"]
		}]
	},
        {
            "$type": "Game.GameData.LootListGameData, Assembly-CSharp",
            "DebugName": "MOD_Abydons_Ring_LL",
            "ID": "1e40fb40-c4cc-40dd-b9d6-89d24bbe9a32",
            "Components": [
                {
                    "$type": "Game.GameData.LootListComponent, Assembly-CSharp",
                    "Conditional": {
                        "Operator": 0,
                        "Components": []
                    },
                    "OutputChance": 1,
                    "OutputMode": "All",
                    "Items": [
                        {
                            "Conditional": {
                                "Operator": 0,
                                "Components": []
                            },
                            "OutputChance": 1,
                            "MinCount": 1,
                            "MaxCount": 1,
                            "Weight": 1,
                            "ItemID": "f77813b4-0cee-437f-8128-07391fea2e05",
                            "LootListID": "00000000-0000-0000-0000-000000000000",
                            "LockedVisible": "false"
                        }
                    ]
                }
            ]
        },
	{
			"$type": "Game.GameData.ModScriptHookGameData, Assembly-CSharp",
			"DebugName": "MOD_Abydons_Ring_Add",
			"ID": "b83388da-57b1-448f-9be9-296d588e88b7",
			"Components": [
				{
					"$type": "Game.GameData.ModScriptHookComponent, Assembly-CSharp",
					"RunOnlyOnce": "false",
					"SucceedOnlyOnce": "true",
					"Script": {
						"Conditional": {
							"Operator": 0,
							"Components": [
								{
									"$type": "OEIFormats.FlowCharts.ConditionalCall, OEIFormats",
									"Data": {
										"FullName": "Boolean IsInActiveScene(Guid)",
										"Parameters": [
											"2c8238c9-7a86-4394-b5e6-45c6a1d0bf10"
										]
									},
									"Not": false,
									"Operator": 0
								}
							]
						},
						"Scripts": [
							{
								"Data": {
									"FullName": "Void GenerateLootListInto(Guid, Guid)",
									"Parameters": [
										"2c8238c9-7a86-4394-b5e6-45c6a1d0bf10",
										"1e40fb40-c4cc-40dd-b9d6-89d24bbe9a32"
									]
								},
								"Conditional": {
									"Operator": 0,
									"Components": []
								}
							}
						]
					}
				}
			]
		}
	]
}

 

I also tried the spawning options swapping from true to false to both of them true. But it helped neither.

It cost me a whole night haha Maybe BMac knows what's wrong here 🙂

 

cheers

Edited by Phipanjo
grammar
Link to comment
Share on other sites

@Phipanjo If I replace '2c8238c9-7a86-4394-b5e6-45c6a1d0bf10' with the object GUID used in the example in the original post, I see your item in that store (Marihi's Metalworks). I don't know what your GUID is since I don't have the tools to search it up right now, but make sure it's an object GUID for a container or inventory in the store scene that's actually tied to that store. Also make sure your mod actually shows up in the Mod Manager, if not your folder structure may be the problem.

  • Thanks 1
Link to comment
Share on other sites

13 hours ago, BMac said:

@Phipanjo If I replace '2c8238c9-7a86-4394-b5e6-45c6a1d0bf10' with the object GUID used in the example in the original post, I see your item in that store (Marihi's Metalworks). I don't know what your GUID is since I don't have the tools to search it up right now, but make sure it's an object GUID for a container or inventory in the store scene that's actually tied to that store. Also make sure your mod actually shows up in the Mod Manager, if not your folder structure may be the problem.

Hi BMac, thanks for the answer!
I forgot to enable the cheat mode haha... Kinda took a whole night to get it 😅

But anyway... thanks for the script and your help, all works perfect now! 

cheers

Edited by Phipanjo
Link to comment
Share on other sites

  • 7 months later...

In the documentation it states that "Boolean expressions are evaluated right-to-left with no operator precendence." Am I correct in my understanding of this as: when a script tries to run it checks off all the conditions required, in the case of formatted json, from "bottom to top"? And if a condition is not met does it still go through the remaining list or does it stop checking at the first conditional that would stop the script from running?

Edited by Kvellen
Link to comment
Share on other sites

19 hours ago, Kvellen said:

In the documentation it states that "Boolean expressions are evaluated right-to-left with no operator precendence." Am I correct in my understanding of this as: when a script tries to run it checks off all the conditions required, in the case of formatted json, from "bottom to top"? And if a condition is not met does it still go through the remaining list or does it stop checking at the first conditional that would stop the script from running?

I looked at the code, and that comment definitely doesn't have enough detail to explain the behavior, which is, to be honest, bizarre.  Typically in Boolean algebra, R1 and R2 or R3 would be evaluated like (R1 and R2) or R3, but that's not the case here.  The conditionals are evaluated in order. If any AND conditional is false, the whole condition fails. If any OR conditional is true, the whole condition passes. In either case, evaluation will short-circuit (not evaluating any more conditionals).

If neither of those things occur, the result is based on the operator of the last conditional.

  • If it's AND, the result is RN (the result of the last conditional)
  • If it's OR, the result is RN OR RN-1

I'm not sure why it says "right-to-left". As far as I can tell, the evaluation order is left-to-right (top-to-bottom in JSON).

 

  • Thanks 2
Link to comment
Share on other sites

  • 10 months later...

cannot for the life of me get a scripted GenerateLootListInto to work, if i do it manually via console it works as intended, items from my lootlist get added to the targeted store(in this case, No-Nut Ned's) but if i do it via script, using the exact same guids, the items get added to Ned's inventory, not his store, so they cant be bought, they need to be looted from his corpse or pick-pocket'd, what am i doing wrong:

 

Quote

{
    "GameDataObjects": [
        {
            "$type": "Game.GameData.ModScriptHookGameData, Assembly-CSharp",
            "DebugName": "New_Items_For_Neds_Store",
            "ID": "29862625-8202-4002-90ce-480515d4b2e8",
            "Components": [
                {
                    "$type": "Game.GameData.ModScriptHookComponent, Assembly-CSharp",
                    "RunOnlyOnce": "false",
                    "SucceedOnlyOnce": "true",
                    "Script": {
                        "Conditional": {
                            "Operator": 0,
                            "Components": [
                                {
                                    "$type": "OEIFormats.FlowCharts.ConditionalCall, OEIFormats",
                                    "Data": {
                                        "FullName": "Boolean IsInActiveScene(Guid)",
                                        "Parameters": [
                                            "60560fd3-2a9e-4250-ac0f-4cefe6e6cd2d"
                                        ]
                                    },
                                    "Not": false,
                                    "Operator": 0
                                }
                            ]
                        },
                        "Scripts": [
                            {
                                "Data": {
                                    "FullName": "Void GenerateLootListInto(Guid, Guid)",
                                    "Parameters": [
                                        "60560fd3-2a9e-4250-ac0f-4cefe6e6cd2d",
                                        "0d3644ce-7785-40e3-ac43-df4470157a84"
                                    ]
                                },
                                "Conditional": {
                                    "Operator": 0,
                                    "Components": []
                                }
                            }]
                       }
                }]
        }]
}

image.png.1cd1ed35ff91b7f7071f86bc52290c4a.png

Edited by TKDancer
Link to comment
Share on other sites

  • 1 year later...

hey, cannot figure out what I am doing wrong...  I am trying to update a mod I have that regenerated from scratch the loot list of a store with a new item using this better method that will not conflict with other mods, but cannot seem to get it to work...


    {
      "$type": "Game.GameData.LootListGameData, Assembly-CSharp",
      "DebugName": "NewGrimoire_Inventory",
      "ID": "cccf24eb-e291-4c9f-a6bd-b6c238061763",
      "Components": [
        {
          "$type": "Game.GameData.LootListComponent, Assembly-CSharp",
          "Conditional": {
            "Operator": 0,
            "Components": []
          },
          "OutputChance": 1,
          "OutputMode": "All",
          "Items": [
            {
              "Conditional": {
                "Operator": 0,
                "Components": []
              },
              "OutputChance": 1,
              "MinCount": 1,
              "MaxCount": 1,
              "Weight": 1,
              "ItemID": "d8bd35e5-cb56-4909-b5ff-b60705ab3c5b",
              "LootListID": "00000000-0000-0000-0000-000000000000",
              "LockedVisible": "false"
            }
          ]
        }
      ]
    },

    {
      "$type": "Game.GameData.ModScriptHookGameData, Assembly-CSharp",
      "DebugName": "NewGrimoire_AddedTo_Store_09_PM_Market_BaseInventory",
      "ID": "57953514-d4b5-41a9-a290-dcfb86ce891d",
      "Components": [
        {
          "$type": "Game.GameData.ModScriptHookComponent, Assembly-CSharp",
          "RunOnlyOnce": "false",
          "SucceedOnlyOnce": "true",
          "Script": {
            "Conditional": {
              "Operator": 0,
              "Components": [
                {
                  "$type": "OEIFormats.FlowCharts.ConditionalCall, OEIFormats",
                  "Data": {
                    "FullName": "Boolean IsInActiveScene(Guid)",
                    "Parameters": [
                      "51192de0-6c00-4c49-b503-bcaa10c18c18"
                    ]
                  },
                  "Not": false,
                  "Operator": 0
                }
              ]
            },
            "Scripts": [
              {
                "Data": {
                  "FullName": "Void GenerateLootListInto(Guid, Guid)",
                  "Parameters": [
                    "51192de0-6c00-4c49-b503-bcaa10c18c18",
                    "cccf24eb-e291-4c9f-a6bd-b6c238061763"
                  ]
                },
                "Conditional": {
                  "Operator": 0,
                  "Components": []
                }
              }
            ]
          }
        }
      ]
    }

 

Link to comment
Share on other sites

Thank you @Kvellen

I think I understand my initial mistake.  I had previously been editing wholesale one of Henric's component lootlists (Store_09_PM_Market_BaseInventory","ID":"51192de0-6c00-4c49-b503-bcaa10c18c18"), and I erroneously used that GUID for the new code in this thread (the loot list not the NPC container itself).  Whereas I should have been referencing the final scene container itself, Henric the NPC GUID which you provided helpfully.  I also figured out now the reason for the PrintInstance oei_hovered instruction in the thread because that NPC GUID for Henric was nowhere to be found in the files I am used to scouring for various GUIDs to mod (i.e., files located at exported\design\gamedata).  Got all that now.  The actual container names in the scenes themselves must be buried in other files and it is just easiest to load the application, play and use the console to grab the relevant GUID.  Or use a site like Pillars of Eternity Wiki which helpfully includes GUID references for NPCs, which I saw after reading your response.  So I believe I am exceedingly close I think to figuring this out, but I'd be damned if my revised code still does not work...   Is there per chance any other glaring things I got messed in trying to use this hook.  Here is the updated code:

   {
      "$type": "Game.GameData.LootListGameData, Assembly-CSharp",
      "DebugName": "MyCustomLootList",
      "ID": "0ca6481c-8359-4936-b5a0-1090360fd697",
      "Components": [
        {
          "$type": "Game.GameData.LootListComponent, Assembly-CSharp",
          "Conditional": {
            "Operator": 0,
            "Components": []
          },
          "OutputChance": 1,
          "OutputMode": "All",
          "Items": [
            {
              "Conditional": {
                "Operator": 0,
                "Components": []
              },
              "OutputChance": 1,
              "MinCount": 1,
              "MaxCount": 1,
              "Weight": 1,
              "ItemID": "d8bd35e5-cb56-4909-b5ff-b60705ab3c5b",
              "LootListID": "00000000-0000-0000-0000-000000000000",
              "LockedVisible": "false"
            }
          ]
        }
      ]
    },


    {
      "$type": "Game.GameData.ModScriptHookGameData, Assembly-CSharp",
      "DebugName": "Henric_NewGrimoireAdded",
      "ID": "6f9c7afc-12e3-46ce-ac72-5677764bf193",
      "Components": [
        {
          "$type": "Game.GameData.ModScriptHookComponent, Assembly-CSharp",
          "RunOnlyOnce": "false",
          "SucceedOnlyOnce": "true",
          "Script": {
            "Conditional": {
              "Operator": 0,
              "Components": [
                {
                  "$type": "OEIFormats.FlowCharts.ConditionalCall, OEIFormats",
                  "Data": {
                    "FullName": "Boolean IsInActiveScene(Guid)",
                    "Parameters": [
                      "2c8238c9-7a86-4394-b5e6-45c6a1d0bf10"
                    ]
                  },
                  "Not": false,
                  "Operator": 0
                }
              ]
            },
            "Scripts": [
              {
                "Data": {
                  "FullName": "Void GenerateLootListInto(Guid, Guid)",
                  "Parameters": [
                    "2c8238c9-7a86-4394-b5e6-45c6a1d0bf10",
                    "0ca6481c-8359-4936-b5a0-1090360fd697"
                  ]
                },
                "Conditional": {
                  "Operator": 0,
                  "Components": []
                }
              }
            ]
          }
        }
      ]
    }

As you can see I created my custom loot list, put my grimoire GUID in it (item of 1), then used the new code from this thread as revised to use the NPC container GUID from the scene which you shared, not one of its component loot lists, in the hook.  Still not getting the new item (my custom grimoire to load), exiting and re-entering port Maje still nothing.  The old code I have where I just edit the NPC's loot list directly is still working, but that I know can conflict.

Anywho, if you still looking at the thread let me know if you have further insights.

 

 

Link to comment
Share on other sites

Ah my bad, what's happened is you used the NPC's GUID instead of GUID of Store. So the loot list is being added to the NPC's personal inventory rather than to their store's inventory.

A store inventory can be in a visible object (a box of somekind) in the level, or it can be a hidden object. In the latter case PrintInstance oei_hovered can't be used to and instead you'll need a different command to find it:

On 8/23/2018 at 11:21 PM, BMac said:

If the object is not visible in the scene, you can search for it by name with the console command "FindObject". For example, "FindObject Store" will output to the combat log all objects with the string "Store" in their name.

http://s3.amazonaws.com/uploads.forums.obsidian.net/monthly_08_2018/post-113185-0-66447900-1535062670.png

You can then copy down the GUID manually or copy it to the clipboard with PrintInstance (e.g. "PrintInstance Store_05_Marihi").

I used the above method to find the Store, but made the mistake of assuming the NPC and Store shared a GUID in my initial reply.
When in fact only the first couple of character are the same:
Store:   "2c8238c9-6979-4b81-90f9-07e834f07ea3"
Henric: "2c8238c9-7a86-4394-b5e6-45c6a1d0bf10"

The following should hopefully work:

{
  "GameDataObjects": [
    {
      "$type": "Game.GameData.LootListGameData, Assembly-CSharp",
      "DebugName": "MyCustomLootList",
      "ID": "0ca6481c-8359-4936-b5a0-1090360fd697",
      "Components": [
        {
          "$type": "Game.GameData.LootListComponent, Assembly-CSharp",
          "Conditional": {
            "Operator": 0,
            "Components": []
          },
          "OutputChance": 1,
          "OutputMode": "All",
          "Items": [
            {
              "Conditional": {
                "Operator": 0,
                "Components": []
              },
              "OutputChance": 1,
              "MinCount": 1,
              "MaxCount": 1,
              "Weight": 1,
              "ItemID": "d8bd35e5-cb56-4909-b5ff-b60705ab3c5b",
              "LootListID": "00000000-0000-0000-0000-000000000000",
              "LockedVisible": "false"
            }
          ]
        }
      ]
    },
    {
      "$type": "Game.GameData.ModScriptHookGameData, Assembly-CSharp",
      "DebugName": "Henric_NewGrimoireAdded",
      "ID": "39519315-eda2-48e7-a498-80f5c442ca03",
      "Components": [
        {
          "$type": "Game.GameData.ModScriptHookComponent, Assembly-CSharp",
          "RunOnlyOnce": "false",
          "SucceedOnlyOnce": "true",
          "Script": {
            "Conditional": {
              "Operator": 0,
              "Components": [
                {
                  "$type": "OEIFormats.FlowCharts.ConditionalCall, OEIFormats",
                  "Data": {
                    "FullName": "Boolean IsInActiveScene(Guid)",
                    "Parameters": [
                      "2c8238c9-6979-4b81-90f9-07e834f07ea3"
                    ]
                  },
                  "Not": false,
                  "Operator": 0
                }
              ]
            },
            "Scripts": [
              {
                "Data": {
                  "FullName": "Void GenerateLootListInto(Guid, Guid)",
                  "Parameters": [
                    "2c8238c9-6979-4b81-90f9-07e834f07ea3",
                    "0ca6481c-8359-4936-b5a0-1090360fd697"
                  ]
                },
                "Conditional": {
                  "Operator": 0,
                  "Components": []
                }
              }
            ]
          }
        }
      ]
    }
  ]
}

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...