View Single Post
Old July 25th, 2011 (9:56 PM). Edited July 27th, 2011 by DavidJCobb.
DavidJCobb DavidJCobb is offline
Join Date: Jul 2010
Gender: Male
Nature: Lonely
Posts: 275
Eh, this is my first tutorial. It could be a bit... rough. Here, I'll demonstrate two things: how to create a number input in-game using a PokeMart, and how to dynamically compile and execute a script at run-time.

The two techniques are a bit off the beaten path when it comes to scripting, but they can be quite useful at times. I didn't see any similar tutorials or descriptions on the site (and I Googled extensively -- I did not want to have to figure this out for myself! XD), so I figured I'd write one.

* * * * *

There are probably lots of situations where you'd like the ability to input some number into a game at run-time for debugging purposes, to test something. For example, you may want...
  • To be able to obtain any item at run-time, without needing to use cheat codes with your emulator
  • To create a music-switcher, to see if a given track "fits" with your map without having to go back and forth between AdvanceMap and your emulator
  • To warp to any map in the game, without having to create a test warp, etc..
Well, I came up with a script for the second one of those -- the ability to pick a track in the game and play it, using a short and concise XSE script. No branching paths for every single track. Just a simple, elegant number-input thing and some irresponsible and probably-dangerous scripting devilry.

Let's take a look at the techniques used in that script, and see how we can generalize them.

Problem: How do we input a number in-game?
This would seem to be a tricky one. Is there a quick, easy way to let a player input a number using one of the game's built-in GUI widgets? Not necessarily.

The very first thing my mind jumped to was using the PokeMart selector -- the thing that lets you choose how many of an item you want to buy. Unfortunately, no special allows you to use it directly, and JPAN's Hacked Engine, despite being epicawesome, does not contain any custom specials for the purpose.

So I figured, if we can't use part of the PokeMart functionality, why not just use the whole thing?

Solution: Use a debug item's quantity as our input.
We are going to reserve an item for use as a "debug selector". When you need to input a number, you call a PokeMart script in which only this item is available for purchase. The number you buy represents your numerical input, in decimal.

My ROM uses the item with index 0x36. It was originally an unused item with the name "????????"; I renamed it to "DebugSelector" and set the price to $1. Here is a rudimentary script for generating our selection box (in this tutorial, it will power a music-picker, hence the message shown in the script):

#dynamic 0x800000

#org @start
removeitem 0x36 0xFF // make sure we have none of the item to start with
msgbox @explanation 0x2
pokemart @pokemart
setvar 0x8010 0x00 // explained later
goto @counter_tens // next part of the script

#org @explanation
= Use the Pokemart to select a song.\nNo. item bought = dec. ID - 256

#org @pokemart
#raw word 0x36
#raw word 0x0

When that script runs, you will be shown a PokeMart in which only the "DebugSelector", priced at $1, is available for purchase. You select a number by simply purchasing that many DebugSelectors. Upon exiting the PokeMart (choosing "SEE YA!"), we move to the next part of the script, where we check how many of the item you purchased.

Selecting a number using the PokeMart.

Problem: How do we check the item quantity?
Thing is, the "checkitem" function lets us check for a certain amount, but we can't check the amount. We can check, for example, if the player has 10 or more (or less) of an item... But we can't, for example, store the quantity of a given item in a variable directly. Which is a problem, because that's exactly what we need to do.

Solution: Recursively decrease the item quantity and increment a variable.
The inelegant way to do it would be to have a checkitem for every item quantity we would accept. checkitem 0x36 0x1, checkitem 0x36 0x2, checkitem 0x36 0x3... That could be a LOT of checkitem calls.

Alternatively, we can create a loop with a counter. Take a look at this:
#org @counter_tens
checkitem 0x36 0xA
compare LASTRESULT 0x1
if 0x0 goto @counter_ones
removeitem 0x36 0xA
givemoney 0xA 0x00 // reimburse player
addvar 0x8010 0xA
goto @counter_tens

#org @counter_ones
checkitem 0x36 0x1
compare LASTRESULT 0x1
if 0x0 goto @counter_done // next part of the script
removeitem 0x36 0x1
givemoney 0x1 0x00 // reimburse player
addvar 0x8010 0x1
goto @counter_ones

If you were wondering what 0x8010 was in the first part of the script, wonder no more: it's our counter variable. When we're done, it will hold the amount of the item we (used to) have.

We start by checking if the player has ten or more of the item. If they do, we remove ten of that item, add ten to our counter, and run the check again. (We also reimburse them for the cost of ten of that item -- $10, in this case.) The routine runs itself over and over until the player has nine or fewer of the item.

After that, we do the same thing, but for ones instead of tens. Once this routine finishes, the player will have 0 of the item, and our counter will tell us how many they had purchased. We now have our numeric input. You can pass it to whatever function you wish.

