Azathoth is a tool for generating, managing, and applying progressive upgrades to arbitrary sets of YAMLs. It is primarily designed to operate as a randomizer and upgrader for game settings YAMLs in a Multiworld Madness challenge.
Multiworld Madness is a gaming challenge created by iateyourpie in which players attempt to complete a solo multiworld randomizer orchestrated via Archipelago. The games involved are typically played with certain high difficulty settings and further conditions such as not being permitted to lose any lives. As a reward for reaching milestones in progress, players can earn upgrades that soften difficulty settings or improve starting equipment in their games, in the style of a roguelite.
Azathoth simplifies the process of managing these upgrades by handling upgrade selection, automated YAML editing, and summarization of the results in a single centralized tool.
When Azathoth starts, you'll see two main buttons: Load Game YAMLs and Load Upgrade Wheel.
Load Game YAMLs prompts you to select the baseline game YAML settings files that you'll be applying upgrades to. These baseline YAMLs should reflect how your challenge would be set up if you had no upgrades whatsoever. You should include all the game YAMLs that you plan to use in your Multiworld Madness.
Load Upgrade Wheel prompts you to select the Azathoth Wheel YAML that defines your particular challenge's possible upgrades. Your Wheel must follow the Wheel Schema. Upon successfully loading your Wheel, the upgrade selector interface pane will open. Azathoth features validation that does its best to identify any problems that your Wheel file may have. If your Wheel is not set up correctly, address any errors and warnings produced by the tool when attempting to load it.
Preferences opens the Preferences editor, where you can make changes to your settings. This includes options like setting files to always load when starting Azathoth or changing how files might be saved. Preferences are stored locally and will persist across sessions.
Exit exits the program.
Once an upgrade Wheel is loaded, the upgrade selector opens. Here, every upgrade possible to earn on your loaded Wheel is listed, grouped by the game to which they belong.
Upgrades appear next to counters that describe how many times that upgrade should be applied. Selected upgrades will additionally display the specific value that they will write to their given YAML address. Counters are bound by the limits described by your Wheel file for each upgrade and cannot be applied more times than allowed.
The Spin button will randomly select upgrades from your Wheel until exhausting the given budget of spins. Your selections are then updated to reflect these selections. If there are already selections made in Azathoth, you will be given the option of either replacing those selections or adding to them with the results of the next spin.
The Clear button will erase any selections already made and set all
upgrades to a count of 0.
The Save button will apply all selected upgrades to all uploaded game YAMLs and write new YAML files reflecting these upgrades to the selected output folder. It will additionally produce a summary file that succinctly collects the selected upgrades and their values.
Adding upgrades to YAML files requires communicating what upgrades are possible to earn, how they are selected, what part of a game YAML the upgrade will alter, and what values they will be set to. Azathoth takes this information in the form of a Wheel file.
The Wheel file, written in YAML, describes exactly one top-level Wheel that follows the Wheel schema and the weighted choices that can be spun on that Wheel.
A common structure for this file starts with a top-level Wheel that separates upgrades for each individual game into their own individual Wheels, each of which lists the upgrades available in that game.
An example wheel file might look like this:
name: Example Wheel for Azathoth
wheel:
- game: My First Game
wheel:
# Goes up to 20. First upgrade sets to 3, then +1 thereafter.
- name: Additional Starting Move
weight: 20
upgrade:
path: starting_move_count
progression:
values: [3]
increment: 1
stopAt: 20
# Goes up to 5. First upgrade sets to 2, then +1 thereafter.
- name: Additional Starting Character
weight: 10
upgrade:
path: starting_character_count
progression:
values: [2]
increment: 1
stopAt: 5
# Indefinite upgrade. Starts at 0, then +1 thereafter.
- name: Start with +1 Banana
weight: 5
upgrade:
path:
- start_inventory
- Banana
progression: ONE_PER
# This is something we can only ever have once.
- name: Start with Double Jump
weight: 1
upgrade:
path:
- start_inventory
- Double Jump
progression: UNIQUE
- game: My Second Game
wheel:
# Lets you die once per upgrade without losing. A self-enforced bonus.
- name: Additional Life
weight: 5
cost: 1
upgrade:
type: manual
progression: ONE_PER
# Lowers the damage multiplier by one setting for each upgrade.
- name: Decrease Damage Multiplier
weight: 7
cost: 3
upgrade:
path: damage_multiplier
progression:
values: [double, normal, half]Every Wheel is written as a dict containing the following keys:
name- The display name for the Wheel.game- The game that upgrades in this Wheel belong to.weight- [Optional] The integer weight assigned to this choice on the Wheel above it. (Default:1)wheel- The list of choices that this Wheel can spin. All entries here are either other Wheels or upgrades.
All Wheels must provide at least one of name or game. If providing game,
it must exactly match the name used by Archipelago YAMLs identifying the game
in question and describes where all upgrades contained by or under this Wheel
will be applied.
Every upgrade is written as a dict containing the following keys:
name- The display name for the upgrade.weight- [Optional] The integer weight assigned to this choice on the Wheel above it. Use this to make choices rarer to select. (Default:1)cost- [Optional] The cost, in spins, of selecting this upgrade. (Default:1)upgrade- A dict describing an upgrade that you can spin, with these keys:path- A list of the nested YAML entries in which to locate this setting. Manual* upgrades should not set apath.type- Set tomanualwhen specifying manual* upgrades, otherwise not used.progression- Describes the value(s) that will be set when receiving this upgrade. See Progressions.
* manual-typed upgrades are upgrades that do not involve changing an actual
game YAML setting and are not enforced by your game. Examples include giving
yourself permission to use an otherwise-restricted tool or to ignore some
number of game losses.
Your upgrade's path and progression ultimately decide what's actually going
to be written in your upgraded YAMLs. If your upgraded YAML is meant to look
something like:
Game Title:
a:
b:
c: dThen your path should be [a, b, c] and your progression should describe
what upgraded values could be produced for d. The value of Game Title is
derived automatically from the nearest containing Wheel's game setting. You
may optionally include Game Title in your upgrade's path, but this is
discouraged.
Upgrades are additive at intermediate levels and overriding at the final level.
i.e., in the above example, the contents of Game Title, a, and b would
be unchanged by this upgrade except to set c equal to d. If b contains
other settings, they will not be changed. If c already existed, it will be
overwritten by the upgrade and set directly to d.
Progressions describe the value that an upgraded setting will be set to if selected. This includes what to do if an upgrade is allowed to be selected multiple times and how many times it may be selected.
Every Progression is a mapping that can contain the following keys:
values- A progressive list of values the upgraded setting will be set to with each successive upgrade.increment- If set, allows upgrades beyond those specified byvalues. After reaching the last entry invalues,incrementis added to that value with each successive upgrade.spinLimit- If set, limits the total number of times this upgrade can be selected.stopAt- An alternative tospinLimit, this specifies the final value that may be reached by addingincrements.
For certain common situations, Progressions can also be expressed as a Macro.
Theoretically, every progression can be purely defined with values alone, but
the other options are useful shortcuts that allow you to concisely describe
repeated behavior and intended caps without having to write every possible
value individually.
n.b. At least one of values or increment must be provided; an
increment without values is assumed to be adding to a baseline of 0.
At most one of spinLimit or stopAt may be provided.
Azathoth features macro support for progressions. Certain common progressions can be given by a simple name instead of giving the entire basic object. The common progressions supported in this way are given in the following table.
| Macro | is equvalent to | is used for |
progression: UNIQUE |
progression:
values: [1] |
Items that can be collected exactly once. |
progression: ONE_PER |
progression:
increment: 1 |
Items that can be collected indefinitely. |
All upgrades must be located beneath a Wheel that specifies a game setting.
These need not be the most recent Wheel.
-
No current support for changing upgrades that use lists. Currently only upgrades scalars and dicts.
-
No current support for making multiple simultaneous changes to different parts of the YAML as part of the same upgrade roll.
- e.g. an upgrade that might be stated as "Instead of starting with 1
Item Xand 0Item Y, start the game with 0Item Xand 1Item Y"
- e.g. an upgrade that might be stated as "Instead of starting with 1
-
No known current support for uploading game YAMLs in a single file. I haven't tried it, technically, but I wouldn't expect much.
YAML can be a real nightmare. As a language, YAML prioritizes readability over many other qualities including, frustratingly enough, writeability. Its design includes several choices that lead to ambiguous parsing, choices that can also differ widely across versions.
As a consequence, this means you can often write the same information in a YAML in multiple ways. Further, Archipelago's interpretation of YAMLs adds still more flexibility. If Archipelago files are your first encounter with YAMLs, the data format can certainly be confusing. If this isn't your first rodeo, they can still surprise you.
Whenever you're editing YAML files, please keep in mind that whitespace in YAML
is significant and special characters can cause surprising problems. Strings
that include important characters like :, -, brackets, or braces can cause
issues unless properly escaped. YAML also """"""helpfully"""""" assigns certain
strings special meaning. This includes values such as false, true, no,
on, y, off, and certain colon-separated numbers.
If a particular upgrade or Wheel isn't processing correctly, here's some things to check:
- Follow any instructions and address any warnings that Azathoth reports.
- Check your YAML content in a YAML linter to make sure it is valid YAML (bearing in mind that it may validly be doing something other than what you intended.)
- Double-check that your spacing is consistent. Whitespace matters in YAML.
- Try wrapping your strings with quotes (
') or double-quotes (") so the value isn't interpreted as something else.
Hidden Characters
The YAML-parsing library that Azathoth depends on can be flummoxed by the presence of hidden characters in your input files. This is actually a safety measure to ensure that the same parser doesn't scan and interpret malicious instructions that could potentially be hidden in files, so its presence is a net good thing that can be slightly annoying in certain, benign circumstances.
While scanning in a YAML to this program, you may see perfectly valid-looking YAML fail with an error that looks like the following:
yaml.parser.ParserError: expected '<document start>', but found '<block mapping start>'
in "<unicode string>", line 28, column 1:
name: BlasphemousPlayer
^
This problem can occur when hidden characters are at the head of your file.
YAML is essentially trying to parse these hidden characters as part of your
game options and failing to understand what kind of data the're meant to
represent. If you do not have leading comments in your YAML, these hidden
characters may instead be quietly packed into the name of your output YAML
files' fields, e.g. changing name: to "\xEF\xBB\xBFname:".
If you're running into this issue, it's because Azathoth doesn't know yet to sanitize those fields out for you. You'll want to remove these characters. How exactly you can do that depends on your text editor, but regular expressions are probably your ally here.
If you encounter similar errors with different bad field names, please alert Azathoth's maintainers so the problem can be addressed for future users.
NOTE: Azathoth specifically sanitizes out the byte sequence \xEF\xBB\xBF from
the example above. If you're curious, it's the Byte Order Mark (BOM)
indicating that the file has been encoded in UTF-8. Most text files don't
include it because many text editors simply assume that a file without a BOM is
encoded in UTF-8 by default and don't bother to state the obvious. A very
common text editor disagrees with this practice.
Sigh. For now, if you're making your own Wheel, then yes. Sorry.
YAML has all kinds of problems that can make it not always obvious how to implement a change you want.
I wrote Azathoth to help players avoid all of that. You still need to have real YAMLs for your game settings and a real YAML that describes your Wheel, but these files are shareable. You can write and share these settings with your friends if you like. A given challenge only needs to have its corresponding Wheel written just once ever.
NOTE: I'm planning to add tooling to help you write your own Wheel in the near future.
meta.yaml is an Archipelago-specific yaml file that you can include with your
other game files when generating an Archi. Its contents are localized overrides
that will be applied to all instances of the indicated games in the same
multiworld.
So wouldn't it be simpler to just use this for overrides?
Perhaps. But meta.yaml has a number of limitations that I felt were simply
avoidable by performing direct YAML editing. Here are a few:
-
meta.yamlis not recognized by the Archipelago website. Using it requires local generation even if your chosen games don't. -
meta.yamldoes not allow you to add to an existing setting, only override it. If someone wanted to always includeA Really Big Shieldin theirstart_inventory, they could never use other upgrades that influencestart_inventorywithout obliterating that starting setting. Direct YAML editing allows us to make additive updates free of destruction.
Azathoth is released under the MIT License. See the file LICENSE for details.