Asset Extensions:Fungibles
The fungibility system allows one tangible asset to dispense fungible assets of another class. For instance, allowing a user to take individual grapes from a bunch of grapes; one nut from a bag of nuts; one marble from a sack of marbles; one piece of chicken from a bucket; etc. Fungibility is meant for small tangible things like grains of sand or pebbles or coins or fruits. Any tangible asset can be a dispenser and any tangible class can be dispensed.
TMI Wait, any tangible class?
Well, we say any, and technically that's true. The fungibility system was made to support small tangible things, and you could try, if you wanted, to dispense doors or walls or exits, which are all tangible. We haven't placed any guardrails or error checking around this feature and technically there's nothing stopping you doing it, though you might not find the results useful. Caveat emptor, is all we're saying.
Example
In the following example we show a custom Pistachio class being set up
as fungible, and a nut sack asset to dispense pistachios.
MyGame.createClass({
class: "Pistachio",
extend: "Edible",
singular: "pistachio",
plural: "pistachios",
description: `It's a pistachio. `,
// It's not necessary to declare a class as fungible.
// It will be done automatically if any dispensers are set.
// However, there are several asset.fungible properties
// to control how fungibles of this class are handled.
fungible: {
// max_extant is the maximum number of dispensed assets
// that can be active at the same time
// Set it, for example, to 1, or 3, or -1 for infinity.
// Be careful with infinite dispensers though.
max_extant: 1,
// max_count is the maximum number of assets that can be
// dispensed in total, across the entire game.
// Set it, for example, to 10, or 100, or -1 for infinity.
max_count: 5,
},
doEat: function () {
// move into limbo
},
doDrop: function () {
// move into limbo
},
doTake: function () {
// move single pistachio into player
},
});
MyGame.createAsset({
class: "Luggage",
name: "nut sack",
place: { in: "Hero" },
synonyms: ["nutsack",],
description: `A small pouch containing a handful of pistachios. `,
// dispensers exist within aspects
// if a player takes a pistachio, it will come from in the nut sack
aspects: { in: { dispense_class: "Pistachio" } },
// setting this asect's dispense_class property is all that's
// required to set up a fungible dispenser
});
TMI Under the hood
The AdventureJS parser compares input against items in the game world to decide whether or not the particular words are "known". In other words, in order for an asset to be identified, it has to exist in the world. When an author creates a lamp and then a player inputs "take lamp", the game knows what the lamp is. But if an author creates a sack of nuts, there might not be any nuts in the world until the player takes one.
When a class is made fungible, we create a platonic asset to act as a sort of proxy. The platonic asset inherits the synonyms and keywords of the fungible class, but it only exists to expose the fungible class's synonyms to the parser. The platonic asset exists in limbo and the player never interacts with it. When the player tries to "take nut", the parser finds the platonic asset and uses it to dispense an instance of the fungible class, aka give the player a nut.