Development

So you would like to start developing your own preset? Fantastic! We would love to see your ideas implemented in game and welcome new randomizer modes. To get started you’ll need to complete the installation of the randomizer per our guide.


Next we’ll need to take a tour of the JSON files used for presets. Here is a Sample JSON file that has an example of almost every feature of the randomizer being used.


Sample JSON DOwnload


There is also a lot of documentation within the Randomizer code, itself. All you need to do is open randomize in Visual Studio Code and read the orange text.


You may also want to familiarize yourself with the information from constants.js so you know how to identify various check locations and the Zone ID’s.


Then, lastly, you need to make sure that any time you reference and item, it matches exactly with the spelling and capitalization of the item in items.js.

A general breakdown of what is and is not required is this, utilizing the Sample.json as a reference: 

Sample.JSON Sections:

metadata:

        Necessity: Required

        Used In: All Presets

        Use: Used to establish all of the identifying information about the Preset

        Fields:

o   id: This is the name of the preset which the Randomizer will use. Must be lowercase

o   name: This is the display name of the preset

o   description: This will tell the player a little bit about the preset before they try to generate it

o   author: Your name and the names of your collaborators go here

o   weight: To be updated at a later time


comment:

        Necessity: Optional

        Used In: Sample.json

        Use: Delivering information to future programmers

inherits:

        Necessity: Optional if using LockLocation. Required if not.

        Used In: Safe, Expedition, Lycanthrope

        Use: Used to pull properties from another preset file which can be overwritten by the this preset file

relicLocationExtension:

        Necessity: Optional

        Used in: Adventure, Expedition

        Use: Allows the preset to change which relic extension to use

        Options are:

o   false (Classic)

o   guarded

o   spread

o   equipment

preventLeaks:

        Necessity: Optional (Recommended)

        Used In: O.G.

        Use: This flag, when set to false, can cause more items to spawn in various areas at the cost of potentially giving away progression item data to the player

stats:

        Necessity: Optional

        Used In: O.G., Guarded O.G., Nimble

        Use: Set to “false” to remove item stat randomization

music:

        Necessity: Optional

        Used In: Boss Rush

        Use: Set to “false” to disable music randomization

turkeyMode:

        Necessity: Optional (Recommended)

        Used In:  Boss Rush

        Use: Set to “false” to disable turkeys in sub-weapon vats, randomized cape colors, 2nd castle music decoupling, and accessibility features

alias:

        Necessity: Optional

        Used In: Glitch, Forge

        Use: Can be used to alias game aspects with another name

        Fields:

o   comment: Identifies which element is being renamed and why

o   zone: Any one of the castle zones (NO3, NP3, TOP, etc.) to be aliased

o   relic: Any one of the relics to be aliased

o   alias: What the name of the elements will be called in the rest of the file

replaceRelic:

        Necessity: Optional

        Used In: Glitch, Forge

        Use: Used for replacing relics with items to substitute for progression or to remove unwanted relics.

        Fields:

o   comment: Explain which item is replacing the relic and why

o   relic: The relic being replaced

o   item: The item replacing it

placeRelic:

        Necessity: Optional (may not always function correctly)

        Used In: Rat Race, Forge

        Use: Forces the randomizer to place a relic or one of a selection of relics in a location

        Fields:

o   Comment: Identify which relics are being placed where and why

o   Relic: The relic or selection of relics to be placed in the location

o   Location: The location where the relics are to be placed

lockLocation:

        Necessity: Optional if using “Inherits”, required if not

        Used In: Casual, Speedrun

        Use: Used to designate what is in logic for your preset

        Fields:

o   location: Which relic or equipment location is being affected

o   comment: Tells others what the location’s logic philosophy is

o   locks: Which relics or combinations of relics / items are required

o   block: Prevents the relics listed from appearing at the location

o   escapeRequires: Ensures that some combination of the relics acquired before and including this check, in logic, will allow the player to escape the     check

enemyDrops:

        Necessity: Optional

        Used In: Speedrun, Gem Farmer, Boss Rush

        Use: Forces certain enemies to drop certain items

        Fields:

o   comment: Tells other programmers what the enemies are dropping or why

o   enemy: Any enemy or can use “*” to indicate ALL ENEMIES

o   level: Identifies the level of the enemy that needs to have it’s drops changed. Required for enemies that have multiple versions throughout the castles

