The PokéCommunity Forums

The PokéCommunity Forums (https://www.pokecommunity.com/index.php)
-   Binary ROM Hacking (https://www.pokecommunity.com/forumdisplay.php?f=284)
-   -   Script Fixing the PokeMart clerk glitch in Pokemon Ruby (https://www.pokecommunity.com/showthread.php?t=456768)

ShinyIksbat August 12th, 2021 12:37 PM

Fixing the PokeMart clerk glitch in Pokemon Ruby
 
Hello everyone,

I got back into playing Pokemon Ruby a few weeks ago and enjoyed it ever since.
Now that I 100%ed it, I wanted to get into ROM hacking of said game.
I have set myself a little challenge of fixing as many glitches in the game as I possibly can.

For now I did some minor corrections using Advance Map 1.92 to edit NPC properties or moving/removing tiles.
Yesterday I finally hit my first roadblock: I want to fix the PokeMart clerk in Oldale Town.
It's a well known glitch where they run into a tree, if you didn't have enough space in your inventory to receive the potion.
Spoiler:


I have already set up XSE 1.1.1 to edit scripts, but I still have questions left, which hopefully one of the gurus here can answer :)

#1 What is the correct way to edit existing code?
From what I understood so far, XSE is able to dynamically use the ROMs available addresses to store data.
Take for example the existing PokeMart clerk:
* do I have to move all the existing code with my new additions to a new address and change the sprites script to the new offset address?
* or can I simply start adding, and XSE will change the offsets of all subsequent existing things (even data outside of the current sprite script) on it's own, without me overwriting stuff?
My goal is to fix the behaviour, but keeping everything as close to vanilla as possible.

#2 Are the flags simple Bits, or are they Bytes?
Just for understanding the concept behind them, I would like to know what the flags are actually.
They get accessed by something that looks like it could be a RAM address, but I would assume from my gut feeling that they actually are just Bits, not whole Bytes.
Some of those flags seem to be implicitly set by routine calls - the lower ones I assume, like 0x0 or 0x1 for example when doing a checkflag.

#3 Why does the script for the PokeMart clerk not work?
The sprites script starts at 0x14DDDC:

The Map Script of Oldale starts at 0x14DD88:

So far I understand that in the Map Script (which is executed when you enter the town) the clerk is moved to his starting position next to the bottom right house, if the flag 0x84 (promo potion received) is NOT set.
That way the walk can properly be played.
When you speak to the clerk and the walk is performed, the flax 0x1 gets set (line #9 of clerk script) - this way the game keeps track if you already have spoken to him (and thus walked up).
If you immediately talk to them again but didn't receive the promotion because of a full bag, the walk will not be performed.
---
To my understanding the bug occurs when you leave the map and enter again, because the Map Script executes and 0x1 is overwritten because of other calls in it.
And when you talk to the clerk again (while 0x84 still being false), the walk will be played.

This in itself wouldn't be an issue, but somehow the Map Script with the movesprite2 call does not execute (properly?) - the clerk still stands just before the PokeMart.
If I would have to guess, I would say that sprites can't be moved if they are considered to be on screen.
That guy isn't on screen when I just barely leave the town and come back, but maybe the game handles sprites up to X tiles outside of the viewable area as "still on screen" - veterans could maybe answer that question :)

Edit: if you walk far enough away from the town and then back in, the clerk will be reset at the correct position again.

#4 is RAM saved to the save file?
So my question is if ALL the flags and variables (or all the RAM for that matter) is stored in the save file.
I would assume so, because when I save the game and reload while something is temporarely changed, the change still exists when reloading.
Be it either the clerk standing next to the PokeMart even when 0x84 is false, or a tile existing near me, even when I changed it with Advance Map (will be corrected once you enter again though) - this at least would make me think certain stuff gets saved.


Kind regards

Anon822 August 13th, 2021 3:55 AM

Quote:

Originally Posted by ShinyIksbat (Post 10392502)
#1 What is the correct way to edit existing code?
From what I understood so far, XSE is able to dynamically use the ROMs available addresses to store data.
Take for example the existing PokeMart clerk:
* do I have to move all the existing code with my new additions to a new address and change the sprites script to the new offset address?
* or can I simply start adding, and XSE will change the offsets of all subsequent existing things (even data outside of the current sprite script) on it's own, without me overwriting stuff?

XSE can automatically find free space but only if you're using the "#dynamic" directive. You should move everything you edit to free space and editing the script to use #dynamic is a good way to do that.

Quote:

Originally Posted by ShinyIksbat (Post 10392502)
#2 Are the flags simple Bits, or are they Bytes?

Each flag is represented by a single bit in an array of bytes.

Quote:

Originally Posted by ShinyIksbat (Post 10392502)
#3 Why does the script for the PokeMart clerk not work?
...
If I would have to guess, I would say that sprites can't be moved if they are considered to be on screen.

That would be my guess too. I believe movesprite2 command is used to move off-screen sprites while movesprite is used to move on-screen sprites.

Quote:

Originally Posted by ShinyIksbat (Post 10392502)
#4 is RAM saved to the save file?
So my question is if ALL the flags and variables (or all the RAM for that matter) is stored in the save file.

The save file contains specific parts of ram (often referred to as saveblock1, saveblock2 and saveblock3/PokemonStorage), which includes all flags and most variables (I believe the 0x8000+ vars aren't saved).

ShinyIksbat August 13th, 2021 10:28 AM

Thanks mate, that was already a big help :)
I think I found an elegant solution and was able to fix the glitch.
Before I want to show my solution, I need some more answers to very specific questions 😅

#1
As far as I know all the variables are unsigned WORDs (16 Bits).
In my solution an underflow can happen because of "subvar" -> the variable might become a big positive number (which is intended), but I am not sure if the overflowing part will affect the variable next to the intended one in memory.
My gut feeling says it might/will do so, because this is programming on a very low level... still I tested it by underflowing variable 0x4001 -> 0x4000/0x4002 seem to not have been affected... but maybe I just tested it wrong.
Maybe someone who has a lot of experience can tell me how the hardware (or emulation in this case) behaves exactly.

#2
I used the #dynamic directive now for the existing code that has been patched by me.
This causes the existing code to be pushed to a new free offset, but the previous bytes that have been used are not freed (set to FF).
Is there a way to automatically (or at least on demand) reset that space?

#3
So there is "call" and "goto" which both enable me to execute code from another pointer.
The difference is that the code from call can jump back with the "return" directive.
My question is now if call behaves like goto if I never run into a return directive, or if it does an implicit return at the end of the code.
Intention behind this question is that I conditionally want to return to the previous point.
Also... can I run into a stack overflow when I do not return in a call (I mean after doing that MANY times... I would assume "call" creates a stack entry).

Kind regards

Anon822 August 14th, 2021 3:44 AM

Quote:

Originally Posted by ShinyIksbat (Post 10392892)
#1
As far as I know all the variables are unsigned WORDs (16 Bits).
In my solution an underflow can happen because of "subvar" -> the variable might become a big positive number (which is intended), but I am not sure if the overflowing part will affect the variable next to the intended one in memory.

Yes, variables are 16 bits and overflowing them does not affect other variables.

Quote:

Originally Posted by ShinyIksbat (Post 10392892)
#2
I used the #dynamic directive now for the existing code that has been patched by me.
This causes the existing code to be pushed to a new free offset, but the previous bytes that have been used are not freed (set to FF).
Is there a way to automatically (or at least on demand) reset that space?

Apparently there are some directives for deleting scripts, but I don't think freeing a few bytes is really worth it when you have megabytes of free space available. You also typically look for free space at the end of the rom so you wouldn't end up reusing that space unless you made conscious effort to do so.

Quote:

Originally Posted by ShinyIksbat (Post 10392892)
#3
My question is now if call behaves like goto if I never run into a return directive, or if it does an implicit return at the end of the code.

There is no implicit return, you can just end the script after a call.

Quote:

Originally Posted by ShinyIksbat (Post 10392892)
can I run into a stack overflow when I do not return in a call (I mean after doing that MANY times... I would assume "call" creates a stack entry).

Just don't do more than 20 calls.

ShinyIksbat August 22nd, 2021 11:24 AM

I finally was able to completely understand the issue with this glitch and how to fix it with just 12 existing bytes fixed, and 11 new bytes introduced.
The issue was that the sprite of the PokeMart clerk potentially doesn't unload if you just bearly leave and reenter the town, and also flag 0x1 gets reset, because it is part of the temporary ones, which implicitly get cleared when you enter a new map.
A detailed explanation can be seen here - took me quite some time to bring it all into video form ^^



All times are GMT -8. The time now is 9:15 AM.


Like our Facebook Page Follow us on Twitter © 2002 - 2018 The PokéCommunity™, pokecommunity.com.
Pokémon characters and images belong to The Pokémon Company International and Nintendo. This website is in no way affiliated with or endorsed by Nintendo, Creatures, GAMEFREAK, The Pokémon Company or The Pokémon Company International. We just love Pokémon.
All forum styles, their images (unless noted otherwise) and site designs are © 2002 - 2016 The PokéCommunity / PokéCommunity.com.
PokéCommunity™ is a trademark of The PokéCommunity. All rights reserved. Sponsor advertisements do not imply our endorsement of that product or service. User generated content remains the property of its creator.

Acknowledgements
Use of PokéCommunity Assets
vB Optimise by DragonByte Technologies Ltd © 2023.