Thanks to the tireless efforts of the illustrious bryon_w, the web platform for the randomizer is live! No more having to download programs to your PC and wrestle with Windows Defender 😛
From now on, the most updated version of the randomizer can be found here:
Okay, so after my discussion with Raeven0 (see it here), I decided to try to approach enemy balancing by swapping enemy “templates”. Turns out, when an enemy is called in the event table, its memory address is immediately followed by a byte of data that acts as a lookup key for a table that then determines its HP, STR, DEF, and DP values. I call these templates.
Since I didn’t know where the raw data were that determined these values, I started out by cataloging the template stats and swapping out enemy templates to more manageable ones. This worked more or less, though there have been some issues:
- If I happened to miss an enemy, it would end up being insanely OP compared to the rest of the enemies.
- Scaling down some late-game enemies ended up being problematic in several ways (glitches, etc.)
I knew that if I could find the data table that specified these values directly, I would have a lot more control; using templates to balance enemies often seemed like trying to fit a square peg into a round hole. Trouble was, I didn’t know exactly what the format of the table was.
So, I guess. I just looked for a known enemy template combination, with the values in “HP, STR, DEF, DP” order — namely, I searched for template 5 (used for the Bats in the Underground Tunnel), which according to the order above would be “03 01 00 01”
First thing that popped up was exactly the table I was looking for. And it happened to be right adjacent in memory to the room-clearing reward table. I guess I should have done that first.
Anyway, the table can be found between 1ABF0 and 1ADA7, and is organized into four-byte groups that specify, respectively: HP, STR, DEF, and DP (Dark Points). It looks like the HP values are signed, because many “infinite” HP values are written as 7F instead of FF. Haven’t tested what happens when you try to assign negative HP to an enemy.
So, yeah, this is exciting. I now have direct control over how enemies are balanced, and I can do it without having to rely on a comprehensive database of game enemies to do it.
Here’s what the table looks like, with annotation:
Well, since appealing for help worked so well last time, here’s another thing I’m struggling with 😛
So, we need to balance out enemy stats. There’s a huge, huge disparity in strength of enemies between early and late game, which means if the randomizer sends you into a late dungeon early, that’s essentially OHKO territory. I want the balance to be closer to ALTTP, where late-game enemies are tough and pack a punch, but you can still reasonably navigate around them even if your stats are nothing to write home about. (To balance this out, I also plan to reduce the total STR/DEF upgrades available to the player, to make everything somewhat even — will take some playtesting to get the dials just right, I imagine.)
Naturally, I want to be able to control an enemy’s effective health (HP), strength (STR) and defense (DEF). HP is easy, for that’s a parameter that’s specified when an enemy script is called in the map event table. STR and DEF… not so much. Those must be buried somewhere deep in the enemy event code itself.
I’ve been looking at the enemy code of various enemies, trying to find patterns and similarities that I can attribute to either how much damage an enemy does, or to how much damage the enemy takes when attacked. Not much luck so far. Here’s a summary of what I’ve been looking at:
Diamond Mine Enemies
As we all know, the enemies in the Diamond Mine are essentially re-skinned for use in the Jeweler’s Mansion. One of the first things I checked was whether the code for these enemies was copied with slight modifications, or whether they used the same block of code for both dungeons. Turns out the latter is the case: When you look at the event table for both Diamond Mine and Jeweler’s Mansion, they call identical enemies, which makes sense from a space-efficiency perspective. This means there must be a handler inside the code blocks for these enemies that checks which map you’re on, and determines from there how the enemy should behave.
Sure enough, this is exactly what happens. Here’s the block of code from the Eye Stalker enemy (@ab103) that checks whether you’re in the Jeweler’s Mansion, which is Map 233 (0xE9):
@ab12b: AD 44 06 / C9 E9 / 00 / D0 05 / 02 C4 [19 B1 8A]
This should be the key to determining how enemy STR and DEF are assigned — I mean, you’d think there would be similar patterns of code on either branching path of this check with some key differences that would denote a strong enemy for a positive check and a weaker enemy for a negative check. It’s probably good for our national security that I don’t work for the NSA, because I haven’t yet been able to find any helpful patterns as I trace through the code. I’m hoping others might have better luck.
Bats and Dynapedes
One problem with looking at code for Diamond Mine enemies is that each enemy in that map can deal damage in a couple ways: Either through contact with their hit box, or through contact with a projectile that they generate. These enemies also react to the player’s behavior, acting differently depending on factors such as the player’s proximity to them. To mitigate these complication, I’ve been comparing the code for two enemies that have neither projectiles, nor much in the way of reacting to player behavior: Bats (from the Underground Tunnel, @a8755) and Dynapedes (from the Sky Garden, @ac533). There are of course some differences in how these enemies behave, but they both generally wonder around at random and only deal damage if you walk into them. I was hoping that comparing the data of these two enemies would shed some light as to how enemy STR and DEF are handled. Again, no such luck yet, but I’m still looking.
Any help or insights from folks would be appreciated!
Okay guys, I’m stuck.
For legitimately months now I’ve been trying to find a way to accomplish what should be a simple task: I want Kara’s portrait to show up in one of five locations, deep in the five minor dungeons: Underground Tunnel (map 16), Diamond Mine (map 71), Angel Village (map 116, the vanilla location), Mt. Kress (map 169), or Ankor Wat (map 191). That’s it. Is that so much to ask?
Apparently it is, because no matter what I do, I can’t get it to work. Here is a summary of the challenges:
Kara’s portrait is a sprite in the Angel Village spriteset. Spritesets are tied to map header data — thus, the sprites that show up are dependent on either the map you enter, or (if the map header doesn’t specify a spriteset) it inherits the spriteset from the last map for which this was specified. This isn’t a big deal, because the rooms in which I want to put Kara’s portrait have little or no sprites in them, so I should just be able to change the spriteset for the map and call it good.
However, everything I’ve tried to this effect has not behaved properly. I’ve tried everything from copying spriteset data from Angel Village into other maps, to looking for event triggers that change spritesets — nothing works. It’s really frustrating me, to be honest.
I refuse to admit defeat, though. This should be doable, and I want this quest to be a thing — if for no other reason than it would make a “0 Statue” category actually doable, which essentially turns a seed into a mad scramble to find the Magic Dust and Kara’s portrait, so you can free her and jump straight to Dark Gaia. I think this randomizer (which seems to take on average 2-3 hours on Easy mode) could use a potentially shorter category.
I do have a few clues left to pursue. The tree I’m currently barking up is a discovery that was made by ZockerStu, where the sprites for Lola and Bill in Will’s House can change into two random dudes from the South Cape spriteset. Upon investigation, I found that setting switch 0x4C (i.e. entering the Gold Ship) triggers this change, so either that flag suppresses the spriteset assignment for that map (causing it to inherit the spriteset from the South Cape map from which you came), or actively reassigns the spriteset for that map. This was encouraging, for that means that there’s some sort of event trigger that would allow for the changing of how spritesets are handled in a map, which is exactly what I’m looking for.
Anyway… so there’s my confession. If there’s anyone out there who can lend any help (I’m looking at you manafreak and Raeven0), I could sure use some!
EDIT: Here are some of the technical notes to bring the developers along with what I’ve discovered/tried so far:
- Switch 4C is in RAM location A09, bit 4 (0x10). This is what triggers the spriteset change in Will’s House. I’ve searched in the event data for the traditional “check switch” commands (i.e. “02 D0 4C” would explicitly check the status of that switch, and “02 d2 4C” would wait for that switch to be set), but haven’t found anything. Another possible way the code could implicitly check for that switch would be to do an AND operation of that memory location (it would show up in memory as “09 0A”, with a “10” byte nearby). The event pointers for map 6 (Will’s House) are @c83fb. Looking into this currently.
- The spriteset data in the map header should look something like this, borrowed from the header data for Angel Village (map 107) — the map header begins @d953e, the spriteset command starts @d9582, and contains: “10 43 0A 00 00 00 DA”. (Copying this into the same header information for another map doesn’t change the sprites of that map to Angel Village sprites, so maybe my understanding of how this works is off.)
- The event data for Kara’s portrait is located @6d14e (which appears in the map event table as “4e d1 86” @cb396). I can successfully force this event to show up and be functional in another map — I just can’t find a way to override the spriteset of another map to make the proper sprite appear for this event.
EDIT: Raeven0, you sweet sweet bastard.
So, inventory management is a big component of the randomizer. Virtually every item in the game grants some sort of progression, and nearly half of the 112 items in the game have to be taken to a location in order to be used — and even then it might not be removed from your inventory.
Well, I just made a discovery that helps with this a little: I found out where in ROM it’s determined which items can be removed from your inventory, and which cannot. This allows me to let the player, for instance, remove hieroglyph tiles from their inventory if Statue 5 is not required (since hieroglyph tiles offer no progression and open up no new item locations). I can also make it so that spoiler items (like Lance’s Letter and Father’s Journal), as well as helpful items (like the black glasses) can be removed at will.
The chunk of memory dedicated to this is found @0x01e12a. In the vanilla game, it’s set to this:
BC FF FF FE DF 00 00 00
So, a 1 in a bit location prohibits item removal, and a 0 allows it.
I at first assumed that the bits read left to right — i.e. the high bit of the first byte would correspond to item 0, then item 1, etc. However, after some investigation, it turns out that each byte does refer to 8 items, in order, but in low-to-high bit order. For example, if I wanted to restrict removal of Red Jewels (item 0x01), I would change the first byte from BC to BD. If I wanted to allow the Magic Dust (item 0x14) to be removed, I’d change the third byte from FF to EF.
So, my new game starts with this flag configuration:
9C FF 9F 27 B0 01 00 00
Among other things, this allows for the removal of non-essential items like Lance’s Letter and Father’s Journal, but restricts removal of items like the Apple, which is not a key item you can only get once.
If it turns out that the pyramid is required, I make the hieroglyphs (items 0x1E thru 0x23) non-removable by updating it to this:
9C FF 9F E7 BF 01 00 00
Hey folks, just created a Discord server for the randomizer, and I’ve released the first beta version for playtesting. Feel welcome to join us!
As of today, the IoGR is officially functional. I have a bug list as long as my arm, but in a couple of weeks it should be ready for play testing. Let me know if you’re interested.
I’ll also probably stream a tutorial on Twitch when I get the kinks ironed out. Feel welcome to follow me for updates, twitch.tv/dontbagume
Today, the first version of the Illusion of Gaia Randomizer was successfully run with shuffled items and abilities.
It’s unearthed a lot more bugs and things that need to be worked out, so we’re not out of the woods yet.
Once I’m generally happy with it, I’ll hand it out to a few folks for beta testing. If you’re interested, let me know!
Am I the only one who thinks that maybe the Moon Tribe might actually be Strong Bad? You be the judge.
Another breakthrough I’d like to share with y’all 🙂
I’ve been spending the morning trying to figure out how Dark Spaces work. Here’s what I’ve found so far:
It seems like there are three configurations for Dark Spaces. These configuration codes are passed in the event table whenever the Dark Space event (a9 d6 88) is called:
- 01 = no character transformation
- 03 = can transform into Fredan and/or Shadow
- 05 = grants a special ability
If 05 is passed, the event looks to an ability table @8eb5a, which contains six tuples specifying the map from which the Dark Space is entered, and the ability that is granted in the Dark Space. Here’s the existing code block with annotation:
- 15 01 >> Map 15 (21) grants Psycho Dash (bit 0)
- 62 02 >> Map 62 (98) grants Psycho Slide (bit 1)
- 86 04 >> Map 86 (134) grants Spin Dash (bit 2)
- 42 10 >> Map 42 (66) grants Dark Friar (bit 4)
- a7 20 >> Map a7 (167) grants Aura Barrier (bit 5)
- b8 40 >> Map b8 (184) grants Earth Quaker (bit 6)
When an ability is granted, the text boxes that are called refer to another couple of tables, one that prints the name of the ability, and the other that prints the ability’s explanation:
@8eb8f – pointers to ability names (6 addresses):
- 9b eb >> “Psycho Dash”
- a2 eb >> “Psycho Slider”
- ab eb >> “Spin Dash”
- b2 eb >> “Dark Friar”
- ba eb >> “Aura Barrier”
- c7 eb >> “Earthquaker”
@8ebd3 – pointers to ability explanations (6 addresses):
- df eb >> Psycho Dash explanation
- 66 ec >> Psycho Slider explanation
- ea ec >> Spin Dash explanation
- 6d ed >> Dark Friar explanation
- f2 ed >> Aura Barrier explanation
- 8b ee >> Earthquaker explanation
Understanding all this, we can make the Dark Space in South Cape grant us the Dark Friar ability through the following modifications:
- Change the Dark Space configuration from 01 to 05 in the event table (@c830a)
- Change the fourth entry in the Ability Table to refer to Map 01 instead of Map 42 (@8eb60)
- Change the fist entry in the Ability Name and Ability Explanation tables to point to Dark Friar (respectively, change @8eb8f to “b2” and @8ebd3 to “6d”)
Further work: There has to be a table somewhere else that indexes the ability names and explanations to the maps somehow. Rearranging the address pointers in the tables themselves is a clunky way to do it, but it works for the time being.
Also, the Dark Space that grants you the Aura at the Pyramid entrance seems to be a complete anomaly. It even calls a completely separate event (68 b6 88). I intend to look into this at some point as well.
Lastly, I don’t know how the game decides whether Shadow shows up in Dark Spaces or not. It’s probably a switch condition I haven’t found yet.