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

Tool: Subscript: The next step for scripting

Touched

Resident ASMAGICIAN
  • 625
    Posts
    10
    Years
    • Age 122
    • Seen Feb 1, 2018


    Subscript


    Scripting revisited.

    A subset of the Python language; scripting has never been easier




    An open-sourced, cross-platform script editor.

    Highly extensible, with a unique, easy-to-learn syntax - you'll never look back.



    Features

    • Python-like syntax
    • Proper control flow - no more gotos
    • Type recognition
    • Custom commands, written in Python
    • Less numbers - Subscript knows the names of things, even when you change them
    • Embedded emulator, with debugger
    • Syntax highlighting and code completion
    • Modules allow you to do awesome things
    • Built in assembly and C support
    • Default parameters make for less typing
    • Full documenation, generated from source
    • More to come...



    Downloads


    Subscript now has an alpha release. This means that not everything works as it should, and is subject to change without notice. Once we have an official release, however, this process will be more streamlined.

    Windows

    Extract the archive to an empty folder, add a FireRed ROM called "test.gba" to the directory, and run gui.py. Make sure you have Python 3.4.

    Linux

    Install PyGObject, GtkSourceView and GtkSpell using your packaged manager. Clone the source from the GitHub repository, add a FireRed ROM called "test.gba" to the directory, and run gui.py.

    Ubuntu

    Open a terminal and run:

    Code:
    $ sudo apt-get install python3 git python3-gi gir1.2-gtksource-3.0 gir1.2-gtkspell3-3.0 gir1.2-clutter-1.0 python3-sphinx
    $ sudo pip3 install sphinx_rtd_theme
    $ git clone https://github.com/Touched/subscript.git
    $ cd subscript
    $ make html
    $ python3 gui.py




    Credits

    Authors


    Thanks to



     
    Last edited:
    Wow, I can't believe no one has commented yet. This is very interesting.
    1) I couldn't agree more with you when you said that it's hard to find the latest version, when my computer ceased to work, and I had to switch over to this same computer I'm using to write this comment, I had to ask a friend of mine to upload his XSE, 'cause I couldn't find one on the Internet.
    2) I don't really know how bad XSE really is, 'cause I haven't ever learned other languages of any sort. I did start off with PokeScript (like most people), but I can't remember much about it, since I switched on over to XSE right after I asked for help here in the forums and someone (Spherical Ice :D) told me to switch on over to XSE. But hey, who knows, this case might be similar to Shiny Quagsire's MEH.
    3) I don't really know how to feel about this, if you do suceed, I hope the community doesn't split into XSE and *insert your program's name*. That way there wont be as much support
    4) Good luck ;)
     
    Well, this looks pretty awesome, I wish you the best of luck in this. The syntax looks pretty cool, and I really really really like this:

    given = Flag(0x828)

    and I was working on a ♥♥♥♥♥♥ program that teaches XSE XD
     
    This looks pretty intresting n_n. If I were to advice something, it would to do something similar to what PokéScript did, and instead of using Hex numbers to determine a give item or chosen Pokémon, you just use the actual names. Can't wait to see how this will end up.
     
    Wow, I can't believe no one has commented yet. This is very interesting.
    1) I couldn't agree more with you when you said that it's hard to find the latest version, when my computer ceased to work, and I had to switch over to this same computer I'm using to write this comment, I had to ask a friend of mine to upload his XSE, 'cause I couldn't find one on the Internet.
    2) I don't really know how bad XSE really is, 'cause I haven't ever learned other languages of any sort. I did start off with PokeScript (like most people), but I can't remember much about it, since I switched on over to XSE right after I asked for help here in the forums and someone (Spherical Ice :D) told me to switch on over to XSE. But hey, who knows, this case might be similar to Shiny Quagsire's MEH.
    3) I don't really know how to feel about this, if you do suceed, I hope the community doesn't split into XSE and *insert your program's name*. That way there wont be as much support
    4) Good luck ;)

    Yeah, I hope adopting this won't be too much of a problem - I (at the risk of sounding pompous) plan to make this so good that you can't NOT adopt it XD
    XSE isn't bad. It's the actual scripting "language" that is. XSE is good at what it set out to do. I'm trying to abstract the language to make it more bearable. But I do think that it might become a problem, since not everyone is a scripter, and learning another language could be too much. But hey, we'll just have to wait and see how it pans out.

    Well, this looks pretty awesome, I wish you the best of luck in this. The syntax looks pretty cool, and I really really really like this:

    given = Flag(0x828)

    and I was working on a ♥♥♥♥♥♥ program that teaches XSE XD

    Thanks. The "language" has some type awareness. That way you can write functions in such a way that they'll do different things depending on the type of the argument. I use this extensively in the control flow: when you say "if flag" the program automatically converts this to a "checkflag" and a "if2" command. That way you don't have to type all of those lengthy "comparebytetofarbyte" commands.

    This looks pretty intresting n_n. If I were to advice something, it would to do something similar to what PokéScript did, and instead of using Hex numbers to determine a give item or chosen Pokémon, you just use the actual names. Can't wait to see how this will end up.

    Yeah, I plan to do that. Maybe I'll add Pokemon as a type, so you can just say give(BULBASAUR) to give a Pokemon, and give(POTION) to give an item.

    Update:

    I've added a new feature: In-script calculations. These are done at compile time, so no extra commands are run, but now you can say things like "lastresult = Var(0x8000 + 0xD) and it'll automatically compute the answer.

    I haven't been able to do much work today - I've been at University all day. But hopefully I can get a significant portion done over the weekend. I won't promise anything, but I just might be able to release this as a command line tool soon. :)
     
    Well this is certainly a cool idea.

    One issue I would have with it is the message system. What you have going looks nice, but one thing you can do with XSE and the like is reuse various texts, movements, etc. at a single offset. So, you could have a three msgbox commands that all call the same text. How would you handle something like this? This is one of the reasons other scripting tools use the section naming method.

    Also, how do you plan on fitting goto commands in?
     
    Well this is certainly a cool idea.

    One issue I would have with it is the message system. What you have going looks nice, but one thing you can do with XSE and the like is reuse various texts, movements, etc. at a single offset. So, you could have a three msgbox commands that all call the same text. How would you handle something like this? This is one of the reasons other scripting tools use the section naming method.

    Also, how do you plan on fitting goto commands in?

    Yeah, I've thought about that. You can declare a variable, like a flag above:

    Code:
    catchphrase = Message("I say this a lot.")
    # As a message
    message(catchphrase)
    # As a question
    question(catchphrase)

    You can do that with most "types", whether they be (script) variables, flags, raw values, movements, etc.

    If you mean goto commands as in the regular sense, then I don't plan on fitting those in. All higher level languages do without them, and languages that have both goto and proper control flow (C/C++) strongly recommend that you leave them out altogether - they're unnecessary. However, I will include them, similar to the call command in the example I gave in the main post. But I don't see anyone needing them. I don't really see how I would implement them anyway.
     
    I guess I can see what you mean, but goto commands seem to be a major part of the scripting as whole. Just look at the Game Corner in FireRed, every person there uses a goto command to share a reused part of the script.

    The reason higher level languages can leave out goto commands and such is because the effect can be gained by using loops and functions and whatnot, but that isn't exactly as feasible with the scripting format used by the games.
     
    I would recommend to keep the Games' Script Engine's syntax.

    So use

    textbox (u32 text, u8 callstdevent)

    instead of

    question (u32 text). This will make things more easier if you add commands or things like that.
    A "script" could look like this:

    function:
    textbox (new String("Hello World"), 2);
    exit();

    That would be an authentic and efficient way of handling with scripts and using a good scripting language.
     
    I guess I can see what you mean, but goto commands seem to be a major part of the scripting as whole. Just look at the Game Corner in FireRed, every person there uses a goto command to share a reused part of the script.

    The reason higher level languages can leave out goto commands and such is because the effect can be gained by using loops and functions and whatnot, but that isn't exactly as feasible with the scripting format used by the games.

    I plan to add in while loops as well as functions (in fact, I'm right in the middle of doing so). However, you can still use those commands. You can call and goto as long as you know the offset. If you compile a script, you can call/goto it if you know the offset, but I'm making no plans to include dynamic offsets and such. You can just use a function, an if or while statement. However, if I think of a way to do it in Python syntax (I have to stick to it very closely, due to my implementation), I will try and add them. Maybe I can allow you to mark certain locations and jump back to them?

    Additionally, I plan to allow integration with other things. For example, I plan to include automatic ASM routine insertion. So maybe I'll allow integration with a more traditional script. But that's not for now. I first want to get a fully functional version of this first. I am making every effort to make sure no features are left out. This is designed to be a superset of the existing scripting language after all.

    I would recommend to keep the Games' Script Engine's syntax.

    So use

    textbox (u32 text, u8 callstdevent)

    instead of

    question (u32 text). This will make things more easier if you add commands or things like that.
    A "script" could look like this:

    function:
    textbox (new String("Hello World"), 2);
    exit();

    That would be an authentic and efficient way of handling with scripts and using a good scripting language.

    I don't quite understand why this is better. The thing is, these functions are created in a separate Python module - adding functions is trivial. For example, here is the implementation of the message and question functions (In Python)

    Code:
    @register
    def message(string, keepopen=False):
        out = []
        out.append(script.Command.create('preparemsg', string.value))
        if keepopen:
            out.append(script.Command.create('callstd', 4))
        else:
            out.append(script.Command.create('callstd', 6))
        return out
    
    @register
    def question(string):
        out = []
        out.append(script.Command.create('preparemsg', string.value))
        out.append(script.Command.create('callstd', 5))
        return out

    As you can see, the function implemented in Python takes the same arguments as the function in the script. If then interprets the arguments and returns real script commands that will be the commands compiled in the final script. It's trivial to add your own commands: all you do is write a function like this, and add the register decorator, and the function will be usable in a script.

    That isn't even the game engine syntax though. A message is made by preparemsg, followed by callstd. So XSE already abstracts that. All I'm doing with the question command is hiding that callstd call.
     
    Last edited:
    This looks super interesting & might be enough to actually be willing to play around with scripting overall!

    I really like the 'message' and 'question' idea -- the less numbers a new person has to memorise, the more accessible scripting becomes. I would love to see this expanded into more commands.
     
    This looks super interesting & might be enough to actually be willing to play around with scripting overall!

    I really like the 'message' and 'question' idea -- the less numbers a new person has to memorise, the more accessible scripting becomes. I would love to see this expanded into more commands.

    Well, the commands aren't really the issue - it's really easy to add more. It's more the other features of the language that are the issue, but I'm really going to try and make it as easy as possible. For example, the givepokemon command won't need all those useless (?) zeroes. Default parameters will help a lot here - I hope to start a discussion about the most commonly used default parameters for each command, as well as frequently used commands.

    UPDATE: I've just compiled my first script to hex. I'm going to decompile it in XSE soon, and see if all was compiled as expected.
    EDIT: It's working :)
    The script decompiles as expected. I just have a few more things to work out, and then I can release the source.

    Does anyone have the XSE source? I can only find single .exe files, and I really need all of it's resource files (Pokemon list, etc.). I have no idea why all the links to it have been removed.
     
    Last edited:
    XSE uses .std files for things like Pokémon and items. https://www.dropbox.com/s/3by72bwg4zocwj8/XSE.zip (Chrome reckons this is unsafe, probably because of the .exe file)

    This has all of them, as well as the source code.

    Thanks so much! That's really going to save me a lot of time. I didn't know about the .std files. My copy of XSE didn't come with them. I didn't get a safety warning, but maybe that's because I'm on Linux :/

    UPDATE: So I've altered the script in the main post slightly (with pointless changes), so you can see sample output:

    Script:
    Code:
    given = Flag(0x828)
    z = 0x800D
    LASTRESULT = Var(z)
    catch = "Are you taking good care\nof Charmander?"
    
    if given:
        message(catch)
        message(catch)
        release()
        exit
    
    question("Hello.\nSorry to trouble you.\nI can't take care\nof my Charmander.\pCan you take care of\nit for me?")
    if LASTRESULT > 0 or LASTRESULT < 5:
        givepokemon(0x4, 0x5, 0x0, 0x0, 0x0, 0x0)
        fanfare(0x13E)
        message("{black}You received a Charmander!", True)
        waitfanfare()
        closeonkeypress()
        given = True
        question("{black}Would you like to give a\nnickname to Charmander?")
        if LASTRESULT:
            call(0x1A74EB)
        message("Please take care of\nCharmander.")
    else:
        message("That's okay.\pI'm sure someone else will\ntake it.")
    
    i = Var(0x4011)
    while i < 4:
        message('Hey')
        i += 1 + 1
    
    release()
    exit

    Output:
    Code:
    Interpreted script:
    @section0:
    	Command("checkflag 0x0828")
    	Command("if1 0x01 @section1")
    	Command("loadpointer 0x00 @section3")
    	Command("callstd 0x05")
    	Command("compare 0x800d 0x00")
    	Command("if1 0x02 @section4")
    	Command("compare 0x800d 0x05")
    	Command("if1 0x00 @section4")
    	Command("goto @section5")
    	Command("compare 0x4011 0x04")
    	Command("if1 0x00 @section11")
    	Command("release")
    	Command("end")
    @section1:
    	Command("loadpointer 0x00 @section2")
    	Command("callstd 0x06")
    	Command("loadpointer 0x00 @section2")
    	Command("callstd 0x06")
    	Command("release")
    	Command("end")
    @section2:
    	b'\xbb\xe6\xd9\x00\xed\xe3\xe9\x00\xe8\xd5\xdf\xdd\xe2\xdb\x00\xdb\xe3\xe3\xd8\x00\xd7\xd5\xe6\xd9\xfe\xe3\xda\x00\xbd\xdc\xd5\xe6\xe1\xd5\xe2\xd8\xd9\xe6\xac\xff'
    @section3:
    	b'\xc2\xd9\xe0\xe0\xe3\xad\xfe\xcd\xe3\xe6\xe6\xed\x00\xe8\xe3\x00\xe8\xe6\xe3\xe9\xd6\xe0\xd9\x00\xed\xe3\xe9\xad\xfe\xc3\x00\xd7\xd5\xe2\xb3\xe8\x00\xe8\xd5\xdf\xd9\x00\xd7\xd5\xe6\xd9\xfe\xe3\xda\x00\xe1\xed\x00\xbd\xdc\xd5\xe6\xe1\xd5\xe2\xd8\xd9\xe6\xad\xfb\xbd\xd5\xe2\x00\xed\xe3\xe9\x00\xe8\xd5\xdf\xd9\x00\xd7\xd5\xe6\xd9\x00\xe3\xda\xfe\xdd\xe8\x00\xda\xe3\xe6\x00\xe1\xd9\xac\xff'
    @section4:
    	Command("givepokemon 0x04 0x05 0x00 0x00 0x00 0x00")
    	Command("fanfare 0x013e")
    	Command("loadpointer 0x00 @section7")
    	Command("callstd 0x04")
    	Command("waitfanfare")
    	Command("closeonkeypress")
    	Command("setflag 0x0828")
    	Command("loadpointer 0x00 @section8")
    	Command("callstd 0x05")
    	Command("compare 0x800d 0x01")
    	Command("if1 0x01 @section9")
    	Command("loadpointer 0x00 @section10")
    	Command("callstd 0x06")
    	Command("goto @section0+8")
    @section5:
    	Command("loadpointer 0x00 @section6")
    	Command("callstd 0x06")
    	Command("goto @section0+8")
    @section6:
    	b'\xce\xdc\xd5\xe8\xb3\xe7\x00\xe3\xdf\xd5\xed\xad\xfb\xc3\xb3\xe1\x00\xe7\xe9\xe6\xd9\x00\xe7\xe3\xe1\xd9\xe3\xe2\xd9\x00\xd9\xe0\xe7\xd9\x00\xeb\xdd\xe0\xe0\xfe\xe8\xd5\xdf\xd9\x00\xdd\xe8\xad\xff'
    @section7:
    	b'\xfc\x01\x01\xd3\xe3\xe9\x00\xe6\xd9\xd7\xd9\xdd\xea\xd9\xd8\x00\xd5\x00\xbd\xdc\xd5\xe6\xe1\xd5\xe2\xd8\xd9\xe6\xab\xff'
    @section8:
    	b'\xfc\x01\x01\xd1\xe3\xe9\xe0\xd8\x00\xed\xe3\xe9\x00\xe0\xdd\xdf\xd9\x00\xe8\xe3\x00\xdb\xdd\xea\xd9\x00\xd5\xfe\xe2\xdd\xd7\xdf\xe2\xd5\xe1\xd9\x00\xe8\xe3\x00\xbd\xdc\xd5\xe6\xe1\xd5\xe2\xd8\xd9\xe6\xac\xff'
    @section9:
    	Command("call 0x1a74eb")
    	Command("goto @section4+11")
    @section10:
    	b'\xca\xe0\xd9\xd5\xe7\xd9\x00\xe8\xd5\xdf\xd9\x00\xd7\xd5\xe6\xd9\x00\xe3\xda\xfe\xbd\xdc\xd5\xe6\xe1\xd5\xe2\xd8\xd9\xe6\xad\xff'
    @section11:
    	Command("loadpointer 0x00 @section12")
    	Command("callstd 0x06")
    	Command("addvar 0x4011 0x02")
    	Command("compare 0x4011 0x04")
    	Command("if1 0x00 @section11")
    	Command("goto @section0+11")
    @section12:
    	b'\xc2\xd9\xed\xff'
    
    Compiled script:
    Script size: 494 bytes
    2B 28 08 06 01 39 00 80 08 0F 00 73 00 80 08 09 05 21 0D 80 
    00 00 06 02 D4 00 80 08 21 0D 80 05 00 06 00 D4 00 80 08 05 
    13 01 80 08 21 11 40 04 00 06 00 CD 01 80 08 6C 02 0F 00 4B 
    00 80 08 09 06 0F 00 4B 00 80 08 09 06 6C 02 BB E6 D9 00 ED 
    E3 E9 00 E8 D5 DF DD E2 DB 00 DB E3 E3 D8 00 D7 D5 E6 D9 FE 
    E3 DA 00 BD DC D5 E6 E1 D5 E2 D8 D9 E6 AC FF C2 D9 E0 E0 E3 
    AD FE CD E3 E6 E6 ED 00 E8 E3 00 E8 E6 E3 E9 D6 E0 D9 00 ED 
    E3 E9 AD FE C3 00 D7 D5 E2 B3 E8 00 E8 D5 DF D9 00 D7 D5 E6 
    D9 FE E3 DA 00 E1 ED 00 BD DC D5 E6 E1 D5 E2 D8 D9 E6 AD FB 
    BD D5 E2 00 ED E3 E9 00 E8 D5 DF D9 00 D7 D5 E6 D9 00 E3 DA 
    FE DD E8 00 DA E3 E6 00 E1 D9 AC FF 79 04 00 05 00 00 00 00 
    00 00 00 00 00 00 00 31 3E 01 0F 00 51 01 80 08 09 04 32 68 
    29 28 08 0F 00 6F 01 80 08 09 05 21 0D 80 01 00 06 01 A3 01 
    80 08 0F 00 AD 01 80 08 09 06 05 2C 00 80 08 0F 00 20 01 80 
    08 09 06 05 2C 00 80 08 CE DC D5 E8 B3 E7 00 E3 DF D5 ED AD 
    FB C3 B3 E1 00 E7 E9 E6 D9 00 E7 E3 E1 D9 E3 E2 D9 00 D9 E0 
    E7 D9 00 EB DD E0 E0 FE E8 D5 DF D9 00 DD E8 AD FF FC 01 01 
    D3 E3 E9 00 E6 D9 D7 D9 DD EA D9 D8 00 D5 00 BD DC D5 E6 E1 
    D5 E2 D8 D9 E6 AB FF FC 01 01 D1 E3 E9 E0 D8 00 ED E3 E9 00 
    E0 DD DF D9 00 E8 E3 00 DB DD EA D9 00 D5 FE E2 DD D7 DF E2 
    D5 E1 D9 00 E8 E3 00 BD DC D5 E6 E1 D5 E2 D8 D9 E6 AC FF 04 
    EB 74 1A 00 05 0C 01 80 08 CA E0 D9 D5 E7 D9 00 E8 D5 DF D9 
    00 D7 D5 E6 D9 00 E3 DA FE BD DC D5 E6 E1 D5 E2 D8 D9 E6 AD 
    FF 0F 00 EA 01 80 08 09 06 17 11 40 02 00 21 11 40 04 00 06 
    00 CD 01 80 08 05 38 00 80 08 C2 D9 ED FF 
    Function main took: 0.0065411969990236685 seconds.

    It looks like most of its working. I've changed the returns and calls to gotos to avoid the nesting problem, and I've seemingly got most of the control flow working. It looks like I'm ready to publish, so I'll write up a quick CLI and then upload a demo :)

    ASIDE: I'm quite happy with that time. That time includes opening the script, compiling and writing the binary data to a ROM, so the utility is very speedy.
     
    Last edited:
    Hey, I have a question.
    In this begginig part:
    Code:
    given = Flag(0x828)
    z = 0x800D
    LASTRESULT = Var(z)
    catch = "Are you taking good care\nof Charmander?"

    Why is the flag set out like this:
    Code:
    given = Flag(0x828)

    And the Var isn't set out like this:
    Code:
    LASTRESULT = Var(0x800D)

    But like this:
    Code:
    z = 0x800D
    LASTRESULT = Var(z)
    ?
     
    Hey, I have a question.
    In this begginig part:
    Code:
    given = Flag(0x828)
    z = 0x800D
    LASTRESULT = Var(z)
    catch = "Are you taking good care\nof Charmander?"

    Why is the flag set out like this:
    Code:
    given = Flag(0x828)

    And the Var isn't set out like this:
    Code:
    LASTRESULT = Var(0x800D)

    But like this:
    Code:
    z = 0x800D
    LASTRESULT = Var(z)
    ?

    That's just a demonstration I believe, to show that it would accept local variables as values.

    Yeah, it's just a demonstration. You could also do something like
    Code:
    z = 0x800D
    LASTRESULT = Var(z + 10)
    if you needed it. I just made that file to test the features I've put in. But don't worry - I'll document everything when I've finished.
     
    Ah, adding those variables definitely makes things better.
    Will you also allow us to create functions?
     
    Ah, adding those variables definitely makes things better.
    Will you also allow us to create functions?

    He said it will be Open Sourced, so make what youwhat you will from that
    Also, what will the program's name be?
    TNSSE? (The next step Script Editor)
    Maybe just TNS.
    Any ideas? (not that it matters too much as of right now)
     
    Nice one, it's another way for scripting.
    Automatic asm routine insertion? WOW! I Can't wait, this saves a lot of time.
    Keep it up!
     
    Back
    Top