• 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.
M
Reaction score
1

Profile posts Latest activity Postings About

  • Hey there! :) I posted a reply to your question in TT&R, but for some reason it's not been approved yet. So here's a copy/pasted version in the meantime... I know how frustrating it can be when you just need an answer and no one's biting, lol.

    **

    First of all, re-editing the script to dynamically allocate its pointers is exactly what you should be doing - so nice work. Doing it any other way, as you detail in step 4a, will overwrite other parts of the code; someday I'll write a proper tutorial explaining how and why this happens, but for now, here's a quick illustration.

    Imagine each script in the map stored, in a long chain, from beginning to end. So in 4.4, you have Oak's speeches, the Pokédexes, Gary's trash-talking and so on. Each of these scripts is a different length and consequently occupies a different number of bytes - but they're all stored back-to-back, one after the other.

    That's the important part - there is no space left between scripts in a vanilla FireRed ROM. (That would be wasteful, after all.) When Game Freak created the ROM, their compiler automatically worked out how many bytes every script needed, and allocated exactly that amount of space to each one. And no more.

    Hopefully, it should now be clear why editing Oak's text to a longer string will inevitably corrupt, overwrite and generally mess up other scripts in the ROM. Since there is no additional space for extra text bytes, XSE will dutifully compile your changes over the top of an existing script. If you're lucky, this will only corrupt other person or signpost scripts, which are relatively easily fixed. If you're unlucky, you'll start messing around with map scripts, ASM code and image data, which are much harder to put right.

    So... is it impossible to extend dialog, or otherwise modify scripts? Of course not! We simply need to find somewhere else to store our new, modified script - a location currently unused by the ROM. Some free space, in other words. And we then need to tell the game to look there when executing the script, instead reading from the original location.

    This is what you've done with your new script. By adding the pre-processing directive #dynamic 0x800000, you're telling the compiler that you want it to start looking for free space, beginning at the offset 0x800000. (This is typically used because it's an easily memorable location, not because it's technically the beginning of the free space in vanilla FireRed. If you'd like to find your own space, crack open a hex editor and start hunting for FF bytes at around 0x71A500. But I digress - and 0x800000 is a perfectly good place to start storing your scripts.)

    Time for a quick terminology lesson. There are two kinds of offset you should be familiar with when scripting: dynamic, and static. Dynamic offsets begin with the prefix '@', while static offsets begin with the prefix '0x'. The difference is, essentially, that static offsets point to a pre-determined location in the ROM - for instance, 0x167F7E - while dynamic offsets are placeholder labels. In other words, they don't point anywhere... yet.

    That's why static offsets are always numerical. Since existing scripts are stored somewhere specific in the ROM, every script you decompile will be labelled with numbers, instead of text. For example:
    #org 0x168F7E
    #org 0x168FE1
    #org 0x168FF0

    These numbers (well, offsets, really) are useful because they're the actual locations of each part of the decompiled script. If you open up a hex editor and 'Goto...' the offset 0x16923E, those bytes right there are the hex symbols corresponding to each of your scripting commands.

    That said, when we're trying to read or write a script in XSE-language, static (or numeric) offsets aren't as easy to understand as dynamic offsets.

    Why? Because dynamic offsets can be anything you like - text, numbers, or a combination of both. So they can be memorable, and they can help you organise your code. For example:
    #org @start
    #org @text_1
    #org @oakSpeaks01

    This is one of the reasons it's preferable to use dynamic offsets when coding your scripts.

    But the most important reason that you'll (almost) always want to use dynamic offsets in your custom code isn't just that it makes things easier to read. It's because any and all dynamic offsets will be automatically replaced with free, unused static offsets by the compiler when you hit the button in XSE!

    This sounds magic, but in fact, all that's happening is that the compiler is doing this:
    1) Calculate length of each script segment beginning with #org @dynamic_offset_XX.
    2) Search through the ROM for free space (FF bytes) of exactly that length.
    3) Replace @dynamic_offset_XX with 0x(free space offset); for example, 0x800000.
    4) Convert XSE commands into hex bytes.
    5) Overwrite FF bytes with custom hex bytes.
    6) Output list of offsets where custom code was stored, ready to be copied and pasted with AdvanceMap.

    You could do this yourself with a copy of FSF, if you had the patience - going through each label, calculating the byte length of each section using the Command Help in XSE, and typing that into FSF manually. But it takes far too long to be practical - and sooner or later, you'd end up making a mistake. XSE's compiler is super-fast, by contrast, and it doesn't screw up.

    There's another, incredibly useful feature which only works with dynamic offsets, too. XSE's pre-processing directive #clean will automatically remove the last dynamically-compiled script from the ROM - working exactly like an Undo button in any other program!

    In other words, it'll replace everything it just compiled with FF bytes again. So, if you mess something up, you haven't wasted any space. You can just clean your old script off the ROM, and compile the modified one in its place!

    tl;dr: static offsets are bad unless you know exactly what you're doing. Dynamic offsets ae easier to understand, safer to use and can be erased at will.

    **

    ...And all of that said, none of that is technically your problem here.

    XSE is a great tool - but it never officially left beta. As such, there are a few quirks and bugs here and there. (Remarkably few, actually; it's one of the most solid hacking tools you'll ever come across. And paired with DavidJCobb's scripting reference, diego's Mega Huge tutorial and Spherical Ice's level script documentation, you'll have essentially everything you need to code whatever you like.)

    But XSE does *not* decompile level scripts properly every time. Which is what's going wrong here.

    Here's the section of your code that's bugging out:

    #org @168FF0
    #raw word 0x4055
    #raw word 0x1
    #raw pointer @16923E
    #raw word 0x4055

    But first, let's step through the code to see what's going on here (and what's going wrong).

    Incidentally, this is the first method I recommend for debugging a misbehaving script. Step through each line of code, commenting what it does as you go.

    If you find yourself confused or unsure what a particular line is doing, chances are you've found your problem. (Or at least, you've found something to research, and you have a starting point for your bug hunt.)

    So...

    #dynamic 0x800000 // start looking for free space at 0x80000.

    '---------------
    #org @168F7E // this section of code is a header; it contains pointers to lists, one for each different type of level script
    #raw 0x3 // this map contains type 3 level scripts
    #raw pointer @168F8E // pointer to our type 3 level script handler
    #raw 0x4 // this map contains type 4 level scripts
    #raw pointer @168FE1 // pointer to our type 4 level script handler
    #raw 0x2 // this map contains type 2 level scripts
    #raw pointer @168FF0 // pointer to our type 2 level script handler
    #raw 0x0 // end of map level scripts

    '---------------
    #org @168FE1 // list of type 4 level scripts
    #raw word 0x4055 // which variable will the game check?
    #raw word 0x1 // if the variable is 0x1, jump to and execute the following pointer
    #raw pointer @168FEB // type 4 level script pointer
    #raw word 0x0 // end of type 4 scripts

    '---------------
    #org @168FF0 // list of type 2 level scripts
    #raw word 0x4055 // which variable will the game check?
    #raw word 0x1 // if the variable is 0x1, jump to and execute the following pointer
    #raw pointer @16923E // type 2 level script pointer
    #raw word 0x4055 // which variable will the game check?

    '---------------
    #org @168F8E
    [...]

    And, you see, we've already found our problem. After checking for a variable, the code appears to stop abruptly - which will break the script execution, as you discovered.

    Why does it stop? Because XSE has failed to decompile everything it found at the pointer 0x168FF0. (This isn't your fault and it's a tricky little bug to spot.) But if XSE won't decompile the whole script, what can we do?

    Brute-force it, of course! Break out HxD and go to the offset 0x168FF0... and here's what you'll see.

    55 40 01 00 3E 92 16 08 55 40 07 00 02 90 16 08 00 00

    We can translate this fairly easily; the bytes are all raw, which means they correspond directly to the hex codes you'll find in your editor. And they've just been flipped:

    #raw word 0x4055
    #raw word 0x1
    #raw pointer 0x816923E
    #raw word 0x4055
    #raw word 0x7
    #raw pointer 0x8169002
    #raw word 0x0

    So now we have our full, decompiled script. (We know to stop at the 00 00 mark because the game terminates its lists of level scripts with two 00 bytes - a raw word of 0x0.)

    Here's my version of your code, cleaned up a bit and with handier labels for your dynamic offsets. Compile this, stick it in the Map Script Offset field in Advance-Map, and you should be ready to roll!

    #dynamic 0x800000
    #include stdpoke.rbh
    #include stditems.rbh
    #include stdattacks.rbh
    #include stdmove.rbh

    '---------------
    #org @level_scripts_master_header
    #raw 0x3
    #raw pointer @list_of_type_3_scripts
    #raw 0x4
    #raw pointer @list_of_type_4_scripts
    #raw 0x2
    #raw pointer @list_of_type_2_scripts
    #raw 0x0

    '---------------
    #org @list_of_type_4_scripts
    #raw word 0x4055
    #raw word 0x1
    #raw pointer @type_4_script_01
    #raw word 0x0

    '---------------
    #org @list_of_type_2_scripts
    #raw word 0x4055
    #raw word 0x1
    #raw pointer @type_2_script_01
    #raw word 0x4055
    #raw word 0x7
    #raw pointer 0x8169002 // this is your @type_2_script_02. It's a little long to decompile here, but feel free to plug 169002 into XSE if you want a look!
    #raw word 0x0

    '---------------
    #org @list_of_type_3_scripts
    setflag 0x2CF
    compare 0x4055 0x1
    if 0x1 call @type_3_script_01
    compare 0x4055 0x7
    if 0x1 call @type_3_script_02
    compare 0x4055 0x8
    if 0x1 call @type_3_script_03
    checkflag 0x247
    if 0x1 call @type_3_script_04
    end

    '---------------
    #org @type_4_script_01
    spriteface 0xFF 0x2
    end

    '---------------
    #org @type_2_script_01
    lockall
    textcolor 0x0
    applymovement 0x4 @oak_m1
    waitmovement 0x0
    hidesprite 0x4
    movesprite2 0x4 0x6 0x3
    spritebehave 0x4 0x8
    clearflag 0x2B
    applymovement MOVE_PLAYER @player_m1
    waitmovement 0x0
    applymovement 0x8 @gary_m1
    waitmovement 0x0
    clearflag 0x4001
    playsong2 0x0
    fadedefault
    msgbox @t1 MSG_KEEPOPEN '"[rival]: Gramps!\nI'm fed up with ..."
    closeonkeypress
    pause 0x3C
    msgbox @t2 MSG_KEEPOPEN '"OAK: Hmm? [rival]?\nWhy are you he..."
    closeonkeypress
    pause 0x1E
    applymovement 0x8 @gary_m2
    waitmovement 0x0
    msgbox @t3 MSG_KEEPOPEN '"[rival]: Hey! Gramps!\nWhat about ..."
    msgbox @t4 MSG_KEEPOPEN '"OAK: Be patient, [rival],\nI'll gi..."
    setvar 0x4055 0x2
    releaseall
    end

    '---------------
    #org @type_3_script_01
    movesprite2 0x4 0x6 0xB
    spritebehave 0x4 0x7
    playsong2 0x12E
    return

    '---------------
    #org @type_3_script_02
    movesprite2 0x4 0x6 0xB
    spritebehave 0x4 0x7
    return

    '---------------
    #org @type_3_script_03
    setvar 0x4055 0x9
    return

    '---------------
    #org @type_3_script_04
    setflag 0x24F
    return


    '---------
    ' Strings
    '---------
    #org @t1
    = [rival]: Gramps!\nI'm fed up with waiting!

    #org @t2
    = OAK: Hmm? [rival]?\nWhy are you here already?\pI said for you to come back\nlater[.]\pAh, whatever! Just wait there.\pLook, [player]! Do you see that ball on\nthe table?\pIt's called a POKé BALL. It holds\na POKéMON inside.\pYou may have it! Go on, take it!

    #org @t3
    = [rival]: Hey! Gramps!\nWhat about me?

    #org @t4
    = OAK: Be patient, [rival],\nI'll give you one later.


    '-----------
    ' Movements
    '-----------
    #org @oak_m1
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0xFE 'End of Movements

    #org @player_m1
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0x11 'Step Up (Normal)
    #raw 0xFE 'End of Movements

    #org @gary_m1
    #raw 0x2E 'Face Up (Delayed)
    #raw 0xFE 'End of Movements

    #org @gary_m2
    #raw 0x26 'Step on the Spot Up (Faster)
    #raw 0x26 'Step on the Spot Up (Faster)
    #raw 0xFE 'End of Movements

    **

    Hope that helps! If you have any questions, just shout...
  • Loading…
  • Loading…
  • Loading…
Back
Top