Skip to content

Race File Specifications

Powell Mims edited this page Jul 3, 2022 · 37 revisions

Race files are normal json files containing the properties of items to drop and any additional information you want to store as custom attributes.

First, lets look at a "standard" race table:

{
    "demotable": {
        "loot_count": 1,
        "items" : {
            "object1":    {"type" : "TestDrop1_1", "always" : 0, "unique" : 1, "enabled" : 1, "chance" : 10.25, },
            "DemoItem1":  {"type" : "TestDrop2_2", "always" : 0, "unique" : 0, "enabled" : 1, "chance" : 50.0,  },
            "DemoValue1": {"type" : "TestDrop3_3", "always" : 0, "unique" : 1, "enabled" : 1, "chance" : 100.0, },
        },
    },
}

demotable is the name of this race table.
It contains the table property loot_count which defines the number of items to drop when this table is queried.

items is the list of items this table contains (the things that can "drop" in game). Each of them has a name ("object1", "DemoItem1", etc...) and also contains the standard race properties type, always, unique, enabled and chance.

However, the most important value here is type.
This field holds the name of the object in the asset browser (the object name) that can drop.
The table will call instance_create_layer() with this name to create an instance of the object when it drops. Each RaceTable object has a variable race_drop_on_layer which must hold the layer name and where to drop the loot.
If you do not use the RaceTable object but instead invoke the race_query() method directly in code, you must supply the layer name as a parameter to this function call. More information about that is in the Race Functions page.

Custom named attributes

Race supports adding custom attributes to each item in a table. This can be done either from the json or at runtime through the race_get_attribute()/race_set_attribute() functions.
With the first attribute you set, an attributes sub-struct is created for that item which holds all attributes. You can assign any value or datatype to an attribute, even methods (at runtime)!

Here is an example of what it looks like to create custom attributes in the json file:

    "object1": {"type": "TestDrop1_1", "always": 0, "unique": 1, "enabled": 1, "chance": 10.25,	
        "attributes" : {
            "gold_value" : 105.00,
            "min_level" : 25,
        },
    },

In this item, two custom attributes are added: the gold_value and a min_level.
They can be retrieved at runtime with race_get_attribute(table, item_name, attribute_name) or set through the race_set_attribute(...) function.

Null drops (no drop)

To create a variable amount of items dropped by a query, you can add a null drop value to the table. This is a special item, that will drop "nothing". Based on the chance and uniqueness of such a null drop, you can create queries that drop, for example, "up-to-four items".

To create a null drop, set the type of the item to the string "<null>". The real example below shows this in the attached race table file.

Sub References (Table recursion)

To understand how recursion is implemented in the json structs, let's look at a standard item first:

    "CoolWeapon": {"type": "WeaponObject", "always": 0, "unique": 1, "enabled": 1, "chance": 36.0, },

The type is WeaponObject which will result in a call to instance_create_layer(x, y, race_drop_on_layer, asset_get_index("WeaponObject")) when it receives a query.

Now, instead of a single item (WeaponObject), we can add entire tables as an entry to a table.
When a query finds such a subtable, it begins recursively querying that subtable. If another subsubtable is found, the recursion continues until finally a single item is found.

There are two ways to add subtables to a table: by_reference or as a copy.
The difference is in the properties of the added tables and all properties of items in the subtable (through all recursion levels!).

If you add a subtable by_reference, it's just a pointer to the other table. All referenced tables share the same properties for type, always, enabled, unique and chance. If you change one of them, you change it for ALL referenced tables.

If you add a table as a copy, the entire structure of this table (including all recursive subtables, a so-called deep copy) is copied to a dynamically created table. All properties are copied as well, meaning, you can adapt each single item in this structure without affecting all other copies of this table and its subs.

Add by_reference

To add a subtable by_reference, use an equals sign (=) together with the referenced table name in the type field like this:

    "Weapons": {"type": "=weapons_table", "always": 0, "unique": 1, "enabled": 1, "chance": 36.0, },

When a query finds this entry, the query simply continues in the weapons_table, using the properties found there.

Add as_a_copy

To add a subtable as a copy, use a plus sign (+) together with the table name to copy in the type field like this:

    "Weapons": {"type": "+weapons_table", "always": 0, "unique": 1, "enabled": 1, "chance": 36.0, },

NOTE: By default, Copy-References are resolved on the first encounter of such an entry. Very deep structures can have a minor performance impact on the frame where the deep copy takes place. However, they are resolved only once with the values the copied table has when the copy is performed.

A real example

To make things more clear, I want to show you a real-life example of one of the race files I used in the game One (K)night alone which I did for a game jam in 2022. This was the file that created all the dungeon levels, chests, barrels, monsters and even decided how much weapon-bonus you get when you use an anvil and even "dropped" the string to use from LG for what the hero would say when the anvil is used!

