PGM
PGM copied to clipboard
Interactive Shops 🛒
Interactive Shops
Time for a brand new PGM feature, interactive shops! 🤯 For years map authors have had to rely on the traditional method of villager trading, setting up custom trades can be tiresome and non-intuitive for those without the experience. With this feature we bring a sleek and simple way to add shop functionality to a map.
Additionally, this feature will benefit immensely from the pending includes PR #1028. Once both are merged, many new possibilities unlock such as standardized shop menus across similar gamemodes.
Shops
Shops are composed of a few different elements, at the highest level we have the shop itself. The only attribute you can customize is its name, the name of a shop must be unique and will be displayed in the title of the GUI menu. Within each shop section you can define a number of categories and then each category can hold up to 28 items.
XML Example:
<shops>
<!-- Our first example is of a generic item shop -->
<shop id="item-shop" name="`bItems">
<!-- You can have an multiple categories, each category will hold up to 28 items -->
<category id="weapons" name="`cWeapons" material="stone sword">
<!-- Free item (you can exclude price if desired) -->
<item material="wooden sword" amount="1" price="0"/>
<!-- Currency & Price in same line -->
<item material="stone sword" amount="1" price="3" currency="emerald" />
<!-- Payment as sub-element but only using a single currency -->
<item name="Cool TNT" material="tnt" amount="5">
<payment price="5" currency="gunpowder"/>
</item>
<!-- Multiple Payment requirements-->
<item material="diamond sword" amount="1">
<enchantment level="2">sharpness</enchantment>
<payment price="10" currency="diamond"/>
<payment price="20" currency="stick"/>
</item>
</category>
<category id="food" name="`aFood" material="apple">
<item material="apple" name="Tasty Apples" amount="100" price="10" currency="gold ingot"/>
</category>
</shop>
<!-- You can define as many <shops> as needed, here's a second example -->
<shop id="MagicShop" name="`5Magic Shop">
<category id="magic" name="`dWands">
<!-- Item where the kit is defined. Icon will render as a stick but kit can give anything -->
<item name="Magic Wand" material="stick" price="100" currency="blaze powder" kit="magic-kit">
</category>
</shop>
</shops>
Shopkeepers
A shopkeeper is an entity that spawns at match start. These entities wil not die and are unable to be moved. Clicking a shopkeeper will open their related GUI. Each shopkeeper may only be associated with a single shop type, but each shop type can have an unlimited number of shopkeepers. Plus, there's no limitations in place to how many players can have a shop open at the same time.
XML Example:
<!-- There's no limit to how many shopkeepers can be in each match, as long as each <shop> has at least 1 keeper -->
<shopkeepers>
<!-- A shop keeper for the "Items" shop (defaults to villager) -->
<shopkeeper name="`5Purple `7Item Shop" shop="Items">
<point yaw="0" pitch="10">-88.5,13,-103.5</point>
</shopkeeper>
<!-- You can define a mob attribute to set the entity type -->
<shopkeeper name="`4Red `dMagic Shop" mob="Witch" shop="MagicShop" />
<point>-90.5,54,-100.5</point>
</shopkeeper>
</shopkeepers>
Screenshots
General layout of interactive shops. You'll notice how categories are along the top, purchasable items below.

Clicking a category will change the purchasable item selection

Hovering over an item will display whether you can purchase the item or not