o   items: Tells the randomizer which items to put where, both fields are required

blockDrops:

        Necessity: Optional

        Used In: Scavenger, Speedrun

        Use: Used to prevent enemies from dropping certain items

        Fields:

o   enemy: Any enemy or can use “*” to indicate ALL ENEMIES

o   level: Identifies the level of the enemy that needs to have it’s drops changed. Required for enemies that have multiple version throughout the castles

o   items: Used to indicate the items being prevented from dropping

startingEquipment:

        Necessity: Optional

        Used In: Nimble, Expedition, Lycanthrope

        Use: Used to define which starting gear to give the player

        Fields:

o   slot: Tells which slot to edit the item for

-  Options Are:

        Right hand

        Left hand

        Head

        Body

        Cloak

        Other

        AxeArmor

        Luck Mode

o   item: Any of the items in the game spelled exactly as they are in items.js

blockEquipment:

        Necessity: Optional

        Used In: Speedrun, Gem Farmer, Rat Race

        Use: Used to tell which equipment cannot be starting gear

        Fields:

o   slot: Tells which slot to edit the item for

-  Options Are:

        Right hand

        Left hand

        Head

        Body

        Cloak

        Other

        AxeArmor

        Luck Mode

o   item: Any of the items in the game spelled exactly as they are in items.js

itemLocations:

        Necessity: Optional (may not always function correctly)

        Used In: Glitch, Boss Rush

        Use: Used to place items directly into certain known item locations

        Fields:

o   comment: Identifies what is placed here and why

o   zone: Any one of the castle zones (NO3, NP3, TOP, etc.) to be aliased

o   item: The item which normally appears in the game in a non-randomized representing which slot to place the new item

o   index: If there are multiple items, index will tell which of those items to replace

o   replacement: The item to place in that location

prologueRewards:

        Necessity: Optional

        Used In: Forge, Bounty Hunter

        Use: Used to give the player items at the start as reward for their performance in the prologue

        Fields:

o   item: The item to replace

-  Options are:

        Heart Refresh

        Neutron bomb

        Potion

o   replacement: The item to use instead of the non-randomized item

blockRewards:

        Necessity: Optional

        Used In: Forge, Bounty Hunter

        Use: Used to prevent the player from receiving an item as a reward from the prologue

        Fields:

o   item: The item to replace

-  Options are:

        Heart Refresh

        Neutron bomb

        Potion

o   replacement: The item to prevent from being given

writes:

        Necessity: Optional

        Used In: Lycanthrope, Warlock, Rat Race

        Use: Used to make direct changes to the ROM outside of the canned capabilities of the Randomizer

        Fields:

o   comment: Identifies what the writes are doing or noting important aspects of the changes

o   address: The ROM address to update

o   type: How long the write will be

-  Options are:

        char – 2 digits

        short – 4 digits

        word – 8 digits

o   value: The actual code to write to the ROM at the defined address. (Can also be listed as "random" to get a random value)

        Additional Notes in the How To Make Writes section of this page

complexityGoal:

        Necessity: Required   

        Used In: All Presets

        Use: Identifies the complexity and which items / relics are intended to be placed at the end of the chain of complexity

        Fields:

o   min: Identifies the minimum complexity where the final relic can be found

o   max: Identifies the maximum complexity the preset can place a final relic

o   comment: Notes the goals of the preset or the philosophy of the preset

o   goals: Can list a single relic, or array of relics, or several arrays of relics / items that are the goals at the end of the chain of complexity

lockLocationAllowed:

        Necessity: Required (May not be required after preset added to SOTN.io, but this is still brand new)

        Used In: Aperture, Forge

        Use: Not required for the Randomizer but required for the tracker. Tells the Tracker how to identify the out of logic checks in the preset

        Fields:

o   Location: Which relic or equipment location is being affected

o   Comment: Tells others what the location’s logic philosophy is

o   Locks: Which relics or combinations of relics / items are required

o   Block: Prevents the relics listed from appearing at the location

o   EscapeRequires: Ensures that some combination of the relics acquired before and including this check, out of logic, will allow the player to escape the check

Building and Testing Presets:

Once you have completed that changes you want to make and are ready to test the preset, you need to complete the following steps to build it and test it:

If you encounter an error when building the preset, there is something wrong with your file structure or the JSON file wasn't saved to the correct file folder.