Take a few minutes and look closely at the file - I hope it will inspire you to see what can be done with race quite easily. In the jam game, there is no complicated code for generating the random levels. It's just setting the loot_count to the number of fields to generate based on level size through race_table_set_loot_count(...) followed by a call to the race_query function.

This is the source code of One (K)niht alone that creates all dungeon levels. No kidding. That's it...

	var tbl = race_table_reset(RACE_FIELDS_TABLE_NAME);
	// x * y -1 because field(0,0) is always empty (hero starts there)
	race_table_set_loot_count(tbl, sizex * sizey - 1);
	var result = race_query(RACE_FIELDS_TABLE_NAME, LAYER_FIELDS);
Click to expand the full data file!
{
    "map_fields": {
        "loot_count": 1,
        "items": {
            "exit":     {"type": "ExitField",        "always": 1, "unique": 1, "enabled": 1, "chance": 1.0,  "attributes": { "type": "LevelExit", },},
            "anvil":    {"type": "AnvilField",       "always": 1, "unique": 1, "enabled": 1, "chance": 20.0, "attributes": { "type": "Anvil", },},
            "empty":    {"type": "EmptyField",       "always": 1, "unique": 0, "enabled": 1, "chance": 30.0,},
            "monster":  {"type": "=monster_spawns",  "always": 1, "unique": 0, "enabled": 1, "chance": 35.0,},
            "trap":     {"type": "=trap_spawns",     "always": 0, "unique": 0, "enabled": 1, "chance": 10.0,},
            "treasure": {"type": "=treasure_spawns", "always": 1, "unique": 0, "enabled": 1, "chance": 25.0,},
        },
    },
    "monster_spawns": {
        "loot_count": 1,
        "items": {
            "vampire":  {"type": "MonsterField", "always": 0, "unique": 0, "enabled": 1, "chance": 10.0, "attributes": { "type": "Vampire", },},
            "bat":      {"type": "MonsterField", "always": 0, "unique": 0, "enabled": 1, "chance": 10.0, "attributes": { "type": "Bat", },},
            "skeleton": {"type": "MonsterField", "always": 0, "unique": 0, "enabled": 1, "chance": 10.0, "attributes": { "type": "Skeleton", },},
            "zombie":   {"type": "MonsterField", "always": 0, "unique": 0, "enabled": 1, "chance": 10.0, "attributes": { "type": "Zombie", },},
        },
    },
    "trap_spawns": {
        "loot_count": 1,
        "items": {
            "single": {"type": "TrapSingleField", "always": 0, "unique": 0, "enabled": 1, "chance": 75.0, "attributes": { "type": "SingleTrap", },},
            "double": {"type": "TrapDoubleField", "always": 0, "unique": 0, "enabled": 1, "chance": 25.0, "attributes": { "type": "DoubleTrap", },},
        },
    },
    "treasure_spawns": {
        "loot_count": 1,
        "items": {
            "barrel":       {"type": "TreasureField", "always": 0, "unique": 0, "enabled": 1, "chance": 10.0, "attributes": { "type": "Barrel",     "contents":"barrel_contents", },},
            "silver_chest": {"type": "TreasureField", "always": 0, "unique": 0, "enabled": 1, "chance": 10.0, "attributes": { "type": "SilverChest","contents":"silver_contents", },},
            "gold_chest":   {"type": "TreasureField", "always": 0, "unique": 0, "enabled": 1, "chance":  5.0, "attributes": { "type": "GoldChest",  "contents":"gold_contents", },},
        },
    },
    
    
    "barrel_contents": {
        "loot_count": 1,
        "items": {
            "empty":         {"type": "<null>",       "always": 0, "unique": 1, "enabled": 1, "chance": 50.0, },
            "health_small":  {"type": "HealthPotion", "always": 0, "unique": 1, "enabled": 1, "chance": 15.0, "attributes": { "value": 20, },},
            "shield_small":  {"type": "ShieldBuff",   "always": 0, "unique": 1, "enabled": 1, "chance": 15.0, "attributes": { "value": 20, },},
            "weapon_small":  {"type": "WeaponBuff",   "always": 0, "unique": 1, "enabled": 1, "chance": 15.0, "attributes": { "value": 20, },},
            "xp_small":      {"type": "XPBuff",       "always": 0, "unique": 1, "enabled": 1, "chance": 20.0, "attributes": { "value": 10, },},
            "health_medium": {"type": "HealthPotion", "always": 0, "unique": 1, "enabled": 1, "chance":  2.0, "attributes": { "value": 40, },},
            "shield_medium": {"type": "ShieldBuff",   "always": 0, "unique": 1, "enabled": 1, "chance":  2.0, "attributes": { "value": 40, },},
            "weapon_medium": {"type": "WeaponBuff",   "always": 0, "unique": 1, "enabled": 1, "chance":  2.0, "attributes": { "value": 40, },},
            "xp_medium":     {"type": "XPBuff",       "always": 0, "unique": 1, "enabled": 1, "chance":  3.0, "attributes": { "value": 20, },},
        },
    },
    "silver_contents": {
        "loot_count": 3,
        "items": {
            "empty":         {"type": "<null>",       "always": 0, "unique": 1, "enabled": 1, "chance": 40.0, },
            "empty2":        {"type": "<null>",       "always": 0, "unique": 1, "enabled": 1, "chance": 10.0, },
            "health_small":  {"type": "HealthPotion", "always": 0, "unique": 1, "enabled": 1, "chance":  5.0, "attributes": { "value": 20, },},
            "shield_small":  {"type": "ShieldBuff",   "always": 0, "unique": 1, "enabled": 1, "chance":  5.0, "attributes": { "value": 20, },},
            "weapon_small":  {"type": "WeaponBuff",   "always": 0, "unique": 1, "enabled": 1, "chance":  5.0, "attributes": { "value": 20, },},
            "xp_small":      {"type": "XPBuff",       "always": 0, "unique": 1, "enabled": 1, "chance": 10.0, "attributes": { "value": 10, },},
            "health_medium": {"type": "HealthPotion", "always": 0, "unique": 1, "enabled": 1, "chance": 20.0, "attributes": { "value": 40, },},
            "shield_medium": {"type": "ShieldBuff",   "always": 0, "unique": 1, "enabled": 1, "chance": 20.0, "attributes": { "value": 40, },},
            "weapon_medium": {"type": "WeaponBuff",   "always": 0, "unique": 1, "enabled": 1, "chance": 20.0, "attributes": { "value": 40, },},
            "xp_medium":     {"type": "XPBuff",       "always": 0, "unique": 1, "enabled": 1, "chance": 30.0, "attributes": { "value": 20, },},
        },
    },
    "gold_contents": {
        "loot_count": 3,
        "items": {
            "health_medium": {"type": "HealthPotion", "always": 0, "unique": 1, "enabled": 1, "chance":  5.0, "attributes": { "value": 40, },},
            "shield_medium": {"type": "ShieldBuff",   "always": 0, "unique": 1, "enabled": 1, "chance":  5.0, "attributes": { "value": 40, },},
            "weapon_medium": {"type": "WeaponBuff",   "always": 0, "unique": 1, "enabled": 1, "chance":  5.0, "attributes": { "value": 40, },},
            "xp_medium":     {"type": "XPBuff",       "always": 0, "unique": 1, "enabled": 1, "chance": 10.0, "attributes": { "value": 20, },},
            "health_large":  {"type": "HealthPotion", "always": 0, "unique": 1, "enabled": 1, "chance": 25.0, "attributes": { "value": 80, },},
            "shield_large":  {"type": "ShieldBuff",   "always": 0, "unique": 1, "enabled": 1, "chance": 25.0, "attributes": { "value": 80, },},
            "weapon_large":  {"type": "WeaponBuff",   "always": 0, "unique": 1, "enabled": 1, "chance": 30.0, "attributes": { "value": 80, },},
            "xp_large":      {"type": "XPBuff",       "always": 0, "unique": 1, "enabled": 1, "chance": 40.0, "attributes": { "value": 35, },},
        },
    },

    "anvil_repair": {
        "loot_count": 1,
        "items": {
            "repair_1":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance":  5.0, "attributes": { "value": 1, "message":"player_strings/anvil_repair_weak" },},
            "repair_2":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance": 15.0, "attributes": { "value": 2, "message":"player_strings/anvil_repair_weak" },},
            "repair_3":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance": 35.0, "attributes": { "value": 3, "message":"player_strings/anvil_repair_weak" },},
            "repair_4":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance": 35.0, "attributes": { "value": 4, "message":"player_strings/anvil_repair_ok" },},
            "repair_5":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance": 30.0, "attributes": { "value": 5, "message":"player_strings/anvil_repair_ok" },},
            "repair_6":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance": 25.0, "attributes": { "value": 6, "message":"player_strings/anvil_repair_ok" },},
            "repair_7":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance": 25.0, "attributes": { "value": 7, "message":"player_strings/anvil_repair_ok" },},
            "repair_8":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance": 20.0, "attributes": { "value": 8, "message":"player_strings/anvil_repair_good" },},
            "repair_9":  {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance": 15.0, "attributes": { "value": 9, "message":"player_strings/anvil_repair_good" },},
            "repair_10": {"type": "SwordRepair", "always": 0, "unique": 1, "enabled": 1, "chance":  5.0, "attributes": { "value":10, "message":"player_strings/anvil_repair_good" },},
        },
    },   
}

Continue reading in Race Objects.

Getting started

Raptor Modules

Clone this wiki locally