Skybook

The Inventory Slot Transfer Simulator Project

中文版 | English Version

Getting started

Having an issue?

Please provide feedback, bug reports and feature requests on GitHub. Alternatively, you can join the communities below for discussion in general.

Discord

Warning

I prefer discussion in public channel over DMs for visibility, in case someone else may have the same problems as you. Please only DM me for privacy or other concerns.

Join my Discord and get the BOTW Tools role to get access to the #botw-ist channel to ask questions about or discuss features of the IST Simulator App.

Join the BOTW Speedrunning Discord and use the #glitch-hunting channel to discuss IST in general, or get help with the glitch in #general-help, or the category channel that you are running.

History

Discovery

Inventory Slot Transfer (IST) was discovered in June 2022 by zxrobbin ( Video 1, Video 2 ). It initially manifested as an innocent glitch to quickly duplicate items in Breath of the Wild. Little did we know that IST will become the most complicated glitch to ever exist in BOTW.

Around June 17, 2022, as the news spread, glitch hunters from the BOTW speedrunning community started to investigate and quickly found some patterns to the glitch.

Hundo Duplication Simulator

Meanwhile, speedrunners of the 100% category started to incorporate it into the route. However, even the simplest form of this glitch required tracking the exact state of the player’s inventory for every action executed, such as picking up or dropping an item. The rules and patterns of this glitch then needs to be applied at each of these steps. It was a painstaking process to manually track all the information.

Luckily, tasks that are difficult for humans are often easy for computers. On June 18, 2022, the Hundo Duplication Simulator was born. Users could type actions such as get 1 core into the simulator. The simulator app will display the inventory state and allow the user to quickly navigate between the steps. This was a massive help for optimizing setups for speedruns because people can change one step and the app will re-calculate the state for every step afterwards. This model is still used by the simulator today.

V2 - IST Simulator

As more applications of IST are found, the glitch has found its use in categories outside of the 100% category, for example in All Shrines and All Dungeons. One of such application is Direct Inventory Corruption. However, the simulator was built in one day and did not support any cases other than the very basic rules that were initially discovered.

Now that this glitch is not specific to 100-percent, the project was renamed to IST Simulator. V2 of the simulator added GameData Inventory and more items to simulate the inventory state closer to how the game handles it. Direct Inventory Corruption support was added. People were happy.

V3 - IST + Weapon Modifier Corruption

…Until more applications of IST are found again, and the simulator being a simulator, did not support these new exploits because I couldn’t predict the future.

Every time a new discovery is made, it almost certainly invalidates assumptions previously made when coding the simulator, and the core has to be re-designed to incorporate the new knowledge. In November 2022, I started re-designing the core by referencing the BOTW decompilation project. This massively improved the accuracy of the simulator and supported new applications such as Weapon Modifier Corruption. The UI was also revamped, adding syntax highlighting to commands and displaying modifier information for items.

V4 - Skybook

After the V3 update, a few small fixes were submitted, but no big re-architecture was made for 2 years.

While V3 was mostly accurate for setups viable for speedruns, it was still far from being perfect:

  • It didn’t handle special cases for champion abilities or Travel Medallion.
  • It didn’t handle cases where tabs aren’t discovered
  • It couldn’t reliably detect and warn user when an action is not possible in game, such as interacting with the inventory when mCount is 0.

It was clear that we can not chase these edge cases forever. Something needs to be remade from the ground up, again. Sometime in 2022-2023, I have theorized to build a mini-emulator, to run the simulator from a small part of the game itself. I submitted this idea as a proposal for senior project at my college, and got a student team who was interested in it. I lead the team as the advisor to build a prototype that would later become BlueFlame, the new core for V4.

At the same time as BlueFlame was being developed by the students, I worked on everything else - a new command system, new simulated systems like Overworld and Screens… And finally, in July 2025, Skybook was born.

For the first time, the IST simulator project is ahead of the game. The simulator is so accurate, it could replicate setups that I didn’t know would be possible (and thought they were bugs, but they are not).

FAQ

What is IST? What is this app?

Inventory Slot Transfer, or IST, is a glitch in BOTW that desyncs the number of items you have in the inventory and number of items the game thinks you have. For more details, check out history of the app and an overview of the glitch.

I am new to IST; how do I use this tool?

You definitely don’t need to be a master of IST to find this tool useful. If you are looking at a speedrun setup made by someone else, you can view the setup in the tool as a step-by-step guide for how to perform the glitch. If you are a glitch hunter or are interested in investigating the glitch in more details, the user manual has everything you need to unlock the full potential of the simulator.

In any case, it might be helpful to understand the basic concepts of IST to get started.

I can’t understand IST, but I still want to speedrun

Don’t worry; IST is very complicated. Most people (including WR holders!) don’t fully understand the glitch. This is exactly why this tool exists.

If your goal is to do the setup in a speedrun, what most people do is simply following each action in the setup exactly, either from memory, or by looking at the steps while they do it. Many categories also have tutorials made for the IST section. For example, here is one for All Dungeons made by Player 5.

I just want to play with IST as a casual player

Be cautious to use IST with your casual file, as effects of IST can persist in saves and may cause the saves to be corrupted or non-loadable.

There are generalized guides for how to achieve certain things with IST in a casual file (for example, 999 korok seeds). You can follow these steps. A good place to look for those steps is the #general-help channel of the speedrunning discord. There are also tutorials online on YouTube (or Bilibili if you are from China) for using IST in a casual file.

How is the simulator made?

V1 to V3 of the simulator was developed by understanding the outcome and patterns of IST, and by referencing the decompilation project. It was a white-box approach, similar to a person that understood everything ever discovered about IST. V4 took the black-box approach, where sections of the real code of the game (not decompiled code) is executed in a sandbox orchestrated by the simulator app. This means the simulator might even be possible to support use cases that are not discovered yet.

Can I contribute?

Certainly! If you see some bugs and want to take a shot at fixing them, feel free to open a PR on GitHub. If you want to add features, please discuss with me first. A decent level of programming knowledge is needed.

Most of this project is open-source and use publicly-available data of the game. However, some parts require that you own a copy of the game to develop.

Please refer to the contributing guide for more information.

Note

If your goal is to add extra functionality, you might be able to do that through an extension. See Extensions.

Note

If you are not familiar with programming, you can still contribute to the test suite by providing your (complicated) scripts as test cases. These test cases help ensure future updates to the simulator don’t introduce bugs. See Snapshot Testing.

If you are not able to generate the snapshot locally, simply make sure the output for every step is correct in the App, then open a PR with just the new .txt script file.

Inventory Slot Transfer

What is IST

Believe it or not, Infinite Stuff Trick is NOT the name of the glitch

IST stands for Inventory Slot Transfer. It is a glitch in Breath of the Wild that exploits behavior of the inventory when the variable tracking the number of the items in the inventory is less than the number of items actually in the inventory.

The developers made sure that these two values are kept in sync during normal gameplay. However, in very specific scenarios, the game removes the item slot from the inventory while subtracting the number of items twice, resulting in the game tracking 1 fewer item slots in the inventory. By repeating the action, we can make the game track fewer and fewer items.

The difference between the number of items in the inventory and number tracked by the game is called Offset or number of Broken Slots. Offset is technically more correct, but because it’s ambiguous in some contexts, this manual will refer to this number as Broken Slots. The action to create the Broken Slots is referred to as Breaking Slots.

Info

Broken Slots is the name used by the glitch hunting community before the underlying concepts of the glitch were fully understood. There’s nothing actually broken about the slots.

The variable that tracks the number of items is commonly referred to by the glitch hunters as mCount - a reference to the name of the variable in the BOTW decompilation project.

This variable is needed because the inventory is stored as a (doubly-)linked list, which has a O(N) time complexity for calculating its length.

The different counts have this relation:

mCount + Number of broken slots = Actual number of items

Inventory Representation

The inventory that you see when opening the inventory in-game - the Visible Inventory - is stored as a (doubly-)linked list. In this list representation, items in different categories are “concatenated” into the same list. For example, in normal inventory order, the list may have all the weapons, followed by all the bows, followed by all the arrows, etc, and at the end are all the key items. In one page of the item in the in-game UI, the top-left corner is first, and it follows row-major order (i.e. the item in row 1 column 2 is after the item in row 1 column 1 in the list), and the bottom-right corner is last, followed by the upper-left corner item of the next page.

Info

The empty spaces and empty tabs in the inventory do not take space in the list.

The items are also stored at the same time in GameData, which is the game’s flag system. The relevant flags are stored with an array type. For example, PorchItem is a flag that stored an array of 420 strings, each string correspond to one item’s name. Other properties of the items are stored each in a different flag, for example PorchItem_EquipFlag is an array of 420 bools (whether the item is equipped), and PorchItem_Value1 is an array of 420 s32s (the value/durability of the item).

Whenever the Visible Inventory changes, the change is synchronized to GameData. We call this process Sync GameData or simply Sync. The GameData is also what is stored in the save files.

Tip

When mCount is 0, you won’t be able to see the items in the inventory when you open it. This is because the game thinks the inventory is empty since the number of items is 0. However, the items are still there. You can throw a weapon or pick up any item - as long as mCount is no longer 0, you will be able to access the inventory again.

Why is it called IST - The main mechanism

The huge number of glitches that are derived from IST all rely on the core mechanism - transferring slots, which is how the glitch got its name.

This all happens when the inventory is Reload-ed, either when loading a save, or restoring the inventory from a quest that takes away your items (i.e. Trial of the Sword, Stranded on Eventide, or any of the Blight Refights).

When restoring the inventory, the game needs to do 3 things:

  1. Delete all of your current items in Visible Inventory.
  2. Load the data of the inventory to restore into GameData.
    • In case of reloading a save, it is loaded from the save file.
    • In case of quests, it is already stored in GameData.
  3. Add the items in GameData one-by-one into the Visible Inventory.

The magic happens in step 1. Since mCount is less than the actual number of items, the game does not fully delete all the items in inventory. These leftover items will still be there after the reload, effectively being transferred from one save to another.

Derivative Glitches

The following glitches depend on IST. Click on the link for more information about each of them.

Direct Inventory Corruption (DIC)

Inventory Corruption is a form of Durability Transfer.

In the game, durability is stored as a fixed-point integer, with 1 being 0.015. For example, a weapon with durability 10 has the internal value of 1000. In the case of corruption, the durability is transferred from an equipment - not to another equipment, but to an item. The value then becomes the count of items. This is very useful, since you can get a LOT of items from an equipment with relatively low durability that’s easy to get.

This form of corruption was previously only possible using Memory Storage - another very complex glitch with a lengthy setup. With IST, however, Inventory Corruption is much easier, hence the name Direct Inventory Corruption.

We will go over a few game mechanics first in order to understand why DIC happens.

Setting Durability on Equipment

The game sometimes need to set the durability of the equipment in the inventory from the overworld weapon actor in order to keep the durability in sync. This could happen in a few scenarios, including:

  • Using an equipment, such as attacking with weapon, shield surf and shooting an arrow (which uses both bow and arrow)
  • Switching equipment
  • After reloading from a save

The algorithm to do this is:

  1. Find the first item in the inventory list that is both equipped and is the same item that is trying to be set (for example, Master Sword)
  2. Set the value of that item

If you remember from Inventory Representation, the game also need to sync this change to GameData. However, syncing the whole inventory whenever one value changes seem inefficient. Therefore, this form of sync is done by directly taking the position of the item in the inventory list, and setting the value of the item in the same position in GameData.

This is correct if the GameData is always in-sync with Visible Inventory, which would be the case most of the time. Therefore, all the inventory corruption aims to do is cause GameData to be desynced, then trigger a durability set.

Info

Note that this is only one form of desync, which is the primary one used for inventory corruption. There is another form of desync used by inventory corruption using Inventory Storage, which is a derivative of Map Storage. We will not be going into the details for that.

GameData Corruption with IST

All it takes for corruption now is 2 things:

  1. Desyncing GameData
  2. Apply durability while GameData is desynced.

IST makes desyncing GameData trivial. Recall the 3 steps for reloading:

  1. Delete all items
  2. Load GameData
  3. Add items in GameData to Inventory

After these steps, the game doesn’t actually sync GameData. Therefore, whenever items are transferred, the GameData is automatically desynced after a reload.

Remember that reloading a save also causes durability to be applied? This means inventory corruption automatically happens after a reload. All the player needs to do to exploit this, is to transfer specific items to desync the GameData in the way so that equipped items are aligned to the item to corrupt. This is why different IST setups exist to corrupt different things in different speedruns.

Tip

This is also why it is important to follow the setup to unequip/equip certain items before reloading, because the equipped item slot is what is used for corruption. To be exact, the durability of the last equipped slot is transferred into the first equipped slot in both Visible Inventory and GameData.

Aligning the Items

In a DIC setup, we need to align the equipped equipment with the item to corrupt. This is done by transferring the right number of Swords, Shields, Bows, and Arrows. Transferring anything after Arrows typically has no effect, because they don’t affect the positions of equipments since those categories are after equipments in inventory order.

