NPC shop Tutorial¶
This tutorial will describe how to make an NPC-run shop. We will make use of the EvMenu system to present shoppers with a menu where they can buy things from the store’s stock.
Our shop extends over two rooms - a “front” room open to the shop’s customers and a locked “store room” holding the wares the shop should be able to sell. We aim for the following features:
The front room should have an Attribute
storeroomthat points to the store room.
Inside the front room, the customer should have a command
browse. This will open a menu listing all items available to buy from the store room.
A customer should be able to look at individual items before buying.
We use “gold” as an example currency. To determine cost, the system will look for an Attribute
gold_valueon the items in the store room. If not found, a fixed base value of 1 will be assumed. The wealth of the customer should be set as an Attribute
goldon the Character. If not set, they have no gold and can’t buy anything.
When the customer makes a purchase, the system will check the
gold_valueof the goods and compare it to the
goldAttribute of the customer. If enough gold is available, this will be deducted and the goods transferred from the store room to the inventory of the customer.
We will lock the store room so that only people with the right key can get in there.
Building the shop¶
There are really only two things that separate our shop from any other Room:
The shop has the
storeroomAttribute set on it, pointing to a second (completely normal) room.
It has the
ShopCmdSetstored on itself. This makes the
buycommand available to users entering the shop.
For testing we could easily add these features manually to a room using
@py or other admin
commands. Just to show how it can be done we’ll instead make a custom Typeclass for
the shop room and make a small command that builders can use to build both the shop and the
storeroom at once.
# bottom of mygame/typeclasses/npcshop.py from evennia import DefaultRoom, DefaultExit, DefaultObject from evennia.utils.create import create_object # class for our front shop room class NPCShop(DefaultRoom): def at_object_creation(self): # we could also use add(ShopCmdSet, permanent=True) self.cmdset.add_default(ShopCmdSet) self.db.storeroom = None # command to build a complete shop (the Command base class # should already have been imported earlier in this file) class CmdBuildShop(Command): """ Build a new shop Usage: @buildshop shopname This will create a new NPCshop room as well as a linked store room (named simply <storename>-storage) for the wares on sale. The store room will be accessed through a locked door in the shop. """ key = "@buildshop" locks = "cmd:perm(Builders)" help_category = "Builders" def func(self): "Create the shop rooms" if not self.args: self.msg("Usage: @buildshop <storename>") return # create the shop and storeroom shopname = self.args.strip() shop = create_object(NPCShop, key=shopname, location=None) storeroom = create_object(DefaultRoom, key="%s-storage" % shopname, location=None) shop.db.storeroom = storeroom # create a door between the two shop_exit = create_object(DefaultExit, key="back door", aliases=["storage", "store room"], location=shop, destination=storeroom) storeroom_exit = create_object(DefaultExit, key="door", location=storeroom, destination=shop) # make a key for accessing the store room storeroom_key_name = "%s-storekey" % shopname storeroom_key = create_object(DefaultObject, key=storeroom_key_name, location=shop) # only allow chars with this key to enter the store room shop_exit.locks.add("traverse:holds(%s)" % storeroom_key_name) # inform the builder about progress self.caller.msg("The shop %s was created!" % shop)
Our typeclass is simple and so is our
buildshop command. The command (which is for Builders only)
just takes the name of the shop and builds the front room and a store room to go with it (always
"<shopname>-storage". It connects the rooms with a two-way exit. You need to add
CmdBuildShop [to the default cmdset](./Adding-Command-Tutorial.md#step-2-adding-the-command-to-a-
default-cmdset) before you can use it. Once having created the shop you can now
@teleport to it or
@open a new exit to it. You could also easily expand the above command to automatically create
exits to and from the new shop from your current location.
To avoid customers walking in and stealing everything, we create a Lock on the storage
door. It’s a simple lock that requires the one entering to carry an object named
<shopname>-storekey. We even create such a key object and drop it in the shop for the new shop
keeper to pick up.
If players are given the right to name their own objects, this simple lock is not very secure and you need to come up with a more robust lock-key solution.
We don’t add any descriptions to all these objects so looking “at” them will not be too thrilling. You could add better default descriptions as part of the
@buildshopcommand or leave descriptions this up to the Builder.
The shop is open for business!¶
We now have a functioning shop and an easy way for Builders to create it. All you need now is to
@open a new exit from the rest of the game into the shop and put some sell-able items in the store
room. Our shop does have some shortcomings:
For Characters to be able to buy stuff they need to also have the
goldAttribute set on themselves.
We manually remove the “door” exit from our items for sale. But what if there are other unsellable items in the store room? What if the shop owner walks in there for example - anyone in the store could then buy them for 1 gold.
What if someone else were to buy the item we’re looking at just before we decide to buy it? It would then be gone and the counter be wrong - the shop would pass us the next item in the list.
Fixing these issues are left as an exercise.
If you want to keep the shop fully NPC-run you could add a Script to restock the shop’s store room regularly. This shop example could also easily be owned by a human Player (run for them by a hired NPC) - the shop owner would get the key to the store room and be responsible for keeping it well stocked.