• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • 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.

A Quick Gold and Silver Scripting Tutorial

29
Posts
9
Years
    • Seen Feb 3, 2020
    I'm aware that there are two other Tutorials, but I don't think either of them really make things easy to understand for the reader. There is also information missing from both of them, so it's probably time for a new one.


    TUTORIAL

    The goal of this tutorial is that the reader learn a few things before diving head-first into scripting:
    • How Addresses(or offsets) and Pointers work, and what the difference is between the two.
    • How to script without causing bugs.
    • How to use the Scripting Compendium to script effectively.
    • What resources are available to aid in scripting.
    • How to find free space manually


    For this tutorial you will need:​

    1. A Pokemon Gold Rom as well as an Emulator to use it with.
    2. THIS version of G2Map(Skeetendo Demo).
    3. The Latest Version of PKSV.
    4. The Scripting Compendium


    1. Set up your Workspace
    First off, lets setup our enviornment.
    Make a single directory where we'll have all of our files contained. Lets call this directory /My Pokemon Hack/.
    Inside this folder, lets make a folder for our ROM Hack, PKSV and G2Map.

    It should look something like:
    /My Pokemon Hack/G2Map
    /My Pokemon Hack/PKSV
    /My Pokemon Hack/ROM Hack


    Next, you should make your GBC Emulator the default program for .gbc files.
    Right click on the rom and use the "Open With..." Dialog to point it to the Emulator's executable with "Always use this program to open files of this type" selected.

    Finally, open G2Map, load a ROM, switch to Event Edit mode, and click on an event.
    Click on "Launch Script Editor" and it will ask you for PKSV's location. Point it in the right direction and everything will be setup at this point in time.


    2. Understanding Addresses and Pointers
    You won't make it very far in scripting if you don't understand how the Addresses(offsets) and Pointers work together.

    First, lets talk about Addresses.
    Addresses are one-to-one with the ROM file.

    Our ROM is 2MB in size, or 2,097,152 Bytes. The number 0x200000 in hexadecimal is equivalent to 2,097,152 in Decimal.
    When computers count, they start at 0 and go up until they don't have space. So, rather than counting from 0x000001 to 0x200000 they'll count from 0x000000 to 0x1FFFFF.

    All Addresses are always in Hexadecimal and range from 0x000000 to 0x1FFFFF.

    I want to point out a few things:
    • Each digit of a hexadecimal number is called a Nibble. It ranges from 0 to F(15), and two Nibbles make a Byte. I won't talk about Nibbles much during this tutorial, I just wanted to be sure that everyone knows what they're called in case they appear in other tutorials or guides.
    • One Byte ranges from 0x00 to 0xFF.
    • Addresses are 3 Bytes total. They appear commented in PKSV as ' $12ABC3 at the end of certain lines.
    Because of the limited memory of a Gameboy Color Catridge, only so much data can be accessed at one time from the ROM; 0x4000 Bytes to be exact.
    While scripting in Pokemon Gold and Silver, you'll first load a bank between 0x00 and 0x7F into the Memory Bank Controller(MBC), then reference all memory in that bank as a pointer between 0x4000 and 0x7FFF(Remember what I said about counting? The first address is 0x4000 and the 0x4000th address is 0x7FFF).

    Why? The first 0x4000 bytes of the Gameboy are allocated to the first 0x4000 bytes of the ROM's data, and may not be changed. The game runs from this area primarily.

    Let me clarify something that would confuse you if you go into it knowing only that, typically scripts run within one memory bank. They use two byte pointers which reference memory within the current bank.
    Commands of this type include '2call','2jump', '2writetext', and several others.


    3. The Scripting Compendium
    Use this as a reference to learn what the exact bytes and parameters of the commands you use
    or see are.

    Here is the compendium's Quick Reference section:
    Spoiler:


    The actual compendium will tell you about each function in detail.


    4. When does the actual scripting start?
    (I'm getting to that!)

    Once you understand what I meant about the compendium.

    Here is a random script from Whitney's Gym:
    Code:
    [FONT=Monospace]#org 0x15C037
    '-----------------------------------
    loadfont
    checkbit1 0x28
    if false 0x4044 ' 0x15C044
    2writetext 0x41F5 ' 0x15C1F5
    closetext
    loadmovesprites
    end[/FONT]
    When editing scripts you MUST be aware of how much space they consume to begin with, and how much space you need to achieve your goal.
    Here is a practice I like to use to keep track of my space consumption and available space while scripting:

    Code:
    [FONT=Monospace]#org 0x15C037
    '-----------------------------------
    loadfont'                    1B
    checkbit1 0x28'              3B
    if false 0x4044 ' 0x15C044   3B
    2writetext 0x41F5 ' 0x15C1F5 3B
    closetext'                   1B
    loadmovesprites'             1B
    end'                         1B
    '                           13 Byte Total[/FONT]
    By keeping track of how many bytes each command uses, you'll be able to make scripts without causing any bugs. How does this prevent you from causing bugs?
    If you write more bytes in script than the original code occupied it will write over the existing code of other scripts. That is not what we want. This will cause bugs, and will /probably/ make your hack unplayable.

    The above example looks like this in the actual ROM when viewed through a hex editor.
    Code:
    47 31 28 00 08 44 40 4C F5 41 53 49 90
    It can be split as:

    Code:
    [FONT=Monospace]47        loadtext
    31 28 00  checkbit1
    08 44 40  if false
    4C F5 41  2writetext
    53        closetext
    49        loadmovesprites
    90        end[/FONT]
    A quick check of the compendium will show you that each of the first bytes matches each of the bytes here in function.


    5. The Example

    In the above code, a string is referenced by the 2writetext command:

    Code:
    #org 0x15C1F5
    = Waaaaah!\pWaaaaah!\p[.]Snivel, hic[.]\n[.]You meanie!\e
    If I'm not mistaken, this block of script is for when you talk to Whitney immediately after beating her. Now, I've played through this generation a LOT, and if there's one thing I /HATE/, it's Whitney being a spoiled crybaby.

    Leeeets, fix this.

    Code:
    [FONT=Monospace]#org 0x15C037
    '-----------------------------------
    loadfont'                    1B
    setbit2 0x1C'                3B[/FONT][FONT=Monospace]
    2writetext 0x4278 ' 0x15C278 3B
    [/FONT][FONT=Monospace]playsound 0x9C               3B
    closetext'                   1B
    loadmovesprites'             1B
    end'                         1B
    '                           13 Byte Total[/FONT]
    That'll teach her! This gives you your badge and displays the string at pointer 0x15C278
    Code:
    #org 0x15C278
    = [PLAYER] received\nPLAINBADGE.\e
    However, this is not a good idea. Each time the player talks to Whitney immediately after defeating her this will give them the Plain Badge and play the sound. This also doesn't stop the second event from occuring when the player leaves and re-enters the gym, nor does it give them her TM.

    Lets change this in a better way:
    Code:
    [FONT=Monospace]#org 0x15C037
    '-----------------------------------
    loadfont'                    1B
    clearbit1 0x28'              3B
    clearbit1 0x0B'              3B
    2jump 0x4044 ' 0x15C044      3B
    closetext'                   1B
    loadmovesprites'             1B
    end'                         1B
    '                           13 Byte Total[/FONT]
    This is a much better method of skipping that fugly exit-and-return process.
    It clears the bit 0x28 which is checked to see whether or not the player can receive the badge, and also the 0x0B bit. The second clear is essentially padding since the bit has not yet been set, however; this is a safe method of ensuring that it absolutely isn't set since a check is made on it before giving the player the badge.

    Press F9 to compile, and CTRL-T to test this. If my code is correct(I haven't actually tested it, but I see no reason why it wouldn't work), then Whitney will surrender her badge and TM without the player having to take a long walk out and a long walk back.

    Another thing I want to make a little mention about is the code below the 2jump line. This is what is called 'unreachable code'. At this point, the game will never use that small portion of script for any reason unless you call it somewhere else in your scripts.

    Don't abuse this by making a whole bunch of things unreachable for no reason. That unreachable script is actually really useful in situations like the guy in Cherrygrove City who gives you the Map Card for the PokeGear. If you use a portion of his MASSIVE sight seeing tour, you'll have some unreachable code hopefully. Now you have free space to put any code you'd like.

    How do you reference it? Another great thing about keeping track of how many bytes you're using is that you can add to the offset to know exactly what address a line is at.


    Code:
    [FONT=Monospace]#org 0x15C037
    '-----------------------------------
    loadfont'                    1B 0x15C037 + 1 =
    clearbit1 0x28'              3B 0x15C038 + 3 =
    clearbit1 0x0B'              3B 0x15C03B + 3 =
    2jump 0x4044 ' 0x15C044      3B 0x15C03E + 3 =
    closetext'                   1B 0x15C041 + 1 =  <-- Unreachable code begins here
    loadmovesprites'             1B 0x15C042 + 1 =
    end'                         1B 0x15C043
    '                           13 Byte Total[/FONT]
    So our unreachable code begins at Address 0x15C041 or Local Pointer 0x4041. It's only three bytes, so all it would hold is a pointer, a 2byte operation and an end, or two 1-byte operations and an end.
    Make sure your code doesn't fall through to other code by the way!

    6. Finding Free Space

    When scripting, especially with large scripts, it's best to look for some free space in the current memory bank and start there.
    In my All Pokemon hack for Pokemon Gold I want to create an event for Mewtwo in Mt. Mortar.

    I'll start by finding an event in Mt. Mortar that I don't particularly care about:

    This event is an Item Ball containing a Guard Spec.
    The player would have a Guard Spec. if they really wanted one, especially at the point where they could freely pass around Mt. Mortar, so I really don't care about it. It's perfect for what I want it for!

    Next, I'll edit the event properties window so that the event looks the way I want it to look:

    I'll also move it to the place I want it to be, and have a look at it in-game:


    Now, I'll click on the "Launch Script Editor" button in the Event Properties window to bring up the script of the event:
    Code:
    #org 0x119DB4
    '-----------------------------------
    takephonenumber 0x1
    end
    It uses exactly three bytes. This could not be more perfect. The reason why is that a 2jump command will perfectly fit in the space already occupied by the script.

    My next goal is finding the free space within this bank.

    By using a hex editor it's pretty easy to locate any available free space. This script is located at 0x119DB4 within the ROM. This means that the actual block loaded into the memory bank starts at 0x118000 and ends at 0x11BFFF.

    For any script, check the third nibble of the address to see what the upper and lower bounds of the block are inside the actual ROM:
    Code:
    [LIST]
    [*]0xYY0ZZZ - 0xYY3ZZZ: Located between 0xYY0000 and 0xYY3FFF in ROM
    [*]0xYY4ZZZ - 0xYY7ZZZ: Located between 0xYY4000 and 0xYY7FFF in ROM
    [*]0xYY8ZZZ - 0xYYBZZZ: Located between 0xYY8000 and 0xYYBFFF in ROM
    [*]0xYYCZZZ - 0xYYFZZZ: Located between 0xYYC000 and 0xYYFFFF in ROM
    [/LIST]
    Using this information, I use my hex editor to scroll for a location between 0x118000 and 0x11BFFF which has nothing but 0's:


    I'll start my code at 0x11A360 because I like to start at 0. It isn't necessary to do so, but the extra space on the 0x11A35X line makes it easier to see that custom code was added on the following line.

    Now, I'll change the existing code to jump to the line of free space where I'll start my code, and let PKSV know that I'm going to start adding script on that line as well:
    Code:
    #org 0x119DB4
    '-----------------------------------
    2jump 0x6360
    
    #org 0x11A360
    '-----------------------------------
    It's worth noting that the full pointer to my new code is 46:6360. You can use the PKSV Calculator(In PKSV, Tools -> Calculator and Notes) to figure out what the pointer to your region will look like. Enter in the ROM Address(11A360 in my case) of your free space and click on Offset2Pointer.

    Now that I have a plethora of free space, I'll just go ahead and script as I want to:
    Code:
    #org 0x119DB4
    '-----------------------------------
    2jump 0x6360 ' 0x11A360
    
    #org 0x11A360
    '-----------------------------------
    checkbit1 0x65B
    if false 0x637C ' 0x11A37A
    loadfont
    2writetext 0x63A6 ' 0x11A3A5
    closetext
    cry 0x96
    pause 0xF
    loadmovesprites
    loadpokedata 0x96 0x50
    startbattle
    returnafterbattle
    setbit1 0x067C
    disappear 0x3
    end
    
    #org 0x11A37C
    '-----------------------------------
    refreshscreen 0x0
    loadfont
    2writetext 0x638E ' 0x11A387
    closetext
    loadmovesprites
    pokepic 0x96
    pause 0xF
    closetext
    refreshscreen 0x0
    loadmovesprites
    end
    
    
    #org 0x11A3A6
    = The strange\nstatue attacked!\e
    
    #org 0x11A38E
    = It's a strange\nstatue.\e
    And here's a look at the end result:



    I may gradually add other things to this tutorial in the future.

    Other resources:
    Bulbapedia:




    <NearEDGE>


    Silly things:
    Spoiler:

     
    Last edited:

    Dr. Seuss

    Will finish GS Chronicles, I swear!
    523
    Posts
    10
    Years
  • Good tutorial, in my times I never see a tutorial aboit scripting with PKSV.

    My hints to your scripting method

    1. Is better use Johtomap, because Johtomap can add more people easy, and can decompile and edit the map scripts.

    2. The script banks are very small, I recomend make the scripts in empty banks using the commands 3writetext, 3call and 3jump (the 3-byte poiners rulez xD)

    3. Using the PKSV calculator you can make more easy the free space finding

    4. And for easy scripts I recommend use the dynamic labels (but no use if you use advances commands like 2callasm, loadmenudata, interpretmenu, etc)

    Good work!
     
    29
    Posts
    9
    Years
    • Seen Feb 3, 2020
    Good tutorial, in my times I never see a tutorial aboit scripting with PKSV.

    My hints to your scripting method

    1. Is better use Johtomap, because Johtomap can add more people easy, and can decompile and edit the map scripts.

    2. The script banks are very small, I recomend make the scripts in empty banks using the commands 3writetext, 3call and 3jump (the 3-byte poiners rulez xD)

    3. Using the PKSV calculator you can make more easy the free space finding

    4. And for easy scripts I recommend use the dynamic labels (but no use if you use advances commands like 2callasm, loadmenudata, interpretmenu, etc)

    Good work!

    Thanks!
    I don't like using dynamically allocated resources in a setting like this. Not having precise control will always bug me.
    Also, I had noticed after browsing through the script banks that none of them are ever filled to start off with. In part 6 of my tutorial I find free space at 0x11A358. The free space starts there and continues to 0x11BFFF for a total of 7328 free bytes in bank 46 alone. The PKSV can find free space of whatever size you need, but It's better to just find the free space in the bank the base script is in yourself and use that since I think there always is at least 1000 bytes free in every bank.

    I try to avoid using the 3 byte pointers for his reason. It can easily cause unexpected results especially if you jump somewhere you should instead call.
     
    Back
    Top