Recall that transferring an item means it is not removed in the inventory. Since the items from GameData are then added on top of that, the transferred items will appear before the items that are loaded in. Effectively, transferred equipments will push items from the save to slots after it.

To determine the right type of equipment to transfer, consider the order of the categories:

  • Since Shield is last, transferring any type of equipment will affect the position of shields
  • Since Weapon is first, only transferring weapon will affect the position of weapons.
  • The rest follow the same concept

Example

For example, transferring 1 Bow, 1 Arrow, 1 Shield, will:

  • Not change position of Weapons
  • Push Bows by 1 slot
  • Push Arrows by 2 slots
  • Push Shields by 3 slots

Unsorted Inventory and Leftward Corruption

Note that GameData desynced in this way can only push the items to the right, not left. For example, Weapons can corrupt everything, but Bows cannot corrupt Weapons, and Shields cannot corrupt Weapons, Bows, or Arrows.

Corrupting items that are not possible to corrupt in normal inventory order is known as Leftward Corruption, or sometimes Forward Corruption. This is done by making the inventory into an order that is not normal. For example, to transfer durability from a Shield to a Sword, the Sword must be put after the Shield. This state is typically known as Unsorted Inventory and opens up a whole different glitch category.

Achieving Unsorted Inventory is relatively easy. All we need to look at is how the game put (sort) the item into the correct category when you get something:

  1. The game always adds the item to the end of the inventory
  2. The game then sorts the inventory with the following rules:
    • Two items should not change their relative order (if A is before B, A must also be before B after sorting)
    • Two items that have the same category are considered equal
    • Items that should appear in a category before other categories have lower values (say Sword is 0, Bow is 1, etc)
    • The list is sorted from lowest value to highest

Info

This type of sort is referred to as a stable sort using a predicate that only compares the category of the items.

The sorting itself cannot achieve the unsorted state, but absence of sort can. The list code has one more optimization, that sort operations are skipped if the list has no more than one element. Normally, having 0 or 1 element in the list means the list is trivially sorted. However, mCount is used for this optimization and we know mCount is not the actual number of items in the inventory. All it needs for the sort to be skipped is to make your mCount less than or equal to one.

Tip

Unsorted Inventory can also be used to transfer more equipment with fewer Broken Slots by putting Weapons after Key Items. This is typically used in speedrun setups where it could be faster to not break as many slots. This is why some setups require dropping some weapons, then immediately pick them up again. While picking up the weapons, the mCount never surpasses 1, causing the weapons you pick up remain at the end of the inventory.

Weapon Modifier Corruption (WMC)

A Weapon Modifier refers to the power-ups that can be found on an equipment, such as Attack Up or Durability Up. Weapon Modifier Corruption is a glitch enabled by IST to transfer data from a cooked item to a weapon, interpreting the memory as a Weapon Modifier.

During this corruption:

  • The Health Recover value of the cooked item becomes the Value of the modifier.
    • For regular food, this value is the number of quarter-hearts recovered. Between 0 and 120.
    • For hearty food, this is the number of yellow hearts.
  • The Sell Price value of the cooked item becomes the Type of the modifier.
    • The Type is a bit-flag, so WMC can enable multiple modifiers on the same equipment.
    • See actWeapon.h for values for each modifier type.

Info

You will see Prompt Entanglement (PE) often brought up together with WMC. This is because WMC relies on the data from a cooked item, and you can only get a very limited subset of possible cook data values by cooking with normal ingredients. PE allows cooking with unusual ingredients, which is required to get some modifier value/flag. However, WMC and PE are 2 separate glitches, and neither is required to perform the other.

Base Mechanism

WMC is possible because:

  1. The data for cooked item and the data for modifier is in the same memory location for each item. i.e. if the item is a food, then the data is used as cooked data, and if the item is a weapon, then it is used as modifier data.
    • This is only true for Visible Inventory, not GameData.
  2. The data for cooked item is added separately from the item itself.

In step 2, the cook data is added after adding the cook item, and the game assumes the last added item is the cook item that is supposed to receive the data.

Therefore, a WMC setup typically involves:

  1. Making sure the last added item is the weapon to receive the corrupted modifier
  2. Make the cooked item that donates the data fail to load while the last added item is still the weapon, transferring the data to the weapon

Note that #2 essentially means that no item should be loaded between the weapon and the cook item.

Depending on how these 2 conditions are satisfied, the WMC setups can be further categorized.

Info

In general, WMC can refer to any of the cases where the last added item is not the item that is supposed to receive the data.

You can technically corrupt any item in Visible Inventory, but only Equipments and Foods will have the data saved to GameData.

It is not possible to transfer modifier between Weapons, because unlike food, the data for weapons are added in the same step as the item itself (condition 2 from above).

Currently, WMC is only possible when reloading the inventory from GameData (for example when reloading a save). This is because there is no known way to trigger adding a cook item during normal game play while at the same time making it so the cook item doesn’t get added successfully.

Food Limit

Using the food limit was one of the first methods to WMC. This WMC setup uses the fact that during a reload, any food after the first 60 will not load (under normal circumstances). This can be achieved by transferring 60 food into a save with a weapon, followed by the donor meal.

Example script:

get 1 hammer 1 wild-green[hp=120, price=113]
save
eat wild-green
get 60 dubious-food
!break 60 slots
reload

The step-by-step explanation:

  1. Note that in the save, the items are 1 hammer and the donor meal: these are what will be added during reload.
  2. Note that we are transferring 60 dubious-food.
  3. During the reload, the hammer loads in first.
  4. When loading the donor meal, since there are already 60 food, it fails to load.
  5. Since the last-added item was the hammer, the data from the donor meal is transferred to the hammer.

Info

The main drawback for this method is that it requires 60 food and 60 broken slots. Getting 60 broken slots is very tedious.

Stackable Food Limit

The reason why the previous setup required 60 broken slots, is because if we don’t transfer 60 food, then there will be food items that get loaded after the hammer. As a result, the cook data will be transferred to that item, instead of the weapon.

But wait! What if that’s exactly what we wanted? Instead of transferring the modifier directly to the weapon, we can do 2 transfers:

  1. Using the Food Limit method, transfer the data from the donor meal to a Stackable food (i.e. a roasted/baked/frozen food)
  2. Using Direct Inventory Corruption, corrupt the food value to >=500
  3. When loading a stackable item slot, if the value being loaded - plus the value of the item in the inventory - is greater than 999, the stack will fail to load (this is why we need >=500 in the previous step)
  4. When the stackable food fails to load, it can trigger WMC

Example script

# Setup
get 1 hammer
!break 1 slots
get 58 dubious 1 roasted-endura-carrot 1 wild-green[hp=120, price=113]

# This will make only the last food fail to load, transferring the data to the carrot
save; reload  

# Clean up the foods now
eat wild-green; eat all dubious
save

# Corrupt the carrot stack
unequip hammer
eat roasted-endura-carrot
reload
save

# Finally transfer the data to the hammer
reload

Info

As you can see, this method can work with a minimum of 1 broken slots, which is a big improvements over 60 broken slots. In speedruns, typically we use more broken slots anyway to corrupt other stuff and for duplicating food to quickly reach the 60 food required for the first transfer.

The Nullptr Exploit

Both previous setups require 60 food to make the first transfer happen. It would be really nice if that’s not the case. This is where the Nullptr Exploit comes in.

Recall that after adding the cook item (or fails to add), the game applies the cook data to the last added item without checking if it’s the cook item. However, when there is no last added item, the data is not added, and crucially, the game does not advance to the next cook data slot. This means when the next food loads, it will keep using the cook data of the previous food!

Info

This can happen because in the GameData, the cook data and the item themselves are not stored in the same array:

  • The item name array has 420 strings, one for each item.
  • The cook data array has 60 elements, one for each food.

When the Nullptr Exploit is triggered, the counter for the item array increments, but not the cook data array.

Using this exploit, 60 food is no longer required. However, this exploit is pretty tricky to trigger; this is because mLastAddedItem is only nullptr before any item is added (with one exception being the Master Sword).

Master Sword (MSWMC)

This setup triggers the Nullptr Exploit using the fact that Master Sword cannot be duplicated.

The condition for this trigger is somewhat complicated:

  1. A Master Sword is transferred (can be broken or not broken)
  2. The save being loaded doesn’t have Master Sword being the first item
    • This is because adding the first item skips the part check we rely on to trigger the exploit
  3. The Master Sword Recharge Timer in the save being loaded is non-zero
  4. The save being loaded has a Master Sword (either broken or not broken)
    • 3 and 4 usually means the save has a broken Master Sword

Tip

In one sentence, this means to “transfer a Master Sword into a save with a broken Master Sword”.

When all of the above conditions are met, the last-added item is set to nullptr, enabling the exploit.

The whole setup would be:

  1. Enable the exploit as described above
  2. In the same reload, load a food unsuccessfully, without loading any item in between
    • This uses the exploit described in the previous section to cause the reuse of cook data
  3. Now any food that loads after will have the data that’s supposed to be on the food before it
  4. Continue the Stackable Food Limit setup

Travel Medallion (TMWMC)

Todo

This section is WIP and may contain inaccurate information. If you see any issues, or want to improve this section, please create a Pull Request.

The Travel Medallion is added in the Master Trials DLC. Unlike any other DLC items, it gets removed if you uninstall the DLC.

This can be used to trigger the Nullptr Exploit:

  1. Make a save with Travel Medallion being the first item, and food after it (using Unsorted Inventory )
  2. Close the game and uninstall the DLC
    • If you have physical game without DLC + digital game with DLC, this can be done by ejecting the Virtual Game Card
    • Otherwise, follow the steps on this Nintendo Support Article
  3. Reload the save, setup IST again to transfer some food to make the food fail to load
  4. Reload the save again, the Travel Medallion will not load as the first item since DLC is uninstalled, and the next food will fail to load because of the transfer
  5. Now any food that loads after will have the data that’s supposed to be on the food before it
  6. Continue the Stackable Food Limit setup

Unlike MSWMC, Travel Medallion does not set last added item to nullptr, so it has very limited usefulness.

Zelda Notes (ZNWMC)

Todo

This section is WIP and may contain inaccurate information. If you see any issues, or want to improve this section, please create a Pull Request.

Warning

This method has not be verified. It is only theorized based on reverse engineering of version 1.8.0 of the Switch 1 Edition.

Zelda Notes is an item exclusive to the Nintendo Switch 2 Edition. When you uninstall NS2E, it gets removed from your inventory and can be used to trigger the Nullptr Exploit:

  1. Make a save with the Daily Bonus and Deposit Item being the first 2 items, and food after it (using Unsorted Inventory )
  2. Close the game and uninstall NS2E
    • If you have the physical NS2E Game Card and the NS1E game (physical or digital), this can be done by removing the physical NS2E Game Card (then inserting the NS1E Game Card if you have a physical copy)
    • If you only have the physical NS2E Game Card, this is not possible.
    • Otherwise, follow the steps on this Nintendo Support Article to download the game without NS2E
  3. The rest is similar to TMWMC

Prompt Entanglement (PE)

Warning

This glitch is related to the Pouch Screen, which has not been reversed engineered. Most of the concepts are based on experiments, and may not reflect the actual implementation in the game.

Todo

This section is WIP and may contain inaccurate information. If you see any issues, or want to improve this section, please edit this file and open a Pull Request.

Prompt Entanglement, or PE, is a glitch that allows you to apply a prompt (like “Equip”, “Drop”, “Hold”, etc) from one item to another item that is not supposed to have that prompt. For example:

  • Holding a Food (only Materials are normally holdable)
  • Equipping a Material
  • Eating a Key Item

Invalid Star Tab

Note

IST refers to Inventory Slot Transfer in contexts pertaining to Invalid Star Tab.

To activate PE, the first step is to activate a state known as Invalid Star Tab. This is a state that allows the cursor (the box that highlights which item is currently selected in the inventory) to go to the “Key Items” icon.

Currently, the only known way to activate Invalid Star Tab is by having items in a category that you have not discovered. For example, have a material without picking up any material.

At first this seems impossible. However, the catch is “picking up” - you can obtain items without picking them up with IST. In an Invalid Star Tab setup, there are 3 general steps:

  • Save with the tab you want to use undiscovered
  • Pick up an item in that tab, which discovers the tab and gets you the item
  • Use IST to transfer that item back into the save you made

Once the setup is done, you can verify Invalid Star Tab is active if any of the following is true:

  • When you scroll to the right very quickly, the cursor ends up on the Star.
  • When you go to the “System” screen, the cursor remains on the left screen. You can only see the cursor when you press “Right”, which moves it on to the “Save” button.

Cursor Glitch

Warning

The Cursor Glitch is not fully understood, since the inventory screen system is not reversed engineered. This section may contain inaccurate information.

