Version 2019-09-08:
https://ufile.io/9jksebfjChanges:
- Improved Magic\Magic_Recharging description
- Second attempt at Items\Farmable_Sword_Orb; adds an out-of-the-way new teleporter in the Mana Fortress (shouldn't be hard to find) to send you to a new room to fight the now functional unused enemy, which now can drop a Sword Orb
- Changed Items\Mana_Fortress_Rarity_Swap to no longer affect Turtlances
- Changed Miscellaneous\Day_Night_Cycle to no longer replace Eggplant Man with Metal Crab at night (since Metal Crab has a weapon orb)
- Implemented Graphics\Retouched_Characters; fixes some color issues with the Girl which also happens to improve Functional_Fashion; stamina recharge / poisoned status palettes haven't yet been adjusted to accommodate
- Implemented Bug_Fixes\Pure_Land_Cave_Trap
- Tidy Map treatment for:
-- Axe Beak's fight room
-- Mana Fortress A (first interior area)
-- Witch's Castle Monty Hall (interior room with three doors north)
-- Kakkara Desert (north, east and west exits; south not changed yet)
-- Ice Palace Library
Mr X, weapon charge being reset when a spell hits a character should be vanilla behavior. Might not be pleasant, but don't think that one counts as a bug.
I think what I hammered out for the Sword Orb is pretty neat, will be curious what you think of it. I used the single unused enemy (apparently there was one), wrote a new AI script for it (it essentially didn't have one) then did some sorta funky stuff to get it animating without looking too glitchy. I can likely polish up its AI and animations further, but I got it to a usable state, unless someone finds some outright bugs with it. I intentionally set it up to have weird rules to fight successfully; it might take some experimenting to figure out how to kill it instead of it despawning (the despawning animation is a glitchy looking mess at the moment). If it does the "splat" death animation it was successfully killed; if it flies apart in a cloud of glitchy graphics, it despawned.
zoolgremlin, thanks again for all the map screenshots, they've been extremely helpful. I wasn't aware of that sticky wall in the Pure Land tunnel; it's now fixed. It was a simple one thankfully: one of the diagonal wall tiles was the wrong type which looks just like the others but is a different "height" where it's intended to be used for a plateau after going up stairs, etc. Your characters could walk onto it, which changed their height and then they couldn't step back off of it down to the normal lower height (without a staircase or other height transitioning tile). I just had to change it to the normal angled corner piece. There was a similar but opposite tile issue in Axe Beak's room, where there was an unwalkable angled corner because they used the lower height version where it should have been the upper.
As for "backgrounds" those are just like any other map, but set to scroll at a weird rate as you move around, but they're essentially no different on the data side of things, so feel free to point out any graphical issue with them as well.
SpiffSpoo, I've wanted to turn Evil Gate into, say, Evil Saber, but sabers are a fairly hard coded mess, so I haven't gotten to it (lots of work for dubious result). Mana Magic is functionally the Dryad saber spell, so what I'd likely do, if I ever get to it, is put mana tapping on Evil Saber, so the sprite could bestow a means of gaining mana on the girl (even if it doesn't jive with game lore on element logic, etc.), and so the last element missing a saber spell would be accounted for. Oh right, also, since enemies can use Evil Gate, that's another reason I hadn't gotten to it yet; turning it into a buff would likely necessitate some AI changes.
I wonder if Sleep Flower being sleep + poison at the same time would be functional. I hope someone tries that; doubt it would be worth casting unless poison is changed to percentage health damage or something, but at least conceptually that sounds usable.
hmsong, thanks for the info on magic recharge timings with different spell animation varieties; that makes a very good argument for overhauling the recharge timings to compensate for the varied behavior.
I like what you're going for with your adjustments to spells; homogeny is super boring so I appreciate that you're not just overly normalizing everything, and conversely having a stand-out best is super boring... obviously it's a fine line to walk, sometimes the best result is when things aren't master planned (e.g. Terraria vs. Starbound, Destiny vs. Destiny 2, etc.; i.e. the master planned one being less compelling than the one that was haphazardly tuned). For some of the vanilla values, I suspect the reasoning was things like "the girl isn't an offensive caster, so if she's going to use attack magic it'll have an MP cost penalty" or "maybe the hardest enemies are weak to light magic so lucent beam will be extra costly" (whether or not enemy resists reflect this). I generally avoid rebalancing games I play (I like working within the constraints someone else comes up with) but sometimes it's unavoidable in the course of making other changes (which from me tend to be bug fixes, aesthetic or base gameplay mechanics in nature). My point being, I'll be happy to include what you do, but I don't really want to have too much input on the final values since I'll hopefully want to play using them.
I wholeheartedly agree with your reasoning on poison. It's boomerang 6, the Cobra Shuttle, that has poison. I usually keep that one for its novelty (and because the Nintendo Power art for it was a tomahawk; a boomeranging tomahawk seemed cool to 1994 me).
Most likely the chest issue in the Grand Palace you mention is due to the 3 enemy limit. Chests are an NPC, and in combat areas, the NPC limit is 3 (9 in non-combat areas like towns, which is primarily why towns have bad framerate). This is part of why I have Kethinov's Gaia's Navel chest off by default.
Okay, let's see if I can explain the next step in changing spells. I'm going to lay out some foundational explanations first.
- Addresses and Offsets
In computer terms, an address is a number that specifies the location of data. An offset is a number added to an address to determine the location of data.
In certain circumstances (especially for data within a file), the offset is the primary information you need to locate the data you want.
The SNES has a 24-bit address space, which means it understands numbers from 0x000000 to 0xFFFFFF, so offsets and addresses will only include 6 hexadecimal digits in this post (more typical with modern computing is 8 digit (4 bytes, 32-bit) or 16 digit (8 bytes, 64-bit) addresses).
Common SNES assembly syntax has $ denote a hexadecimal number and # denote a literal number (and often combines them as #$ to mean a literal hexadecimal number). In other domains of software, 0x usually denotes a hexadecimal number. The ZPS patcher the Turbo project uses has an atypical variation of SNES assembly syntax, where $ specifically denotes an address, and # specifically denotes a literal number, all numbers are hexadecimal, and #$ isn't recognized. The ZPS patcher also uses un-prefixed hexadecimal numbers for its RAW and COPY commands (e.g. RAW 00 10 ABCD 32 999999, etc., it strips out spaces).
I will be using $000000 (6 digits) to denote SNES memory addresses, 0x000000 (6 digits) to denote offsets within the ROM file, 0x00 or 0x0000 (2 or 4 digits) to denote hexadecimal numbers in general, and # only in ZPS code examples (if any).
When talking about file offsets or memory addresses for editing SNES ROMs, a file offset is going to be what number byte into a file the data is located at, and a memory address is going to be what address in SNES memory the current ROM is at. Secret of Mana is a HiROM game, meaning its ROM file, when mapped in the SNES address space, is at the values $C00000 to $FFFFFF; with a 4 MB ROM, which is 0x400000 bytes, file offset 0 (0x000000) corresponds to SNES memory address $C00000, 0x000001 to $C00001, and so on, up to 0x3FFFFF = $FFFFFF. If you edit byte 0x101000 in the Secret of Mana ROM, then load that ROM, that changed byte will be at memory address $D01000. The conversion is as straightforward as adding or subtracting 0xC00000.
The ZPS patcher expects all addresses to be specified in HiROM address form, so
@OFF $D01000
RAW 00
in the ZPS file will write the value 0x00 to file offset 0x101000 in the output ROM file.
- Byte Order Endianness
The SNES is a little-endian architecture, meaning if you have a 16-bit or 24-bit value, the byte order goes from lowest to highest, left to right. The ROM data 0x34 0x12, if used as a 16-bit value, will mean 0x1234. The address $C01020, will be 0x20 0x10 0xC0 in ROM data. This is only relevant for data that is read directly as a 16-bit or 24-bit value; data that is used one byte at a time will still effectively go in the order you expect.
See the Wikipedia article on Endianness for further info:
https://en.wikipedia.org/wiki/EndiannessThis will matter when hex editing a ROM by hand, or when specifying 16-bit or 24-bit numbers (such as addresses) using ZPS's RAW command.
When assembling instructions or using the ADR command, the ZPS syntax allows you to enter numbers in 16-bit or 24-bit form and puts the bytes in the correct order for you.
As an example, consider the status effect value in the spell data: 0x2000 is poison, but you had to type it as 0x00 0x20 due to that 16-bit value being stored in little-endian order.
- Banks
While the SNES has a 24-bit address space, most of its instructions don't accept full 24-bit addresses. Instead, most of its instructions only specify the lower 16-bits of a full 24-bit address, and use the same highest 8-bits when using an address. This current highest 8-bits is called the data bank (simply bank from here on).
So, if some code wants to read the value at $D01000, it might use
LDA $D01000
but if the bank is set to 0xD0, it could use
LDA $1000
and it would read the same byte. There's also an even shorter form where you only specify the lowest byte, but I won't cover that at this time as it won't be relevant.
Secret of Mana's code and data are split up all over the place in its ROM, but related code and or data is often located within the same 0x10000 range of bytes: within the same bank. For example, all enemy AI scripts are between file offsets 0x110000 and 0x11FFFF (addresses $D10000 and $D1FFFF), which is bank 0x11 (because the highest byte of the file offset corresponding to the data is 0x11). It would probably make more sense to call it bank 0xD1 because banks are more relevant for memory address than file offsets, but other people decided to number the banks based on the file offsets, so that's what we'll use here as well.
If I say X is located in bank 0x10 and Y is located in bank 0x11, I mean X is somewhere between 0x100000 and 0x10FFFF and Y is somewhere between 0x110000 and 0x11FFFF.
- Software Isn't Magic
The game has spell data in bank 0x10, starting at offset 0x102AA0 in 64 byte (0x40 byte) blocks of data that share a common layout.
You've noticed that in each 64 byte block, the last three bytes specify the status effect and mana cost, but that changing the status effect value didn't seem to do anything. This is because just because that data is there, doesn't mean some programming actually reads and uses it.
When you choose a spell to cast, the game executes some code that reads an address that specifies more code to execute that then may or may not read some of the spell data. A list of addresses that specify chunks of code is generally called a function table. When you cast a spell, the game uses the number of the spell to specify which entry in the function table (which entry in the list) to read; the entry itself is a 16-bit address that specifies the next phase of code to be executed to make the spell happen. All of the damage only spells have the same 16-bit address entry for the code that performs a "cast a damage only spell" and that code never reads the status effect value in the spell data. To make a spell do damage and apply a status effect, this address entry in the spell function table must be changed from "cast a damage only spell" to "cast a damage + status effect spell" which in vanilla is only Blaze Wall.
That paragraph isn't the most simple of explanations, so please go over it a few times in case it doesn't make sense on the first read.
- Making Magic
Okay, so the function table for spells is in bank 0x08, specifically at 0x08E801. It has 42 entries (because there are 42 spells) that are 2-bytes each. Each 2 bytes is a 16-bit address specifying the location of code that is also located in bank 0x08.
The first spell is Earth Slide, so the first entry in the spell function table is for the code that performs "cast a damage only spell" which is located at 0x08EA8B (memory address $C8EA8B). I've kept saying it's a list of 16-bit addresses; this means it's only the low 2 bytes of the address, so 0xEA8B. Because the SNES is little-endian, in the ROM, that 16-bit address is specified in little-endian order, so the bytes at 0x08E801 is 0x8B 0xEA. This is crucial to understand. For the sake of the changes you want, it doesn't matter why the code at 0x08EA8B is "cast a damage only spell"; it matters that you understand how the function table specifies that address value.
Blaze Wall is the 17th spell. Notice that while Earth Slide is the 1st spell, and the function table is at 0x08E801, the address + offset for the Earth Slide entry is 0x08E801 + 0. This is called zero-indexing (
https://en.wikipedia.org/wiki/Zero-based_numbering). The first entry is 0, not 1. This is because, in programming, it's very common to have a list of things, but the first thing is zero distance from the beginning of a list. While you can count the entries however you want, the code that actually accesses the function table starts with the address of the table (0x08E801) then adds an offset relative to the spell's number. Every entry in the function table is a 16-bit address, so 2 bytes. That means the calculation is to take the spell's number (where Earth Slide is 0) and multiply it by two, then add it to the address of the table.
>> Continued two posts down. <<