• Just a reminder that providing specifics on, sharing links to, or naming websites where ROMs can be accessed is against the rules. If your post has any of this information it will be removed.
  • Ever thought it'd be cool to have your art, writing, or challenge runs featured on PokéCommunity? Click here for info - we'd love to spotlight your work!
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

[Script] Fixing the PokeMart clerk glitch in Pokemon Ruby

  • 12
    Posts
    3
    Years
    • Seen Jul 19, 2022
    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:
    Spoiler:

    The Map Script of Oldale starts at 0x14DD88:
    Spoiler:

    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 :)
    Spoiler:

    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
     
    Last edited:
    #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.

    #2 Are the flags simple Bits, or are they Bytes?
    Each flag is represented by a single bit in an array of bytes.

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

    #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).
     
    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
     
    Last edited:
    #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.

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

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

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

     
    Back
    Top