Skip to content

Race File Specifications

Grisgram edited this page Jun 25, 2022 · 37 revisions

Race files are normal json files containing the properties of items to drop, plus 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 gets 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, 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 on that at 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 on that item, which will hold all attributes. You can assign any value or datatype to an attribute, even methods (at runtime)!

To create custom attributes already in the json file, here is an example, what this would look like:

    "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 retrievedat runtime with race_get_attribute(table, item_name, attribute_name) or set through the race_set_attribute(...) function.

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 gets hit by a query.

Now, instead of a single item (WeaponObject), we can add entire tables as entry to a table.
When a query hits such a subtable, it goes into recursion, querying that subtable. If this hits again another subsubtable, the recursion continues until finally a single item is hit.

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 hits 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 first hit of such an entry. Very deep structures can have a minor performance impact on that frame, where the deep copy takes place. But they are resolved only once, with the values the copied table has at the point when the copy is performed.

A real example

To let this things come to life a bit more clear, I want to show you here 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 what the hero will say when the anvil is used!

Take a few minutes and look closely at the file - I hope it will inspire you, what can be done with race quite easily. In the jam game, there is no comlicated 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!
{
    "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