Castlevania: Symphony of the Night

Development Guide

Introduction

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 an item, it matches exactly with the spelling and capitalization of the item in items.js.

Sample.JSON Sections

metadata

Necessity: Required

Used In: All Presets

Use: Establishes all identifying information about the preset.

comment

Necessity: Optional

Used In: Sample.json

Use: Provides notes or explanations for future programmers.

inherits

Necessity: Optional if using LockLocation; required if not.

Used In: Safe, Expedition, Lycanthrope

Use: Pulls properties from another preset file, which can then be overwritten.

relicLocationsExtension

Necessity: Optional

Used In: Adventure, Expedition

Use: Allows the preset to change which relic extension is used.

Options:

preventLeaks

Necessity: Optional (Recommended)

Used In: O.G.

Use: When set to false, more items may spawn in various areas at the cost of potentially revealing progression information.

stats

Necessity: Optional

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

Use: Set to “false” to disable 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: Allows you to rename game elements for use elsewhere in the file.

replaceRelic

Necessity: Optional

Used In: Glitch, Forge

Use: Replaces relics with items, either to substitute progression or remove unwanted relics.

placeRelic

Necessity: Optional (may not always function correctly)

Used In: Rat Race, Forge

Use: Forces the randomizer to place a relic (or one of several relics) in a specific location.

lockLocation

Necessity: Optional if using “inherits”, required if not.

Used In: Casual, Speedrun

Use: Defines what is in logic for your preset.

enemyDrops

Necessity: Optional

Used In: Speedrun, Gem Farmer, Boss Rush

Use: Forces specific enemies to drop specific items.

blockDrops

Necessity: Optional

Used In: Scavenger, Speedrun

Use: Prevents enemies from dropping certain items.

startingEquipment

Necessity: Optional

Used In: Nimble, Expedition, Lycanthrope

Use: Defines which starting gear the player begins with.

Valid Slots:

blockEquipment

Necessity: Optional

Used In: Speedrun, Gem Farmer, Rat Race

Use: Prevents certain equipment from being selected as starting gear.

Valid Slots:

itemLocations

Necessity: Optional (may not always function correctly)

Used In: Glitch, Boss Rush

Use: Places items directly into known item locations.

prologueRewards

Necessity: Optional

Used In: Forge, Bounty Hunter

Use: Gives the player items at the start based on prologue performance.

Valid Options:

blockRewards

Necessity: Optional

Used In: Forge, Bounty Hunter

Use: Prevents the player from receiving certain prologue reward items.

Valid Options:

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:

Valid Types:

value: The actual code to write to the ROM at the defined address.

Valid Values:

Additional notes are found in the “How To Make Writes” section below.

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 it 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 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.

Injected Code Example

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. Using the Player Level V99 code as an example:

{
  "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)"
}
    

Return From Injected Code

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: “sub address” isn’t actually a thing. The whole RAM address is actually 97BEE, but we cannot denote that in the format used by GameShark or the randomizer’s writes section.

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.

lockLocationAllowed

Necessity: Required (May not be required after preset is 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 out‑of‑logic checks in the preset.

Building and Testing Presets

Once you have completed the 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, something is wrong with your file structure or the JSON file wasn’t saved to the correct folder.

Now you should be able to attempt to generate a seed. If you encounter an error generating the seed, debugging can be difficult. Double‑check capitalization of item/relic names, location names, and zone names. Ensure punctuation is correct. If you still can’t figure it out, ask in the #tech-support channel of the Long Library Discord.

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 have not been 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 aside from the randomizer's canned capabilities. We look forward to seeing your vision brought to life!

Disclaimer

This fan-made site is NOT an endorsement of pirating games and does not encourage theft. The file linked in this site is a memory card file (PlayStation One Memory Card Data) of progress in the game. As fans, we want to support the companies that made and supported the games we love.

PlayStation and the PlayStation BIOS are all trademarks of Sony Computer Entertainment and will never be offered by this website and cannot be obtained from any members of our community. We encourage anyone wanting to play Castlevania: Symphony of the Night PlayStation Discs to purchase an applicable PlayStation One, PlayStation 2, or PlayStation 3 system from a reliable second-hand game sales store. If you really want to support Sony, go watch Spider-Man in the theater or buy a PS5. I also heard their TVs are really nice.

Castlevania and Castlevania: Symphony of the Night is a registered trademark and property owned by Konami Group Corporation and will never be offered by this website and cannot be obtained from any members of our community. We trust and advocate all players offered assistance by this guide to purchase a PlayStation copy of Castlevania: Symphony of the Night from a reliable second-hand game sales store to use with the Randomizer. You could also do Konami a solid by buying a copy of the current version of C: SOTN from the PlayStation Store, X‑Box Live Arcade, or for your Mobile Device.