When Invalid Star Tab is active, you can now perform the Cursor Glitch to achieve PE. This glitch uses a sequence of controller inputs to quickly move the cursor while Invalid Star Tab is active to confuse the inventory code, and puts the cursor on a tab that’s not the current tab you are viewing.

      |- you are looking at this page
      v
    MATERIAL                 FOOD
[ ] [ ] [ ] [ ] [ ]  [ ] [ ] [ ] [ ] [ ]
[ ] [ ] [ ] [ ] [ ]  [ ] [ ] [X] [ ] [ ] <- cursor (X) is on another page
[ ] [ ] [ ] [ ] [ ]  [ ] [ ] [ ] [ ] [ ]
[ ] [ ] [ ] [ ] [ ]  [ ] [ ] [ ] [ ] [ ]

                         ^
                         |- (this is where Link's model usually is on screen)

Typically, we refer to the position of the cursor by the Row and Column. In the example above, we say the Cursor Glitch is active at Row 2, Column 3, or simply Row 2 Column 3 is activated.

Tip

Since the Cursor Glitch is not fully understood, the community has put together a spreadsheet of different input sequences you can use to active each slot.

When Cursor Glitch is active, you can keep it active by moving tabs in groups of 3 (tap right-stick right 3 times, or left 3 times), without pausing too long between them. Pausing while not on a multiple of 3 tabs from where the slot is activated will reset the cursor’s position, losing the glitched state.

Capturing the Prompt

What the Cursor Glitch enables, is that we can now move the cursor to another item, while the inventory screen still “thinks” we are on the original item, so it opens the prompt of the original item when we trigger it.

When we trigger the prompt (pressing A), the prompt used comes from the item that is currently showing the description on the screen:

      |- you are looking at this page
      v
    MATERIAL         
[ ] [ ] [ ] [ ] [ ]  Link is displayed here
[ ] [ ] [ ] [ ] [ ]          [X] <- cursor (X) is on another page
[ ] [ ] [ ] [ ] [ ]  ===================
[ ] [ ] [ ] [ ] [ ]  <The item's name and description is displayed here>

When moving tabs in groups of 3, the Cursor Glitch causes the prompt to be locked, meaning it will not update the name/description of the item. You can force update it by going to the System screen and back (pressing R then L). This is often referred to as “resetting” or “capturing” the prompt.

With the prompt locked, you can now move the cursor 3 tabs left or right, which will change which item the cursor is on, but will not update the prompt. Now, you can press A to trigger and use the prompt.

In general, these are the steps to do any PE setup:

  • Use the input sequence (the Cheat Code) to activate a slot
  • Ensure the right prompt is captured by moving tabs in groups of 3, and optionally go to the System screen and back to reset the prompt
  • Move tabs in groups of 3 to the target item, and use the prompt

Tip

Since you can only keep the glitch by moving tabs in groups of 3, this means PE can only be used between 2 items that are multiple of 3 tabs apart (i.e. have 2 other tabs between the source and target items).

Applications

Weapon Modifier Corruption

With PE, you can hold ingredients like stackable food that aren’t holdable normally. When these ingredients are cooked, their properties gets processed by the complex cooking system and produces meal values that aren’t obtainable by cooking normal ingredients, which can give better values for Weapon Modifier Corruption.

You can hold stackable food by either using “Hold” prompt from material, pressing X while locked to a material, or using the “Drop” prompt from equipments. Using the “Drop” prompt will not put Link in the holding state in inventory screen, allowing you to perform other actions while technically holding.

Note

Note that if you try to hold items that do not have a model (Food, or some Key Items), the game will crash when trying to render it in the inventory. You can workaround this with Super Hold Smuggle or some other method, which we will not go into depth here.

The simulator allows you to hold anything without crashing.

Removing Arrow Slots and Permanent Items

When you obtain a type of arrow, that arrow slot is stuck in the inventory forever, even if you shoot the arrow to 0. With PE, you can entangle a material prompt with the arrow prompt. Now you can press X and use hold to remove arrows. When you have 0 arrows, you can use the eat prompt to remove the slot completely.

Similar to removing arrow slots, you can remove permanent items like Sheikah Slate, Glider, Zora Armor, etc, by eating it.

Duplicate Materials

When using PE to hold, it subtracts the amount from the item you are holding, but checks the amount of the original item to see if you can keep holding. Since the amount of original item never decreases in the process, you can keep holding the item even if the stack is at 0. You can either unhold, or drop the items on the ground to realize the gain.

Note that if you use this method to duplicate material, you need at least 4 tabs of materials.

Durability Transfer and Desync Equipment

You can use PE to change equipment, while not changing the equipped status of the slot. This is very similar to Desyncing with Menu Overload.

To transfer durability:

  • Equip the item to receive the durability
  • Activate the slot with the item to give the durability
  • Use the “Equip” prompt of that item on something else (for example, a material)
  • Close Inventory

This will switch the equipment in the overworld while not in the inventory. The change equip action will cause a durability update, which transfers the durability.

Tip

Note that unlike durability transfer with Menu Overload, you do not need to use the equipment to update the durability. This is because the desync achieved by PE is the exact opposite of Menu Overload:

  • Menu Overload desyncs by switching the equipment in the Inventory, but not in overworld
  • PE desyncs by switching the equipment in the overworld, but not in the inventory

Since with Menu Overload, you do not switch equipment in the overworld, which does not trigger the change equip action. Therefore, using the equipment manually is required to update the durability.

You can also use this to unequip the One-hit Obliterator, which is more consistent than using Menu Overload. After performing the steps above, you will be able to unequip the OHO from the DPad Quick Menu.

User Manual

Info

This section covers how to use the Simulator App. While not required, understanding IST itself could make it easier to understand some of the concepts here. You can read about IST here.

How the Simulator works

The Simulator runs on a Script, which is a text file that contains Commands for the simulator. Usually, the commands are the steps or actions you perform in the IST setup.

Here’s an example of such script; each line is a command.

get 1 pot-lid 1 apple 1 slate 1 glider
equip Shield
!break 3 slots
save
unequip shield
hold apple; drop
reload
save
drop apple
reload

To learn more about commands, see Command Syntax and Command Reference.

In the simulator UI, you can edit the script in the Script Editor. Whenever the script changes, the simulation will automatically rerun in the background. You can navigate different steps of the simulation by moving your cursor in the editor. The UI will display the state of the inventory after the command the cursor is on.

Modes

The simulator app has 3 editing modes:

  • Auto Saved: This is the default mode. Any change you make to the script will be saved locally in your browser, so the same script will be there when you open the app the next time.
  • Not Saving: When editing script in this mode, the changes won’t be saved to your browser.
  • View Only: This is the default mode when you open a URL that directly loads a script. The script is read-only in this mode. You can switch to Not Saving to enable editing. Note that errors will NOT show in the editor in this mode.

You can switch the mode anytime in the top-left corner of the header.

Warning

When switching to Auto Saved, your locally-saved script will be overwritten with whatever script that’s currently in the script editor!

If you accidentally overwrite your local script and you need it, you can still recover it by open the devtool console (F12) and type in the following:

console.log(localStorage.getItem("Skybook.AutoBackupScript"))

Press enter, and copy the output.

This entry is updated whenever you switch to Auto Saved from the other modes. If the backup is lost, your script will be lost forever.

Migration from V3

URLs with a V3 script embedded (one that starts with https://ist.itntpiston.app) can be migrated automatically to V4, by simply replacing itntpiston with pistonite in the URL.

Since the script is mechanically converted, it might not work out of the box. You can change the mode to Not Saving (see above), and see if there are any errors in the script. Or, you can simply check if the last step has the correct outcome.

Notable differences:

  • drop in V4 only allows dropping droppable items. For example, drop hasty-elixir will not work.
    • Workaround: Manually change to !remove or eat.
  • pick up is translated to get, since pick-up in V4 specifically targets items that are on the ground. This won’t lead to errors, but there will be extra items on the ground.
    • Workaround: Add a !system [clear-ground] at the end of the script.
  • For setups with Prompt Entanglement, you need to activate PE in V4 with entangle command.

Info

While it’s technically more consistent to translate the old script using supercommands like !remove that mimics the old behavior more consistently, supercommands are not meant to be overused, so it’s not worth to change the semantic of the script just for the edge cases.

Command Syntax

The simulator script is used to describe the steps to setup IST. The script is made up of commands. Most commands describe one or more actions in game, such as getting an item, dropping some items, or equip something.

The commands can be divided into 3 groups:

  • Actions: These correspond to actions you do in game, such as get, pick-up and hold
  • Annotations: These commands start with : and are used to change the current configuration, such as :slots
  • Supercommands: These command start with ! and are more powerful than the actions. They often interact directly with the game’s state in a way that’s not possible with a regular action.

Whitespaces are insignificant in the syntax, including new lines. This means one command can be broken into multiple lines and more than one command can be put on the same line. Commands can also have an optional trailing ;.

# These 2 commands are equivalent
get 1 apple 2 pot-lid hammer;

get
  1 apple
  2 pot-lid
  hammer

# Trailing ; is optional even for multiple commands on the same line
hold 2 apples drop
# but it's clearer if you separate them with a ;
hold 2 apples; drop

In general, the syntax is case-sensitive. Although some features like item search is case-insensitive, it’s recommended to keep everything lower-case, unless upper-case is needed (for example when specifying actor name or GDT flag name).

Note

In the simulator, the inventory displayed are the state after executing the command the cursor is on.

The simulator parses the commands by span, not by line. You can view the state for each command even if multiple of them are on the same line.

Item Syntax

Item syntax is used to specify items for commands like get or drop. See Item Syntax.

Meta Syntax

The meta syntax is a versatile syntax used to specify additional contextual metadata, in the form of ordered key-value pairs, the syntax is:

[key1=value1, key2=value2, ...]
# `:` and `=` are interchangeable
[key1:value1, key2:value2, ...]

Generally, keys are kebab-case words, and values can be one of:

  • bool - either the keyword true or false.
    • true can be omitted, i.e. [equip] is the same as [equip=true]
  • integer - an integer in decimal, or hex prefixed with 0x, like 10 or 0xa.
  • float - a floating point number in decimal, like 1.2 (scientific notation not supported).
  • words - one or more words consisted of alphabetical characters, - and _, with spaces allowed in between, like hello my-world
  • quoted - a quoted string where any character is allowed "你好世界".
  • angled - like words, but surrounded by < and > and no spaces are allowed, like <Foo>.

Tip

Generally, the 3 string formats are all accepted and can be interchangeable. In some cases however, the formats can have different meanings.

Item Syntax

The Item Syntax has 3 components: amount, name, and metadata

get    3        pot-lid   [durability=3]
#      ^ amount ^ name    ^ metadata

To specify multiple items in the same command, simply write them one after another with an optional comma (,) between each item. Also, when the amount is 1, you can omit it.

More examples:

get 2 apples 3 bananas

# The following are all equivalent
get apple banana 2 core
get apple, 1 banana 2 cores
get 1 apple banana, 2 cores 

The item syntax could be used in 3 scenarios, depending on the command:

  1. FINITE_ITEM_LIST

    • The amount must be a number, not keywords like all.
    • The name must be an item, not category.
    • The metadata is used to describe extra properties of the item.
    • Generally used by commands for adding items, such as get.
  2. INFINITE_ITEM_LIST

    • The amount could be a number or the keyword infinite.
    • The name could be an item or a category.
    • The metadata is used to describe extra properties of the item.
    • Currently, this form is not used by any command.
  3. CONSTRAINED_ITEM_LIST

    • The amount could be a number, or:
      • The keyword all
      • In the form all but X, where X is a number.
    • The name could be an item or a category.
    • The metadata is used to match from items in some existing list (such as your inventory).
    • Generally used by commands that targets some item, such as hold and eat.
    • Position properties can be used.

Amount

The amount of item may have different meaning in different commands. For example, when using the eat command, the amount is always the internal value (i.e. the stack size), since you can eat from corrupted food or decrease armor value/durability by eating. When using sell, however, the amount means how many slots for unstackable items.

In CONSTRAINED_ITEM_LIST, you can use 2 special amount forms: all and all but:

  • all will repeatedly find the item and perform the action on the item, until the item cannot be found.
  • all but X will first count the total number of times the action can be performed, then perform the action count - X times. How the total number is counted depends on the command, similar to the eat vs sell situation mentioned earlier.

Note

The implementation may vary slightly based on the command, but the concepts are the same. One notable example is that all in dnp is implemented as all but 0, since otherwise it will be stuck in an infinite loop.

Warning

In rare cases, all but could be inaccurate, if the total number of items changes unexpectedly due to the action. Please report if you encounter this issue.

Name

You can specify the name of the item in 4 ways:

  1. By Identifier - An Item Identifier is multiple english words (A to Z, case-insensitive), combined with - or _. For example, royal-claymore and trav-bow are both valid identifiers. There is a fixed algorithm for resolving the identifier to an item.
    • The result is an item that contains all the individual words, for example trav-bow results in traveller’s bow.
    • You can add an effect before a food item to specify the cook effect. For example hasty-elixir, sneaky-wild-greens
    • Plural forms with -s, -es, -ies postfixes are supported. They don’t affect the amount of the item, only makes the command sounds more natural in English.
    • Some shorthands are supported in this form. For example, geb for great-eagle-bow, aa for ancient-arrow.
  2. By Actor - You can use angle brackets (<>) to specify the internal actor name directly, for example get <Weapon_Sword_070>. You cannot specify cook effect in this way.
  3. By Localization - If English is not your preferred language, you can specify items by their localized name using a quoted-string. For example, "espadon royal" or "王族双手剑".
    • The string is fuzzy-searched in all languages.
    • To lock the language, prepend the query with the language and a colon, for example, "fr:espadon royal". This could be useful if the query is short, and is matching in another language that you didn’t expect.
    • Localized search only applies to items, not commands (like get, hold, etc).
  4. By Category - When selecting items from inventory or some other list of items, you can also use a category in the place of the item name to match the first item of that category. This can be useful in situations like unequip shield where you don’t need to care what shield is currently equipped, or pick-up 3 weapons, where it doesn’t matter which weapons are picked up.

Info

See token for possible category values.

Metadata

The Meta Syntax is used to specify additional properties for the item:

  • In FINITE_ITEM_LIST, it is used to specify extra data on the item to be added.
    • For example, get pot-lid[durability=1] gets a new pot-lid with 1 durability
  • In CONSTRAINED_ITEM_LIST, it is used to specify extra data used to match the item to operate on.
    • For example, if you have multiple pot-lids, drop pot-lid[durability=1] targets the one with exactly 1 durability.

Available metadata properties:

PropertyAliasesDescription
durabilitydura(int) Sets value to 100 times the specified number
effect(int or string) Sets the effect ID for cooked-food. Integer values are used directly (even when invalid), and string values are converted. See Cook Effects for possible values
equippedequip(bool) If the item is equipped (May not have effect when adding item)
heldhold, holding(bool)
ingr(string) Set the ingredient of the cooked-food. The string must be an item identifier (see above). The property can be specified multiple times to add multiple ingredients.
level(int) Sets the level of the effect for cooked-food
life-recoverhp, modpower(int) Sets the number of quarter-hearts cooked-food recovers, or value of a weapon modifier
modifiermodtype(int or string) Set weapon modifier.

Cannot be used to set food effect type.

Integer values are the same as price. String values can be specified multiple times to build up the weapon modifier.

When used for matching, if only one modifier is specified, it will match any modifier flag that includes the specified one (i.e. other modifiers are allowed), if more than one bit is specified, the modifier flag must match exactly.
See Weapon Modifiers for possible values
price(int) Sets the price of the cooked-food. This can also be used to set multiple weapon modifiers as a bit mask
star(int) Armor star (upgrade) number, valid range is 0-4, inclusive.
Note that this is syntactic sugar to change the name of the item, as armor with different star numbers are different items.
time(int) Sets the duration of the food effect in seconds
valuelife(int) The value of the item, which is the count for stackables or durability multiplied by 100 for equipments.
Note: not to be confused with life-recover

Selecting from multiple matches

In CONSTRAINED_ITEM_LIST, there could be the case where there are multiple items that are exactly the same. There are additional meta properties that you can use to pick exactly which slot to select.

With from-slot property, you can pick the i-th matched item. For example, if there are 3 Pot Lids, you can use drop pot-lid[from-slot=2] to drop the second Pot Lid. The number is 1-indexed.

You can also target an item by its position in the inventory directly with one of the following methods:

# This is the same as using `from-slot`
# If there are >=2 slots of apple, this will eat from the second slot
eat 2 apple[slot=2]

# Category can be used as the name
# This eats the second slot in the entire inventory that is a material
eat 2 material[slot=2]

# Eat 2 apples from the material tab, in the first row and second column
# When using `category`, the indices are 1-indexed
eat 2 apple[category=material, row=1, col=2]

# Eat 2 apples from the second material tab, in the first row and second column
eat 2 apple[category=material, tab=2, row=1, col=2]

# Eat 2 apples from the second material tab, in the 0-th slot.
# The tab is 1-indexed.
# The slot is the 0-indexed slot in that tab, arranged like this:
# 00 01 02 03 04
# 05 06 07 08 09
# 10 11 12 13 14
# 15 16 17 18 19
eat 2 apple[category=material, tab=2, slot=0]

# Eat 2 apples from the 0-th tab, in the 3rd slot
# The tab index here is the 0-based index in the entire tab array
# The slot is the 0-indexed slot in that tab, see above
eat 2 apple[tab=0, slot=3]

Note

  • If the slot selected by position has a different item, you will receive an error.
  • When using row and col, they must be specified after category or tab.

Warning

The positions are calculated right before the simulator tries to find the item to target. This means if the action performed on previous items in the same command changes the inventory, the position you need to specify to target the correct item could be different from what you see in the inventory in the previous step. For this reason, it’s not recommended to specify position when performing an action on multiple items. Separate the position-dependent action to its own command instead.

Comments and Notes

Comments and Notes are text in the script that don’t affect the output of the command.

Comments

Comments are lines that start with # or //. They are completely ignored.

# This is a comment
// This is also a comment

Tip

In the script editor, you can use the hotkey Ctrl + / to quick toggle selected lines between commented/uncommented.

Block Literals

Block Literal is a multi-line block that starts and ends with ''' (triple single-quotes).

'''
This is a block literal

It can have multiple lines
'''

Addtionally, a block literal can have a tag, which is a string after the ''' that starts the block. For example, the note tag can be used to add notes to blocks of commands, which can be viewed in the Notes extension.

'''note
Drop these in the same pile
'''
drop all weapons
drop all shields

Info

The Notes feature is not implemented yet.

Systems in the Simulator

Skybook aims to be a 100% accurate IST simulator. To achieve that, it emulates subsystems of the game as much as possible. However, not all subsystems can be emulated, especially those that are not reversed-engineered fully or not at all. Some subsystems also may not be worth to emulate since a simulation is good enough.

The systems that are involved in the simulator include:

Screen System

The Screen system simulates different dialogs and pause menus in the game. For example, when pausing to access the inventory, or when selling items by talking to a shop keeper.

Most of the time, the simulator can switch between screens automatically depending on the actions, so the effect of this system should be transparent to those who are used to previous versions of the simulator.

Understanding this system could be useful, if you want to explicitly control when you open a screen, which can be helpful when optimizing and verifying IST setups.

Tip

The simulator UI has a little icon next to the “Visible Inventory” title to indicate which screen you are currently on.

Game State

While not technically a screen, the Game itself can also have 2 different states: Running and, well, not Running (closed).

The initial state of the simulation is similar to a new game. When executing most commands, the game will keep running, unless:

  • You manually closed the game with the close-game command.
  • The game crashes.

Note

When you encounter a game crash, note that it’s also possible it’s a bug in the simulator. Please report it on GitHub if the simulator crashes on a step that you don’t think is supposed to crash in game.

You can also view the detail of the crash in the Crash Viewer Extension.

Whenever the game is closed in the middle of a simulation (either closed manually or crashed), it will not automatically restart. You have to use either of the commands below:

  • new-game to start a new game
  • reload or reload SAVE_NAME to start the game and reload a save
    • SAVE_NAME is the name of the save, see Save Files

Screen Types

While in game, there are 4 screens that are simulated:

  • Overworld:
    • The default state when you start a game.
    • Player is able to move
    • You can get/drop items
    • … all the other things you can do in the overworld
  • Inventory:
    • When you pause the game with the + button
    • Player can interact with items in the inventory
  • Shop Selling:
    • When talking to a shop owner to sell items
    • Player can select items in the inventory to sell
  • Shop Buying:
    • When talking to Beedle or some other NPC to buy items
    • Player can select from a list of items to buy

The Screen system works like a state machine; when an action needs a certain screen, it will try to transition to that screen state if possible, and display an error if it couldn’t. The transition looks something like this:

             Overworld
         /---------|---------\
        /          |          \
       /           |           \
Inventory     Shop Buying  ---  Shop Selling

For example, if you are in the inventory menu and need to talk to a shop owner to sell something (i.e. to execute the sell command):

  • The simulator first checks if you are already in the Shop Selling screen
  • Since you are in the inventory screen, the simulator must return to Overworld by closing the inventory menu
  • Then, it simulates talking to a shop owner by transitioning to the Shop screen
  • Finally, it sells the item
  • After all of that, it will stay in the Shop screen until it has to transition again

That all happens in a single sell command!

Manually switching screens

The following actions count as manually switching the screen. If the screen has been manually switched, the simulator will prevent certain automatic screen switches.

  • Inventory:
    • pause to open the inventory
      • No automatic screen switches can happen until returned to overworld
    • unpause to close the inventory and return to overworld
  • Shop (buying and selling):
    • talk-to NPC to start buying or selling (NPC can be any - or _ connected word)
      • Screen can be automatically switched between Buying and Selling, but not to Overworld
      • When returned to overworld, screen can be automatically switched again to all types
    • untalk or close-dialog to return to overworld

Note that annotations for screen switching like :pause-during get do not count as manually switching screens, and the simulator will still automatically switch screens afterward.

Overworld System

The Overworld system simulates objects that the player interacts in the overworld, known as Actors. However, the actual overworld in the game is very complex, and most of the actors don’t even have anything to do with the inventory. Therefore, the Overworld system is a ultra-simplified simulation of only the actors that are involved in inventory glitches:

  • The player’s equipment (Weapon, Bow and Shield)
  • Any items currently being held by the player in the overworld
  • Any items (including materials and equipments) dropped by the player

Material Drop Limit

In the game, you can drop at most 10 items on the ground at a time. When you drop the 11-th item, the least-recent dropped item will despawn. This limit is simulated by the Overworld system in the following way:

  • When dropping material with the drop command, or auto-dropped from a smuggled state, it gets added to the list of items on the ground
  • The least-recently dropped items will be removed from the list, until there are at most 10 items on the ground
  • The removed items are not deleted immediately. You will see Will despawn in the tooltip text of the item in the simulator UI.
  • If you perform any action that takes some time so it’s impossible to preserve the despawning item, the item will be deleted.

Tip

It is implemented like this because it is possible to drop more than 10 items, but pick up the items fast enough before it despawns to keep the materials on the ground under the limit. This can be used to optimize IST steps.

For example, the following script will result in 15 apples in the overworld, 5 of which are in the Will despawn state.

hold 5 apples; drop
hold 5 apples; drop
hold 5 apples; drop

Then:

  • If you pick-up 5 apples right after, there will be 10 apples left on the ground, and 5 are added to the inventory.
  • If you pause, there will still be 15 apples on the ground, since you could unpause and pick them up.
  • If you get 3 bananas, the despawning items will now be deleted, and there will be 10 apples left on the ground. This is because it’s unlikely the apples are still there after you pick up some other item.

Resetting the Overworld

In a long IST setup, there might be times where you travel between different areas in the game, or exit/enter shrines, that cause the overworld to change without necessarily any inventory-related action. There are a few ways you can simulate this:

  • Any action that is supposed to reset the overworld will do so automatically, for example reload.
    • The !system [loading-screen] supercommand can be used to simulate regenerating the game stage with a loading screen, if none of the action commands match your needs.
  • The !system [clear-ground] supercommand can be used to delete all items on the ground. Use this if you are traveling to another area without a loading screen.

Command Reference

This is the full list of commands sorted in alphabetical order. Clicking on a command will take you to the corresponding documentation page.

CommandDescription
:accurately-simulate
for (get, sort )
Turn off optimizations that may be inaccurate
!arrowless-smugglePerform Arrowless Smuggle with the items currently held (use this if :smug is not an option)
!add-slotAdding a new slot to the inventory list by editing memory, bypassing all checks
!breakEdit memory to simulate generating Broken Slots
buyBuying items
close-dialogAlias for untalk
close-invAlias for unpause
close-inventoryAlias for unpause
close-gameClose the game
:discoveredChange whether a tab is discovered
dnpDrop material or equipment, then pick them up
:dpadSpecify change equipment should be done using DPad menu
dropDrop material or equipment
eatEat an item
entangleActivates Prompt Entanglement
equipEquips an item
getGetting an item
holdHold materials
!initResets the inventory memory to the list of items
new-gameStarts a new game
open-invAlias for pause
open-inventoryAlias for pause
overloadActivate Menu Overload
:overworldSpecify the next action to be performed in the overworld
pauseOpen the inventory
:pause-duringOpen the inventory during certain operations
:per-useChange the durability to decrease per use
pick-upPick up an item from the ground
!removeForcefully remove items from inventory, even non-interactable ones
reloadReload a manual or named save
:same-dialog
(for buy, sort )
Specify the next action should be in the same dialog sequence
saveMake a manual save
save-asMake a named save
!set-gdtSet any GDT flag
:slotAlias for :slots
:slotsChange number of equipment slots
:smugPerform Arrowless Smuggle with the next hold or drop command
sortSort a category of items
spawnSpawn Items in the Overworld
!swapSwap two item nodes
!systemSystem operations
talk-toTalk to an NPC for buying or selling
:targetingChanges the target for Prompt Entanglement
!trial-endEnd trial and restore inventory
!trial-startStart trial and clear inventory
unequipUnequip an item
unholdStop holding materials
unoverloadCancel Menu Overload
unpauseClose the inventory
untalkCloses buying or selling dialog
useUse equipments or materials in the overworld
!writeEdit inventory item data

Get Items

Adding new items to the inventory.

  • get command adds new items from an unspecified source (makes the item from thin air).
  • pick-up can only be used to get items previously dropped on the ground.
  • buy is similar to get, but has additionally functionality to simulate buying from an NPC in the same dialog as selling.

Syntax

get FINITE_ITEM_LIST
buy FINITE_ITEM_LIST
pick-up CONSTRAINED_ITEM_LIST

Annotations:

Examples

get diamond             # 1 Diamond
get 2 apple 2 banana    # 1 Diamond, 2 Apples, 2 Bananas
drop all apples         # 1 Diamond, 2 Bananas. 2 Apples on the ground
pick-up all materials   # 1 Diamond, 2 Bananas, 2 Apples
buy 5 eggs

Picking up previously dropped Items

The only difference between get and pick-up is that pick-up is used to target items previously dropped on the ground.

You cannot pick-up items that aren’t on the ground. Use get instead.

Buying from NPC

Normally, you buy items in this game by “talking” to the item directly in the overworld. Certain NPCs are exceptions, such as Beedle, Travelling Merchants, and Kilton. For these NPCs, you need to talk to them, and buy from a separate dialog.

By default, buy will ALWAYS assume you are buying from overworld, unless you tell it to not do so.

To talk to an NPC and buy, use the talk-to command.

talk-to beedle    # Opens Shop Buying screen
buy 5 arrows
shoot             # Automatically close the screen and shoot arrow
                  # To manually close the screen, use `untalk` or `close-dialog`

To sell, then buy within the same dialog sequence, use the :same-dialog annotation

sell ruby                 # Opens Shop Selling screen
:same-dialog buy 5 arrows # Without exiting dialog, opens Shop Buying screen
close-dialog

Also see Selling.

Pause on Item Text Boxes

During get, pick-up, or buy, you may encounter a “New Item” text box that allows you to open the inventory.

The :pause-during annotation can be used to simulate this action.

Warning

The simulator does NOT check if you are allowed to open the pause menu when you get an item, nor does it check if normal pause menu operations can be performed.

For example, usually you can eat something immediately in the text box that you got it, but you cannot hold another item. Currently, this situation is too complex to simulate correctly.

One use case is to force hold items during an item text box by performing Item Smuggle for Arrowless Offset, then get an item text box (similar to performing Arrowless Offset).

get 2 shrooms
:smug hold 2 shrooms
# Open a chest, for example
:pause-during get lynel-shield 
# Here, you are in pause screen while holding 2 shrooms
unpause
# Now the 2 shrooms will drop to the ground because of how the smuggle works

You can also use this feature to explicitly annotate optimizations for speedruns.

:pause-during get zora-armor; equip zora-armor

Performance

The preferred way to simulate getting multiple stackable items, is by invoking the function for adding the item to inventory repeatedly. However, when the number of items to get is large, this is a very expensive operation and can slow down script execution significantly.

Therefore, when the amount specified is greater than some internally determined amount, the implementation switches to a single call of the function with a value. Functionally, it turns:

get 999 apple

into:

get apple[value=999]

Most of the time (if not all), this will not cause inaccuracies. However, if it matters, you can use the :accurately-simulate annotation to force the more accurate implementation.

# This may take 30 seconds or more to execute, depending on your hardware
:accurately-simulate get 999 apples

Detail

  • get, pick-up and buy all require Overworld screen.
  • You cannot get new items while holding items in the overworld
    • with :smug, the held items will be dropped after getting the item

Material Operations

Performing actions on materials in the inventory. Some actions may apply to non-materials.

  • hold command performs the “hold” prompt.
    • When used without a list of items, it enters holding state in inventory screen without holding anything.
  • unhold command stops holding in inventory, or put away the items in overworld.
  • drop command is used to hold and drop items
    • When used without a list of items, it drops the currently-held items on the ground.
  • dnp command is a shorthand for drop and pick-up.
  • eat command performs the “eat” prompt.

Syntax

hold
hold CONSTRAINED_ITEM_LIST
unhold
drop
drop CONSTRAINED_ITEM_LIST
dnp CONSTRAINED_ITEM_LIST
eat CONSTRAINED_ITEM_LIST

Annotations:

  • :smug - Enable Smuggling for Arrowless Offset

Examples

hold apple
hold 2 apple
hold 1 shroom 1 pepper
unhold
:smug hold 1 shroom 1 pepper
unhold
eat all materials all food
dnp 5 weapons

Smuggle State for Arrowless Offset

The :smug annotation can be used to activate the item smuggle state required for Arrowless Offset (also known as Arrowless Smuggle), for the next hold command, which is when the held materials are attached to Link’s right hand instead of being held in front of him.

To do this in the simulator, put :smug right before the hold command.

:smug
hold 2 shrooms
# Now you are in Overworld, and held items are attached to Link's hand

You can also put :smug hold on the same line (which sounds like smuggled, hehe).

To do this in the game, you need:

  • A Shield
  • A one-handed Weapon

To perform this:

  1. Enable Weapon Smuggle and make sure a shield is equipped
  2. Hold the ZL button
  3. Hold items from up to 5 slots
  4. Switch to a one-handed weapon
    • Switch to another one-handed weapon, or to something else and back if you are already equipping a one-handed weapon
  5. Jump and let go of ZL button, after landing, when the shield is to Link’s side, unequip the shield

While in this state, you can perform actions which are not normally possible, such as getting items or talking to NPC. While doing so, the simulator will delay-drop the items. This is essential to generate offsets. In game, you can do this by either:

  • Whistle and perform the action (Dpad Down > A) quickly before the items drop
  • Pull out Bow and perform the action (ZR > A) quickly before the items drop

The :smug annotation can also be used with drop, for example, when using Prompt Entanglement to drop-hold some item:

# Activate PE
entangle apple
# Suppose Torch is entangled with Apple
# This will drop-hold the Apple, then close inventory and perform Arrowless Smuggle
:smug drop torch

Warning

:smug requires automatically switching to Overworld screen. If the screen was manually switched, the operation will fail. In this case, you can use the !arrowless-smuggle supercommand to manually activate the state while already holding items in the overworld.

pause
# This will fail, because screen was manually switched
:smug hold 1 apple 

# Do this instead (if removing the `pause` is not an option)
pause
hold 1 apple
unpause
!arrowless-smuggle

Dropping Items

Tip

The drop is also used for dropping equipments, which has a slightly different semantic. The description here only applies to materials.

When using drop without any items, it means to drop whatever is currently being held to the ground.

When using drop with items, it will attempt to hold the items in up to groups of 5, and drop them. This may not work as expected in rare cases, like if you hit mCount=0 in the middle of dropping, you will no longer be able to hold more items. In this case, you will get an error.

The dnp command is equivalent to drop, then pick-up the same items. Note that dropped items will not despawn after pick-up.

Detail

  • hold requires Inventory screen, and you can only hold a maximum of 5 items.
  • drop requires Overworld screen when dropping held items. When a list of items is specified, it may switch screens multiple times to facilitate the action.
  • Certain actions are not possible when you are holding items.

Equipment Operations

Operations on equipments in inventory.

  • equip and unequip changes the equipped status.
  • drop drops the equipment from the inventory.

Syntax

equip CONTRAINED_ITEM_LIST
unequip CONTRAINED_ITEM_LIST
drop CONTRAINED_ITEM_LIST

Annotations:

  • :dpad - Use the DPad menu instead of inventory menu to change equipments.

Change equipments

Example

# Equip the first weapon
equip weapon 
# Unequip the first **equipped** weapon
unequip weapon
# Equip multiple items
equip 1 royal-claymore 1 hylian-shield
# Unequip multiple items
unequip all shields all bows
# Unequip the second equipped Hylian Shield
unequip hylian-shield[slot=2]

# You can also equip armors and champion abilities
equip champion-tunic
unequip gale
# You cannot unequip arrows
unequip fire-arrow # Error! cannot unequip arrow

Warning

When using from-slot or slot for unequip, note that unequip only targets the equipped items. So slot=2 means the second equipped item. equip and other commands target all items, so equip weapon[slot=3] equips the third weapon in the inventory, regardless of which weapon is currently equipped. If the third weapon is already equipped, you will get an error.

This may seem like a weird design choice, but it makes intuitive sense when you use the command in most cases.

Tip

Normally, you would omit the amount for equip or use 1 for multiple categories, since equipping another item of the same category would just unequip the previous one. However, in some configurations, the items won’t be auto-unequipped. If you actually want to equip more than one item, you have to specify [equipped=false]. Otherwise, it will error when it hits an item that’s already equipped. For example, equip all weapons[equipped=false].

unequip all should always work as expected.

By default, changing equipments are assumed to be done in the pause menu. This should be ok in most cases. However, there are edge cases where action must be done through the DPad Quick Menu, examples include:

  • You are holding items in the pause menu.
  • The item slot is not visible in the pause menu, only in quick menu.

In these scenarios, you can use the :dpad annotation to specify the equipment change should be done via the quick menu.

Example

# Switch equipments with DPad quick menu
:dpad equip fire-arrow
:dpad unequip weapon

Warning

Note that :dpad unequip can only be used to unequip the first equipped item in the quick menu, and cannot be used to unequip arrows.

Dropping Equipments from Inventory

Use the drop command to drop equipments, which deletes the item in inventory, and spawns the item in overworld when the inventory is closed.

The game has a limitation on how many weapons can be dropped, but this is not implemented in the simulator.

Examples

drop all shields
drop all but 1 axe

Overworld Operations

Things you can do in the overworld:

  • use command uses and decreases durability of equipments, or can be used to remove items while in the overworld, e.g. use fairy.
  • shoot is an alias of use bow.
  • :overworld drop command drops equipped equipments.

Also, the spawn command can be used to add items directly to the overworld (i.e. on the ground).

Syntax

use CATEGORY_OR_ITEM (defaults to 1 time)
use CATEGORY_OR_ITEM X times
shoot
shoot X times
:overworld drop CONTRAINED_ITEM_LIST

Annotations:

  • :per-use X - sets the value to decrease per use
  • :overworld - changes the semantic of drop

Using Equipments

To use an equipped weapon, you can either specify the category, or the item name (given the item is equipped).

# Use the currently-equipped weapon to hit something
use weapon
# Use the currently-equipped weapon to hit something 5 times
use weapon 5 times
# Shoot with Royal Bow. Royal Bow must be the currently equipped bow
use royal-bow
# Shoot with currently equipped bow
shoot

The :per-use annotation changes how much durability is consumed per use. The default is 100.

# Bombing the shield takes 30 durability off
:per-use 3000 use shield

Special cases:

  • Using a weapon with IsLifeInfinite=true will not decrease durability
  • Using Bow of Light/Twilight Bow will not decrease arrows
  • Using MasterSword while the GDT Open_MasterSword_FullPower=true will consume 0.2x specified value, if its value is currently >=300

Using Non-equipments

When the item specified for use is not an equipment, it will attempt to remove the item instead. The only legitimate use of this is use fairy. However, the simulator will permit any item to be used this way.

For example, create Broken Slot with fairy:

hold fairy; use fairy; drop

Dropping the Overworld Equipment

In some cases, you can drop the equipment in the overworld without interacting with the inventory; for example, when getting shocked.

get axe
:overworld drop weapon

Spawning Items in the Overworld

The spawn command lets you create new items on the ground. Items created this way does not count in the drop limit. Furthermore, spawning items this way will work even when menu overloaded.

# Simulate shooting a bomb arrow on the ground that doesn't explode
shoot
spawn bomb-arrow

Detail

  • use requires Overworld.
  • In Arrowless Smuggle state, using a weapon or shield will unhold the item, while using a bow will drop the items.

Selling Items

The sell command simulates selling items to a shop keeper, or trading away Spirit Orbs or Korok Seeds.

Syntax

sell CONSTRAINED_ITEM_LIST

Example

talk-to beedle
sell all shroom all pepper
:same-dialog buy 5 arrows
close-dialog

Also see Buy.

Break Slots

Breaking Slots refers to the action of generating offsets to enable IST. See Inventory Slot Transfer for more info.

You can either break slots by simulating actions, like what you do in the game, or use the !break supercommand to directly edit the memory.

Arrowless Offset

Info

References for commands used for Arrowless Offset:

The most commonly used method of breaking slots is known as Arrowless Offset, which requires a shield, a one-handed weapon and a shop keeper and can break up to 5 slots at once:

  • Enter the Arrowless Smuggle state.
  • Talk to a shop keeper (by pressing Dpad Down > A or ZR > A quickly).
  • Sell all the items from slots that are being held.
  • Close the dialog.

Example script for Arrowless Offset in the simulator:

get 2 shroom 2 pepper 1 banana
:smug hold shroom pepper
sell all materials[held]
close-dialog

Hold Smuggle Offset

Hold Smuggle Offset is similar to Arrowless Offset, by selling smuggled items and dropping them after.

  • Activate Hold Smuggle (By Menu Overloading, for example with Shock Arrows).
  • Sell the items that are smuggled.
  • Hold another item, to cancel the smuggle.
  • Drop held items.

Since it is required to hold another item after selling, this method can only make up to 4 Broken Slots at a time.

get 2 shroom 2 pepper 1 banana
overload
hold shroom pepper
unoverload
sell all materials[held]
close-dialog
hold banana
drop

Tip

Other than selling the held slots, you can also do this by entering a trial, such as Trial of the Sword. This will remove the slot in the inventory. Now you can get a new item, hold it to cancel hold smuggle, and drop the items. Note that since getting a new item will take out one held slot out, you can also only make up to 4 slots at a time with this method.

This is also how IST was initially discovered.

Fairy Offset

You can use fairies to break slots by using the last fairy while holding one.

Example script:

hold all but 1 fairy;
use fairy; # by bombing yourself, or stand on fire, etc...
drop; # drop the held fairy

Eat and Hold Offset

With Prompt Entanglement, you can eat and hold the same slot to make offsets.

  • Eat all of the material in the slot you are using PE with
    • Since targeting a translucent slot with PE will target the first slot, you need to either make sure you are eating the first slot, or eat the slot you are using AND the first slot.
  • Use a “drop” prompt to hold the slot you ate.
  • Unpause and drop the items in your hand.

The example script below uses the 3rd slot in a tab (shroom) so it also needs to eat all of the first slot (apple).

:discovered [bow, shield] # spacing for PE
get
  1 torch 1 axe 1 hammer
  1 apple 1 banana 1 shroom
eat all apple all shroom
entangle hammer
:targeting <empty>[category=material, row=1, col=3]
drop hammer
drop

Entangle

Activate Prompt Entanglement and perform actions on a slot using prompts from another.

  • entangle activates PE and sets the target item
  • :targeting changes the target item, or allows you to target empty slots

Warning

PE is not 100% accurate.

Syntax

entangle ITEM
:targeting ITEM

Activate PE

While the Cursor Glitch is active, you can switch tabs in groups of 3 to keep the glitch active. Therefore, conceptually, when you activate a slot, all slots that are 3 tabs apart can be considered activated as well.

This action is simulated by the entangle command.

# Targets the Pot Lid, and activates that slot, as well as all slots that
# are 3 tabs apart
entangle pot-lid

While a slot is activated, you will see a “Link” icon next to it.

Tip

If a slot that’s supposed to be activated does’t exist in a tab (i.e the tab doesn’t have enough items), there will be a phantom slot displayed in that location when in Tabbed View. This is only a visual effect of the simulator.

The effect of the activation will last until the inventory is closed. You can also use another entangle command to change which slot is activated.

Targeting an Item

The second step to using PE is to select a target item that will receive the prompt. The :targeting annotation is used to do that.

# If the item is in an activated slot, you can use the name to select it
:targeting apple
# You can also select the slot directly
# This is useful if you are targeting an empty slot (which can't be selected
# by item name, since there's no item there)
# Note that specifying the first item directly will not work, if the activated
# slot is not in row 1 and col 1.
:targeting <empty>[category=material, tab=1, row=1, col=3]
#          ^ the name is ignored while targeting a slot directly, so it
#            doesn't matter what you put here

Warning

:targeting currently also searches slots that are not activated. If there are multiple matches, you might need to use a Position Property to specify the activated slot.

Finally, in the next command after :targeting, you can perform an action on a PE-enabled slot. If the target item can be reached by the item in the action, the action will be performed on the target item instead.

entangle roasted-endura-carrot :targeting roasted-endura-carrot
drop pot-lid # will hold the roasted endura carrot

Since it can be redundant to activate, then target the same item, entangle will also target the item by default. The command above can be shortened as:

entangle roasted-endura-carrot
drop pot-lid # will hold the roasted endura carrot

However, sometimes it might be clearer to write it as entangle then :targeting. For example, during speedrun, it’s usually faster to entangle the source item, to skip resetting the prompt, which means the item to setup the entangle is different from the item to target. However, it’s up to your preference how to write the command.

The effect of :targeting will only last until the next command, but you can use multiple :targeting within the same entangle.

Save Files

Handling game’s running and closed state, and simulation of save files:

  • save command saves to the manual save slot.
  • save-as file-name command saves to a named save slot. You can later reload this save by its name. This is used to simulate auto-saves, but you can have unlimited number of them.
  • reload command reloads a manual or named save.
  • new-game reloads an imaginary save with the state of a new game.
  • close-game closes the game.

Syntax

save
save-as FILE-NAME
reload
reload FILE-NAME
close-game
new-game

FILE-NAME can either be the same format as item identifier (like-this, or with _ instead of -), or a quoted string. Spaces and non-alphabetical characters are also allowed in quoted names, like "Outside of Shrine" or "神庙外".

Example

# Save to the manual save slot
save
# Save to a named save called `my-save`
save-as my-save
# Reload the manual save
reload
# Reload a named save
reload my-save

Inspecting Save Files in the App

Amongst other things, a save file for the game includes a copy of savable flags in GameData, which contains the items in the save. Therefore, the simulator displays save files similar to how it displays GameData.

Using the Save Files extension, you will see a list of save names available at the current step in the simulation. Clicking on a save will then display the items in that save.

Note that you can inspect saves even on steps where the game isn’t open.

New Game and Restarting the Game

The reload and new-game are the only 2 commands that can restart the game after it’s closed, either due to crash or was manually closed.

Currently, new-game is implemented as reloading an imaginary save made at new game. The implementation is as follows:

Game StateActionImplementation
RunningreloadReload the save
Runningnew-gameReload the “new game” save
ClosedreloadStart a new game, then reload the save
Closednew-gameStart a new game

This is not 100% accurate to what the game does, but should be close enough.

Simulating the System menu

save (and save-as if in inventory) will currently automatically unhold the currently held items, similar to how the game does that when you switch to the System menu.

Detail

  • save requires Inventory screen.
  • save-as can be performed in either Inventory or Overworld.
  • reload requires Inventory screen if the game is running.
    • new-game is similar, see implementation detail above.

Sorting

The sort command can be used to sort items in a category.

Syntax

sort CATEGORY
sort CATEGORY X times

Annotations:

Examples:

sort weapons
sort materials 2 times
talk-to shop-keeper
sell all apples
:same-dialog sort material
untalk

Sorting Order for Equipments

For Weapon, Bow, Shield, and Armor categories, the game has 2 modes to sort them that it toggles every time you sort.

For Weapon, the modes are:

  • By weapon type first, then weapon power, then weapon modifier, and finally item name.
  • By item name first, then weapon type, then weapon power, and finally weapon modifier.

For Bow and Shield, the modes are:

  • By power first, then modifier, then item name.
  • By item name first, then weapon power or guard power, then modifier.

For Armor, the modes are:

  • By armor type first, then item name.
  • By item name first, then armor type.

By default, the first time you sort one of these categories, it will be the first sorting mode (not by item name first). You can toggle this with the !set-gdt supercommand.

sort armors # will sort by armor type first (SortTypeArmorPouch changes from false to true)
sort armors # will sort by item name first (SortTypeArmorPouch changes from true to false)
!set-gdt <SortTypeArmorPouch>[bool=true] # manually set the flag to true
sort armors # will sort by item name first
sort armors # will sort by armor type first

# Flag name for other categories:
# - SortTypeWeaponPouch
# - SortTypeBowPouch
# - SortTypeShieldPouch

Sorting in Selling Screen

You can sort Armor, Material and Food while selling items. By default, sort assumes you want to sort in inventory screen, and it will closes the shop screen and open inventory screen even if the category specified would allow you to sort in shop screen. This would cause less confusion with automatic screen switch.

To actually sort in selling screen, you need to use the :same-dialog annotation, similar to buying from the same NPC without exiting dialog:

talk-to beedle
sort materials # error: cannot auto switch to inventory
:same-dialog sort materials # ok: opens selling screen

Performance

For performance, the number of times is capped at an internal max, since sorting an already-sorted list should have no effect. Use :accurately-simulate to override this behavior.

# actually press Y 300 times for some reason
:accurately-simulate sort materials 300 times

Detail

  • sort requires Inventory screen
    • unless :same-dialog is used in either Shop Buying or Shop Selling screen, in which case it will automatically switch to Shop Selling.
  • Sorting will attempt to remove translucent items. If mCount is 0 afterward in inventory screen, the inventory will become inaccessible.
  • Only Armor, Material and Food categories are allowed to be sorted while selling.
  • Sorting any armor subtype has the same effect as the armor type.
  • Sorting category with broken slots may not fully sort it the first time. This is related to how the merge sort is implemented by the game, and is not a bug in the simulator.

Menu Overload

In the game, when spawning more actors then the limit within the actor system, new actors will fail to be created. This is known as Menu Overload.

Since the simulator does not have an actor system like the game, there are only commands for simulating entering and leaving the overloaded state.

  • overload for triggering overload.
  • unoverload for canceling overload.

Syntax

overload
unoverload

Hole Smuggle

If menu is overloaded while closing inventory, the held items won’t be spawned in the overworld. This is known as Hold Smuggle.

# Get some random items
get 2 apple 2 banana 2 core
# Activate menu overload
overload
# Hold some items and close
hold 1 apple 1 banana 1 core
unpause
# Since we have hold smuggle, we can get items normally
get diamond

Item Transmutation

Hold smuggling can be used to perform Item Transmutation.

# Get sacrificial items, overload, and hold 5 of them
get 6 apple; overload hold 5 apple
# Sell the last item in the slot
sell apple
# Get the item you want to transmutate into
get giant-ancient-core

unhold # Now have 6 giant ancient cores!

Breaking Slots

Hold Smuggle can be used to make Broken Slots, see Breaking Slots.

Durability Transfer

Menu Overload allows you to switch inventory equipment without switching it in the overworld. This desynced state allows for transfering durability within the same type of equipments.

# Transfer the durability of Axe to Royal Guard Claymore
get axe royal-guard-claymore
overload
equip royal-guard-claymore
unoverload
use weapon

Details

  • overload can be used in any screen.
  • unoverload requires Overworld screen.
    • This is because it is not possible to cancel menu overload while the inventory is open.

Trials

Trials are special quests in the game where the game takes away your items, and give you a temporary inventory until the trial is completed.

The trials in the game include:

  • Trial of the Sword
  • Stranded on Eventide
  • 4 Blight Refights for Champion’s Ballad

Since each trial has a event flow associated with it, it would be too complex to try to simulate every possible interaction with the trials. Therefore, the simulator only provides !trial-start and !trial-end commands to put the inventory in or out of the trial mode.

Syntax

!trial-start
!trial-end

  • Starting a trial will:
    • delete all items except key-items, and set the trial mode flag.
    • re-create equipments in overworld.
  • Leaving the trial will reset the trial flag and reload your items from GDT.
    • Most of the time reload a save to exit the trial should work as expected with no issues.

Most of the time, a single !trial-start or !trial-end is accurate enough and you don’t need to worry about the specific details for each trial. See below if you are in an edge case where the specific event flow matters.

Danger

You should almost always make sure you are in the overworld to start a trial. Starting trial with inventory open is supported but may have unintended results. The simulator will NOT automatically switch the screen for you.

Eventide

When walking on eventide, the held items will be dropped if Arrowless Smuggle is active, or unheld if not. You should handle this manually

# Make sure you are not holding items (hold smuggle is fine)
unhold
# Make sure you are in the overworld to walk onto Eventide
unpause
# Trigger the trial
!trial-start

If you give up the Eventide challenge, there will be a loading screen. However there will NOT be one if you complete the challenge.

!trial-end
!system [loading-screen]

Trial of the Sword

Trial of the sword event is more complicated to simulate correctly:

# Make sure you are not holding items (hold smuggle is fine)
unhold
# The event flow will automatically equip master sword, remove it, then add it back
# This is also not fully accurate
equip master-sword; unpause; !remove master-sword; get master-sword
# enter trial mode
!trial-start
# there's a loading screen that sends you to TOTS map
!system [loading-screen]

Leaving TOTS:

# First end the trial and go through the loading screen
!trial-end
!system [loading-screen]
# The game automatically gives you a master sword
get master-sword
# If All 3 trials are completed, set the MS full power flag
!set-gdt <Open_MasterSword_FullPower>[bool=true]

Game Flags

Change flag values in GameData (GDT), such as number of upgrade slots, whether a tab is discovered, and quest flags.

  • :slots can be used to change the flag value for how many slots are available for Weapons, Bows and Shields
  • :discovered can be used to change which tabs are discovered

Moreover, !set-gdt can be used to set any GDT flag.

Syntax

:slots [CATEGORY=NUM]
:discovered [CATEGORY=true|false]
!set-gdt <FLAG>[GDT_META]

Number of Slots (i.e. Hestu Upgrade)

:slots or :slot sets these GDT flags WeaponPorchStockNum, BowPorchStockNum and ShieldPorchStockNum, according to the category that is specified.

Examples:

# sets the number of weapon slots to 20 (singular/plural forms are both accepted)
:slots [weapon=20]
# sets the number of weapon slots to 10, and number of bow slots to 8
:slots [weapons=10, bows=8]
# you will get an error if the number is out of range
:slots [weapons=21, bows=4, shields=3]
# Ranges: (inclusive)
# Weapon: 8-20
# Bow: 5-14
# Shields: 4-20

Discovered Tabs

:discovered edits the IsOpenItemCategory flag array. The category is parsed in the same way as item categories.

With a few minor differences:

  • arrow and arrows are allowed, and they are the same as bow/bows
  • Specific armor subtype (Upper/Lower/Head) is not allowed, use armor/armors instead

You can turn a tab to discovered with true and to undiscovered with false, unspecified tabs are unchanged.

Examples

# Discover the weapon, bow, shield tabs
:discovered [weapon, bow, shield]
# Undiscover the armor tab
:discovered [armor=false]

Note

When changing a tab from undiscovered to discovered, the inventory will not automatically update until it is changed (i.e. when updateInventoryInfo() is called again).

Any Flag

!set-gdt can set any flag by name.

The name is specified with angled brackets (e.g. <PorchItem>), to indicate the value should be interpreted literally, just like with angled-bracketed item names.

The meta is where you specify the value. First, you need to specify one of the property keys:

  • bool
  • s32 (alias: i32)
  • f32
  • str32 (alias: string32)
  • str64 (alias: string64)
  • str256 (alias: string256)
  • vec2f (alias: vector2f)
  • vec3f (alias: vector3f)

For non-vector types, the meta value is the value to set. Examples:

# Make Master Sword stay in True Form
!set-gdt <Open_MasterSword_FullPower>[bool=true]
# Sets Master Sword cool down timer
!set-gdt <MasterSwordRecoverTime>[f32=10.0]

For array values, use the index property to specify the array index (alias: i or idx):

# Set the first item in GDT to Travel Medallion
# String values work without quotes if it only contains alphabetical characters and _ or -
!set-gdt <PorchItem>[str64="Obj_WrapDLC", i=0]
# Set the value of the 20-th item in GDT (0-indexed) to 1000
!set-gdt <PorchItem_Value1>[s32=1000, idx=20]
# Set the modifier value of the 10-th shield in GDT (0-indexed) to 1000
!set-gdt <PorchShield_ValueSp>[s32=1000, idx=10]

For vector values, instead of specifying the value with the vec2f or vec3f key, use x, y, z keys to specify each component. Only the components that are specified will be changed

# Set the effect level of the first food to 3, leaving effect id unchanged
!set-gdt <CookEffect0>[vec2f, i=0, y=3]
# Set the sell price of the second food to 101, and the unused y value to 500
!set-gdt <CookEffect1>[vec2f, i=1, x=101, y=500.0]

Note

Integer values for vector components are automatically converted to 32-bit IEEE-754 floats.

Low Level Operations

These supercommands allow directly editing memory for prototyping or testing, or to workaround limitations of the simulator.

Danger

Because these commands edit memory directly, instead of mimicking what the game does, they could be very inconsistent with the behavior of the game sometimes!

Make sure you read the doc carefully before using them!

Syntax

Examples are available at each section below.

Generate Broken Slots

!break X slots

Adding item slots directly

!init FINITE_ITEM_LIST
!add-slot FINITE_ITEM_LIST

Forcefully remove item

!remove CONSTRAINED_ITEM_LIST

Change item data

!write [META] to ITEM
!swap ITEM1 and ITEM2

Generate Broken Slots

Tip

The simulator supports breaking slots using actions you would do in-game. See Break Slots.

The !break command edits mCount of list1 and list2 directly to effectively break slots “by magic”.

Example

!break 20 slots

Add Item Slots

The !init and !add-slot command will directly push a new item from list2 to list1, and set the memory according to the item you specified. This will bypass ALL checks for adding item to inventory.

Note that it will still prevent adding items when list2.mCount is 0.

Furthermore, !init will reset list1 and list2 to the initial state (where list2 has 420 items and list1 has 0). This means all broken slots will be cleared as well.

Example:

# setting items without sorting
!init 1 slate 1 glider 5 apples
# adding items not addable (doesn't have CanGetPouch flag)
!add-slot <DgnObj_EntanceElevator_A_01>
# when adding stackable items with [value=...], the "amount" becomes
# how many slots to add. e.g. the command below will add 5 slots of 300x arrows
!add-slot 5 arrow[value=300]

Note

The inventory state and GameData will be synced. This will also set the corresponding IsGet flag for the item, and the IsOpenItemCategory for the corresponding category.

Danger

If !init or !add-slot is used while the inventory screen is open, the new items may not be accessible until the inventory screen is closed and opened again!

While we could implement a force resync, doing that would change some internals of the pouch state that you may not want - for example, which item nodes correspond to the equipments in the overworld.

Forcefully Remove Items

The !remove supercommand lets you forcefully delete items from the inventory:

  • For Arrows, Materials, Foods, and Key Items, the value of the slot will decrease by the amount.
  • For the rest, the amount in the command corresponds to how many slots of this item you want to remove.

Inventory and GameData state are fixed and synced afterward.

Example:

!remove all cores

Warning

This command can target items that are normally inaccessible in the pouch screen. For example, when mCount is 0, or when the item slot is over the maximum available slots for Weapon/Bow/Arrow/Shield.

Change Item Data

The !write supercommand lets you edit the data for an item using the Item Meta Syntax. Inventory is fixed afterward, but GameData is NOT synced (for historical reason).

Currently, changing the ingredients is not supported.

Examples:

# Change the value of the Master Sword to 0
# NOTE THAT THIS DOES NOT BREAK IT 
# to break Master Sword properly (allowing MSWMC), you have to use the `use` command
!write [value=0] to master-sword

# Change the price of wild greens with price 102 to 101
!write [price=101] to wild-greens[price=102]

# When targeting the item by position, you can also change the item name
# Change the item in material tab 1, row 1, column 2, to royal-claymore
# with durability 20 (no matter what the item at that position is)
!write [dura=20] to royal-claymore[category=material, row=1, col=1]

Warning

This command can target items that are normally inaccessible in the pouch screen. For example, when mCount is 0, or when the item slot is over the maximum available slots for Weapon/Bow/Arrow/Shield.

The !swap supercommands targets 2 items, and swap their nodes in the list. Inventory is fixed afterward, but GameData is NOT synced (for historical reason).

Examples:

# Swap the first apple stack and the first banana stack
!swap apple and banana

# Swap the equipped royal claymore and the equipped bow (whatever the bow is)
!swap royal-claymore[equipped] and bow[equipped]

Danger

If !write or !swap is used while the inventory screen is open, some inventory state may not reflect immediately until the inventory is closed and opened again! (For example, when writing [equip=false].

While we could implement a force resync, doing that would change some internals of the pouch state that you may not want - for example, which item nodes correspond to the equipments in the overworld.

System Operations

The !system command provides low-level access to the simulated systems. Since these commands expose internals of the simulator, they should be considered unstable. If the simulator internals change in the future, the !system command could break without warning.

Syntax

!system [SYSTEM_META]

The SYSTEM_META meta properties are parsed into a list of “system commands”, then executed in order.

PropertyDescription
dlc(int or string) Change the DLC version stored in AocManager in the game memory.

Note that due to implementation detail, DLC version can only be changed while the game is running. See the example below.
Numbers 0, 1, and 2 correspond to No DLC, Day-1 (ver1), and Master Trials (ver2). Any other value means Champion’s Ballad (ver3). See DLC version for supported string values.
delete-save(omit value or string) Delete save data. A save name should be specified (omit value to mean manual save).
clear-ground(omit value) Delete all items on the ground, including the ones that are being spawned.
clear-overworld(omit value) Delete all items in the overworld, including the ones equipped by the player.
sync-overworld(omit value) Sync (i.e. re-create) player equipments in the overworld.
reload-gdt(omit value or string) Load save data into GDT, but do not load the inventory. A save name should be specified (omit value to mean manual save).
loading-screen(omit value or string) Trigger loading screen. The special value no-remove-translucent means trigger a loading screen without first attempting to remove translucent items.

Examples:

# Get Travel Medallion, save, then uninstall DLC and reload
get travel-medallion; save;
# Simulate a state where only DLC pack 1 is installed
# Note that due to implementation details, !system only works
# when the game is still running, so close-game needs to be after
!system [dlc=master-trials]
close-game
reload # Travel Medallion is gone!

# Simulate entering a shrine and clearing it
!system [loading-screen]
get spirit-orb
!system [loading-screen]

Constant Values

These are non-keyword constants that can be matched by commands in the right context.

When parsing, _, -, and spaces ( ) are ignored. So for example resist-cold is the same as resistcold.

Cook Effects

These values can be used for the effect item meta property to specify cook effect for a food.

ConstantDescription
allspeedAlias for hasty
attackAlias for mighty
attackupAlias for mighty
chillAlias for chilly
chilly“Chilly” Cook Effect

Internal Value: Some(4)
defenseAlias for tough
defenseupAlias for tough
electro“Electro” Cook Effect

Internal Value: Some(6)
endurAlias for endura
endura“Enduring” (Yellow Stamina) Cook Effect

Internal Value: Some(15)
enduringAlias for endura
energizingAlias for stamina
exgutsAlias for endura
exgutsmaxupAlias for endura
fire“Fireproof” Cook Effect

Internal Value: Some(16)
fireproofAlias for fire
gutsAlias for stamina
gutsrecoverAlias for stamina
hasty“Hasty” (Speed Up) Cook Effect

Internal Value: Some(13)
hearty“Hearty” Cook Effect

Internal Value: Some(2)
lifemaxupAlias for hearty
mighty“Mighty” (Attack Up) Cook Effect

Internal Value: Some(10)
movingspeedAlias for hasty
noneNo cook effect

Internal Value: Some(-1)
quietAlias for sneaky
quietnessAlias for sneaky
resistcoldAlias for spicy
resistelectricAlias for electro
resistfireAlias for fire
resistflameAlias for fire
resisthotAlias for chilly
sneaky“Sneaky” (Stealth Up) Cook Effect

Internal Value: Some(12)
speedAlias for hasty
speedupAlias for hasty
spicy“Spicy” Cook Effect

Internal Value: Some(5)
stamAlias for stamina
stamina“Energizing” (Stamina Up) Cook Effect

Internal Value: Some(14)
staminaupAlias for stamina
stamupAlias for stamina
stealthAlias for sneaky
stealthupAlias for sneaky
tough“Tough” (Defense Up) Cook Effect

Internal Value: Some(11)

Weapon Modifiers

These values can be used for the modifier/modtype item meta property to specify modifier for an equipment.

ConstantDescription
addguardAlias for guard
addguardplusShield Guard Up+

Internal Value: Some(0x80000100u32 as i32)
addlifeAlias for durability
addlifeplusDurability Up+

Internal Value: Some(0x80000002u32 as i32)
addpowerAlias for attack
addpowerplusAttack Up+ (Weapon or Bow)

Internal Value: Some(0x80000001u32 as i32)
attackAttack Up (Weapon or Bow)

Internal Value: Some(0x1)
attackupAlias for attack
criticalCritical Hit

Internal Value: Some(0x4)
criticalhitAlias for critical
durabilityDurability Up

Internal Value: Some(0x2)
durabilityupAlias for durability
guardShield Guard Up

Internal Value: Some(0x100)
guardupAlias for guard
longthrowLong Throw

Internal Value: Some(0x8)
multishotBow Multishot

Internal Value: Some(0x10)
noneNo modifier

Internal Value: Some(0)
plusMake the modifier “Yellow”

Internal Value: Some(0x80000000u32 as i32)
quickshotBow Quickshot

Internal Value: Some(0x40)
rapidfireAlias for quickshot
shieldsurfAlias for surfmaster
shieldsurfupAlias for surfmaster
spreadfireAlias for multishot
surfAlias for surfmaster
surfmasterShield Surf Up

Internal Value: Some(0x80)
surfupAlias for surfmaster
throwAlias for longthrow
yellowAlias for plus
zoomBow Zoom

Internal Value: Some(0x20)

DLC Version

These values can be used for the dlc system property in the !system command.

ConstantDescription
allAlias for ver3
championballadAlias for ver3
championsballadAlias for ver3
day1Alias for ver1
mastertrialAlias for ver2
mastertrialsAlias for ver2
nodlcAlias for none
noneNo DLC

Internal Value: Some(0)
plateauAlias for ver1
ver1“Day 1” DLC

Internal Value: Some(1)
ver2Master Trials (DLC Pack 1, ver 2.0)

Internal Value: Some(2)
ver3Champion’s Ballad (DLC Pack 2, ver 3.0)

Internal Value: Some(3)

Custom Image

Todo

Custom Image functionality is WIP. Please reach out to me if you want to play with it.

When running into code outside the normal inventory logic using glitches like Item Stack Underflow, the simulator will probably crash because it does not contain the full game to be able to execute setups that involves code outside of the normal inventory code.

BUT, the simulator is capable of executing the whole game’s code if it is given access. This is referred to as the Full Mode or Custom Image Mode. To do this, you need to create a BlueFlame image (a .bfi file) from the game files.

Create Image

To create the image, you need the following things:

  • Dump of the game (only some files are needed, not all of them)
  • A Nightly Rust toolchain
    • If you don’t have rust installed, see here

For detailed instructions on what is needed and steps to create the image, see the uking-relocate tool. Follow the instruction on the tool’s README.

The env Block

To tell the simulator to use a custom image instead of the default image, put an env block literal at the beginning of the script. The env block must be at the beginning, before any lines and comments. Empty lines are allowed before the block.

The env block should contain one <key> = <value> per line. Here is an example:

'''env
image = 1.5
dlc   = champions-ballad
program-start  = 0x0000001234500000
stack-start    = 0x0000005678900000
stack-size     = 0x40000
heap-free-size = 0x40000
pmdm-addr      = 0x0000003456789ab0
'''

Note

The numeric values must be hexadecimal; the leading 0x is optional.

Note

If a value is invalid, it’s equivalent to that value being not specified. At the same time, you will see an error in the script editor.

The image key specifies the version of the game. Allowed values are 1.5 and 1.6.

Warning

Currently, only 1.5 is supported. 1.6 is recognized but not supported. Newer versions won’t be recognized by either the simulator or uking-relocate.

The rest of the keys are optional. If not specified, the simulator will use the internal default values.

KeyValueDescription
dlca DLC specifier (see below)Specify the DLC version to simulate
program-startRegion AddressThe physical memory address of the start of the program region. This is checked against the program-start of the image file. The simulator cannot start if this doesn’t match. If this is not specified, any program-start will work.
stack-startRegion AddressThe physical memory address of the start of the stack
stack-sizeSizeThe size of the stack, must be aligned to 4KB
heap-free-sizeSizeSize of the free region of the heap for the simulator to allocate memory
pmdm-addrPhysical AddressThe address of the PauseMenuDataMgr (in other words, the value of PauseMenuDataMgr*). This is used to calculate heap start

Danger

Large stack/heap size can slow down simulator start-up. It is recommended to only change these if the default does not work for you.

DLC specifier can be any string that contains 0, 1, 2, or 3, which correspond to no DLC installed, DLC ver1.0 (Day 1), DLC ver2.0 (Master Trials) and DLC ver3.0 (Champions' Ballad). One of the following shorthand is recommended:

DLC VersionPossible Specifiers
No DLCnodlc, none, uninstalled
ver 1.0dlc-1, ver1.0, day-1
ver 2.0dlc-2, ver2.0, master-trials
ver 3.0dlc-3, ver3.0, champions-ballad

Invalid DLC version specifier defaults to ver3.0.

A Region Address must be a hexadecimal string aligned to 0x100000, the most significant 6 hex-digits must be all 0.

A Size must be a 32-bit positive integer, aligned to 0x1000. A size of 0 is the same as unspecified, and the internal defaults will be used.

pmdm-addr must be aligned to 0x8.

Furthermore, the program, stack, and heap regions must not overlap.

Upload the Custom Image

Once image is specified in the env block, refresh the page. You should see a prompt that asks if you want to upload a custom image. Select Setup and follow the on-screen instructions to upload the .bfi file you created.

Note

If the custom image fails to load, you can always select Use Default Image in the prompt to start the application normally and fix your script.

The uploaded image is stored in your local browser.

Use Custom Image By Default

You can opt-in to always use your custom image for you own script, even when the env block doesn’t specify a custom image.

To enable this, check the box that says Use Custom Image by default for my scripts when uploading the custom image.

Delete Uploaded Custom Image

You can open the 3-dot menu on the top of the app and select Delete Custom Image. This clears the custom image file that is stored in your local browser.

Developer Manual

This section is highly technical and only intended for people who work with, or are interested in, the IST simulator as a developer. For example:

  • Contributing to the project
  • Building extensions for the simulator
  • Integrating components of the simulator to other things

The following chapters assume you have basic programming knowledge. The IST Simulator is built with TypeScript, Rust and some Python scripts for data processing. Familiarity in at least one of these languages will help with understanding.

It is recommended that you start with Architecture if you want to contribute code.

Architecture

Skybook can be divided conceptually into 5 layers (or systems, or components, whatever you want to call them):

  • The Core: The Core is named BlueFlame. This layer is essentially a stripped-down, mini-emulator that interprets the game’s code. The core’s functionality is not limited to simulating IST, but can be expanded to other areas of the game if enough research is put into it.
  • The Runtime: The Runtime layer parses the script into steps and keeps track of the memory state of those steps in a simulation. It orchestrates the core for most of the simulation, and implements some sub-systems that are not supported by the core. The Runtime layer also manages multiple Cores to utilize multiple processors efficiently.
  • The Worker: This is the front-end of the Runtime. The Runtime layer is implemented in Rust, which is bad at handling async processes and requests. However, TypeScript is very good at that. So, the Worker layer essentially wraps the native interface with a TypeScript interface that the Application works with.
  • The Application: This is the main UI of the simulator web app, such as Pouch and GameData display.
  • The Extensions: The extensions are extra UI widgets that interface with the Application to easily add new features without changing the underlying architecture.

Info

For the web application, there is also a server that handles DirectLoad - loading script from another source such as GitHub or embedded URL. The server doesn’t really have anything to do with the core functionality, so it’s not discussed here.

These layers can be composed based on different requirements for endpoints. For example, the configuration for the web app is:

  • Core and Runtime are built into WASM
  • Worker is bundled into a WebWorker that loads the WASM
  • Application is bundled into the main entry point index.html
  • Extensions are both bundled into the main entry point (for displaying directly in the page), and secondary entry point popout.html for displaying as popouts.

Restricted imports in the UI Application

To simplify bundling, all UI code is in the app package. However, be careful when importing from a different part of the package, since that can affect the code splitting for bundling.

Current endpoints and what they import:

  • main:
    • application/extension
    • application/runtime
    • application/store
    • extensions
    • ui/components
    • ui/components
  • popout:
    • extensions
    • ui

Getting Started

The first step to contributing is to setup a development environment locally on your PC.

I aim to make the setup process as streamlined as possible. If you encounter any issues, please feel free to reach out and suggest to me how it can be improved!

Info

Before starting the setup, follow the mono-dev Standard to setup the required tools:

  • Rust Toolchain
  • Node, PNPM, and Bun
  • Python
  • Task
  • Magoo

Coreutils is required for Windows development.

Clone repository and one-time setup

Run the following commands:

git clone [email protected]:Pistonite/botw-ist --depth 1
cd botw-ist
magoo install
task exec -- research:install
task install-cargo-extra-tools
task build-artifacts
task install
task check

This will:

  • Clone the repository to your PC using your GitHub account
    • If you don’t have GitHub account or don’t have SSH key setup, use https://github.com/Pistonite/botw-ist as the URL instead
  • magoo will setup the submodules for you
  • Research scripts will be ran to ensure data files are setup
  • Data artifacts will be built from the data files
  • Dependency packages will be downloaded
  • Configuration files will be generated

Keeping up-to-date

After pulling, you need to update the repo locally to sync tools to the latest state.

Run:

task install

That’s it!

Building Runtime

The setup above will let you build and run the web app without building the Runtime locally. To build the Runtime, you need to either:

  • Set up uking-relocate, and put the game files in packages/runtime-tests/data/botw150.
    • Then, run task exec -- runtime-tests:build-mini to build the mini image.
  • Set up a BlueFlame image, and put the image file at packages/runtime-tests/data/program-mini.bfi. This will use whatever image you provide as the default runtime image.

Now, you can build the runtime WASM module:

task exec runtime-wasm:build

After that, the local build of the app will use the locally built Runtime.

Build and Run

This applies to development of:

  • The Web App
  • The Manual (this thing you are reading)
  • The Server

Tip

For all the commands like this:

task exec -- app:dev

You can also cd to the package and execute the task:

cd packages/app
task dev

Web Application

Warning

Running the web app locally requires Secure Origin, which means either running from localhost or setting up HTTPS, see here.

To run the web application:

task exec -- app:dev

The UI will automatically reload as you make changes.

Note that DirectLoad will not work when running the application locally since it’s a server feature.

Manual

To run the manual (this website):

task exec -- manual:dev

The manual will automatically reload when making changes.

Server

Warning

Currently, running the server locally also requires building the runtime as part of the assets, which requires a dump of the game to build.

To run the server, first build and pull the application assets locally:

task exec -- server:pull-local

Then the dev workflow can be started with:

task exec -- server:dev

Changes in the server code will reload the server automatically. However, changes in the client code requires re-running the client build and restarting the server (sometimes).

Building the Runtime

While you can make changes and build the runtime packages locally, to run it, you must have a BlueFlame image.

Building the BlueFlame image requires you to dump some parts of the game. See uking-relocate for more info.

Once you have the game files prepared, put them in /packages/runtime-tests/data/botw150:

The directory structure should be like

packages/
  runtime-tests/
    data/
      botw150/
        romfs/
        main.elf
        rtld.elf
        sdk.elf
        subsdk0.elf

Then run task exec -- runtime-tests:build-mini to setup the mini image used for running the runtime locally.

Alternatively, you can put the built BlueFlame image at /packages/runtime-tests/data/program-full.bfi, then run:

cd packages/runtime-tests
task update-trace-hash
python scripts/relocate.py

This will also produce program-mini.bfi.

Once you have the mini image, you can now make changes to the runtime packages, and rebuild the WASM package:

cd packages/runtime-wasm
task build

After building the WASM package, run the web app (or reload if already running) for it to pick up the newly built changes.

Checking Testing

You should make sure your changes are properly checked and tested.

Most of the time, this means you should at least manually tested the change, and if needed, add at least one Snapshot Test if the runtime is changed.

Checking

Running task check in the root of the repo will run a handful of checks and lints. You can also run task exec -- XXX:check to only run checks for one package.

When there are formatting issues, task exec -- XXX:fix can automatically fix that.

skybook-api modification checks

If you changed code that affects the generated skybook-api code, you need to commit those changes to git for skybook-api checks to pass.

localization checks

Certain changes require adding keys to localization (e.g. new error types). Please reach out to me for adding localizations.

The modification check also requires the changes to be committed to pass.

Unit and Manual Tests

We use Vitest for unit testing TypeScript code and standard Cargo test for Rust.

Also make sure to test your changes manually in the app. See Build and Run for how to run the app locally.

Snapshot Tests

Snapshot tests are the most important tests in the project. These tests are simulator scripts that the test tool runs and captures the state at each step, and saves them to a text file in a human readable format. Any diff in the snapshot is considered a failure.

To add a snapshot test, put a .txt file containing the script in /packages/runtime-tests/src/script_tests/, then run task exec -- runtime-tests:run-full (or run-mini). This will generate a new snapshot in snapshots/. Open the file and make sure the state is what you expect at every step.

If a snapshot test fails, the new snapshot will be saved to snapshots/wip. You can diff the snapshot with task diff -- NAME_OF_TEST. (Requires the delta tool). When you think the new snapshot is ready, copy it to snapshots and replace the old snapshot.

You can also run the ust task to update all snapshots.

Translations

The translation files are located in packages/localization/src/ui/*.yaml.

Here’s what you need to know when modifying the translation files:

  • Each translation entry is in the format of <key>: "<value>"
  • To escape " inside the value, put \".
  • Tokens like {{data}} are placeholders that will be replaced with the actual text in the app. Keep this in mind if you can’t find the entry you are looking for.

To add new translations, follow these steps:

  1. Add the new key and English value in en-US.yaml
  2. Prepare a translation file like this:
     # en-US is optional and will be ignored, 
     en-US: 
       button.ok: "OK"
     # order of languages/keys below does not matter
     # you can also include multiple keys
     de-DE:
       button.ok: "OK"
     es-ES:
       button.ok: "Aceptar"
     ko-KR:
       button.ok: "확인"
     zh-CN:
       button.ok: "好的"
    
  3. Go to the localization package (cd packages/localization)
  4. Run the edit task and pass in your file:
    task edit -- path/to/your/file
    

This will apply the edits to the translation files and make sure everything is formatted. Note that existing values will be overridden and values not found in English will be deleted.

To change the translation for one language, simply modify the language files.

Extensions

Todo

The extension system and documentation is WIP. This page is a placeholder.