Example of item with custom name and lore
Feedback
As always I've tested this extensively and should work as intended. Though with a feature this flexible, I'm sure there's likely to be situations I've not thought of. Please let me know if there's any feedback and I'll be happy to see what can be improved.
Also while not exactly custom villager trades, I believe this PR would be enough to close #463
Signed-off-by: applenick [email protected]
ToDo: (so we don't forget)
- [x] support free items
- [x] complex payment
- [x]
filterattribute to change item visibility
Complex payment example:
<item ...>
<payment currency="a" price="1"/>
<payment currency="b" price="1"/>
</item>
ToDo: (so we don't forget)
filterattribute to change item visibility- support free items
- complex payments
Complex payment example:
<item ...> <payment currency="a" price="1"/> <payment currency="b" price="1"/> </item>
Should payments be defined in filter style?
<!-- 1 Gold -->
<item ...>
<payment currency="gold"/>
</item>
<!-- 1 Gold AND 1 Iron -->
<item ...>
<all>
<payment currency="gold"/>
<payment currency="iron"/>
</all>
</item>
<!-- 1 Gold AND 1 Iron, OR 2 Emerald -->
<item ...>
<any>
<all>
<payment currency="gold"/>
<payment currency="iron"/>
</all>
<payment currency="emerald" price="2"/>
</any>
</item>
Really cool idea! Once concern though is finding a clean format for the cost lore.
At the moment cost looks like this:

Here's my proposal for how the format would look with multiple currency requirements:
Cost:
✔ 3 Emerald
X 10 Diamond
We'll use the symbols to let users know if they meet the required conditions. Though I'm not sure the best way to format it for the third example you gave. Any suggestions?
Should payments be defined in filter style?
<!-- 1 Gold AND 1 Iron, OR 2 Emerald --> <item ...> <any> <all> <payment currency="gold"/> <payment currency="iron"/> </all> <payment currency="emerald" price="2"/> </any> </item>
I thought about it but I think this adds alot more complexity than it solves, we cannot straight rely on filters for this functionality because we need to actually go and remove the items, if you have multiple ways of paying up which is the preferred?
If you want
Here's what a free item looks like:

XML:
<item material="wood" amount="1" price="0"/>
When the price of an item is below 1, then currency is no longer considered a required attribute. I'm thinking perhaps we should have the parsing default price to 0, so that in an instance where price is missing then we can just assume the item is free.
New feature: customizing currency 💵
XML:
<!-- New `color` attribute changes currency text color -->
<item material="gold chestplate" amount="1" price="3" currency="emerald" color="green"/>
<!-- Usage with individual payments -->
<item material="gold chestplate" amount="1">
<payment currency="emerald" price="1" color="green"/>
<!-- Different currency can have different colors-->
<payment currency="diamond" price="2" color="aqua"/>
</item>
<!-- New custom currency uses a child <item> under <payment> -->
<item material="diamond sword" amount="1">
<payment price="1">
<!-- Item name is shown under the cost lore instead of the raw material name -->
<item name="`bEnchanted Diamond" material="diamond">
<enchantment level="2">sharpness</enchantment>
</item>
</payment>
</item>
Pushed changes to hopefully finish-up the pr:
Shop keepers
Shop keepers must use a single point provider, if you want multiple locations simply use multiple shop keepers:
<shopkeepers>
<shopkeepers name="`5Purple `7Item Shop" mob="Villager" shop="items">
<shopkeeper yaw="-99" pitch="-22">-88.5,14,-103.5</shopkeeper>
<shopkeeper yaw="90" pitch="-22">100.5,16,-103.5</shopkeeper>
<shopkeeper yaw="180" pitch="-22">10,18,-10.5</shopkeeper>
<shopkeeper yaw="0" pitch="-22">105,20,200.5</shopkeeper>
</shopkeepers>
<shopkeeper name="Other shop" shop="items" yaw="-99" pitch="-22">-88.5,14,-103.5</shopkeeper>
<shopkeeper name="Yet another shop" shop="items">
<cylinder yaw="-99" pitch="-22" base="-88.5,14,-103.5" radius="5" height="1"/>
</shopkeeper>
</shopkeepers>
All of the shopkeepers are valid, they can have the point be themselves (directly a location defined within them, and optionally pitch & yaw) or have a sub-element point or other type of region within them. They must however have a single location provider, multiple inner points/regions are not allowed.
Kit overflow handling
Historically pgm just did nothing if the items in the kit overflew and did not fit the inventory. Now kits support dropping those items in the ground, and/or sending the player a message when that happens (you don't need both, you can just send a message without dropping, or you can drop without a message too). You can also combine this with #1049 to ensure the items are given even if you already have them on your inventory.
<kit id="spawn" drop-overflow="true" overflow-warning="Woopsie, your inventory is full">
<item .../>
</kit>
<kit id="spawn" drop-overflow="true">
<item .../>
<overflow-warning>{"translate": "shop.purchase.overflow"}</overflow-warning>
</kit>
Using the translate message shop.purchase.overflow will result in a âš Purchase could not fit in your inventory, so it's been dropped at your feet message, which is what the shop will utilize by default when buying single-items (non-kit/action selling).
Note:
These changes have NOT been fully tested, someone needs to validate they work and potentially fix any derived issues
Update:
- Tested and confirmed shops and shopkeeper changes are working as intended
- Changed it so shopkeepers will be spawned on cycle, not on match start. Had to make it so chunk is loaded before spawning.
- Made it so observers clicking shopkeepers don't get the default vanilla villager shop.
It's been tested and I believe it's ready to merge, @Electroid