Now you should be able to attempt to generate a seed. If you encounter an error generating the seed, debugging can be difficult so make sure to double check how you capitalized item / relic names, location names, or zone names. Make sure your punctuation is correct and try again if you correct a mistake. If you still can't figure it out, take it to the #tech-support channel of the Long Library and someone might be able to help you there.

How to Make Writes:

You might be wondering how to make writes work for you. This is not going to be an expansive guide, but this will help you get started. This section will be expanded upon as we get more and better examples.

Example 1: Game Shark

Most of the time, you can infer something that can be done with writes directly from Game Shark codes available for the game. Writing these is fairly easy and we can do by understanding how the randomizer needs it broken down.
First, we need to tell it that we're writing to RAM, not ROM. That's where "injected code" comes in. In the Sample.JSON you saw that there was a section with the comment:
"Jump to injected code (Used for editing things requiring to be edited in RAM)"
Anything that is a Game Shark Code is going to occupy this space.

First, establish which RAM section we're writing to, then we'll use the address to tell it where to write, and then the actual value to write what we need the change to be. Use this diagram to find the information.

So to establish the address section to write to, we need to establish that we're going to write to RAM:

  {
    "comment": "Jump to injected code (Used for editing things requiring to be edited in RAM)",
    "address": "0x000fa97c",
    "type": "word",
    "value": "0x0c04db00"
  }

Then we need to tell the randomizer what value we want to write. I'm going to use the Player Level V99 code as an example, here.

  {
    "address": "0x00158c98",
    "type": "word",
    "value": "0x34020fff",
    "comment": "ori v0, 0x0fff"
  }

Now let's tell it where to write, starting with the section.

  {
    "comment": "Write to 8009 RAM addresses (Granting relics, stats, items, etc.)",
    "type": "word",
    "value": "0x3c038009",
    "comment": "lui v1, 0x8009"
  }

And now, we give it a specific sub address to write to.

  {
    "comment": "Player Level V99",
    "type": "word",
    "value": "0xa0627bee",
    "comment": "sb v0, 0x7bee (v1)"
  }

Next we return from the injected code and close the operation.

  {
    "comment": "Return from injected code",
    "type": "word",
    "value": "0x0803924f",
    "comment": "j 0x800e493c"
  }, {
    "comment": "Finish Return from injected code (Writes after this allow for direct editing of the ROM)",
    "type": "word",
    "value": "0x00000000",
    "comment": "nop"
  }

Finally, our writes section with just this code should look like this:

  "writes": [{
    "comment": "Jump to injected code (Used for editing things requiring to be edited in RAM)",
    "address": "0x000fa97c",
    "type": "word",
    "value": "0x0c04db00"
  }, {
    "address": "0x00158c98",
    "type": "word",
    "value": "0x34020fff",
    "comment": "ori v0, 0x0fff"
  }, {
    "comment": "Write to 8009 RAM addresses (Granting relics, stats, items, etc.)",
    "type": "word",
    "value": "0x3c038009",
    "comment": "lui v1, 0x8009"
  }, {
    "comment": "Player Level V99",
    "type": "word",
    "value": "0xa0627bee",
    "comment": "sb v0, 0x7bee (v1)"
  }, {
    "comment": "Return from injected code",
    "type": "word",
    "value": "0x0803924f",
    "comment": "j 0x800e493c"
  }, {
    "comment": "Finish Return from injected code (Writes after this allow for direct editing of the ROM)",
    "type": "word",
    "value": "0x00000000",
    "comment": "nop"
  }],

Important to note, here, that "sub address" isn't actually a thing. The whole RAM address is actually 97BEE, but we can't denote that the way that it's specified in either the Game Shark code or the randomizer's writes section.

Additional Notes:

It should be noted that there was specific intent behind every preset which has been released on SOTN.io and similar stands true for every preset available through TinMan on the Long Library Discord. There are elements which make those presets worthwhile. While not all of them need to be true at once, a good candidate for being added will have most of these checked:

These design philosophies will guide any preset development in a positive direction for the community. 

There are also things which cannot be done through the Randomizer yet:

There are also limitations which cannot be completed by the randomizer because of limitations with the coding of the game. These are hard barriers that completely prevent the items in question. A few examples include but are not limited to:

Please remember that development will require a lot of patience, especially if you want to implement writes that change the code of the game asid from teh randomizer's canned capabilities. We look forward to seeing your vision brought to life!