Skip to content

Advanced Shop

Rijam edited this page Jan 1, 2026 · 4 revisions

Here are some additional things we can do with our shops.

public override void AddShops() {
	// We can use the chaining syntax on our shops too.
	// Notice how there are no semi colons until the end.
	var npcShop2 = new NPCShop(Type, Shop2)
		.Add(ItemID.Starfury)
		.Add(ItemID.TerraBlade, Condition.DownedPlantera, Condition.Eclipse)
		.Add(ItemID.RainbowRod, Condition.DownedMechBossAll)
		.Add(ItemID.Terragrim, Condition.PlayerCarriesItem(ItemID.EnchantedSword))
		.Add(ItemID.Megashark, Condition.NpcIsPresent(NPCID.ArmsDealer));

	// This will hide one of the items in the shop. It will throw an exception if the item is not found, though.
	npcShop2.GetEntry(ItemID.RainbowRod).Disable();

	// TryGetEntry will not throw an exception if it fails.
	if (npcShop2.TryGetEntry(ItemID.Megashark, out var entry)) {
		// We can add additional conditions.
		entry.AddCondition(Condition.DownedMechBossAny);
	}

	npcShop2.Add(ItemID.LightsBane, Condition.DownedEowOrBoc);
	// The Space Gun will be added to the shop before the Light's Bane
	npcShop2.InsertBefore(ItemID.LightsBane, ItemID.SpaceGun);
	// The Influx Waver will be added after the Terra Blade.
	npcShop2.InsertAfter(ItemID.TerraBlade, ItemID.InfluxWaver, Condition.DownedMartians);

	npcShop2.Add(ItemID.DD2BallistraTowerT1Popper, Condition.DownedOldOnesArmyAny);
	npcShop2.GetEntry(ItemID.DD2BallistraTowerT1Popper).ReserveSlot(); // We reserve this slot it always appear here.

	npcShop2.Register();
}

The ModifyActiveShop() hook can be used to edit the shop inventory each time the shop is openned, as opposed to once during mod load like with AddShops(). This hook should only really be used to editing exist items and you should avoid adding new items with this hook.

public override void ModifyActiveShop(string shopName, Item[] items) {
	// ModifyActiveShop() can modify the shop each time the shop is opened (as opposed to once during mod load).
	// This hook should mostly be used for modifying items already in the shop instead of adding new ones.

	// Here is an example using a foreach loop
	foreach (Item item in items) {
		// If the item in the list isn't a real item, continue to the next item in the list.
		if (item is null)
		{
			continue;
		}
		// If the item is a Terragrim, set it's price to double it's value.
		if (item.type == ItemID.Terragrim) {
			item.shopCustomPrice = item.value * 2;
		}
		// If the item is a Starfury in the second shop, set its modifier to Legendary.
		if (item.type == ItemID.Starfury && shopName == NPCShopDatabase.GetShopName(Type, Shop2)) {
			item.Prefix(PrefixID.Legendary);
		}
		// If the player has the Discount Card equipped, then make the prices even cheaper for our Town NPC.
		if (Main.LocalPlayer.discountAvailable) {
			// We want to discount the shopCustomPrice number if it exists. If it doesn't, then we discount the value instead.
			// Item.GetStoreValue() will return the shopCustomPrice if it exists, otherwise it'll return the value.
			// (It is the same as writing `item.shopCustomPrice ?? item.value` if you prefer that.)
			// Changing the price like this is multiplicative with the discount from the Discount Card. (So 0.8f * 0.8f == 0.64f or 36% discount)
			item.shopCustomPrice = (int?)(item.GetStoreValue() * 0.8f);
		}
	}

	// Here is an example using a for loop
	for (int i = 0; i < items.Length; i++) {
		// Here we find the first item that doesn't exist and set it to the Universal Pylon, then we break out of the loop to stop it.
		if (items[i] is null) {
			items[i] = new Item(ItemID.TeleportationPylonVictory);
			break;
		}
	}

	// Here is an example adding an item to the last slot of the shop.
	items[^1] = new Item(ItemID.PoopBlock) { shopCustomPrice = Item.buyPrice(platinum: 1) };
}


Clone this wiki locally