LootLib is a simple, but powerful library that allows you to generate reusable randomized tables that can generate any kind of datum.
Loot tables are defined in JSON, and parsed into a data structure that can be passed to the generate_loot() global command, or indexed in global.generator_cache by name. Loot generators can also reference one another by name from a local or global generator cache to greatly simplify the creation of complex loot tables.
Check out the documentation for more information.
ID:2874306
Jun 11 2023, 10:00 pm
|
|
Multiple items can be generated with weightless nodes:
[ {"type":"/obj/item/meat","prob":80,"roll":"1d2"}, {"type":"/obj/item/hide","prob":50,"amt":2}, {"type":"/obj/item/bones","prob":90,"range":"2 to 3"}, {"type":"/obj/item/sinew","prob":5} ] The above generator has a chance to drop everything in it. This is because it has no weighting. Instead, it uses probabilities. An item will always drop if it has no weight, the item before it in the list has tried to drop, and its probability passes. Let's say we want to always drop meat, but only drop one of a skull, sinew, or a horn: [ {"type":"/obj/item/meat","roll":"1d2"}, {"type":"/obj/item/skull","weight":1}, {"type":"/obj/item/sinew","weight":1}, {"type":"/obj/item/horn","weight":1} ] In this case, because meat has no weight, and it comes before weighted items, it will always generate. Only one of the weighted items will generate. In this case, because the total weight of this array is 3, the chance for each of these weighted outcomes is 1 in 3. However, if we were to move meat from the front of the list to the back of the list: [ {"type":"/obj/item/skull","weight":1}, {"type":"/obj/item/sinew","weight":1}, {"type":"/obj/item/horn","weight":1}, {"type":"/obj/item/meat","roll":"1d2"} ] This version of the generator would only generate meat if the horn was picked. If we were to move it into the middle of the list: [ {"type":"/obj/item/skull","weight":1}, {"type":"/obj/item/sinew","weight":1}, {"type":"/obj/item/meat","roll":"1d2"}, {"type":"/obj/item/horn","weight":1} ] In this case, it would only generate if the sinew was generated. So your loot would either be a 1) skull, 2) a horn, or 3) a sinew and 1 to 2 peices of meat. Now let's say we only want horns, sinew, and meat to generate 10% of the time: [ {"type":"/obj/item/meat","roll":"1d2"}, {"type":"/obj/item/skull","weight":1,"prob":10}, {"type":"/obj/item/sinew","weight":1,"prob":10}, {"type":"/obj/item/horn","weight":1,"prob":10} ] [ {"type":"/obj/item/meat","roll":"1d2"}, {"table":[ {"type":"/obj/item/skull","weight":1}, {"type":"/obj/item/sinew","weight":1}, {"type":"/obj/item/horn","weight":1} ],"prob":10} ] We can add a probability to each item, OR, we can add an unweighted table with a 10% probability. The outcome will be the same, however, the table method will not generate a random number between 1 and 3 unless the prob(10) roll succeds, whereas the inline method will rand(1,3) first, pick and item, and then check if prob(10) succeeds, so their impact on random number generation will differ slightly. With the table argument, you can pass an id instead of an array of nodes. The id will be looked up in the generator_cache passed to the generate_loot function first, and if not found, will be looked up in the global.generator_cache list. If not found at all, it will simply be skipped. So let's say we wanted every animal to have similar rules to how it drops loot, but vary in how it works. We could use local table ids for this: creatures table: [ {"table":"flesh"}, {"table":"rare_products","prob":10} ] This generator would just be the structure for setting up the final generator. We'd pass it to generate_loot(), which would then use additional generators you pass into the generator cache argument: flesh table: [ {"type":"/obj/item/meat","roll":"1d3"} {"type":"/obj/item/hide","prob":80} ] rare_products table: [ {"weight":1,"type":"/obj/item/skull"} {"weight":1,"type":"/obj/item/horn"} {"weight":1,"type":"/obj/item/sinew"} ] If we passed references to these loot tables in with a cache, we could use the creatures table as a basis, and then have each monster have its own flesh and rare_products table reference. When we kill that monster, we call var/c = global.generator_cache The seed table can now be pointed to the local cache we just built, and is now a complete loot table combining both rule sets. |
Arguments:
roll - a range string or a range list
Returns:
A random integer between L and H.
This proc takes a list in the format: list(L,H), where L is the lower integer and H is the upper integer of a range value. It can also take a string "L to H", where L and H are the same integer components of the range. Invalid range values throw an "invalid range" exception.
loot_generator() proc
Arguments:
json a JSON string containing an array of nodes to process into a generator
Returns:
A processed generator, represented as a list of lists.
Passing this proc a JSON array containing objects representing nodes will process the array and nodes, validating them. Any invalid records will be removed from the final product. In order to be valid, a node must have a "type" or "table" argument at minimum.
Node structure:
weight - num (optional)
prob - num [0 to 100] (optional)
One of:
type - a path string (optional)
table - a generator id string, or a list of nodes (optional)
One of:
amt - num
roll - a dice string
range - a range string
Processed nodes:
generate - ["type" | "table"]
rand - ["amt" | "roll" | "range"]
total - helps the generator pick a value from the table using weights
Once a node is processed, a few properties are added to help the generator handle it. Excess properties are removed. If an "amt" property is present, a "roll" or "range" property will be discarded, for instance. If a "type" property is present, the "table" property is discarded. If both type and table properties are missing, the node will be discarded.
Weighting
The total weight for each array of nodes is added up during processing. The weight of all prior nodes is added to the total of that node. Nodes with a weight of 0 have special behavior during generation.
generate_loot() proc
Arguments:
generator - a processed loot generator as built by loot_generator()
generator_cache - an associative list of named generators for table references (optional)
Returns:
a list of generated datums, or an empty list if nothing was generated
datum.onGenerated() proc
Arguments:
&amt - a pointer to a counter that is used to generate multiples of this item.
Returns:
src
This is called when an object is generated by a loot generator. By default, this action subtracts one from the value of the amt pointer that is passed from the generate_loot proc that created this object. Overriding this allows you to reduce this number by more, or to return an entirely different object, or to do additional setup work on the object.
global.generator_cache list
This variable is used by loot generators as a fallback lookup for cached generators. Storing a processed loot generator by an id allows you to reference the id in the table property of a generator node, allowing loot generators to reference and compound one another. When passing a local cache to a generate_loot call, it's possible to refer to a global loot generator by a different name locally. Take advantage of this to reduce the complexity and memory impact of your loot generators. Loot generators are capable of very complex sets of rules without themselves having to be particularly large, provided they are constructed from a series of cached generators instead of repeated throughout your cache.