The game echoes back the number we selected in decimal format.

Problem: USING the selected amount. In this case, to pick a music track.
But how do we put that amount into use? Most script functions only accept raw values, not variable references. How do we use the value of a variable to select a song ID for playsong, or an item ID for giveitem, or a Pokemon ID for givepokemon, or something else?

Simple: we are going to do something that is (comparatively speaking) overcomplicated and unconventional. We are going to recreate a tricky little function seen in some formal programming languages.

Solution: Enter "eval".
"Eval" is a function seen in some programming languages. It allows you to take a string of text and treat it as code. It's a lot like compiling a script at run-time, modifying the commands as necessary -- which is exactly what we're going to be doing here. FireRed has a "call" function that allows us to take any offset in RAM, treat it as if it were a script, and execute it. We are going to use the offset of the LASTRESULT (0x800D) variable, by writing our instructions to it and to other variables. We're pretty much recreating "eval" by hand, in Pokemon FireRed.

Here's the script for compiling an eval script that will play the song with the number we selected earlier (plus 0x100). Explanatory code comments are provided.

Note: The next post details a safer, but much more complicated, method. If you are very good at scripting, then I advise you adapt the technique described in the next post rather than using the method I am about to describe in this post. If, on the other hand, you're a novice or intermediate scripter, then feel free to use the below code -- just remember to keep your eval scripts small.

#org @counter_done
 I don't remember if you can hold more than 
 99 of an item in FireRed. So we add 0x100 
 (decimal 256) to the number you chose.

 24 DebugSelects (0x18) become 0x0118, the 
 ID of the Lavender Town track. 0 of the item 
 (backing out without buying anything) become 
 0x0100, the RS Pokecenter Healing tune.

 So this next line is just some value-editing for 
 music selection. If you're using this for 
 something other than music, exclude this line:
addvar 0x8010 0x100
 Here, we echo the chosen number back to the 
 player to make sure that all went well:
buffernumber 0x0 0x8010
msgbox @echo_selection 0x2
 Here, we begin writing our script. 0x33 is 
 the hex for the "playsong" command.

 We're writing our eval script into the 
 RAM that is used for variables. I couldn't 
 think of any other safe location in RAM to 

 You could probably use setvar and copyvar, 
 and doing so would be more "friendly", but 
 writebytetooffset will work as well.

 Anyway, playsong:
writebytetooffset 0x33 0x020370D0
 Here, we copy the value of 0x8010, our 
 entered number, into the script. We're 
 passing that value to playsong.

 The values are "backwards" in RAM, i.e. 
 a variable with 0x1234 in it is stored as 
 34 12. But some (or all?) commands take 
 their values in reverse, as well, so in 
 this case we don't need to de-reverse the 
copybyte 0x020370D1 0x020370D6
copybyte 0x020370D2 0x020370D7
 Next, the filler byte that playsong 
writebytetooffset 0x00 0x020370D3
 0x03 is the "return" command:
writebytetooffset 0x03 0x020370D4
 0xFF marks the end of the script:
writebytetooffset 0xFF 0x020370D5
msgbox @about_to_script 0x2
 We call the script:
call 0x020370D0
 And that's it! It's done!
msgbox @confirm_playing 0x2

#org @echo_selection
= You selected: [buffer1]

#org @about_to_script
= Script written to variables. About\nto execute.

#org @confirm_playing
= Now playing: [buffer1]

And here is the script that we actually compile at run-time, where XXXX is the value that we insert into it at run-time. In other words, this is what we're creating with all those writebytetooffset and copybyte lines:
playsong XXXX 0x00

You can use this to pass the value of any variable into any function that takes only values. Playsong, warp, additem, giveitem, givemoney, multichoice (for different numbers of options, if using JPAN's hacked engine)... Very useful for debugging-specific numeric inputs, and I can vaguely see a few uses for it in a finished ROM as part of a publicly-accessible script.

The only thing you have to remember is that when you create the eval script, you are overwriting variables' values with the script data. Make absolutely certain that you don't overwrite any variables that you need to use. (For the same reason, you don't want to use these variables in the eval script... Unless you want portions of the script to overwrite themselves. I'm not sure how that would turn out, but I don't think it would end well.)

There you have it: a script for inputting a number via the PokeMart, and a script for dynamically compiling and executing a new script (allowing you to pass variables into functions that would not normally accept them). Piecing the two together is an exercise left up to the reader.

If you have any questions, feel free to post and I'll try to answer to the best of my ability. If there's anything I could have explained better, please let me know. (I'm aware that this tutorial is... dense. I just don't know how to improve it; I'm hoping that asked questions would give me an idea of where to revise things.)
Reply With Quote