Your First Script
Writing your first script is scary, but all scripting is a matter of looking up little puzzle pieces and slotting them together. The more you practice, the bigger the puzzle pieces get, and the more impressive your puzzle!
Gather your Files
You'll need four things to make a scripted level:
- A text editor. Notepad is better than Word or Google Docs, but you'll have the best luck with something like Notepad++ or VSCode which understands coding syntax and colors things for you.
- A level editor that includes the AngelScript parameter on Text events. If you place a Text event in JCS and it doesn't have that parameter, replace your 207 line in JCS.ini with this line (and then reopen JCS):
207=Text |+|Trigger|Text| |TextID:8|Vanish:1|AngelScript:1|Offset:8
- A level! Here's one you can download if you want, since this guide focuses on writing a script, not on making a level.
- A script file. This should be a plain text file that meets three conditions:
- The file extension is .j2as
- In the same folder as your level
- Has the same filename (except extension) as your level.
C:\Games\Jazz2Plus\plusFirstScript.j2asforC:\Games\Jazz2Plus\plusFirstScript.j2l, orD:\Games\Jazz2\castle1.j2asforD:\Games\Jazz2\castle1.j2l\.
Call a Function
Do you know how placing a Warp event requires a Warp Target event with the same WarpID parameter? This script will feel a lot like that. Whenever the player touches a Text event, the game's attention will instantly "warp" over to the script, where you've written a hook function with the same function ID.
Let's use the classic programming example. Put this in your script:
void onFunction0() {
jjAlert("Hello world");
}
And place a Text event in your level with the following event parameters: TextID = 0; AngelScript = 1
Now run your level. When you touch that Text event, "Hello world!" will appear in the bottom left corner of the window! Congratulations—you wrote a script!
Adding Arguments
An "argument," also called a "parameter," is a number or some other information that you "pass" to a function. In our script so far, the string "Hello world" is an argument passed to the jjAlert function.
But the code you wrote—called a "hook" function because your code hooks into the game's code—can have an argument too. Let's say that you're making a Single Player level and want the player to have only one heart for a section, so they'll die in one hit. Put this in your script:
void onFunction1(jjPLAYER@ play) {
play.health = 1;
}
Can you guess what you need to do with a Text event to call onFunction1 instead of onFunction0? You need the event parameters TextID = 1; AngelScript = 1. Now when you touch that Text event, your health will be set to 1.
This works because the first argument to an onFunction# hook is the player that touched the Text event. The characters jjPLAYER@ indicate that this is a player object specifically, and then play is an arbitrary name you choose to refer to that player with, in the code. Any of the following would work just the same:
void onFunction1(jjPLAYER@ player) {
player.health = 1;
}
void onFunction1(jjPLAYER@ jazz) {
jazz.health = 1;
}
void onFunction1(jjPLAYER@ swordfish) {
swordfish.health = 1;
}
A Second Argument
Let's say that after that section, you want the player to get their health back. You could just write another hook function with play.health = 5;, but there's a better way. Try this:
void onFunction2(jjPLAYER@ play, uint8 newHealth) {
play.health = newHealth;
}
Now you can have two different Text events:
TextID = 2; Offset = 1; AngelScript 1;TextID = 2; Offset = 5; AngelScript 1;
The first event will give the player one heart. The second, five hearts. The number in the Offset event parameter is passed to the hook function's second argument.
Here are all the different possible variants of the same-numbered hook function. But don't include more than one variant per number, or JJ2+ will get confused!
void onFunction#() void onFunction#(jjPLAYER@) void onFunction#(jjPLAYER@, uint8) //a number from 0 to 255 void onFunction#(jjPLAYER@, int8) //a number from -128 to 127 void onFunction#(jjPLAYER@, bool) //either false (0) or true (not 0)
onLevelLoad
So far all your code has been wrapped in onFunction# hooks. But there are many other hooks, which are called in response to other things besides touching Text events. The same scriptwriting principle always applies: most of the function signature must be copied exactly, but you can name the arguments anything you like.
The most common is probably onLevelLoad, which runs exactly once—at the start of the level—not in response to the player doing anything. It's a good place to make permanent changes. For example, in a level that uses the Set Ambient Lighting event, you could write this, which would change the color of darkness:
void onLevelLoad() {
jjPALCOLOR darkRed(60, 0, 0); //Stands for "palette color." The three arguments are its Red, Green, and Blue components.
jjSetDarknessColor(darkRed);
}
Because that's in onLevelLoad, it will happen immediately. But you can always change it again later, for example:
void onFunction3(jjPLAYER@, uint8 blue) {
jjPALCOLOR newDarkness(0, 0, blue);
jjSetDarknessColor(newDarkness);
}
Basically anything you do in AngelScript will be reset after the level is over. In the next level, darkness will be black again! You don't need to worry about breaking the game.
Next Steps
You've done it! You've written a script. Your level writes text to the screen, changes the player's health, and uses colors for darkness. None of those are things that level designers could do in unpatched JJ2! Here's the whole thing to download so you can be sure you didn't miss anything.
Now it's time to look at JJ2+'s AngelScript API: the interface between you, the scriptwriter, and JJ2+. The API is a massive list of possible hooks (like onLevelLoad), functions (like jjSetDarknessColor), and classes (like jjPALCOLOR).
Take a look at the API documentation for the stuff you've already seen. That will give you a sense of how the API works. Then you can start looking around at other things that seem like they'd be just as easy to write, and planning out your next script!
And don't forget the manual for writing AngelScript in general, not just the stuff specific to JJ2+.