## Introduction

Arguably one of the biggest advances JJ2+ brings to JJ2 is the introduction of AngelScript. The AngelScript website describes it as "an extremely flexible cross-platform scripting library designed to allow applications to extend their functionality through external scripts." What this means for you as a level designer is that you can write code in a C/C++-like scripting language—anywhere from two or three to potentially hundreds or thousands of lines—that will directly alter the level, characters, and other objects within JJ2. AngelScript lets you change tiles without setting up trigger scenery, change the palette without cycling to a new level with a different tileset, force players to be Spaz without setting up elaborate systems of springs and sucker tubes, create enemies from nowhere, alter or totally replace individual ammo types, load new graphics or sounds from external files, transmit arbitrary sequences of data among players in online servers, and so much more.

(Note, however, that JJ2's AngelScript implementation is primarily local in its domain, and if you are writing code for a multiplayer level, you will need to be careful to find ways to sync gameplay experiences among clients. A few properties are naturally server-global, such as the jjTeamScore properties or the current game mode or various properties for configuring how your local players are drawn, but most other code—individual items, bullets, graphical effects, etc.—will need to be configured more carefully, in particular using the jjSTREAM network communication class. (Of course, none of this is not a concern for Single Player levels.) Fortunately, everyone in the server will be running all the same AngelScript code, making for somewhat more equal experiences than one might otherwise expect.)

This document is not intended teach you the syntax of AngelScript, which has a dedicated online manual for this very purpose. It should however be noted that JJ2's AngelScript implementation includes the string, array, math, and dictionary modules.

First, though, you need to know a) how to call AngelScript functions within JJ2 and b) how to change things within JJ2 once a function has been called. To begin with, open an old level or begin a new level that you would like to add some AngelScript functionality to, and take note of the filename (e.g. `myLevel.j2l`). Next create a plain text file (in Notepad or some similar editor, or a code syntax highlighter program that supports AngelScript such as Notepad++ extended with Sir Ementaler's XML files) and save it to your JJ2 directory, giving it the same filename as the level but with the extension .j2as instead of .j2l. Thus: `myLevel.j2as` for `myLevel.j2l`, `castle1.j2as` for `castle1.j2l`, `plusPalettes.j2as` for `plusPalettes.j2l`, etc. This new .j2as file is where all the code for this particular level will live, unless you load additional files using the #include statement or various mutators.

Next you will need to define one or more functions in the .j2as file for JJ2+ to call. Let's say that you're making a Single Player level and want to change the level's music, but not to either boss.j2b nor boss2.j2b, which are the only filenames the Activate Boss event would allow you. With AngelScript, this couldn't be simpler. Write the following in myLevel.j2as:

    void onFunction0(jjPLAYER@ player) {
    	jjMusicLoad("tubelec.j2b");
    }

Finally you need to set up to the level to call onFunction0. For this, you will need to edit your JCS.ini to include the following entry for 207 in the [Events] group:

    207=Text |+|Trigger|Text| |TextID:8|Vanish:1|AngelScript:1|Offset:8

The boolean parameter "AngelScript," when set to 1, will call a hook function from within the level's corresponding .j2as file. Which function it calls is determined by the "TextID" parameter, which ranges from 0–255. TextID=3,AngelScript=1 will call onFunction3; TextID=150,AngelScript=1 will call onFunction150; and so on. In this case, each time a local player touches a Text event with TextID=0,AngelScript=1, JJ2+ will try to load and play the music file "tubelec.j2b."

The jjMusicLoad function is actually something of a special case, because it will not load a new music file if the file requested is already the currently playing music file. Thus hitting a Text event with TextID=0,AngelScript=1 multiple times will not have any undesirable effect like restarting the music. However, plenty of other functions do have cumulative effects, and it is not always desirable for a player to call the same function more than once, e.g. if there's a whole column of identical Text events to ensure that the player hits at least one of them while passing through an area. This is where the boolean "Vanish" parameter comes in handy. Unlike when AngelScript=0, when AngelScript=1, Vanish=1 disables the entire onFunction# function from being called by any Text events anywhere in the level. You can reenable, for example, onFunction5 by writing `jjEnabledASFunctions[5] = true;` in another AngelScript function or by calling the global function jjEnableEachASFunction, but unless you do one of those things, the function will remain disabled for the rest of the level. (It is still possible to call a disabled function directly from another function in the .j2as file, but not from another Text event.)

Finally the "Offset" parameter of Text events provides an optional one-byte parameter to the AngelScript function. The function for a given TextID, e.g. 5, can be defined one of four different ways, and in each case the "Offset" parameter is parsed accordingly. Note that if the AngelScript parameter is of type int8—a signed 8-bit integer—then Offset will be interpreted as a JCS parameter of type -8, instead of type 8. Note also that if you define multiple AngelScript functions with the same name, JJ2+ will not know which one to call. This is undefined behavior and the result may vary across JJ2+ releases, so don't do it.

    void onFunction5(jjPLAYER@ player) {}
    void onFunction5(jjPLAYER@ player, bool paramName) {}
    void onFunction5(jjPLAYER@ player, uint8 paramName) {}
    void onFunction5(jjPLAYER@ player, int8 paramName) {}

The contents of your .j2as file must all be syntactically correct AngelScript. Any error in or outside of any function will prevent any and all AngelScript from running in the level. To get information about such errors, you will need to add a line `AngelscriptDebug=True` to the [General] group of plus.ini (AngelscriptDebug defaults to false), and information about errors and warnings will then be displayed in the chatlogger window. It is also possible to send your own debugging information using the global function jjDebug(string text, bool timestamp = false), though, as with any other AngelScript function, it will only work if everything is formatted correctly.

Besides this, there are a number of hook functions which, if defined in a .j2as file, will be called at various points. More may well be added in subsequent releases, but here are the basic ones available so far (a few more are described in the jjCANVAS section below; onObjectHit is described in the Custom Objects section, and onReceive in the description for jjSendPacket).

void onChat(int clientID, string &in stringReceived, CHAT::Type chatType)
bool onLocalChat(string &in stringReceived, CHAT::Type chatType)
  onChat is called whenever a chat message pops up in game. onLocalChat is called only when chat is received from players on the same machine the script is executing on. A return value of true indicates that the chat message should be suppressed, whereas a return value of false will cause the message to be handled normally. clientID is a unique ID of the game client that sent the chat message. stringReceived is the text of the chat message that was received. chatType can take one of three values: NORMAL, TEAMCHAT, and WHISPER.
  Any message beginning with "/" is interpreted as a command, not as chat, and so will not be passed to either of these hooks. Messages beginning with "!" will, though.
bool onCheat(string &in cheat)
  This function is called in single player mode when a player attempts to enter a cheat code. It will be called once for each letter appended to the cheat code after jj. For example, in typing jjgod, onCheat will be called three times, with cheat equal to "jjg", "jjgo" and "jjgod". A return value of true indicates the cheat should be suppressed, whereas a return value of false will cause the cheat to be handled normally. If you want to compare cheat to the full list of standard cheat codes, use jjIsValidCheat, which will be updated if JJ2+ adds or changes any cheat codes in future releases.
void onLevelLoad()
  This function is called once, at the beginning of the level, and never again. If your level's tileset has a red textured background but you want a blue one, this is the place to change its colors. If you want the level to start with water at a certain height without placing Water Level events all around every start position, this is the place for that. And so on.
void onLevelBegin()
  Also called only at the beginning of the level, but a little bit later, once things have had time to be initialized. For example, onLevelLoad is fired before any jjOBJs have been created and before certain jjPLAYER properties have been initialized, so onLevelBegin allows you to modify those things as well.
void onGameStart()
void onGameStart(bool firstTime)
void onGameStop()
void onGameStop(bool firstTime)
  In multiplayer games, these functions are called whenever the game gets started or stopped. The firstTime parameter, if defined, is intended to capture starts/stops resulting from the `/autostart` command, so it should be true for the game being started or stopped at the beginning of the level, and false thereafter. However, it will also be true for clients who join the level partway through.
void onLevelReload()
  This function is only ever called in Single Player, where it is called every time the player dies, after everything in the level has been reinitialized. Since a great number of things get reset in Single Player when the player dies, this is your chance to make sure some things stay the same.
void onMain()
  Unlike the above functions, which are very rare, this function is called absolutely constantly. It is important to understand that JJ2 measures time in "ticks," which number 70 to a second. (Correspondingly, when setting the duration for a sugar rush or somesuch, you will need to multiply by 70 to get the desired number of seconds.) The global property jjGameTicks will tell you how many ticks have elapsed at any given point. onMain is fired once per tick, and allows you to check constantly whether a given property has changed, move an object in a circle, or whatever else you find desirable.
void onPlayer(jjPLAYER@ play)
  This function can be called even more often than onMain: it is called once per tick per local player. If you're playing splitscreen with two players, for instance, it will be called two times per tick. This is a useful distinction because of the jjPLAYER@ parameter, which points to the local player every time onPlayer is called. If you want to prevent blaster from ever being used in a level, for instance, `void onPlayer(jjPLAYER@ play) { play.noFire = (play.currWeapon == WEAPON::BLASTER); }` is one way of doing that, whereas trying to do the same in onMain would at minimum require manually looping through the jjLocalPlayers array. To check which jjPLAYER is being invoked during any given onPlayer, use the `jjPLAYER.localPlayerID` property.
  (JJ2+ used to encourage use of a global property p (or occasionally jjP), which performed the same function as the jjPLAYER@ argument. This coding style still works in certain cases, to ensure backwards compatibility with older scripts, but can be unclear and is deprecated/stylistically discouraged.)
void onPlayerInput(jjPLAYER@ play)
  This function works essentially the same way as onPlayer, and is called very slightly earlier in the tick, but with a caveat: it is only called when the player's input properties, keyRight and keyJump and so on, have had the potential to change. In particular, it is not called while chatting in a multiplayer game. Because of this unpredictability, it should only be used for situations specifically relating to input, for instance swapping the effects of a player's right and left keys.
void onPlayerTimerEnd(jjPLAYER@ play)
  This function is something of a special case. By default, when a Player Timer runs out (see the jjPLAYER section below), this function will be called for the player whose timer just ran out. However, a single level may have multiple timer sections with different purposes -- one where you die for not doing something quickly enough, one where you warp after a certain delay, and so on -- so it is also possible to change the function called using the jjPLAYER method timerFunction. Thus onPlayerTimerEnd is simply the default function name, not the only one you are allowed to use for this purpose.
void onRoast(jjPLAYER@ victim, jjPLAYER@ killer)
  This function is called every time a player is roasted, including non-local players, including self-inflicted deaths from pits and other environmental hazards (in which case `victim is killer` will be `true`). Note however that not every death counts as a roast, e.g. deaths from the `/frustration` command or from unspectating.

The rest of this document is divided into three sections. First there are sections for all the available classes that you may create and manipulate, and also full lists of their properties and methods. Second are two lists, one of global properties and one of global functions. Like the classes, all global properties and functions begin with the letters "jj," with the sole exceptions of the deprecated p and the hook functions, all of which begin with the letters "on." This is a guarantee of backwards compatibility from future versions of JJ2+. While new global properties and functions will undoubtedly be added to AngelScript in future releases of JJ2+, as long as you don't begin any of your own properties and functions with "jj" or "on," there will be no naming conflicts and your scripts will continue to function correctly.

Finally there are a series of different appendices containing instructions for specific topics, followed by a sixth which contains the various enums used by certain properties, methods, and functions. Each enum is nested within a namespace, often unique to that enum, and both the name of the namespace and all the values of the enum are in all caps. Such enums may be the sole options for the properties and parameters that use them. For example, to configure the level so that water and ambient lighting can coexist, you will need to set the global property jjWaterLighting to WATERLIGHT::GLOBAL. You do not set it to 1 (a number), or GLOBAL (a variable that doesn't exist in the main namespace), or "GLOBAL" (a string), or CHAR::JAZZ (another enum value), etc. As well, certain arrays are indexed by enums rather than (or as well as) by integers; for example, you access a player's number of red gems through `jjPLAYER.gems[GEM::RED]`, not `jjPLAYER.gems[1]`, although `jjPLAYER.ammo[WEAPON::BOUNCER]` and `jjPLAYER.ammo[2]` are both acceptable. Depending on the commonality of an enum and the number of different values it has, either its values will be listed whenever it comes up in a property/method/function or they will be listed in full in the appendix at the bottom of this file. Like any other data type, enums may be used as parameters in your own defined functions, so long as you remember to attach the namespace to the enum name.

It is perfectly understandable if much of this seems overwhelming at first. You are advised to look through the example levels provided in your JJ2+ download, many of which include examples of AngelScript in action. Even if the specific properties being affected in a given level do not interest you, that level can still serve as an example as how AngelScript is written generally. The Jazz 2 Online website additionally hosts an AngelScript Snippets section, where people share pieces of generally useful (and hopefully well-commented) code. And if all else fails, head to the JazzJackrabbit Community Forums to ask for help. Good luck!

## Classes

### class jjPLAYER

If you're writing some AngelScript for Jazz Jackrabbit 2, one of the most natural things to want to affect is Jazz Jackrabbit himself! Or Spaz, or Lori, or so on, as the case may be. Many hook functions—all the onFunction# functions, onPlayer and onPlayerInput, all the onDraw# functions, and so on—include `jjPLAYER@` parameters so that you know which (local) player you should be affecting within a given chunk of code.

In some cases you'll want access to some player other than the one who triggered a function. For this, there are two global arrays: jjPlayers[32] and jjLocalPlayers[4]. While JJ2/AngelScript shouldn't actually crash if you try to access a player that doesn't exist in the game, it may not give you the most useful results. Thankfully, jjPLAYER objects have boolean isActive and isLocal read-only properties to help you figure out which jjPLAYER objects to affect with a given section of code.

(Of course, since AngelScript is predominantly local in its domain, the `isLocal==false` jjPLAYER objects are mostly only useful for reading values, not writing, and even then only a few of those values—xPos, yPos, currWeapon, and so forth—will actually be locally accurate. Trying to set, say, jumpStrength for a non-local jjPLAYER won't do you any good. When you encounter a need to change properties of other players, this is possible to achieve with use of custom packets; for that you will need the jjSTREAM class and the jjSendPacket function.)

For the most part, properties and methods of jjPLAYER objects are familiar from regular JJ2, or at least can be easily understood. What merits discussion is a new feature known as the Player Timer. Put loosely, it is a player-specific on-screen countdown which, once its time runs out, calls a certain AngelScript function (usually onPlayerTimerEnd), passing as an argument the player that it belonged to. The most important jjPLAYER methods are timerStart and timerFunction, which respectively begin the Player Timer (and set how long it will last) and set the effect of the Player Timer running out. Explaining the Player Timer in detail is not the function of this document, however, so see `plusTimerEx.j2l/.j2as` for a fuller explanation and examples.

bool alreadyDoubleJumped
  Whether it is currently impossible for the player to double jump, assuming they're Spaz and currently in the air. To allow compatibility with multiple air jumps, this was replaced with doubleJumpCount.
int ammo[WEAPON::Weapon]
int ammo[9]
  How much ammo the player has of each ammo type. Possible constants appear in the appendix below, or you may use simple 1-indexed numbers instead (all values besides 1-9 will evaluate to WEAPON::CURRENT).
  Note that JJ2+ prevents the use of weapons without corresponding +3/+15/powerup events in the level while in online servers. To remedy this, hide some such ammo-providing event somewhere in the level, or else set their allowed properties to true in the jjWeapons array.
bool antiGrav
  Whether the player falls up (true) or down (false). Also causes the player to be drawn upside-down.
  (For the record, this mode is still a work-in-progress. A handful of objects—bridges, pinball flippers, and rotating poles—don't work very well with it yet. However, future revisions will be backwards compatible with what works already, so don't hold off on using it for that reason unless it truly can't do yet what you need it do.)
int ballTime
  If greater than 0, how much longer (in ticks) the player will be tucked into a ball.
int blink
  If greater than 0, how much longer (in ticks) the player will be blinking and invincible and unable to collide with other players, as if recently hurt. Works online.
int boss
  The object ID of the jjOBJ whose energy is shown in the boss health meter, or 0 if inapplicable.
  More specifically, the boss health meter will show the energy of the jjOBJ as a percentage of its initial health as described in its entry in jjObjectPresets. If you create a Tuf Turtle enemy, give it 100 health, and assign a jjPLAYER's boss its object ID, JJ2 will still assume the Tuf Turtle started out at 4 health, not 100. On the other hand, if you write `jjObjectPresets[OBJECT::TUFTURT].energy = 100;` first, then the boss health meter will work as you might want it to.
bool bossActivated
  The bool set by the Activate Boss event or the activateBoss method. In general, you should make sure this property is true for at least one local player when writing behaviors for custom bosses.
int buttstomp
  Buttstomp phase. Values below 41 mean the player is initializing a buttstomp, 41 means the player is currently buttstomping, values between 41 and 121 mean the player is landing on the ground and values of 121 and higher mean the player is not currently buttstomping.
const float cameraX
const float cameraY
  The top left corner of the player's current view of the level, as measured from the top left corner of layer 4.
const CHAR::Char charCurr
  The player's current character (CHAR::JAZZ, CHAR::SPAZ, CHAR::LORI, CHAR::BIRD, CHAR::FROG or CHAR::BIRD2). This is a read-only value, because you should use morphTo or another related method to change it instead.
CHAR::Char charOrig
  Which character the player began the level as (CHAR::JAZZ, CHAR::SPAZ, or CHAR::LORI), aka the character that the Revert Morph event switches them to.
const int clientID
  ID of the game instance controlling the player. This is particularly useful for packet exchange with use of jjSendPacket but can also be used to determine whether players are splitscreeners.
int coins
  How many coins the player has.
  If you want to require the player to have a certain number of coins to do something, like with coin warps, consider using the more elaborate testForCoins method instead.
const uint16 curAnim
  The current animation the player takes its frames from, serving as an index to the jjAnimations array. Each animation has a distinct value; for example, Jazz standing in place might be 81, but Spaz standing in place might be 185.
const uint curFrame
  The overall current frame displayed to the screen to represent this player, serving as an index to the jjAnimFrames array. For any given jjPLAYER@ `p`, `p.curFrame` will equal `jjAnimations[p.curAnim].firstFrame + p.frameID`.
const int currTile
  A shortcut value, always equalling `int(xPos)/32 + int(yPos)/32*65536`. Since both xPos and yPos are easily accessible properties, it really only makes sense to use currTile to compare against previous values of currTile, i.e. to see if the player has moved or not.
uint8 currWeapon
  Which ammo type the player currently has selected. Possible constants appear in the appendix below, or you may use simple 1-indexed numbers instead (all values besides 1-9 will evaluate to WEAPON::CURRENT).
const int deaths
  In competitive game modes, the number of deaths the player experienced or a negative value if unknown (this is currently the case when the property is checked by clients in XLRS mode).
int8 direction
  Which direction the player is facing. Negative values mean left and non-negative ones mean right.
int doubleJumpCount
  How many times the player used double jump in the air since their last regular jump.
int fastfire
  The waiting time between shots, as decreased by Fastfire events or the JJFIRE cheat code. Starts out at 35—half a second—and decreases to a minimum of 6 (from Fastfire events) or 1 (JJFIRE).
const int flag
  The object ID of the flag the player is carrying, or 0 if the player is not carrying a flag.
int fly
  Possible special constant values are FLIGHT::NONE, FLIGHT::FLYCARROT, or FLIGHT::AIRBOARD, from the FLIGHT::Mode enum.
  If the player is currently using a copter or Cheshire2 object, fly will equal the object ID of that object plus one, which is to say, the jjOBJ the player is holding will be `jjObjects[p.fly - 1]`.
int food
  How much food the player has eaten.
  Setting this to 100 will not cause a sugar rush. Use the startSugarRush method instead.
const uint8 frameID
  The index of the current frame displayed to the screen to represent this player within its animation. For example, Jazz's standing animation is only one frame long, so his frameID will always equal 0 while playing that animation, while Spaz's will range from 0 to 4. For any given jjPLAYER@ `p`, `p.curFrame` will equal `jjAnimations[p.curAnim].firstFrame + p.frameID`.
int8 frozen
  0 if unfrozen; otherwise, constantly counts down towards 0.
uint32 fur
  Palette indexes of the player's fur in the form of 4 colors, 8 bits each. For potentially more intuitive ways of fur color manipulation refer to jjPLAYER methods furGet and furSet. Changes to this setting are automatically shared between all clients in the server.
int gems[GEM::Color]
  How many gems the player has collected. Possible values of GEM::Color are GEM::RED, GEM::GREEN, GEM::BLUE, and GEM::PURPLE.
uint8 health
  How many hearts the player has remaining. If you set this in an online server, all other players will be notified of the change.
int helicopter
  The amount of time in which player's copter ears will run out or 0 if the player is currently not using copter ears.
int helicopterElapsed
  The amount of time player spent using copter ears since their last jump.
int idle
  The amount of time since the last change of idle animation or 0 if the player is not idle.
int invincibility
  How much longer the player will be invincible. (Does not work in servers.) Specifically, the absolute value is the remaining duration of the invincibility; a positive number will display the invincibility effect around the player, but a negative number (or zero) will not.
bool invisibility
  Whether the player is invisible. Invisible players don't have their sprites or names drawn. Changes to this setting are automatically shared between all clients in the server.
const bool isActive
  Does this jjPLAYER object correspond to an actual player in the server, local or otherwise? If not, few if any of this jjPLAYER's other properties will be reliable. This is a fairly weak check and equals true for several types of players who may be present in the server but not actually playing—isInGame may often be more useful.
const bool isAdmin
  Whether the player is logged in as a Remote Admin in the current online server.
const bool isConnecting
  Whether the player is a client who has not finished joining the current online server yet, as represented by a "C" or "D" (if downloading) next to their name on the player list.
const bool isIdle
  Whether the player is idle and does not appear in the level or player list. Currently this can only ever be true of the server.
const bool isInGame
  Equals true if isActive is true but isConnecting, isIdle, isOut, and isSpectating are all false. If more such properties need to be added in future revisions of JJ2+, isInGame will be updated to reflect them.
const bool isJailed
  In Jailbreak game mode, whether the player is currently in jail. False in all other game modes.
const bool isLocal
  Is this jjPLAYER object controlled by this instance of JJ2?
const bool isOut
  Equals true if the player has lost all their lives (or joined too late) in an LRS-based gamemode.
const bool isSpectating
  Equals true if the player is spectating normally, i.e. not forced into spectating by being out or an idle server.
const bool isZombie
  In Pestilence game mode, whether the player is currently a zombie. False in all other game modes.
float jumpStrength
  Vertical speed gained by the player on jump. Defaults to -10.
bool keyDown
bool keyFire
bool keyJump
bool keyLeft
bool keyRight
bool keyRun
bool keySelect
bool keyUp
  A series of bools controlling whether the player believes its various control keys are being pressed. Note that always setting keyFire to true is not the same as making the player constantly fire, unless their fastfire property equals 1.
int8 light
  The intensity of light emitted by the player. Changes to this setting are automatically shared between all clients in the server.
uint8 lighting
  The player's current level of ambient lighting, as affected by the Set Light and Reset Light events.
LIGHT::Type lightType
  The type of light emitted by the player. Possible values are NONE, NORMAL, POINT, POINT2, FLICKER, BRIGHT, LASERBEAM, LASER, RING, RING2, and PLAYER (default). Changes to this setting are automatically shared between all clients in the server.
int lives
  In single player or cooperative mode, how many lives the player has remaining. For LRS-based game modes use lrsLives instead.
const int localPlayerID
  Which local player the player is, in case of splitscreen. 0-3.
const int lrsLives
  In LRS-based game modes (LRS, TLRS, XLRS), how many lives the player has remaining, a negative value for other modes. For single player and cooperative modes use lives instead.
const string name
  The player's name. This is a constant property; to change it, you can use the setName method.
bool noclipMode
  Whether the player is currently in Noclip Mode, as caused by Sucker Tube events with the "BecomeNoclip" parameter set to 1. Setting this to true could be dangerous if the level design is not prepared for it.
bool noFire
  Whether the player is currently allowed to shoot bullets. Hides the default current weapon/ammunition display while true.
int platform
  The object ID of the object the player is currently standing on, or 0 if inapplicable.
const int8 playerID
  In online play, which number the player is in the server's list of players. 0-31.
bool powerup[WEAPON::Weapon]
bool powerup[9]
  Whether each ammo type is powered-up or not. Possible constants appear in the appendix below, or you may use simple 1-indexed numbers instead (all values besides 1-9 will evaluate to WEAPON::CURRENT).
  Note that JJ2+ prevents the use of powered-up weapons without corresponding powerup events in the level while in online servers. To remedy this, hide powerup events somewhere in the level, or else set their allowedPowerup properties to true in the jjWeapons array.
const int roasts
  In competitive game modes, how many kills the player made or a negative value if unknown (this is currently the case when the property is checked by clients in RT, LRS, Pestilence and TLRS modes). If the game mode doesn't allow players to kill other players, as is the case in Single Player, Cooperative, Treasure Hunt and Race, this property will always be equal to 0.
bool running
  Is the player currently running? Detects the run key, capslock (if there is only one local player), and the /run <on|off> command. Apparently running is set based on keyRun, and then other parts of code query running exclusively.
int score
  In single player and cooperative modes, the player's current score. JJ2 only increments this in multiples of 50 (or 10 if you count the unpatched Butterfly enemy), but that's up to you.
int scoreDisplayed
  In single player and cooperative modes, the number currently displayed for the player's score. Whenever score increases, scoreDisplayed takes a few moments to catch up to the new value. Unless the level defines an onDrawScore function, in which case this property could mean or do anything.
const ANIM::Set setID
  Which set of animations the player uses, serving as an index to the jjAnimSets array. This is nearly a 1:1 mapping with charCurr, except that the two birds (CHAR::BIRD and CHAR::BIRD2) share a setID.
  The RABBIT::Anim enum is a version-independent list of all possible animations a rabbit player might use, expressed as indices of animations within the player's anim set. To see if a rabbit player `p` is currently standing still, for example, you can check `if (p.curAnim - jjAnimSets[p.setID].firstAnim == RABBIT::STAND)`.
int shieldTime
  How much longer (in ticks) the player's shield will last, or 0 if the player doesn't have a shield.
int shieldType
  Which shield the player currently has, assuming the player has a shield at all. In place of numbers, you may also use the dedicated SHIELD::Shield enum, options being NONE, FIRE, BUBBLE/WATER, LIGHTNING/PLASMA, and LASER. Using values from outside of the range of existing shields may be a reason of incompatibility with future versions of JJ2+ in case more shields are added.
int specialMove
  How long the player has been performing their special move or 0 if the player is currently not using a special move.
SPRITE::Mode spriteMode
  Sprite mode the player's sprite is drawn in. Possible constants appear in the appendix below. Changes to this setting are automatically shared between all clients in the server.
uint8 spriteParam
  A sprite mode parameter further specifying how the player should be drawn. Changes to this setting are automatically shared between all clients in the server.
int stoned
  How much longer will the player be stoned, like after touching a smoke ring.
const int subscreenX
const int subscreenY
  Where the player's subscreen begins in the window. These will usually equal 0, but playing with more than one local player, in one of the two 3D modes, or both, may produce other numbers. For example, in a level with horizontal splitscreen while playing with Top-And-Bottom 3D, player 2's second subscreen will be drawn with subscreenY equal to 75% of jjSubscreenHeight.
const TEAM::Color team
  The player's team in team-based game modes. Possible values are TEAM::BLUE, TEAM::RED, TEAM::GREEN, and TEAM::YELLOW.
const bool teamRed
  Incorrectly equals true whenever the player is not on the blue team, i.e. including if they're on green or yellow. Use the property above instead.
bool timerPersists
  Should dying disable the Player Timer (false) or have no effect on it (true)?
const TIMER::State timerState
  The current state of the Player Timer (TIMER::STOPPED, TIMER::STARTED, or TIMER::PAUSED), for comparisons only. Use the corresponding timerStart, timerStop, timerPause, and timerResume methods to set this instead.
int timerTime
  How many ticks are left on the Player Timer.
const int warpID
  If this number is higher than 0, the player is currently warping, and it will equal the ID of the Warp event plus one, a range of 1-256.
  It is often wise to make sure this property equals 0 before calling a warp method, lest the player be locked into a constant loop of beginning to warp but never finishing it. Something like `if (conditionsForWarping && player.warpID == 0) player.warpToID(25);`. Performing this check is however unnecessary for fast warps, or warps triggered by Text events, since that code is only called when the player first enters the tile.
float xAcc
  Horizontal acceleration in pixels per game tick squared, positive or negative.
float xOrg
  If this or yOrg are non-zero, where the player should respawn after death.
float xPos
  Horizontal location in pixels.
float xSpeed
  Horizontal speed in pixels per game tick, positive or negative.
float yAcc
  Vertical acceleration in pixels per game tick squared, positive or negative.
float yOrg
  If this or xOrg are non-zero, where the player should respawn after death.
float yPos
  Vertical location in pixels.
float ySpeed
  Vertical speed in pixels per game tick, positive or negative.
void activateBoss(bool activate = true)
  Activates all bosses and disables the player's sugar rush if applicable. Unlike the Activate Boss event, does not change the music track. (Use jjMusicLoad for that instead.)
  Setting activate to false will attempt to deactivate bosses, but this mostly only results in the boss health meter going away. The jjPLAYER object's boss is left unchanged unless you change it manually, and bosses do not stop moving around.
void cameraFreeze(float xPixel, float yPixel, bool centered, bool instant)
void cameraFreeze(bool xUnfreeze, float yPixel, bool centered, bool instant)
void cameraFreeze(float xPixel, bool yUnfreeze, bool centered, bool instant)
void cameraFreeze(bool xUnfreeze, bool yUnfreeze, bool centered, bool instant)
  Fixes the camera in place, like when encountering a Robot Boss, until the corresponding cameraUnfreeze method is called. If instant is left false, the camera will take roughly half a second to scroll to its target. If centered is left false, the camera will position itself so that xPixel,yPixel is in the top left corner of the screen; otherwise, that position will be in the very center. In overloads containing xUnfreeze or yUnfreeze, their values determine whether the corresponding axis should be unfrozen by the function, allowing its free movement (true), or left unaffected (false). In particular, `player.cameraFreeze(true, true)` is equivalent to `player.cameraUnfreeze()`.
void cameraUnfreeze(bool instant = true)
  If cameraFreeze has been called, undoes the effect and lets the camera freely follow the player once again. If instant is left false, the camera will take roughly half a second to scroll to its target.
bool doesCollide(const jjOBJ@ object, bool always = false) const
  Returns true if the player's sprite collides with that of the specified object, otherwise false. This is a convenient wrapper for jjANIMFRAME::doesCollide, using the positions, directions and curFrame properties of the player and object involved. No other variables—including playerHandling, bulletHandling, or the scale and rotation their sprites are drawn in—are taken into account.
int extendInvincibility(int duration)
  A convenience method to extend the absolute value of the player's invincibility property by the absolute value of the duration parameter, which also makes invincibility positive (visible) if duration is visible. For example, collecting a full energy carrot extends invincibility by +350, whereas buttstomping most enemies extends invincibility by -70.
int fireBullet(uint8 gun = WEAPON::CURRENT, bool depleteAmmo = true,
bool requireAmmo = true, DIRECTION::Dir direction = DIRECTION::CURRENT)
int fireBullet(uint8 gun, bool depleteAmmo, bool requireAmmo, float angle)
  Causes the player to fire, using either the specified ammo type or the current one. The return value will be the object ID of the new bullet, unless the weapon fires more than one bullet at a time. This action is visible for all players in an online server.
  Possible gun constants may be found in the appendix at the bottom of the page, or you may use simple 1-indexed numbers instead (all values besides 1-9 will evaluate to WEAPON::CURRENT).
  If depleteAmmo is false, the method will not affect how much of the ammo type the player has remaining.
  If requireAmmo is false, the player can fire a bullet of that type even if they don't have any ammo of that type.
  Possible values for direction are DIRECTION::RIGHT, DIRECTION::LEFT, DIRECTION::UP, and DIRECTION::CURRENT. If mouse aiming is enabled, DIRECTION::CURRENT will evaluate to whatever angle the mouse cursor is at, rather than whichever direction the player is physically facing. Alternatively you may pass a float angle argument instead of an orthogonal direction argument, in which case 0 is up, .5 * pi is right, pi is down, 1.5 * pi is left, and 2 * pi is up again.
void freeze(bool frozen = true)
  Freezes the player for the same length of time as the Freeze Enemies pickup, or unfreezes the player if frozen is set to false. Helper method.
void furGet(uint8 &out, uint8 &out, uint8 &out, uint8 &out) const
  Sets provided parameters' values to palette indexes of the player's fur colors.
void furSet(uint8, uint8, uint8, uint8)
  Sets player's fur colors to those represented by provided palette indexes. Changes made by this method are automatically shared between all clients in the server.
int getObjectHitForce(const jjOBJ@ target = null) const
  Returns a value expressing whether the player is currently in a state to harm objects or other players, equivalent to the "force" argument in objectHit or the onObjectHit hook. Specifically, this method returns -1 if the player is performing a special move and the canHurt property of their respective jjCHARACTER is true, 1 if the player is buttstomping or has a sugar rush, or 0 if none of the above are true. Doesn't work especially well when called on non-local players.
  If a non-null jjOBJ@ is specified as target, two additional checks will be performed, returning -101 if the object is frozen and the player is running into it really fast, or -1 if the player is a frog (with a true canHurt) hitting the object with its tongue.
bool hurt(int8 damage = 1, bool forceHurt = false, jjPLAYER@ attacker = null)
  Attempts to hurt the player damage hearts, or at least strip the player of their bird or reduce their shield time. If attacker is left null, or if it's the same player as the one getting hurt, the injury will be counted as coming from the level, and if a death results, it will be marked online with the "ate it/you killed yourself" text. This is what you should do for injuries from enemies and other level-based factors. On the other hand, if the player dies from the hurt call and attacker is another player in the server, that player will get credit for the roast. Returns false if neither the hurtee nor the hurter are local players and the method is not called by the host, or if forceHurt (which bypasses traditional safety sources like the invincibility and blink properties and buttstomping) is false and something or other prevents the hurting from happening.
void kill()
  Kills the player instantly. If you want the player to be roasted by some other player in an online server, use hurt with a high damage value instead.
void limitXScroll(uint16 left, uint16 width)
  Works like a Limit X Scroll event with the corresponding Left and Width parameters. Remember that these are measured in tiles, not pixels.
CHAR::Char morph(bool rabbitsOnly = false, bool morphEffect = true)
  Cycles the player's character to the next on the list, just like the JJMORPH cheat: Jazz-Spaz-(Lori-)Bird-Frog-Bird2. Or if rabbitsOnly is true, skips birds and frog and acts like a morph monitor instead, restricting available morph targets to those whose jjCHARACTER::morphBoxCycle properties equal true. Returns the player's new character: CHAR::JAZZ, CHAR::SPAZ, CHAR::LORI, CHAR::BIRD, CHAR::FROG or CHAR::BIRD2.
CHAR::Char morphTo(CHAR::Char charNew, bool morphEffect = true)
  Sets the player's character to charNew, possible values CHAR::JAZZ, CHAR::SPAZ, CHAR::LORI (in TSF), CHAR::BIRD, CHAR::FROG or CHAR::BIRD2.
bool objectHit(jjOBJ@ target, int force, HANDLING::Player playerHandling)
  Causes the game to think the player is colliding with object target, even if they are not in the same area, and returns true if (generally speaking) the object is affected somehow. Calling this method on a non-local player will return false unless called by the host of a server.
  The force parameter hints at whether the player should be able to damage the object, if the object can be damaged to begin with. A value of 0 means that the player is simply bumping into the object at random; 1 means a buttstomp or sugar rush; -1 means a special move; and -101 means the object is frozen and the player runs into it really fast. If you want this parameter to reflect reality, get the value from jjPLAYER's getObjectHitForce method.
  The playerHandling parameter specifies how the player should interact with the object—for example, passing HANDLING::PICKUP will force the player to treat the object as a pickup, even if its actual playerHandling property is set to something else. If the value is not HANDLING::ENEMY, HANDLING::SPECIAL, HANDLING::PICKUP, HANDLING::ENEMYBULLET, or HANDLING::PLAYERBULLET, nothing will happen. If the value is one of the first three and the object has scriptedCollisions set to true, the onObjectHit hook function will be called with a null bullet argument, this jjPLAYER as player, and whatever force you call this method with as force.
  The results of this collision will be broadcast to other players in the server exactly as if it had happened naturally through the sprites colliding. For example, collecting a pickup object half the level away will cause other players to see the pickup disappear.
bool offsetPosition(int xPixels, int yPixels)
  Instantly moves the player xPixels pixels to the right and yPixels pixels down. The camera instantly readjusts itself to follow, as does the glowing trace following the player while running. The best way of creating seemlessly looping levels.
uint8 resetLight()
  Resets player's ambient lighting value to the level's default.
CHAR::Char revertMorph(bool morphEffect = true)
  Reverts the player to the character they were when they began the level, just like the Revert Morph event. If morphEffect is true, displays the default visual effect. Returns the new character (equal to the player's charOrig).
bool setName(const string &in name)
  Sets the player's name. This method will only succeed if called server-side or in a local game, and it will then return true to indicate success. Otherwise no change will occur and the method will return false.
int setScore(int score)
  Sets the player's score. While setting the score through the score property is slightly delayed as scoreDisplayed increases/decreases to catch up to the new value, this function sets both properties at once. Probably most helpful as a function for quickly showing you debug information.
void showText(uint8 textID, uint8 offset, STRING::Size size = STRING::SMALL)
void showText(string text, STRING::Size size = STRING::SMALL)
  Displays text on the player's screen either like a Text event, with the corresponding textID and offset parameters, or simply a specified text. Unique size values are SMALL, MEDIUM, and LARGE. Note that not all glyphs that appear in one size character set may appear in another; for instance, the underscore character is unique to SIZE::SMALL.
bool startSugarRush(int time = 1400)
  Gives the player a sugar rush lasting time ticks, unless their bossActivated property equals true, in which case the method returns false. Otherwise returns true.
bool testForCoins(int numberOfCoins)
  If the player has at least numberOfCoins coins, depletes their coins by numberOfCoins and returns true. Otherwise displays a warning onscreen that they need more coins to continue and returns false. Basically the same as a coin warp event, but you get to choose the result.
bool testForGems(int numberOfGems, GEM::Color type)
  If the player has at least numberOfGems type-colored gems, depletes their type gems by numberOfGems and returns true. Otherwise displays a warning onscreen that they need more gems to continue and returns false. Basically the same as a coin warp event, but you get to choose the result, and it's for gems instead of coins.
  Possible values of type are GEM::RED, GEM::GREEN, GEM::BLUE, and GEM::PURPLE.
void timerFunction(string functionName)
void timerFunction(jjVOIDFUNC@ function)
void timerFunction(jjVOIDFUNCPLAYER@ function)
  When the Player Timer hits zero without being stopped artifically, AngelScript will call the function named by this method (a string is acceptable, but pointing directly to the function is advised instead), setting the (technically optional) jjPLAYER@ property to point to the player whose Player Timer just expired. This defaults to onPlayerTimerEnd, aka `void onPlayerTimerEnd(jjPLAYER@)`. It is up to you to define this function and decide what should happen to the player.
TIMER::State timerPause()
  Pauses the Player Timer and returns TIMER::PAUSED.
TIMER::State timerResume()
  Resumes the Player Timer and returns TIMER::STARTED.
TIMER::State timerStart(int ticks, bool startPaused = false)
  Begins the Player Timer (and optionally pauses it) with ticks ticks remaining on the clock. Returns TIMER::STARTED or TIMER::PAUSED, depending.
TIMER::State timerStop()
  Stops the Player Timer and returns TIMER::STOPPED.
bool warpToID(uint8 warpID, bool fast = false)
  Warps the player to a Warp Target event with the specified Warp ID, instantly if fast is true or using the standard warp effect if fast is false.
bool warpToTile(int int xTile, int yTile, bool fast = false)
  Warps the player to the specified tile, instantly if fast is true or using the standard warp effect if fast is false.

### class jjOBJ

Objects are the enemies, pickups, platforms, light sources, and so forth.

The most important global property is jjObjects[], a list of all the potential objects in the game. To obtain a jjOBJ to play with, you'll need to specify one of the entries in jjObjects[], e.g. `jjOBJ@ o = jjObjects[1]`. The global property jjObjectCount is useful for looping through the contents of jjObjects[]. When performing such a loop, it is important to test that a given jjOBJ has a true isActive property, since deleted objects may leave traces of themselves in memory and these traces can only be distinguished from currently-extant objects using isActive. To narrow your results, comparing the eventID class to specific OBJECT::Object constants is also useful, e.g.

    for (int i = 1; i < jjObjectCount; i++) { //jjObjects[0] is never active, so we start the index at 1
    	jjOBJ@ o = jjObjects[i];
    	if (o.isActive && o.eventID == OBJECT::NORMTURTLE) {
    		o.state = STATE::KILL;
    	}
    }

Besides a loop invoking jjObjectCount, object IDs can also be obtained through the jjPLAYER property platform, which gives the ID of the object the player is standing on (if any), the jjPLAYER property boss, which gives the ID of the object whose health is displayed in the boss health meter on that player's screen (if any), and the jjOBJ properties creatorID and creatorType, since if the latter equals CREATOR::OBJECT, the former will refer back to another object which created that one. Usually creatorID equals 0, but occasionally—for example, enemy bullets or objects created by generators—it will point to an object, though you should still remember to check isActive to make sure the object's creator hasn't already been destroyed.

Another reliable way to obtain an object ID is the global function jjAddObject, which returns the ID of the object it adds so that you can then look it up in jjObjects and set some of its properties. The last three parameters can be—and usually are—left out, but you're free to do as you like. Both jjPLAYER and jjOBJ additionally have fireBullet methods which will also usually produce object IDs.

As ever, note that AngelScript's scope is all but exclusively the JJ2 copy on your computer, even if you're in an online server, and jjAddObject and the various jjOBJ properties are no exception to this. If one player fires a slice of AngelScript that creates a morph monitor, the other players will not see any morph monitor until and unless they too fire the same slice of code. Likewise, changing the xPos and yPos of a pinball bumper will only be recognized by the local players, not by anyone else in the server. To allow the script to have this kind of impact on game state of other clients it's necessary to use the jjSTREAM class and the jjSendPacket function. These details are of course of no concern for single player levels, but should be kept in mind for multiplayer design.

Certain of these properties—curFrame in particular—will constantly be set by the object itself, so trying to change them manually will have little effect unless you also redefine their behavior. Others, such as state or lightType, will have immediate and possibly enduring effects. There are also a significant number of properties whose function (if any) varies from object (i.e. eventID) to object; these should be better explained in future iterations of JJ2+, but you're welcome to experiment in the meantime.

Finally: there are also nearly 256 proto-objects stored in the global array jjObjectPresets[256]. These do not correspond to objects currently active in the game, but are instead the value-collection prototypes from which all in-game objects are initially derived. Whenever jjAddObject is called using, say, OBJECT::GREENGEM, the created object will get its initial values for points, curAnim, var[0], and more from `jjObjectPresets[OBJECT::GREENGEM]`. The contents of jjObjectPresets are reset every level, so it is totally safe for you to decide that, e.g., you'd really rather if all Skeleton enemies were lightning-fast and took nineteen hits to destroy instead of three (`jjObjectPresets[OBJECT::SKELETON].energy = 19; jjObjectPresets[OBJECT::SKELETON].xSpeed = 5;`).

int age
  A variable totally unused by JJ2. The name comes from its original intended purpose as indicating how long had elapsed since the object was created.
int animSpeed
  Intended to determine how fast the object animates. However, only gem rings, speed destruct scenery, and collapse scenery actually use this property for that purpose, and all other non-bullet objects leave it untouched.
  BULLETS: This property stores the amount of damage a bullet does to enemies, e.g. 1 for normal blaster, 2 for normal seekers, or 3 for certain shield bullets.
jjBEHAVIOR behavior
  Which function is called for this object's behavior. See the Custom Objects section.
  In order to avoid common bugs, it's strongly discouraged to modify behavior of objects of eventID OBJECT::GENERATOR, and especially of `jjObjectPresets[OBJECT::GENERATOR]`. Currently a great portion of game code tests it against BEHAVIOR::GENERATOR and if it doesn't succeed, it may take unexpected actions, typically involving kicking and banning clients for invalid weapons. Similarly, replacing BEHAVIOR::DIAMONDSAREFOREVER is liable to break Treasure Hunt or Head Hunters games. Replacing BEHAVIOR::DESTRUCTSCENERY, BEHAVIOR::TRIGGERSCENERY, or BEHAVIOR::SUPERGEM will prevent the states of those objects from being shared with newly joining clients in online servers.
HANDLING::Bullet bulletHandling
  What happens when a bullet (or turtle shell, or TNT blast, or attacking bird) comes into contact with this object, assuming that playerHandling is either ENEMY or SPECIAL and state is anything other than KILL?
  - HURTBYBULLET: If this object has a non-zero energy property, then hitting it will decrease its energy by the force of the bullet. If the object is frozen, it will be unfrozen. If the object's energy sinks to 0 or less, its state will change to STATE::KILL and the player behind the bullet will receive the object's points. Otherwise, its justHit property will be set to 5. The bullet will be destroyed unless it has bit 16 set for its var[6], like fireball bullets do, or if the object has causesRicochet as true. If the object's playerHandling is SPECIAL, other things may happen.
  - IGNOREBULLET: The object will be unaffected in every way by the collision, and the bullet will not be destroyed.
  - DESTROYBULLET: The object will be unaffected in every way by the collision, and the bullet will be destroyed no matter what its properties are.
  - DETECTBULLET: The object is by default unaffected, but the bullet will be destroyed unless it has bit 16 set for its var[6], like fireball bullets do, or if the object has causesRicochet as true. If the object's playerHandling is SPECIAL, other things may happen.
bool causesRicochet
  When true, colliding bullets will ricochet off of this object unless bulletHandling is set to DESTROYBULLET.
int counter
  A general purpose property, usually used for counting up or down to some future event.
uint8 counterEnd
  A general purpose property, usually used for counting up or down to some future event. Only goes up to 255, so not as versatile as counter.
  BULLETS: how long a bullet will exist before exploding. Used by pretty much every bullet behavior but BEHAVIOR::BOLLYBULLET, although no behavior-external code seems to reference it specifically.
int creator
  The sum of the creatorID and creatorType properties, which is less useful than you might hope.
int creatorID
  The object ID or player ID of the object or player that created this object, as usable as the index for jjObjects or jjPlayers. 0 if no creator is actually known.
CREATOR::Type creatorType
  CREATOR::OBJECT if the object was created by another object, CREATOR::PLAYER if it was created by a player, or CREATOR::LEVEL if it was added directly from the event map.
  If the object was created by a Generator object specifically, then creatorType will equal CREATOR::LEVEL, but creatorID will equal the object ID of that Generator object rather than 0.
int16 curAnim
  The current animation the object takes its frames from; an index for the jjAnimations array. You can obtain useful values for this from the determineCurAnim method or else any jjANIMSET::firstAnim.
uint curFrame
  The overall current frame displayed to the screen to represent this object, taking into account both curAnim and frameID; an index for the jjAnimFrames array. You can obtain useful values for this from the determineCurFrame method or else any jjANIMATION::firstFrame.
bool deactivates
  When true, this object will be deleted from memory if the player wanders too far away from it in local single player mode, and all its properties will be reset next time it gets loaded. This property has absolutely no effect in multiplayer, and setting it to false cannot force an object to remain in memory in single player when the player dies.
int8 direction
  Which way the object is facing. Generally, direction >= 0 is right and < 0 is left. Some objects may also correctly interpret SPRITE::Direction constants: SPRITE::FLIPNONE, SPRITE::FLIPH, SPRITE::FLIPV and SPRITE::FLIPHV but the ones involving vertical flip will not be perfectly reliable for objects without custom behavior, except for box objects (crates, barrels, monitors), which will take vertically flipped direction values as a cue to fall upwards instead of downwards.
uint8 doesHurt
  A variable totally unused by JJ2. The name comes from its original intended purpose as specifying whether boss objects could hurt the player.
int8 energy
  If this object's playerHandling value is HANDLING::ENEMY, this number is how many more hits the object can take before it is destroyed, unless it equals 0, in which case the object is invincible to bullets (but not to special attacks). This property is also used to determine the fullness of the boss bar.
uint8 eventID
  e.g. 158 for a peach, 43 for a bomb, 243 for an airboard, 1 for a blaster bullet, and so on. Is a uint8 for maximum flexibility, but you should probably set/compare it to OBJECT::Object constants instead most of the time, if for no other reason than readability.
  While this value is not strictly constant/read-only, changing it can lead to unpredictable and undesirable effects, since this is the only truly reliable way of knowing what kind of object a given jjOBJ really is, and JJ2 queries it very frequently. The effects can vary from a food pickup playing the wrong sound effect when collected to, say, a red spring simply not working at all when touched. You have been warned.
int8 frameID
  The object's current frame within a single animation set, e.g. which direction the Tube Turtle faces while it rotates in place.
uint8 freeze
  0 if the object is unfrozen, otherwise counts down to 0 for most behaviors, though you'll need to implement that manually if writing your own.
const bool isActive
  Does this jjOBJ correspond to a real object, or is it just the abandoned memory of one? For any jjOBJ `o`, `(o.isActive)` is the same as `(o.behavior != BEHAVIOR::INACTIVE)`, but one is obviously much shorter than the other.
bool isBlastable
  When true, nearby TNT explosions will set this object's xSpeed and ySpeed properties.
bool isFreezable
  When false, this object will treat ice bullets just the same as any other bullet with the same properties. This is what Caterpillar and Queen do, for example.
bool isTarget
  When true, this object will be attacked by birds and seeker missiles.
uint8 justHit
  When this property has a non-zero value, most objects will be drawn as pure white until the property counts back down to 0 one tick at a time. JJ2 deincrements this property for all objects, so you needn't worry about it when defining your own behavior functions.
int16 killAnim
  Which animation the object uses while being destroyed, in the same format as curAnim.
int8 light
  The intensity of the light produced by the object.
LIGHT::Type lightType
  The type of light produced by the object. Possible values are NONE, NORMAL, POINT, POINT2, FLICKER, BRIGHT, LASERBEAM, LASER, RING, RING2, and PLAYER.
STATE::State oldState
  If the object is frozen, what state it was in before it was frozen. Possible constants are listed in the appendix at the end of this file.
HANDLING::Player playerHandling
  How does this object interact with the rest of the game, most specifically coming into contact with players, although also bullets?
  - ENEMY: If a player touches this object, they will be hurt (barring invincibility and such), unless they are using a special attack, in which case the object's energy property will decrease by 4. If energy reaches 0 or lower, the object's state will be set to KILL and the player will receive its points. Objects of playerHandling PLAYERBULLET that come into contact with this object have the potential to collide with it, depending on its bulletHandling setting.
  - PLAYERBULLET: When the object's state is anything but START or EXPLODE, this object will constantly be checked for collision with players other than its creatorID, as well as objects with playerHandling ENEMY, PICKUP, or SPECIAL.
  - ENEMYBULLET: When the object's state is anything but START or EXPLODE, if a player touches this object, they will be hurt (barring invincibility and such).
  - PARTICLE: Effect unknown (semantic only?)
  - EXPLOSION: Effect unknown (semantic only?)
  - PICKUP: If a player touches this object, something special will happen, exactly what depending on its eventID value. Usually if nothing else the player will receive its points, and its behavior will be set to BEHAVIOR::EXPLOSION2, though sometimes (e.g. carrots when touched by a player with full health) nothing will happen at all. If the object has scriptedCollisions set to true, that will override the effect of the eventID value. Should it have 0 xSpeed/ySpeed properties and be overlapped by an object of playerHandling PLAYERBULLET whose state is not START or EXPLODE, the pickup object will partially inherit that bullet object's speed and direction.
  - DELAYEDPICKUP: Effect unknown (semantic only?)
  - HURT: Effect unknown (semantic only?)
  - SPECIAL: Whenever this object is overlapped by a player or an object of playerHandling PLAYERBULLET (depending in the latter case on this object's bulletHandling value), something special will happen, exactly what depending on its eventID value. If the object has scriptedCollisions set to true, that will override the effect of the eventID value.
  - DYING: Effect unknown (semantic only?)
  - SPECIALDONE: Effect unknown (semantic only?)
  - SELFCOLLISION: Effect unknown (semantic only?)
uint16 points
  How many points a player will gain for destroying the object.
int8 noHit
  Instead, use the properties bulletHandling, causesRicochet, isFreezable, and isBlastable.
const uint16 objectID
  For all values of `n` such that `n < jjObjectMax`, `jjObjects[n].objectID == n`.
uint8 objType
  Instead, use the properties playerHandling, isTarget, triggersTNT, deactivates, and scriptedCollisions.
bool scriptedCollisions
  When true, JJ2 will call the onObjectHit hook function to determine what to do if a bullet or player is detected as having collided with this object. See the Custom Objects section. This property's effect if playerHandling is anything other than ENEMY, PICKUP, or SPECIAL is presently undefined.
int special
  A general-purpose variable, means different things for different objects.
  BULLETS: This property stores the animation (curAnim-style) that is used if the bullet is shot upwards, not horizontally. If this property equals 0, it will not be possible to shoot the bullet upwards except by mouse aiming; this is how shields work.
STATE::State state
  The current state of the state machine that is the object. Possible constants are listed in the appendix at the end of this file.
bool triggersTNT
  When true, TNT will explode if this object is nearby.
int var[11]
  A series of general-purpose variables, used for different things by different objects. In general, earlier values are more likely to be used by the game's native behavior functions than later ones.
  BULLETS:
  - var[3] represents what ammo type the bullet is, 1-9, which is primarily used for destroying destruct scenery with a non-zero "Weapon" parameter.
  - var[6] is used as a series of boolean flags that specify how the bullet interacts with various objects: bit 2 for fire-based bullets which can melt springs and burn enemies into fire/smoke particles; bit 4 for the laser shield's laser; bit 8 for bullets that do two damage in multiplayer; and bit 16 for bullets that pass through enemies, like the fireball, rather than explode on impact.
  - var[7] is the xSpeed that the player who fired this bullet was moving at when the bullet was fired, reduced to a range of -8–8... multiplied by 65536. Sorry about that.
  - var[9] is a counter for how many times the bullet has ricocheted in its lifetime, beginning at 0.
  - var[10] is a counter for how long it's been since the bullet has last ricocheted; each ricochet resets it to 0, but traditional bullet behaviors constantly increment it, and a bullet cannot ricochet if the value is less than 8.
float xAcc
  Horizontal acceleration in pixels per game tick squared, positive or negative.
float xOrg
  Original horizontal location in pixels.
float xPos
  Current horizontal location in pixels.
float xSpeed
  Horizontal speed in pixels per game tick, positive or negative.
float yAcc
  Vertical acceleration in pixels per game tick squared, positive or negative.
float yPos
  Current vertical location in pixels.
float yOrg
  Original vertical location in pixels.
float ySpeed
  Vertical speed in pixels per game tick, positive or negative.
void behave(jjBEHAVIOR behavior = BEHAVIOR::DEFAULT, bool draw = true)
  Causes this jjOBJ to perform the specified jjBEHAVIOR function, or its own behavior property if behavior is set to BEHAVIOR::DEFAULT. If draw is false, it will not draw anything to the screen. See the Custom Objects section.
void bePlatform(float xOld, float yOld, int width = 0, int height = 0)
  Makes the object act as a platform for players. Unlike beSolid, this method will only cause interactions with players on top of the object and not on its sides. Additionally, this method better accounts for the object's self-induced movement. The parameters xOld and yOld should be set to horizontal and vertical position of the object in the previous tick, whereas the jjOBJ properties xPos and yPos will be used for its current position. The remaining two parameters, width and height, should indicate the platform's dimensions in pixels. The default value of 0 will result in dimensions chosen automatically based on the size of the object's curFrame.
int beSolid()
  Causes this jjOBJ to serve as a solid block for players trying to move into it. Used by crates, monitors, etc. Returns -1 if a player is trying to push the object to the left, 1 if a player is trying to push it to the right, or otherwise 0, in case you wish to write some code to make the object pushable.
void blast(int maxDistance, bool blastObjects)
  Sends all players within maxDistance of the object flying away, like RFs or Bombs do when they explode. If creatorType equals CREATOR::PLAYER and players can currently hurt each other, the blast will hurt nearby players unless their playerID is equal to this object's creatorID. If blastObjects is true, then other jjOBJs within maxDistance whose isBlastable property equals true will be damaged and/or sent flying, just like when a TNT object explodes.
void clearPlatform()
  Causes all local players that are currently standing on top of this object or pushing it to no longer be standing on top of it or pushing it. Should be called when deleting an object that calls bePlatform or beSolid.
void deactivate()
  A wrapper method, called by most objects when their state property equals DEACTIVATE:

      obj.delete();
      if(obj.creatorType == CREATOR::LEVEL) {
      	jjEventSet(obj.xOrg/32, obj.yOrg/32, obj.eventID);
      	jjParameterSet(obj.xOrg/32, obj.yOrg/32, -1, 1, 0);
      }

void delete()
  Permanently deletes the object. Like jjAddObject, this method is purely local in its scope.
int16 determineCurAnim(uint8 setID, uint8 animation, bool change = true)
int16 determineCurAnim(ANIM::Set setID, uint8 animation, bool change = true)
  Determines the value of the curAnim corresponding to Set ID setID and Animation animation as seen in Jazz Sprite Dynamite. (0-indexed.) If change is specified as false, this serves as essentially a static method, calculating the proper curAnim value but not actually setting this particular jjOBJ's curAnim to that value.
  You are allowed to use a simple uint8 to specify the setID, but an ANIM::Set constant is strongly recommended, since the values for certain sets differ between 1.23 and 1.24. The full list of constants can be found in the appendix at the bottom of this file.
  Internally, this method runs the following code:

      if (jjAnimSets[setID].firstAnim == 0) //not yet loaded
      	jjAnimSets[setID].load(); //load from anims.j2a or plus.j2a, depending on setID
      const int16 newCurAnimValue = jjAnimSets[setID].firstAnim + animation;
      if (change)
      	this.curAnim = newCurAnimValue;
      return newCurAnimValue;

uint determineCurFrame(bool change = true)
  Determines the value of the curFrame corresponding to this jjOBJ's current curAnim and frameID values. If change is specified as false, this calculates the proper value but does not actually set this particular jjOBJ's curFrame property to that value.
  Internally, this method runs the following code:

      const jjANIMATION@ animation = jjAnimations[this.curAnim];
      const uint newCurFrameValue = (animation.frameCount == 0) ? 0 : (animation.firstFrame + (this.frameID % animation.frameCount));
      if (change)
      	this.curFrame = newCurFrameValue;
      return newCurFrameValue;

bool doesCollide(const jjOBJ@ object, bool always = false) const
bool doesCollide(const jjPLAYER@ player, bool always = false) const
  Returns true if the object's sprite collides with that of the specified object or player, otherwise false. This is a convenient wrapper for jjANIMFRAME::doesCollide, using the positions, directions and curFrame properties of the player and object involved. No other variables—including playerHandling, bulletHandling, or the scale and rotation their sprites are drawn in—are taken into account.
int draw()
  Essentially a wrapper for jjDrawSpriteFromCurFrame; uses the jjOBJ's xPos, yPos, direction, freeze, justHit, and curFrame properties to determine where to draw the sprite and what mode to use. This isn't specific enough for all objects, but it does the job in a high percentage of cases.
int findNearestPlayer(int maxDistance) const
int findNearestPlayer(int maxDistance, int &out foundDistance) const
  Returns the playerID property of the nearest jjPLAYER object within maxDistance, or a negative number if none exist. If the foundDistance parameter is included, it will be set to the distance of the found jjPLAYER. Both maxDistance and foundDistance are actually squares of the distance expressed in pixels. Used by numerous enemies and other objects in order to react to nearby players.
int fireBullet(OBJECT::Object eventID) const
  A much-simplified version of jjAddObject. This method creates a new object of type eventID, directly at the "gunspot" position of the jjOBJ's current curFrame sprite, and sets its direction, xSpeed, and xAcc based on the jjOBJ's direction. The return value is the object ID of the new bullet object, or 0 if the method was unsuccessful. Used by dragons, hatters, Bilsy, and so on.
void grantPickup(jjPLAYER@ player, int frequency) const
  Potentially creates a random pickup (red gem, green gem, blue gem, or carrot) in front of the jjOBJ, the likelihood depending on frequency (higher values are less likely). Traditionally, this method is called when an enemy or crate is destroyed by a bullet, and frequency equals 5 if the bullet was an unpowered-up blaster bullet or otherwise 10.
  The player parameter is necessary because one in every eight pickups a player is granted is a fastfire instead, so JJ2 needs to be able to keep track of when each individual player should next receive a fastfire.
  Only works if creatorType equals CREATOR::LEVEL.
void objectHit(jjOBJ@ target, HANDLING::Player playerHandling)
  Assumes that this object is a player bullet colliding with object target, even if they are not in the same area or if this object is not actually HANDLING::PLAYERBULLET. You are allowed to set what HANDLING::Player value the target object will be treated as having—HANDLING::ENEMY, HANDLING::SPECIAL, or HANDLING::PICKUP, with all other values having no effect—but otherwise the code will run exactly as if the two objects really did collide. Effects may or may not be broadcast to other players in the server, and the onObjectHit hook function will be called (with force as this object's animSpeed property) iff target has scriptedCollisions set to true and HANDLING::SPECIAL is passed as playerHandling.
void particlePixelExplosion(int style) const
  A fast wrapper for jjAddParticlePixelExplosion, using the jjOBJ's own xPos, yPos, direction, and curFrame properties.
void pathMovement()
  Makes the object use the same waypoint-based path movement as the Butterfly and Rocket Turtle objects. The method potentially sets the xAcc, yAcc, xPos, yPos, xSpeed, ySpeed, counter, direction, var[6] and var[7] properties in the process.
void putOnGround(bool precise = false)
  Moves the object downward until it's on top of the nearest available masked tile below it, or else the bottom of the level. If precise is left false, the resulting yPos may be off by as much as three pixels either up or down, which is still fine for most objects.
bool ricochet()
  To be used on bullet objects. Reverses the bullet's xSpeed/xAcc/direction, gives it a randomized ySpeed, plays one of the SOUND::AMMO_BUL* samples, and calls `jjAddParticle(PARTICLE::SPARK)` several times. Returns false if the bullet last ricocheted too recently.
int unfreeze(int style)
  Sets freeze to 0, plays SOUND::COMMON_ICECRUSH, and creates an explosion of ice fragments radiating outward from the object. Unique values for style are 0, 1, or any other number.

### class jjPARTICLE

JJ2 doesn't use the jjObjects jjOBJ list for everything: in particular, a number of visual effects are much too simple to require that many values, let alone all the specialized handling for light types, special states, multiplayer packets, and so on. For these, JJ2 employs a series of particle objects whose function is merely to make the screen look prettier without actually interacting with any other object, and disappear upon going offscreen (if not before that). These particles are in fact so very simple that AngelScript is able to give you absolute control over their movements and population.

Like full objects, particles are listed in a global array, jjParticles[1024]. Since particles are totally capable of taking care of themselves once they've been created, though, this is of only so much use. Most of your time will probably be spent around the jjAddParticle function, which takes a sole PARTICLE::Type parameter and returns a newly created jjPARTICLE, or a null pointer if the function was unsuccessful. Sample code:

    jjPARTICLE@ particle = jjAddParticle(PARTICLE::SNOW);
    if (particle !is null) {
    	particle.xPos = p.xPos;
    	particle.yPos = p.yPos + 12;
    	particle.xSpeed = -1.66;
    	particle.snow.frame = jjRandom() & 7;
    }

Note the peculiar `particle.snow.frame` property. Each particle type has its own unique property or properties in addition to the common properties, which will be listed at the end of this section. Since all particles are affected by gravity, if nothing else, setting the xSpeed/ySpeed properties is only so important, but xPos and yPos are of course essential.

bool isActive
  The point of this property is somewhat unclear, since JJ2 prefers to check if a given particle is active by testing whether type equals PARTICLE::INACTIVE or not, but it does exist and does get set sometimes.
PARTICLE::Type type
  Any of the standard type options allowed by jjAddParticle: INACTIVE, FIRE, FLOWER, ICETRAIL, LEAF, PIXEL, RAIN, SMOKE, SNOW, SPARK, STAR, STRING, or TILE.
float xPos
  Horizontal location in pixels.
float xSpeed
  Horizontal speed in pixels, positive or negative.
float yPos
  Vertical location in pixels.
float ySpeed
  Vertical speed in pixels, positive or negative.
fire
  Corresponds to PARTICLE::FIRE. Fire particles are drawn as small horizontal ovals, and before they disappear, may at any time create a smoke particle. Fire particles are usually created when an enemy or other destructible object is destroyed using a fire-based weapon.
  `uint8 fire.color` is the color (palette entry) which a fire particle will be drawn as. This property gradually increases as the particle remains active, until it reaches `colorStop` (default 48, pink), at which point the particle will disappear. The default initial value is 40 (yellow).
  `int8 fire.colorDelta` is the rate at which the `color` property is going to change. The most commonly used values for this are 1 and -1 meaning that the color value will respectively increase or decrease by 1 every time the game decides to modify it. The default value is 1.
  `uint8 fire.colorStop` is the `color` value which, when reached, will cause the particle to disappear. The default value is 48 (pink).
  `uint8 fire.size` decides how large the oval will be, ranging from 0-3. The default value is 3.
flower
  Corresponds to PARTICLE::FLOWER. Flower particles are drawn as single color, partially transparent, rotationally symmetric flowers, and drift for a while while getting progressively smaller. They are traditionally created by setting Type=1 on a Snow event.
  `uint8 flower.angle` is the current angle of rotation the flower is drawn at.
  `int8 flower.angularSpeed` is how much the angle changes every tick, positive or negative. By default they do not rotate at all.
  `uint8 flower.color` is the color which a flower particle will be drawn as, and does not change during the particle's lifetime. The default value is 16 (green)
  `uint8 flower.size` is how large the flower should be drawn, though this is not equal to its size in actual pixels. Decreases by 1 every tick until it reaches 0, at which point the particle disappears. The default starting value is 64.
  `uint8 flower.petals` is the number of petals the flower has. The default value is 5.
icetrail
  Corresponds to PARTICLE::ICETRAIL. Ice trail particles are drawn as single pixels. In regular JJ2, ice bullets leave them in their wake as they fly.
  `uint8 icetrail.color` is the color (palette entry) which an ice trail particle will be drawn as. This property gradually increases as the particle remains active, until it reaches `colorStop` (default 40, yellow), at which point the particle will disappear. The default initial value is 32 (light blue).
  `int8 icetrail.colorDelta` is the rate at which the `color` property is going to change. The most commonly used values for this are 1 and -1 meaning that the color value will respectively increase or decrease by 1 every time the game decides to modify it. The default value is 1.
  `uint8 icetrail.colorStop` is the `color` value which, when reached, will cause the particle to disappear. The default value is 40 (yellow).
leaf
  Corresponds to PARTICLE::LEAF. Leaf particles are drawn as frames of the ANIM::PLUS_SCENERY animation set, and float along based on their speed properties and some random jiggling, until they pass offscreen or hit a wall and fall to the ground. They are traditionally created by setting Type=3 on a Snow event. Note that you will need to load ANIM::PLUS_SCENERY manually in order for leaf particles to display properly.
  `uint8 leaf.countup` is set to 1 when the leaf hits a wall, then increments every tick until it hits 140 and the particle is deleted.
  `uint8 leaf.frame` specifies which frame of the animation will be drawn. This ranges from 0-31 when the leaf is in motion and 0-2 once the deathcounter property is non-zero. The value of this property does not exactly correspond to frames in the animation in plus.j2a, however, since the leaf animation repeats many frames that are only included once in the file.
  `uint16 leaf.frameBase` is ID of the frame the particle will use as a base sprite (curFrame-style), and has the default value of `jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim].firstFrame`.
  `uint8 leaf.height` is the distance from the ground in pixels that the particle must be in order to stop falling after hitting a wall. The default value is 2.
  `bool leaf.noclip`, if set to true, makes the leaf pass through walls and floors, making its countup and height properties irrelevant.
pixel
  Corresponds to PARTICLE::PIXEL. Depending on their size value, pixel particles will be drawn as 1x1, 2x2, or 3x3 rectangles of pixels. They move both horizontally and vertically, and will also bounce off of masks. These are the particles created by destroying most enemies.
  `uint8 pixel.color[9]` specifies which colors will be drawn at each pixel in the particle's rectangle. Note that if the rectangle is smaller than 3x3, not every number in the array will be used. Values of 0 represent transparent pixels and will therefore not be drawn.
  `uint8 pixel.size` specifies the size of the rectangle drawn to the screen. 0 for 1x1, 1 for 2x2, or any other number for 3x3. The default value is 0.
rain
  Corresponds to PARTICLE::RAIN. Rain particles are drawn as transparent, resized sprites, and move around according to their xSpeed/ySpeed properties until they hit a masked pixel, at which point they are either deleted (if the pixel is to their side) or begin a splashing animation (if the pixel is above or below). They are traditionally created by setting Type=2 on a Snow event.
  `uint8 rain.frame` ranges from 0-7 while the particle is in motion, switches to 8 upon hitting a floor or ceiling, and then increases until it hits 18 and the particle is deleted.
  `uint16 rain.frameBase` is the first frame of the animation used by the particle, and has the default value of `jjAnimations[jjAnimSets[ANIM::COMMON].firstAnim + 2].firstFrame`. The displayed frame will be equal to this property plus the current value of the `frame` property.
smoke
  Corresponds to PARTICLE::SMOKE. Smoke particles are drawn as small gray rectangles and always move erratically upwards. Traditionally they are created from fire particles or from the BEHAVIOR::BURNING objects created by powered-up toaster or a frozen Bily.
  `uint8 smoke.countdown` gradually decreases until it reaches 64, at which point the particle will disappear. The default value is 71.
snow
  Corresponds to PARTICLE::SNOW. Snow particles are drawn as frames of the ANIM::SNOW animation set, and fly slowly around based on their speed properties and a general wind force until they hit a masked tile and slowly fade away. They are, of course, traditionally created by the Snow event. Note that you will need to load ANIM::SNOW manually in order for snow particles to display properly.
  `uint8 snow.countdown` specifies how long (in ticks) the particle may pass through masked tiles after first being created before masked tiles cause it to fade away and disappear. The default value is 35.
  `uint8 snow.countup` has something unknown to do with the particle disappearing once it hits a masked tile. The default value is 0.
  `uint8 snow.frame` specifies which frame of the animation will be drawn. This remains constant until the particle hits a wall, at which point it will increase to 7 before disappearing. The default initial value is 0.
  `uint16 snow.frameBase` is ID of the frame the particle will use as a base sprite (curFrame-style), and has the default value of `jjAnimations[jjAnimSets[ANIM::SNOW].firstAnim].firstFrame`. The displayed frame will be equal to this property plus the current value of the `frame` property.
spark
  Corresponds to PARTICLE::SPARK. Spark particles move around according to their xSpeed/ySpeed properties and also gravity, and are drawn as short trails left behind as they move. They are traditionally created by bullets ricocheting off of turtle shells or metallic surfaces, or by electro-blaster bullets in flight.
  `uint8 spark.color` is the color (palette entry) which a spark particle's trail will be drawn as. This property gradually increases as the particle remains active, until it reaches `colorStop` (default 46, maroon), at which point the particle will disappear. The default initial value is 40 (yellow).
  `int8 spark.colorDelta` is the rate at which the `color` property is going to change. The most commonly used values for this are 1 and -1 meaning that the color value will respectively increase or decrease by 1 every time the game decides to modify it. The default value is 1.
  `uint8 spark.colorStop` is the `color` value which, when reached, will cause the particle to disappear. The default value is 46 (maroon).
star
  Corresponds to PARTICLE::STAR. Star particles were added to the game to indicate sugar rush. They are drawn as a rotated star sprite. Besides linear speed they also have angular speed and change colors.
  `uint8 star.angle` is the current angle of rotation the star is drawn at.
  `int8 star.angularSpeed` is how much the angle changes every tick, positive or negative. By default they do not rotate at all.
  `uint8 star.color` is the color the star is drawn as. The default value is 40 (yellow).
  `uint8 star.colorChangeCounter` counts down to 0, at which point it resets to the value of the colorChangeInterval property and changes the color property to one of the sprite colors at random.
  `uint8 star.colorChangeInterval` is how many ticks the particle goes before changing colors again. If it is set to 0 (the default value), however, the color will never change.
  `uint8 star.frame` determines whether the star is filled (0) or only an outline (1). The default value is 0.
  `uint8 star.size` is how large the star is. It decreases over time until it hits 0, at which point the particle is deleted. The default value is 16.
string
  Corresponds to PARTICLE::STRING. String particles move at ever-increasing speeds until they leave the screen, drawing up to eight consecutive characters as they go. They are traditionally used to show many points a player gained for destroying an object.
  `string string.text` is the series of characters that will be drawn. Strings longer than eight characters will be truncated.
tile
  Corresponds to PARTICLE::TILE. Tile particles move around according to their xSpeed/ySpeed properties and also gravity, and are drawn as single tiles (or quarters of single tiles) from the tileset used by the level. They are traditionally created from the destruction of destruct or collapse scenery blocks.
  `TILE::Quadrant tile.quadrant` specifies how much of the tile will be drawn to the screen. Possible values of TILE::Quadrant are TOPLEFT, TOPRIGHT, BOTTOMLEFT, BOTTOMRIGHT, and (default) ALLQUADRANTS.
  `uint16 tile.tileID` specifies which tile the particle draws in the first place. The default value is 0, so remember to change it.

### class jjCONTROLPOINT

This is a simple class representing a Domination game mode control point. It contains a set of read-only properties that let you obtain information about control points present in the level. Instances of the class can be accessed via the global jjControlPoints[16] property.

const TEAM::Color controlTeam
  The team that this point is presently under control of. Possible values for TEAM::Team are TEAM::NEUTRAL, TEAM::BLUE, TEAM::RED, TEAM::GREEN, and TEAM::YELLOW.
const int direction
  Direction of the gem sprite displayed by the control point. The value has purely visual influence on the game.
const string name
  The control point's name.
const float xPos
  Horizontal position in pixels.
const int xTile
  Horizontal position in tiles.
const float yPos
  Vertical position in pixels.
const int yTile
  Vertical position in tiles.

### class jjCHARACTER

This class contains a character profile, that is, a number of character-specific properties that determine various aspects of behavior followed by players who use the characters. Character profiles can be accessed using the jjCharacters[] array with CHAR::Char constants as its indexes.

AIR::Jump airJump
  Determines the character's reaction to pressing jump in the air. Possible values are AIR::NONE, AIR::HELICOPTER and AIR::DOUBLEJUMP. No effect for birds or frogs.
bool canHurt
  Whether the character can cause damage by using their special moves. This property also applies to Chuck's beak attack and Frog's tongue attack. Defaults true for all rabbits and false for birds and frogs.
bool canRun
  Whether the character is capable of fast movement. Defaults true for all rabbits and false for birds and frogs.
int doubleJumpCountMax
  The maximum number of jumps the character can perform in the air if their airJump property allows it. Defaults 1 for Spaz.
float doubleJumpXSpeed
float doubleJumpYSpeed
  What horizontal and vertical speed the character will gain by using double jump. doubleJumpYSpeed defaults -8 for Spaz. doubleJumpXSpeed defaults 0 and is relative to the player's current direction, so for example a negative doubleJumpXSpeed will make the character move backwards.
int helicopterDurationMax
  The maximum amount of time the character can keep using helicopter ears if their airJump property allows it.
float helicopterXSpeed
float helicopterYSpeed
  What horizontal and vertical speed the character will gain by using helicopter ears. helicopterYSpeed defaults 1 for all rabbits; its effects when <= 0 are currently undefined. helicopterXSpeed defaults 0 and is relative to the player's current direction, so for example a negative helicopterXSpeed will make the character move backwards.
bool morphBoxCycle
  Whether the character can be morphed to using the "Jazz<->Spaz" morph box. Also affects the jjPLAYER function morph when its rabbitsOnly argument is set to true.

### class jjWEAPON

This is a fairly simple class, containing several properties allowing for more control over weapons and their ammunition. The class exists solely in the jjWeapons[9] array.

bool allowed
bool allowedPowerup
  Whether the weapon and/or its powerup can be legally used in the level. This value is used by the server to detect attempts of cheating and kick offenders, and by all players to determine which weapons they get upon use of the `/ready` command. By default, this is determined by JJ2+ between onLevelLoad and onLevelBegin on the basis of the weapon's ammo and powerup occurring in the level or not, however, scripts may want to give players ammo or powerups despite their lack in the level, in case of which the appropriate properties should be set to true.
bool comesFromGunCrates
  Determines whether or not the ammo for the specified weapon can drop from Gun Crates and Gun Barrels. Defaults to true for WEAPON::BOUNCER, WEAPON::ICE, WEAPON::SEEKER, WEAPON::RF, WEAPON::TOASTER, and false for WEAPON::TNT, WEAPON::GUN8 and WEAPON::GUN9. Has no effect on WEAPON::BLASTER. In an online server, the pickups dropped will be according to the comesFromGunCrates settings for the player who destroyed the crate/barrel.
bool defaultSample
  When false, the default sample of the bullet will not be played.
int gemsLost
int gemsLostPowerup
  How many gems a player shot by this weapon will lose in Treasure Hunt. (If the player has fewer gems than the weapon should cause them to lose, they will lose all their gems.) Defaults to 10 for WEAPON::SEEKER, WEAPON::RF, WEAPON::TNT, WEAPON::GUN8, and WEAPON::GUN9, and 3 for the others, with powerup status making no difference by default. Doesn't have any effect when changed by clients, only by the server (or in local games).
bool gradualAim
  Whether it takes a few shots for bullets to adjust their direction to the direction the player is aiming, as is the case with pepper spray. If this weapon's spread property is SPREAD::GUN8 and jjAllowsFireball is true, this property will be treated as false.
bool infinite
  When true, ammo displays an infinity symbol for its quantity and can never be deplenished. Defaults to true for WEAPON::BLASTER. When spectating another player, the ammo count will appear according to your infinite setting for their currently chosen weapon, not their own.
int maximum
  Determines how much of each ammo type a player can hold at a time. Defaults to -1, which is interpreted as "99 in single player or cooperative, otherwise 50," but you may want to change some numbers individually (e.g. limit the number of seekers but not bouncers).
int multiplier
  The factor by which ammo pickups/powerups increase a weapon's ammo count, and by which that count is divided to be displayed onscreen. Defaults to 32 for WEAPON::TOASTER and 1 for everything else. When spectating another player, the ammo count will appear according to your multiplier setting for their currently chosen weapon, not their own.
bool replacedByBubbles
  When true, the bullet will be replaced by an air bubble when shot underwater. Defaults to true for WEAPON::TOASTER.
bool replacedByShield
  When true and a shield is active, shield ammo will replace default weapon ammo. Defaults to true for WEAPON::BLASTER.
  Under certain circumstances this setting is used to determine validity of network packets, so keep in mind that if its value is not the same for the host as it is for clients, they may be kicked for cheating.
bool replenishes
  When true, ammo jumps back up to 50 or 99 on level (re)load. Should be set in onLevelLoad, rather than onLevelBegin, since it actually takes effect between the two. Defaults to true for WEAPON::BLASTER.
SPREAD::Spread spread
  How many bullets are spawned by a single use of the weapon and in what manner. Possible values are:
  - NORMAL: Fires one bullet.
  - ICEPU: Fires two bullets; one in the direction you face, one in the direction you aim.
  - ICE: Same as NORMAL when not powered up, and ICEPU when powered up.
  - RFNORMAL: Fires two bullets.
  - RFPU: Fires three bullets.
  - RF: Same as RFNORMAL when not powered up, and RFPU when powered up.
  - TOASTER: Fires one bullet with its speed partially determined by how much fastfire the player has.
  - PEPPERSPRAY: Fires two pepper spray bullets.
  - GUN8: Same as NORMAL if jjAllowsFireball is true (but ignoring this weapon's gradualAim property), and PEPPERSPRAY when it's off.
  Any assignments of GUN8 or PEPPERSPRAY should be shared between clients and server to prevent kicking for invalid gun use.
WEAPON::Style style
  Determines how often will the weapon fire a bullet when the player holds the fire button. Possible values are WEAPON::NORMAL (fires continously, respecting the player's fastfire property), WEAPON::MISSILE (fires once per press of the button), WEAPON::POPCORN (fires continously and respects the player's fastfire property, but at a capped minimum rate of fire), and WEAPON::CAPPED (like NORMAL, but prevents players from shooting faster than allowed by their fastfire property).

### class jjSTREAM

The jjSTREAM class is used to save/transfer specific data. Its main functions are push and pop. You can create a jjSTREAM variable, and push any values onto it. Then, when you need to read from it, you pop them. jjSTREAM is a container working on FIFO basis, i.e. the variables are popped in the same order they were pushed. For example:

    jjSTREAM myStream; //create an empty stream
    int a = 4;
    string b = "TexT";
    bool c = false;
    myStream.push(a); //push the int "a" onto the stream
    myStream.push(b); //push the string "b" onto the stream
    myStream.push(c); //push the bool "c" onto the stream
    int d;
    string e;
    bool f;
    myStream.pop(d); //the int "d" will have the same value as pushed "a"
    myStream.pop(e); //the string "e" will have the same value as pushed "b"
    myStream.pop(f); //the bool "f" will have the same value as pushed "c"
    //now the stream is empty again.

It's very important to keep the order of removing variables from jjSTREAM the same as the order of inserting them into it, as well as keep consistency in use of variable types. Attempts to pop a variable of different type than the pushed type will not be blocked but will generally result in obtaining a wrong section of data.

As you can imagine, the potential for usage of this class as a container is fairly limited and most of the time you'd prefer to use the built-in array class instead. The real strengths of jjSTREAM show when you implement networking or file manipulation in your script.

To demonstrate file manipulation capabilities of jjSTREAM, let's assume your single player campaign requires that various items be carried between levels. JJ2+ doesn't currently offer means of transfering custom data between separate levels, but it allows you to save a temporary file in one level that you can then load in another. The following code shows possible implementation of functions that would do so.

    array<string> items; //In this example we assume items are stored as text strings, representing their names. However, any other basic type, such as int identifiers, would work as well.
    bool saveItems() {
    	jjSTREAM file;
    	file.push(items.length()); //The first uint in the file will represent the number of items.
    	for (uint i = 0; i < items.length(); i++) {
    		file.push(items[i]); //The rest of the file will consist of elements of the items array.
    	}
    	return file.save("items.asdat"); //Return whether successful.
    }
    bool loadItems() {
    	jjSTREAM file("items.asdat");
    	uint length;
    	if (file.pop(length)) { //Pop the first uint, assumed to be the number of items. If anything fails, abort the entire operation.
    		items.resize(length);
    		bool success = true;
    		for (int i = 0; i < items.length() && success; i++) {
    			success = file.pop(items[i]); //Retrieve consecutive elements of the items array from the file in the same order they were saved.
    		}
    		return success;
    	}
    	return false;
    }

The exact way you write and read files will of course vary depending on what information you need to save, but you can expect it to be generally based around this scheme.

Networking works in a similar way as file manipulation but has its own quirks, especially due to the distinction between a server and clients. We'll demonstrate it using a fragment of a hypothetical script that allows players to press buttons and pull levers, each time informing all players in the server about the performed action. We'll think of a button as of something that doesn't have a state, and each time it's pressed it performs the same action. On the other hand, a lever will be something that at any given time can be either on or off. The fragment assumes that all buttons and levers in the level already have unique IDs assigned to them elsewhere, that are the same for the server and all clients.

    enum packet_type {packet_button, packet_lever, packet_all_levers}; //We enumerate possible packet types. You will probably want to do this in almost every script that uses streams, as it simplifies the process of giving them more than one purpose. In our case, we want packets that inform about pressing buttons, pulling levers, and a special packet that informs newly joining clients about states of all levers in the level.
    array<bool> leverStates;
    void performButtonAction(jjPLAYER@ player, int buttonID) {
       //...
    }
    void sendButtonPacket(int8 playerID, int buttonID, int skippedClientID) {
       jjSTREAM packet;
       packet.push(uint8(packet_button));
       packet.push(playerID);
       packet.push(buttonID);
       jjSendPacket(packet, -skippedClientID);
    }
    void pressButton(int8 playerID, int buttonID) {
       performButtonAction(jjPlayers[playerID], buttonID);
       sendButtonPacket(playerID, buttonID, 0);
    }
    void sendLeverPacket(int8 playerID, int leverID, int skippedClientID) {
       jjSTREAM packet;
       packet.push(uint8(packet_lever));
       packet.push(playerID);
       packet.push(leverID);
       packet.push(leverStates[leverID]);
       jjSendPacket(packet, -skippedClientID);
    }
    void pullLever(int8 playerID, int leverID) {
       leverStates[leverID] = !leverStates[leverID];
       sendLeverPacket(playerID, leverID, 0);
    }
    void onLevelLoad() {
       if (!jjIsServer) {
          jjSTREAM packet;
          packet.push(uint8(packet_all_levers));
          jjSendPacket(packet); //When the level loads for clients, they request status of all levers from the server by sending a packet_all_levers packet.
       }
    }
    void onReceive(jjSTREAM &in packet, int clientID) {
       uint8 type;
       packet.pop(type);
       if (jjIsServer) {
          switch (type) {
             case packet_button: //A client reports having pressed a button.
                {
                   int8 playerID;
                   packet.pop(playerID);
                   if (jjPlayers[playerID].clientID == clientID) { //Check if the received player ID really belongs to the client who sent the packet. Otherwise we might have to do with a hacking attempt.
                      int buttonID;
                      packet.pop(buttonID);
                      performButtonAction(jjPlayers[playerID], buttonID);
                      sendButtonPacket(playerID, buttonID, clientID);
                   }
                }
                break;
             case packet_lever: //A client reports having pulled a lever.
                {
                   int8 playerID;
                   packet.pop(playerID);
                   if (jjPlayers[playerID].clientID == clientID) { //Check if the received player ID really belongs to the client who sent the packet. Otherwise we might have to do with a hacking attempt.
                      int leverID;
                      packet.pop(leverID);
                      packet.pop(leverStates[leverID]);
                      sendLeverPacket(playerID, leverID, clientID);
                   }
                }
                break;
             case packet_all_levers: //A client requests states of all levers.
                {
                   jjSTREAM response;
                   response.push(uint8(packet_all_levers));
                   for (uint i = 0; i < leverStates.length(); i++) {
                      response.push(leverStates[i]);
                   }
                   jjSendPacket(response, clientID);
                }
                break;
             //A default case might also be in place to react to hacking attempts. To simplify the example it was not included.
          }
       } else {
          switch (type) {
             case packet_button: //The server informs about a button having been pressed.
                {
                   int8 playerID;
                   int buttonID;
                   packet.pop(playerID);
                   packet.pop(buttonID);
                   performButtonAction(jjPlayers[playerID], buttonID);
                }
                break;
             case packet_lever: //The server informs about a lever having been pulled.
                {
                   int8 playerID;
                   int leverID;
                   packet.pop(playerID);
                   packet.pop(leverID);
                   packet.pop(leverStates[leverID]);
                }
                break;
             case packet_all_levers: //The server responds to request for all lever states.
                for (uint i = 0; i < leverStates.length(); i++) {
                   packet.pop(leverStates[i]);
                }
                break;
          }
       }
    }

The example may seem arbitrary, but it contains all types of packets you want to send most of the time. Generally all network actions you will want to take are: report an event occurred (analogous to buttons), report a state of something changed (analogous to levers), or request state of the entire level when you join it (analogous to packet_all_levers in the above example).

jjSTREAM()
jjSTREAM(const string &in filename)
  Constructs an instance of the class. The default constructor creates an empty stream. If filename is provided, the stream will be built from the file's contents.

jjSTREAM& operator = (const jjSTREAM &in stream)
  Assigns contents of one stream to another and returns a reference to self.

void clear()
  Clears content of the stream leaving it empty.
bool discard(uint count)
  Discards count bytes from the front of the stream. Returns whether successful, i.e. whether the stream contained sufficiently many bytes to perform the operation. If the operation fails, the stream is left in an undefined state.
uint getSize() const
  Returns size of the stream in bytes.
bool isEmpty() const
  Returns whether the stream is empty, i.e. whether getSize() == 0.
bool pop(T &out var)
  Pops value from the stream into var, causing var to be filled and reducing the size of the stream. Returns true on success and false on failure. Supported types are all primitive types (int and its size variations, uint and its size variations, bool, float and double) as well as strings.
bool push(const T &in value)
  Pushes value onto the stream. Returns true on success and false on failure. Supports the same types as pop.
bool save(const string &in filename) const
  Saves contents of the stream to file filename and returns whether successful.
  There are several limits to this method. The file cannot be saved in any directory other than the default one, which is the directory containing the executable (for local games and servers) or cache (for clients). File extension has to be ".asdat" and if any other or no extension is provided in the string, it will be replaced. Additionally, scripts downloaded from a server can only save up to 16 files on the computer of a client; they are, however, allowed to overwrite files they saved previously.

### class jjBEHAVIOR

jjBEHAVIOR is a very simple class that exposes no properties or methods (other than operator overloads) to users. Its sole purpose is to be used as the behavior property of jjOBJ instances and occasionally an argument of their behave method. At its core jjBEHAVIOR contains only one value, which is either a BEHAVIOR::Behavior constant or an AngelScript behavior function handle. In terms of C-based programming languages, it can be considered a union.

As it's based around implicit conversions, you rarely have to keep existence of this class in mind.

jjBEHAVIOR(const BEHAVIOR::Behavior &in behavior = BEHAVIOR::INACTIVE)
  Constructs an instance of the class and assigns behavior as its value.

jjBEHAVIOR& operator = (const jjBEHAVIOR &in behavior)
jjBEHAVIOR& operator = (const BEHAVIOR::Behavior &in behavior)
jjBEHAVIOR& operator = (const jjVOIDFUNCOBJ@ &in behavior)
  Assigns behavior to the instance and returns a reference to self.
bool operator == (const jjBEHAVIOR &in behavior) const
bool operator == (const BEHAVIOR::Behavior &in behavior) const
bool operator == (const jjVOIDFUNCOBJ@ &in behavior) const
  Compares the instance with behavior and returns whether they are the same.

### class jjPAL

This is the class used by each level's ingame palette, used for drawing pretty much everything besides 16-bit water and textured background fade colors, which can be changed by the global function groups jjSetWaterGradient and jjSetFadeColors respectively. As such, changes made here can be very far-reaching and dramatic. Palette-editing in AngelScript follows essentially a two step process: first, make all the changes you desire to a (non-const) jjPAL object, and second, call that object's apply function to make it be the current palette in use by the game. Here's a basic example:

    void onLevelLoad() {
    	jjPAL newPal;
    	newPal.load("Castle1.j2t");
    	newPal.gradient(190,0,255, 0,0,147, 112, 8); //change the colors of the knight statues
    	newPal.apply(); //the game will now use a slightly-modified version of the Castle1 palette
    }

In the simple example above, it would not actually be needed to declare a new jjPAL object. Instead, you could use jjPalette, which contains the current palette actually in use by the game. When you call apply on any other jjPAL object, you copy its contents to jjPalette. Editing it directly is good for cumulative changes, but if you'd like to revert back to a previous state, you'll need a second jjPAL object. Alternatively, you could save the current state by copying it to a second jjPAL object using the assignment operator, e.g. `jjPAL myPal = jjPalette;`.

There is also a const jjPAL, jjBackupPalette, which maintains the palette that came with the tileset and will never change.

jjPAL()
  Constructs a default instance of the class. The resulting palette has all colors set to black.

jjPAL& operator = (const jjPAL &in palette)
  Copies contents of one palette to another. Returns a reference to self.
bool operator == (const jjPAL &in palette) const
  Compares two palettes and returns true if all pairs of fields on corresponding indexes are identical or false otherwise.

jjPALCOLOR color[256]
  At its heart, a palette is a collection of 256 colors, and you can access them individually through the color array. In fact, you could if you chose reproduce nearly every jjPAL method here by directly altering the colors, but the methods are here to save you time, so don't do that. The details of jjPALCOLOR objects are listed below this section, though most basically you just use their properties red, green, and blue.
void apply() const
  Causes the contents of this jjPAL object to be the current colors in use by the game, and by extension jjPalette.
  Note that this function does a fair bit of housekeeping behind the scenes to make sure everything is propertly converted to the new palette—there's a difference between `myPal.apply();` and `jjPalette = myPal;`—and so it should always be called after doing any edits, even if you've been making the edits directly to jjPalette. Failure to do so can have unpredictable consequences.
void copyFrom(uint8 start, uint8 length, uint8 start2, const jjPAL &in source, float opacity)
  Overlays length colors from source onto the current palette, beginning at start on this palette and start2 on source. If opacity is below 1.0, the new colors will only partially replace the old.
void fill(uint8 red, uint8 green, uint8 blue, uint8 start, uint8 length, float opacity = 1.0)
void fill(uint8 red, uint8 green, uint8 blue, float opacity = 1.0)
  Replaces a series of length colors beginning at start with the color red,green,blue. If opacity is specified and below 1.0, the new color will only partially replace the old ones, making for a tinting effect. Leave out the start and length arguments to fill (or tint) the entire palette.
uint8 findNearestColor(jjPALCOLOR color) const
  Returns index of the palette entry whose components differ the least from those of the provided color. Ensures the result has no special treatment by never returning values from the range of 0-15.
void gradient(uint8 red1, uint8 green1, uint8 blue1, uint8 red2, uint8 green2, uint8 blue2, uint8 start = 176, uint8 length = 32, float opacity = 1.0)
  Replaces a series of length colors beginning at start with a gradient beginning with red1,green1,blue1 and ending with red2,green2,blue2. If opacity is specified and below 1.0, the new colors will only partially replace the old, making for a tinting effect.
  The default values for start and length will set a gradient for the colors used by water and (in most tilesets) textured backgrounds.
bool load(string& filename)
  Loads a palette from the specified file. If the file extension is ".j2t", will treat it as a tileset file and will try to access the palette stored in the file. Otherwise, tries to treat it as a palette file saved in "Color Map" format in Palette Suite, and if that fails, simply reads the first 1024 bytes of the file. Returns false if the file cannot be found in either the main game folder or the cache subfolder, or if the file is fewer than 1024 bytes long; otherwise true.
void reset()
  Loads the original colors used by the tileset. There is no non-stylistic difference between `myPal.reset();` and `myPal = jjBackupPalette;`.

### class jjPALCOLOR

The basic units of jjPAL objects. At their heart, they're just tiny collections of RGB values, but methods for playing with the HSL values are also provided in case you don't mind using up a little bit more processing power. You can define and initialize your own jjPALCOLOR instances in two ways: either by using the default constructor and setting each property manually or by using a provided jjPALCOLOR(uint8 red, uint8 green, uint8 blue) constructor. Alphabetical order is ignored in favor of traditional order in the list below

jjPALCOLOR()
jjPALCOLOR(uint8 red, uint8 green, uint8 blue)
  Constructs an instance of the class. The default constructor results in black color, i.e. one having all component values set to 0. The other constructor builds an instance with the provided RGB values.

jjPALCOLOR& operator = (const jjPALCOLOR &in color)
  Does a component-wise assignment of one instance to another. Returns a reference to self.
bool operator == (const jjPALCOLOR &in color) const
  Performs a component-wise comparison and returns true if all components of both instances have the same values or false otherwise.

uint8 red
  How red this palette color is.
uint8 green
  How green this palette color is.
uint8 blue
  How blue this palette color is.
uint8 getHue() const
  The hue of this palette color.
uint8 getSat() const
  The saturation of this palette color, where 0 is grayscale and 255 is most saturated.
uint8 getLight() const
  The lighting of this palette color, where 0 is black and 255 is white.
void setHSL(int hue, uint8 sat, uint8 light)
  Changes the entire color to a brand new one derived from the given HSL values. Due to the complexity of the calculations involved, you can only change all three values at a time, so if you want to leave, for instance, saturation constant, you'll need to read the old value through getSat and then use that as the sat parameter.
  hue is an int instead of a uint8 because it's a loop instead of a scale. 2, 258, 514, -254, etc., are all equally valid and all mean the same hue.
void swizzle(COLOR::Component red, COLOR::Component green, COLOR::Component blue)
  Swaps values of selected components of the color. The value of the red component will be replaced with that specified by parameter red, the value of the green component with that specified by green and the value of blue with that specified by blue. Values accepted by the parameters are COLOR::RED, COLOR::GREEN and COLOR::BLUE.

### class jjCANVAS

The jjOBJ::draw method is okay, but sometimes you just need to draw things to the screen that aren't jjOBJs. The upper right corner of the player's screen shows how much health they have, for example... how can you get in on that action? The answer is the jjCANVAS class, which presents the JJ2 window as a canvas for you to draw stuff on.

Probably the most straightforward use of jjCANVAS is for drawing your own HUD elements, for which the following hooks are presently available. Note that these hooks have bool return values: if you return true, JJ2 will not draw the standard visuals for that subject. So if you want to show a player's health in the upper left instead of the upper right, for example, not only will you need to call jjCANVAS methods to draw the health where you want it to go, you'll also need to have onDrawHealth return true.

    bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {}
    bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {}
    bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {}
    bool onDrawPlayerTimer(jjPLAYER@ player, jjCANVAS@ canvas) {}
    bool onDrawScore(jjPLAYER@ player, jjCANVAS@ canvas) {}

Each of the above hooks presents you with an entire player screen to draw HUD-style elements upon. If you're curious, the origin of the screen may be determined through the subscreenX and subscreenY properties of the player, and its size by the jjSubscreenWidth and jjSubscreenHeight global properties. Any (portion of any) graphic to be drawn outside of that rectangle will simply be ignored, so don't worry too much about fitting into the boundaries.

There are additionally eight hooks which draw to the level instead of the HUD, as determined by the positions of each of the eight layers, in the format `void onDrawLayer#(jjPLAYER@ player, jjCANVAS@ canvas)` where # is any number from 1 to 8. These hooks are only called for those layers for which jjLayerHasTiles[] is true, and let you draw tiles not fixed to the 32x32 grid, text in front of sign tiles, or whatever else you may please. Just note that jjCANVAS's drawing methods are just that—drawing methods—and do not actually add tiles or enemies or anything to the level to be interacted with. Additionally, the layer hooks don't care if a layer has 'Tile Width' or 'Tile Height' checked, and will instead only perform each drawing operation once per player subscreen.

Most of the methods below take a pair of parameters SPRITE::Mode mode and uint8 param. A full list of options and their explanations can be found in the appendix, but briefly, the sprite modes control how the basic individual pixel colors of the image (sprite, rectangle, whatever) you're drawing will turn out as colors on the screen. For example, consider drawing the first frame of the powered-up toaster animation:

    bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ screen) {
    	screen.drawSprite(50, 50, ANIM::AMMO, 0, 0, 1, SPRITE::NORMAL, 123); //if using SPRITE::NORMAL, param's value doesn't actually matter; 123 is as good as any
    	return false;
    }

The method as called above uses SPRITE::NORMAL (the default value), which draws each pixel of the sprite exactly as it appears in anims.j2a, but you have plenty of other options. `SPRITE::PALSHIFT, 16` will draw the sprite as pink instead of blue (16 colors farther down in the palette). `SPRITE::BRIGHTNESS, 64` will draw the sprite as dark blue (64 is half of 128, which for SPRITE::BRIGHTNESS is the normal brightness value) instead of bright blue. `SPRITE::BLEND_LUMINANCE, 255` (the BLEND_ sprite modes use 255 to signify complete opacity) will draw each pixel of the sprite using the colors (but not the brightness) of the pixel that was already there on the screen. JJ2's commonest sprite modes are NORMAL, TRANSLUCENT, FROZEN, PLAYER, and (for coloring text characters) PALSHIFT, but JJ2+ provides you with many more options, so try them all! Note also that unlike in previous releases of JJ2+, your choice of SPRITE::Mode does not determine whether sprites may be flipped horizontally or not.

Alphabetical order is defied slightly while listing the methods of jjCANVAS, in order to present the most fundamental method first and then its variants afterwards.

void drawSprite(int xPixel, int yPixel, int setID, uint8 animation, uint8 frame, int8 direction = 0, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
  Draws a single sprite frame at xPixel,yPixel. The setID, animation, and frame parameters correspond to the "Set ID," "Animation," and "Frame #" sliders respectively in Jazz Sprite Dynamite, but are 0-indexed. You are allowed to use a simple integer to specify the setID, but an ANIM::Set constant is strongly recommended, since the values for certain sets differ between 1.23 and 1.24; the full list of constants can be found in the appendix at the bottom of this file. For an example, to draw a broken bird cage (9-5-1 in Jazz Sprite Dynamite), you would write `ANIM::BIRD, 4, 0`.
  The direction parameter determines which direction the sprite will be facing; values 0 to 63 will not flip the sprite, -64 to -1 will flip it horizontally, 64 to 127 will flip it vertically, and -128 to -65 will flip it both horizontally and vertically. Alternatively to integer values, you can use constants made for this very purpose that belong to the SPRITE::Direction enum: SPRITE::FLIPNONE, SPRITE::FLIPH, SPRITE::FLIPV, SPRITE::FLIPHV.
  Note again that unlike in past releases of JJ2+, the value of mode has no effect on whether the sprite may be flipped.
void drawSpriteFromCurFrame(int xPixel, int yPixel, uint sprite, int8 direction = 0, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
  The same as drawSprite, but takes a single sprite parameter instead of one each for Set, Animation, and Frame. The name references the curFrame property of jjOBJ and jjPLAYER, whose real significance is as an index to jjAnimFrames[].
  Internally any draw*Sprite method is actually a call to the corresponding draw*SpriteFromCurFrame method, with the following formula applied to find the curFrame value: `(jjAnimations[jjAnimSets[setID].firstAnim + animation].firstFrame + frame) % jjAnimations[jjAnimSets[setID].firstAnim + animation].frameCount`
void drawResizedSprite(int xPixel, int yPixel, int setID, uint8 animation, uint8 frame, float xScale, float yScale, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
void drawResizedSpriteFromCurFrame(int xPixel, int yPixel, uint sprite, float xScale, float yScale, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
  Draws a single resized sprite frame at xPixel,yPixel. This swaps out drawSprite's direction argument with a pair of float arguments, xScale and yScale, which determine how much the sprite is enlarged (or shrunken) on each axis. For example, a 32x32 sprite drawn with xScale 3 and yScale 1.5 would be drawn across a 96×48 area. Making one or both scale values negative will cause the sprite to be flipped horizontally and/or vertically. No anti-aliasing will be performed while resizing the sprite.
void drawRotatedSprite(int xPixel, int yPixel, int setID, uint8 animation, uint8 frame, int angle, float xScale = 1, float yScale = 1, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
void drawRotatedSpriteFromCurFrame(int xPixel, int yPixel, uint sprite, int angle, float xScale = 1, float yScale = 1, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
  Draws a single resized and rotated sprite frame at xPixel,yPixel. This carries over the xScale and yScale arguments from drawResizedSprite, and adds a new angle parameter with the same 0-1023 domain as jjSin and jjCos. (Numbers outside the domain will be seemlessly moduloed.) An angle of 0 will not rotate the sprite at all. Without getting into matrix math, you can think of the sprite as being resized before it is rotated, which in most cases is exactly what you'd want. For example, a 32×32 sprite drawn with angle 256, xScale 2, and yScale 1 would be drawn across an area of 32×64, not 64×32.
  Unlike in previous releases of JJ2+, any sprite at all may be rotated. However, rotated sprites should be no larger (before transformations are applied) than 128 pixels wide or 256 pixels high. The results of drawing larger sprites are, for now, undefined while we decide whether this restriction should or can be removed.
  Any sprite that drawSprite can draw, drawResizedSprite can draw in the same way by setting xScale and yScale each to 1. And any sprite that drawResizedSprite can draw, drawRotatedSprite can draw unless it's greater than 128×256, by setting angle to 0. As you might imagine, though, the simpler methods (besides being quicker to write) run faster than the more complicated methods, so you might as well use them when you can get away with it.
void drawSwingingVineSpriteFromCurFrame(int xPixel, int yPixel, int sprite, int length, int curvature, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
  Draws a single sprite frame at xPixel,yPixel in the style of a Swinging Vine object. The parameters named length and curvature correspond respectively to properties var[1] (normally always 128) and var[2] of swinging vines; if the sprite is shorter than the length value, its pixels will be repeated again from the top as necessary.
  For boring internal code reasons, this method expects that sprites will have no transparent pixels. Instead, to tell this method not to draw an individual pixel from the frame, its value must be 128 instead of 0. Using normal transparent pixels instead will cause the sprite to be drawn as a jumbled mess.
void drawPixel(int xPixel, int yPixel, uint8 color, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
  Changes the color of the pixel at xPixel,yPixel into the palette color corresponding to index color. If you are drawing anything more than a handful of pixels at a time, you should consider creating a new sprite of your own using jjPIXELMAP, and then drawing that sprite as a single unit with some other drawing method.
void drawRectangle(int xPixel, int yPixel, int width, int height, uint8 color, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0)
  Draws a rectangle with one of its vertices at xPixel,yPixel of specified width and height in the palette color corresponding to index color.
void drawString(int xPixel, int yPixel, const string &in text, STRING::Size size = STRING::SMALL,
STRING::Mode mode = STRING::NORMAL, uint8 param = 0)
void drawString(int xPixel, int yPixel, const string &in text, STRING::Size size, const jjTEXTAPPEARANCE &in appearance, uint8 param1 = 0, SPRITE::Mode spriteMode = SPRITE::PALSHIFT, uint8 param2 = 0)
  Draws a string of characters of your choice at position (xPixel, yPixel) on the screen. Unique size values are SMALL, MEDIUM, and LARGE. The following options are available for the mode parameter:
  - NORMAL: Draws a left-aligned string as if drawing chat or player names; | changes the text color after itself, and @ and # are ordinary symbols.
  - DARK: Draws a left-aligned string with darkened letters, as if unselectable. |, @, and # have no effect.
  - RIGHTALIGN: Draws a right-aligned string with normal letters. |, @, and # have no effect.
  - SPIN: Draws a left-aligned string whose letters spin around in place. | has no effect; @ introduces a newline; # makes every letter after it a new color. The param value determines how close together the letters are: 0 for a fairly ordinary looking string, or 255 for complete chaos, with other numbers falling in-between.
  - BOUNCE: Draws a left-aligned string whose letters bounce up and down in place. |, @, and # have no effect. The param value determines how close together the letters are: 0 for a fairly ordinary looking string, or 255 for complete chaos, with other numbers falling in-between.
  - PALSHIFT: Draws a left-aligned string with normal letters whose colors are all shifted param palette entries. A value of 24, for instance, would produce purple text, or 32 would enter into the non-sprite-color portion of the palette. |, @, and # have no effect.
  Most of these modes use alignment of STRING::DEFAULT, meaning that values of xPixel higher than 0x4000 have a special meaning in drawing strings, most noticably changing their alignment on the screen. View documentation for jjTEXTAPPEARANCE::align for more detail.
  In the second prototype of the function, arguments mode and param are split into four arguments appearance, param1, spriteMode and param2 for more customizability: appearance controls several different aspects of text discussed in the jjTEXTAPPEARANCE section, param1 is a multiplier applied to amplitude of text determined by appearance, spriteMode is the sprite mode the characters will be drawn in, and param2 is the sprite mode parameter. Notice that the choice of spriteMode will also determine the effect of using special characters such as # and | in text (if appearance allows them to take effect).
void drawTile(int xPixel, int yPixel, uint16 tile, TILE::Quadrant tileQuadrant = TILE::ALLQUADRANTS)
  Draws a tile of your choice at some position on the screen. This is purely a drawing operation; using it to draw masked tiles to the screen does not create masked areas for players or other objects to interact with. Possible values of tileQuadrant are TOPLEFT, TOPRIGHT, BOTTOMLEFT, BOTTOMRIGHT, and (default) ALLQUADRANTS.

### class jjTEXTAPPEARANCE

jjTEXTAPPEARANCE is a structure used as an argument in functions that draw or otherwise operate on text, determining its traits such as alignment, treatment of special symbols, and amplitude of movement. Functions that currently rely on this class are jjCANVAS::drawString, jjDrawString, and jjGetStringWidth.

The majority of jjTEXTAPPEARANCE properties are of type STRING::SignTreatment and determine whether a symbol should be hidden, displayed normally, or have special behavior. These three treatments correspond respectively to constants HIDESIGN, DISPLAYSIGN and SPECIALSIGN.

jjTEXTAPPEARANCE()
jjTEXTAPPEARANCE(STRING::Mode mode)
  Constructs an instance of the class. The default constructor creates an instance with 0 horizontal and vertical amplitude, default alignment, and no special treatment for any symbols. The constructor from STRING::Mode uses values specific for that mode instead, according to the following table, where "H" and "D" and "S" stand for HIDESIGN, DISPLAYSIGN, and SPECIALSIGN respectively:

  | align | at | caret | hash | newline | pipe | section | tilde | skipInitialHash | xAmp | yAmp
  --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
  NORMAL | DEFAULT | D | S | D | H | S | S | D | true | 0 | 0
  DARK | DEFAULT | D | S | D | H | H | S | D | true | 0 | 0
  RIGHTALIGN | RIGHT | D | S | D | H | H | H | H | false | 0 | 0
  BOUNCE | DEFAULT | D | S | D | H | H | H | H | false | 0 | 1
  SPIN | DEFAULT | S | S | S | H | H | S | S | false | 1 | 1
  PALSHIFT | DEFAULT | D | S | D | H | H | H | D | false | 0 | 0

jjTEXTAPPEARANCE& operator = (STRING::Mode mode)
  Assigns text display traits specific to mode to the instance. Returns a reference to self.

STRING::Alignment align
  Horizontal alignment of text. This can take values of DEFAULT, LEFT, CENTER or RIGHT.
  Whereas LEFT, CENTER and RIGHT should be self-explanatory, DEFAULT is the value JJ2 used for all strings originally, defined as follows: if xPixel is less than 0x4000, the string will have its left side aligned to the left side of the screen or layer. If in the range of 0x4000 to 0xFFFF, the string will be center-aligned relative to 0x8000, so for instance 0x8020 would align the center of the string to 0x20 pixels right of the center of the screen. Finally, if xPixel is greater than or equal to 0x10000, it will be drawn with its right side aligned to the right side of the screen instead, with higher values moving farther left. Note that these special cases currently apply only for drawing to the screen; the use of values of 0x4000 or higher for `onDrawLayer#` hooks is currently undefined.
STRING::SignTreatment at
  Treatment of the at ('@') character. If it's SPECIALSIGN, at will begin a new line of text 20 pixels below the previous one.
STRING::SignTreatment caret
  Treatment of the caret ('^') character. If it's SPECIALSIGN, caret will be displayed as infinity signs.
STRING::SignTreatment hash
  Treatment of the hash ('#') character. If it's SPECIALSIGN, characters following hash will use a pattern of colors (or other parameters depending on sprite mode of the string).
STRING::SignTreatment newline
  Treatment of the line feed character (created by the escape sequence '\n' in AngelScript). If it's either DISPLAYSIGN or SPECIALSIGN, line feed will begin a new line of text below the previous one, with vertical spacing varying depending on font size.
STRING::SignTreatment pipe
  Treatment of the pipe ('|') character. If it's SPECIALSIGN, pipe will change text color (or other parameter depending on sprite mode of the string) of the following characters.
STRING::SignTreatment section
  Treatment of the section sign ('§') character. If it's SPECIALSIGN, section sign will use ASCII code of the directly following character to determine spacing used for further text.
bool skipInitialHash
  Whether or not to ignore the first character of a string if it's a hash ('#'). The ignored hash will not be displayed or otherwise affect the string. If the string is preceded by more than one hash, all but the first one will be displayed.
  This property is provided mainly for compatibility with several STRING::Mode values, as it's used by JJ2 wherever strings are meant to be conditionally colored, such as menu items.
STRING::SignTreatment tilde
  Treatment of the tilde ('~') character. If it's SPECIALSIGN, tilde will cancel the special effect of hash or, if it's already inactive, it will change the color (or other parameter depending on sprite mode) to default.
int xAmp
int yAmp
  Horizontal and vertical amplitudes of text movement.

### class jjANIMSET

JJ2's thousands of sprites and animations are accessible through AngelScript as a three-tiered system of classes and matching global arrays. If you're familiar with the contents of anims.j2a from Jazz Sprite Dynamite, that should make it easier to understand how this works; most simply, the three tiers of classes/arrays (jjANIMSET, jjANIMATION, jjANIMFRAME) correspond to the three scrollbars (Set ID, Animation, Frame #) in JSD, although JSD uses 1-indexed numbers for everything and JJ2+ does not. To take a concrete example, the sprite frame used by the Cake pickup is Set ID=68, Animation=13, and Frame #=1 in JSD. (Or Set ID=72 in 1.24.) In AngelScript terms, this is `jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PICKUPS].firstAnim + 12].firstFrame]`.

An image may help to explain the three arrays. The lowest level, jjAnimFrames, is a long series of individual animation frames (sprites) with some basic properties for setting the hotspots, default transparency, and the like. (To edit the actual sprite image, you'll need the jjPIXELMAP class described below.) Individual frames have no idea where they fit in any given animation; that is handled by the jjAnimations array, each entry in which defines a single animation by its starting frame (firstFrame) and number of frames (frameCount). Finally the jjAnimSets array points you to the first animations of each anim set, and additionally carries a few methods for creating new anim sets either from scratch or from specific .j2a files.

A few examples should make clearer the three arrays' (and classes') uses and relationships. As seen above, the twenty animations and many corresponding frames of ANIM::BIRD are always loaded immediately before the ninety-five animations and many frames of ANIM::PICKUPS. An animation is essentially nothing more than a first frame and a frame count. So if you wanted Extra Life pickups to use the dead bird animation for some reason, there are (at least) three ways to do that:

    	#1
    jjObjectPresets[OBJECT::EXTRALIFE].curAnim = jjAnimSets[ANIM::BIRD].firstAnim + 19;
    	#2
    jjANIMATION@ extraLifeAnimation = jjAnimations[jjAnimSets[ANIM::PICKUPS].firstAnim];
    extraLifeAnimation.frameCount = 1;
    extraLifeAnimation.firstFrame -= 1;
    	#3
    jjAnimations[jjAnimSets[ANIM::PICKUPS].firstAnim] = jjAnimations[jjAnimSets[ANIM::BIRD].firstAnim + 19];

Crucially, none of those options would have any effect on what actual birds looked like after being roasted; any frame may be used in any number of animations. Moreover, the boundary lines between animations are completely arbitrary. Consider the following code:

    jjAnimations[jjAnimSets[ANIM::PICKUPS].firstAnim].firstFrame -= 5;

This would cause Extra Lives (and any other objects using the same animation) to display an animation ten frames long: four frames of a bird sitting idly in place, one frame of a roasted bird, and five frames of green "1UP" text. The reason for this is that `jjObjectPresets[OBJECT::EXTRALIFE].curAnim` is, on level load, exactly equal to `jjAnimSets[ANIM::PICKUPS].firstAnim`. (Likewise, `jjObjectPresets[OBJECT::APPLE].curAnim` is equal to `jjAnimSets[ANIM::PICKUPS].firstAnim+1`, and so on.) Anytime you see a property or argument called curFrame, that means it is an index for jjAnimFrames[]. Likewise, anything called curAnim is an index for jjAnimations[], and anything called setID is an index for jjAnimSets[].

At the top level is the jjAnimSets array, which loads new series of animations and frames from various .j2a files and then points you to where they can be found in the lower two arrays. At the beginning of a level, all three animation arrays are nearly empty (jjAnimFrames and jjAnimations each contain an empty entry at index [0] for various internal purposes). Then JJ2+ runs the following line of code internally:

    jjAnimSets[ANIM::FONT].load(ANIM::PLUS_FONT, "plus.j2a");

This opens up the plus.j2a file and discovers that ANIM::PLUS_FONT is exactly four animations long. It loads those animations (basically their firstFrame and frameCount properties) into jjAnimations[1–4]. Between them, those four animations include 224+224+224+2=674 frames, so those (images, hotspots, etc.) are loaded into jjAnimFrames[1–674]. And lastly, `jjAnimSets[ANIM::FONT].firstAnim` (and `jjAnimations[jjAnimSets[ANIM::FONT].firstAnim].firstFrame`) are set to 1. Next:

    jjAnimSets[ANIM::JAZZ].load(); //in full: .load(ANIM::JAZZ, "anims.j2a");

ANIM::JAZZ has a massive 104 different animations (or 78 in TSF). But jjAnimations[1–4] are already in use, so JJ2+ loads the first ANIM::JAZZ animation into jjAnimations[5], and carries on the rest from there. Likewise the hundreds of individual frames are loaded into jjAnimFrames starting at jjAnimFrames[675], where the last jjANIMSET::load call left off. And `jjAnimations[jjAnimSets[ANIM::JAZZ].firstAnim].firstFrame` is set to 675, and the firstFrame properties of the remaining animations from ANIM::JAZZ are also adjusted accordingly.

So the process continues, loading first all the animations that every level uses, then a few level-specific ones (mostly enemies), then a few more that JJ2+ thinks might be useful. After that, you are free to mess up the existing order of frames and animations in any way you like, or simply to load or otherwise create all-new ones. Any call to jjANIMSET::load or jjANIMSET::allocate appends new entries to the ends of jjAnimFrames and jjAnimations, adjusting the animations' firstFrame properties to match their new positions, and then sets jjANIMSET::firstAnim to match. If an anim set has not yet been loaded from any .j2a file (or allocated), its firstAnim will equal 0.

And now, the list of jjANIMSET's methods and properties:

operator uint () const
  Implicit conversion to uint; returns firstAnim.

uint firstAnim
  ID of the first animation in the set, to be used with the jjAnimations array.
jjANIMSET@ allocate(const array<uint> &in frameCounts)
  Creates space for an animation set with the number of animations equal to length of frameCounts, each with a number of frames determined by the corresponding frameCounts array element.
jjANIMSET@ load(uint fileSetID = 2048, const string &in filename = "")
  Loads animation set specified by fileSetID from a j2a file specified by filename. Using the default value of fileSetID results in loading the set appropriate for the jjANIMSET it's called on. If filename is left empty, it deduces the file name from fileSetID, using either Anims.j2a or plus.j2a.

### class jjANIMATION

jjANIMATION& operator = (const jjANIMATION &in animation)
  Copies all properties of another jjANIMATION and returns a reference to self. This does not copy any jjANIMFRAME objects refered to by the animation; however, after the assignment, both jjANIMATION objects will have their firstFrame refer to a common jjANIMFRAME.
operator uint () const
  Implicit conversion to uint; returns firstFrame.

int16 fps
  This property corresponds to the "FPS" number for animations in Jazz Sprite Dynamite, but that name is only a guess, because its value is never referenced by JJ2 at all. Most commonly equals 10.
uint16 frameCount
  The number of frames used by the animation.
uint firstFrame
  ID of the first frame of the animation, to be used with the jjAnimFrames array.

### class jjANIMFRAME

jjANIMFRAME& operator = (const jjANIMFRAME &in animFrame)
  Copies all properties of another jjANIMFRAME including the picture it uses and returns a reference to self.

int16 coldSpotX
int16 coldSpotY
  Horizontal and vertical position of the frame's cold spot, relative to its hot spot. It is considered to be the point at which the frame would touch the ground. It's mostly used by objects such as walking enemies and pickup crates, as well as players themselves. In particular, the jjOBJ method putOnGround, if its argument is set to true, uses coldSpotY to determine the outcome.
int16 gunSpotX
int16 gunSpotY
  Horizontal and vertical position of the frame's gun spot, i.e. the point that's used to determine initial position of projectiles, relative to its hot spot. Originally this is only defined for player animations and sprites of enemies that produce projectiles.
const uint16 height
  Frame height in pixels.
int16 hotSpotX
int16 hotSpotY
  Horizontal and vertical position of the frame's hot spot, added to the sprite position by all drawing operations and used by collision detection, etc. These properties are analogous to the hot spot coordinates shown by Jazz Sprite Dynamite, however it should be noted that JSD inverts their values for user convenience, while JJ2 doesn't. This means that you will usually want to use negative values for these properties.
bool transparent
  Whether the frame is to be drawn as translucent by default. The only AngelScript function this directly affects is the jjOBJ method draw but JJ2 objects often use this property, e.g. to draw freezer ammo pickups as translucent even though they use the same behavior as any other pickup.
const uint16 width
  Frame width in pixels.
bool doesCollide(int xPos, int yPos, int direction, const jjANIMFRAME@ frame2, int xPos2, int yPos2, int direction2, bool always = false) const
  Returns true if this sprite (at position xPos,yPos and direction direction) would collide with another sprite with its own specified coordinates and direction, taking both sprites' hotspot positions into account; otherwise false. If always is true, collisions will be registered even if there is only one overlapping pixel (used by bullets); the default value of false will require at least about eight common pixels (used by players), which is probably a better test in most cases. Both direction parameters will be tested for the sprites being horizontally flipped, vertically flipped, or both, and the collision detection will be carried out accordingly.

### class jjPIXELMAP

A straightforward, yet very powerful class that lets you manipulate most graphics in the game. A pixel map is a simple two-dimensional array of pixels, represented as `uint8`s, each an index for jjPAL::color. A value of 0 is transparent, 24 is (in most tilesets) red, 92 is dark purple, etc. Any pixel in the map may be edited at any time, so the only part that's complicated at all is how to get images from or into various parts of the game, which is accomplished through a handful of constructors and methods. For example, the following code renders a single tile in the tileset completely black, except for the transparent sections:

    void onLevelLoad() {
    	jjPIXELMAP flowerTile(692); //generate a 32×32 pixel map from tile 692 (the first flower in carrot1.j2t)
    	for (uint x = 0; x < flowerTile.width; ++x)
    		for (uint y = 0; y < flowerTile.height; ++y)
    			if (flowerTile[x,y] != 0) //non-transparent; also note the special two-dimensional array syntax, [x,y] instead of [x][y] or something
    				flowerTile[x,y] = 23; //very dark color
    	flowerTile.save(692, true); //save to tile 692, whether horizontally flipped or not
    }

That's pretty much all there is to it! To turn carrot1.j2t's entire flower animation black, you would wrap the above code in a `for (uint tileID = 692; tileID <= 699; ++tileID)` loop and replace instances of `692` with `tileID`. Another simple idea would be to recolor one of the shootable pole sprites (Jungle Pole, Psych Pole, etc.) to use palette indices more in line with the palette of whatever particular tileset you're using. You're also allowed to construct a jjPIXELMAP instance from a tile but save it to a jjANIMFRAME, or vice versa, or construct a jjPIXELMAP from scratch and make it into a textured background... the important thing when saving a pixel map (except to a jjANIMFRAME) is its dimensions, not how it was constructed. If a tileset has some tiles that look like enemies or pickups (many tileset conversions from other games do), by all means create pixel maps from one or more of those tiles and save them as new sprites to liven up your level. Absolutely any 2D 8-bit image may be stored and manipulated in a pixel map and, dimensions allowing, saved to most of JJ2's major drawing components.

(What jjPIXELMAP currently lacks is any way (post-constructor) to manipulate pixel data more than one byte at a time; e.g. cropping or rotating or copying sections from one pixel map to another. Since users can recreate any such operation on their own, going pixel by pixel, this was not made a priority, but if there's any particular operation you think would really save you some time, let us know and it might get added later.)

jjPIXELMAP(uint width, uint height)
  Creates a totally transparent pixel map with dimensions width×height.
jjPIXELMAP(const jjANIMFRAME@ animFrame)
  Creates a pixel map with the same dimensions and image as the specified jjANIMFRAME.
jjPIXELMAP(TEXTURE::Texture texture)
  Creates a 256×256 pixel map using the graphics of the specified textured background texture. Consequently, all pixels will range in color from 176–207 unless you use TEXTURE::LAYER8
jjPIXELMAP(uint16 tileID = 0)
  Creates a 32x32 pixel map using the graphics of the specified tile, with color 0 standing in for transparent pixels. If `(tileID & TILE::VFLIPPED) != 0`, the tile image will be vertically flipped while creating the pixel map.
  TILE::HFLIPPED is somewhat more complicated, because JJ2 stores horizontally flipped tiles separately from their non-flipped versions. Trying to construct a jjPIXELMAP from a tileID when `(tileID & TILE::HFLIPPED) != 0` will only give you the expected results if the tile's flipped version has been generated, which is typically done by flipping the tile in some layer in JCS. Otherwise you will just get a totally transparent 32x32 map.
jjPIXELMAP(uint left, uint top, uint width, uint height, uint layer = 4)
  Creates a pixel map with dimensions width×height, based on a rectangular area with origin left,top and size width×height in layer layer. This allows you put any number of arbitrary tiles next to each other in JCS and then create a single image out of them for use as a jjANIMFRAME or something else. See `plusPixelMapEx.j2l/.j2as` for several examples of this form.

uint8& operator [] (uint x, uint y)
const uint8& operator [] (uint x, uint y) const
  Gets and (if non-const) sets any pixel anywhere in the pixel map, provided x is less than width and y is less than height. These are `uint8`s, so acceptable values are 0–255, with 0 being transparent. Caution: colors 1–9 and 246–254 may appear different in 8 bit vs. 16 bit color modes and so should be used rarely at best.

const uint height
const uint width
  Dimensions of the map in pixels, set by the map's constructor
bool makeTexture()
  Changes the level's current textured background to contents of the pixel map. The map has to be exactly 256×256 to allow this action and if that is not the case, the function will report failure by returning false. On success returns true.
bool save(uint16 tileID, bool hFlip = false) const
  Overwrites tileset tile selected by tileID with contents of the pixel map. The map has to be exactly 32×32 to allow this action and if that is not the case, the function will report failure by returning false. On success returns true.
  If `(tileID & TILE::VFLIPPED) != 0`, the pixel map image will be vertically flipped while saving the tile. TILE::HFLIPPED is somewhat more complicated, because JJ2 stores horizontally flipped tiles separately from their non-flipped versions. By default, and to avoid unnecessary memory usage, jjPIXELMAP::save will only save to either the regular version of the tile or the horizontally flipped version, depending on whether `(tileID & TILE::HFLIPPED) != 0`. By setting hFlip to true, though, you can ensure that both the regular and the horizontally flipped version of the tile will be overwritten.
  The results of saving when `(tileID & TILE::ANIMATED) != 0 || (tileID & TILE::RAWRANGE) == 0` are undefined.
bool save(jjANIMFRAME@ frame) const
  Overwrites the image used by frame with contents of the pixel map. Returns true on success and false on failure.

### class jjMASKMAP

The simpler cousin of jjPIXELMAP, used only for editing the clipping masks of tiles in the tileset. As such, it uses `bool` instead of `uint8`, is always 32×32, and can only be constructed from or saved to tiles.

jjMASKMAP(bool filled = false)
  Creates a mask map where every pixel's value matches filled, either masked (true) or unmasked (false).
jjMASKMAP(uint16 tileID)
  Creates a mask map using on the mask of the specified tile. All notes mentioned for jjPIXELMAP::jjPIXELMAP(uint16) apply in exactly the same way here.

bool& operator [] (uint x, uint y)
const bool& operator [] (uint x, uint y) const
  Gets and (if non-const) sets any pixel anywhere in the mask map, masked pixels equalling true and unmasked pixels false.

bool save(uint16 tileID, bool hFlip = false) const
  Overwrites mask of tileset tile selected by tileID with contents of the mask map. All notes mentioned for jjPIXELMAP::save apply in exactly the same way here, although you don't have to worry about dimensions, since mask maps are always 32×32.

## Global Properties

Note: to be convenient, the arrays of layer properties are all 1-indexed. For example, the X speed of layer 4 (the sprite layer) is `jjLayerXSpeed[4]` and not `jjLayerXSpeed[3]`.

const uint jjActiveGameTicks
  How long the game has been actively running, at a rate of 70 ticks per second. Unlike jjGameTicks, this value is not incremented when the game is paused, stopped, or in pregame. This is a local value that counts up from 0 except in online Race games, where it is used to track lap times and is therefore synced between server and clients.
const bool jjAllowsFireball
  Whether weapon 8 is fireball instead pepper spray, as set by the /fireball command.
const bool jjAllowsMouseAim
  Whether the server (or the SP level) allows mouse aim, as set by the /allowmouseaim command.
const bool jjAllowsReady
  Whether the server allows players to use the /ready command, as set by the /allowready command.
jjANIMATION@ jjAnimations[1500]
  Loaded animations. See the jjANIMATION class description for more information.
jjANIMFRAME@ jjAnimFrames[15000]
  Loaded animation frames. See the jjANIMFRAME class description for more information.
jjANIMSET@ jjAnimSets[ANIM::Set]
  Loaded animation sets. See the jjANIMSET class description for more information.
const bool jjAutoWeaponChange
  Whether automatic weapon change is locally enabled, as set by the /weaponchange command.
const jjPAL jjBackupPalette
  The tileset's original palette. See the jjPAL documentation above for further details.
jjPLAYER@ jjBottomFeeder
  In Roast Tag game mode, the player who is currently the bottom feeder or null if none. For the other special role in Roast Tag, see jjTokenOwner.
jjCHARACTER@ jjCharacters[CHAR::Char]
  Character profiles. Use either CHAR::JAZZ, CHAR::SPAZ, CHAR::LORI, CHAR::BIRD, CHAR::FROG or CHAR::BIRD2 as an index. Refer to the jjCHARACTER section for more information.
const jjCONTROLPOINT@ jjControlPoints[16]
  An array containing all Domination control points in the level. See the jjCONTROLPOINT section for further details.
const bool jjDeactivatingBecauseOfDeath
  When the player dies in Single Player mode, this property is set to true before all jjOBJs have their state property set to DEACTIVATE. Since DEACTIVATE is also used for when an object goes too far off-screen, this property is how to discover the reason for the state change. In practice, is probably only ever consulted by destruct scenery and trigger scenery.
bool jjDelayGeneratedCrateOrigins
  If set to true, box objects (trigger crates, all wooden crates, and bird morph monitors) spawned from Generator objects will derive their parameters from the tile they begin at, not the tile they are created at. If the Generator object is in the air, the crate will appear on top of the nearest solid tile below the Generator, and will get its parameters from the tile there.
int jjDifficulty
  The current difficulty level. 1 for Normal difficulty; 0 and below for Easy; 2 for Hard; 3 and above for Turbo. Numerous enemies base their speeds at least partially on the difficulty, so numbers outside of the well-tested 0-3 range may have unexpected or undesirous effects with certain enemies; still, it's worth a try! This property cannot be used to determine whether to load events specified in JCS as Easy or Hard, since that has already been checked by the time AngelScript starts running in a level.
const bool jjDoZombiesAlreadyExist
  In Pestilence game mode, whether any player is already a zombie.
int jjEcho
  The current degree of echo, as set by the "Echo" event.
bool jjEnabledASFunctions[256]
  Usually all true. When a Text event is touched with AngelScript=1,Vanish=1, the jjEnabledASFunctions[#] bool for that Text event's TextID value will be set to false and the corresponding onFunction# will be uncallable by other Text events until the bool is set to true again.
const bool jjEnabledTeams[TEAM::Team]
const bool jjEnabledTeams[4]
  Currently enabled teams. Possible values for TEAM::Team are TEAM::BLUE, TEAM::RED, TEAM::GREEN, and TEAM::YELLOW.
LIGHT::Enforce jjEnforceLighting
  This setting defines the minimal ambient lighting options required from the game. It will not change game settings if they don't fulfill the requirements but it will display lights as if the settings were changed. Allowed values are:
  - OPTIONAL: The default value; ambient lighting can be freely disabled and enabled with no limits.
  - BASIC: Ambient lighting can be disabled but basic lights, such as those emitted by objects, players and laser shields, have to be drawn. This only affects the game if ambient lighting is disabled and low detail is enabled, because that's when basic lights stop being drawn.
  - COMPLETE: Ambient lighting cannot be disabled, all lights have to be drawn.
uint8 jjEventAtLastMaskedPixel
  Whenever one of the mask-detection functions, e.g. jjMaskedHLine, finds a masked pixel in layer 4, this property will be set to the event at the tile containing that pixel. This allows you to write code for object like seeker missiles, which ignore masked pixels on tiles with the AREA::ONEWAY, AREA::HOOK, or AREA::VINE events. There's not much reason to edit it manually, since JJ2 changes its value all but constantly, but you can if you want.
const int jjFPS
  The current frames per second rate, as viewable by pressing F9 twice.
const bool jjFriendlyFire
  Whether friendly fire is enabled, as set by the /friendlyfire command.
const GAME::Connection jjGameConnection
  Is this game joinable by players from other computers, and if so, must they be connected to the same network or just the internet? Options are LOCAL, ONLINE, and LAN.
const GAME::Custom jjGameCustom
  If using a custom gamemode, what is it? Options are NOCUSTOM, RT, LRS, XLRS, PEST, TB, JB, DCTF, FR, TLRS, DOM, and HEAD.
const GAME::Mode jjGameMode
  What is the current base gamemode, irrespective of whether there is a custom gamemode or not? Options are SP, COOP, BATTLE, CTF, TREASURE, and RACE.
const GAME::State jjGameState
  In an online/network server, is the game started, stopped, or some other variation? Options are. STOPPED, STARTED, PAUSED (only possible if there is a time limit), PREGAME, and OVERTIME.
const int jjGameTicks
  How long the game has been actively running, at a rate of 70 ticks per second.
const bool jjIsAdmin
  Whether the current game executable is logged in as a Remote Admin in the current online server. To check this property for any client in the server, use jjPLAYER property isAdmin instead.
const bool jjIsServer
  Whether the current game executable is hosting an online server.
bool jjIsSnowing
  Whether there's any active weather effect. The type of the effect is determined by jjSnowingType.
bool jjIsSnowingOutdoorsOnly
  Whether the current weather effect is specified to only take effect on transparent tiles, i.e. appear to be limited to outdoors areas.
const bool jjIsTSF
  Whether the current game executable is 1.23+ or 1.24+. Useful for Lori, XMas enemies, etc.
const bool jjKey[256]
  Whether any given key on the keyboard is currently pressed, assuming JJ2 is able to check it, including the left and right mouse buttons. Uses virtual key codes for indexation. Note that jjKey[1] and jjKey[2] refer to the primary and secondary mouse buttons respectively, rather than left and right.
bool jjLayerHasTiles[8]
  Simply, whether JJ2 should draw the layer or not. Setting this to true for a layer that does not actually have any tiles may have unpredictable consequences; it is best to use this solely to hide layers that really do have tiles.
const int jjLayerHeight[8]
  The height of each layer, in tiles.
bool jjLayerLimitVisibleRegion[8]
  Assuming the layer is not vertically tiled, whether the layer should be vertically offset some pixels downward, the exact value depending on the current resolution.
bool jjLayerTileHeight[8]
  Whether each layer should be vertically tiled, as seen in the JCS Layer Properties window.
bool jjLayerTileWidth[8]
  Whether each layer should be horizontally tiled, as seen in the JCS Layer Properties window.
  Setting this to true for a layer that is not saved with Tile Width checked in JCS may lead to unpredictable effects if the layer's width is not a multiple of 4. If you wish to turn it on partway through the level, it is best to check Tile Width in JCS and then disable it in onLevelLoad.
const int jjLayerWidth[8]
  The width of each layer, in tiles.
const int jjLayerWidthReal[8]
  If a layer has Tile Width checked, the lowest common multiple of its width in tiles and 4. Otherwise, its width in tiles.
const int jjLayerWidthRounded[8]
  The lowest multiple of four greater or equal to each layer's width in tiles.
float jjLayerXAutoSpeed[8]
  The X auto speed of each layer, as seen in the JCS Layer Properties window.
float jjLayerXOffset[8]
  Constant pixel values added to the X position of each layer, regardless of their speeds.
float jjLayerXSpeed[8]
  The X speed of each layer, as seen in the JCS Layer Properties window.
float jjLayerYAutoSpeed[8]
  The Y auto speed of each layer, as seen in the JCS Layer Properties window.
float jjLayerYOffset[8]
  Constant pixel values added to the Y position of each layer, regardless of their speeds.
float jjLayerYSpeed[8]
  The Y speed of each layer, as seen in the JCS Layer Properties window.
const int jjLocalPlayerCount
  The number of local players.
const jjPLAYER@ jjLocalPlayers[4]
  The local players.
const bool jjLowDetail
  Whether the Low Detail video setting is enabled.
const int jjMaxHealth
  The most health a player can ever have, as set by the /maxhealth command. Defaults to 5 in Single Player/Cooperative/Battle, or 3 in Capture The Flag.
const int jjMaxLives
  In LRS-based game modes, the maximum number of lives a player can have, as set by the /maxlives command.
const int jjMaxScore
  In competitive game modes, the score required to win, as set by the /maxscore command.
const uint jjScriptModuleID
  Each script module (including mutators) will see this property as equalling a different value: 0 for the .j2as script (if any), and 1 or higher for all mutators (loaded in alphabetical order). For use only as the toScriptModuleID parameter for jjSendPacket.
const bool jjMouseAim
  Whether mouse aim is locally enabled, as set by the /mouseaim command.
const int jjMouseX
const int jjMouseY
  The current position of the mouse cursor relative to the top left corner of the game window. To convert these coordinates to coordinates within layer 4, you'll need to use the jjPLAYER cameraX and cameraY properties.
const bool jjMusicActive
  Mute Music, as seen in the Sound & Music Properties window.
const int jjMusicVolume
  Music Volume, as seen in the Sound & Music Properties window.
const bool jjNoBlink
  Whether the no blink mode is enabled, as set by the /noblink command.
const bool jjNoMovement
  Whether the game blocks movement during stopped games, as set by the /nomovement command.
const int jjObjectCount
  When looping through jjObjects, this is the endpoint; there should never exist a jjOBJ with an object ID higher than jjObjectCount. It is not however the number of distinct jjOBJs in existence at any given time, since for instance `jjObjects[1]` and `jjObjects[3]` may both be active but `jjObjects[2]` inactive, but jjObjectCount would still equal 4.
const int jjObjectMax
  The most jjOBJs that can ever exist at the same time. This equals 2048 in local Single Player/Coop, or 4096 otherwise.
jjOBJ@ jjObjectPresets[256]
  The templates from which each object is built. Tends to contain default xSpeed, ySpeed, points, curAnim, and so on. Make changes here in onLevelLoad for maximum efficiency.
jjOBJ@ jjObjects[jjObjectMax]
  All the objects currently in memory.
jjPLAYER@ jjP
  The current player.
jjPAL jjPalette
  The current palette. See the jjPAL documentation above for further details.
jjPARTICLE@ jjParticles[1024]
  All the particles currently in memory. See the jjPARTICLE documentation above for further details.
const int jjPlayerCount
  Doesn't work! Check back later.
const jjPLAYER@ jjPlayers[32]
  All the players in the game, local or otherwise.
const int jjRenderFrame
  How long the game has been running. Unlike jjGameTicks, jjRenderFrame updates when the game is paused. This is the value used for drawing layers with automatic x/y speeds.
const int jjResolutionHeight
const int jjResolutionWidth
  The size of the current game window in pixels, usually 640 by 480.
const int jjResolutionMaxHeight
const int jjResolutionMaxWidth
  The maximum size the game window is allowed to be in the current level/server.
uint8 jjSnowingIntensity
  Intensity of the current weather effect. Note that that this setting only influences the game if jjIsSnowing is true.
SNOWING::Type jjSnowingType
  Type of the current weather effect. Note that that this setting only influences the game if jjIsSnowing is true. Possible values are SNOW, FLOWER, RAIN, and LEAF, each spawning particles of the corresponding PARTICLE::Type.
const bool jjSoundEnabled
  Whether JJ2 should produce any form of audio at all.
const bool jjSoundFXActive
  Mute Sound, as seen in the Sound & Music Properties window.
const int jjSoundFXVolume
  Sound Volume, as seen in the Sound & Music Properties window.
const int jjStartHealth
  How much health a player starts with, as set by the /starthealth command. Defaults to 5 in Single Player/Cooperative/Battle, or 3 in Capture The Flag.
const bool jjStrongPowerups
  Whether strong powerups is enabled, as set by the /strongpowerups command.
const int jjSubscreenHeight
const int jjSubscreenWidth
  The size of a player's subscreen in pixels. If there is only one local player and the game is not being viewed in 3D, these will be equal to jjResolutionHeight and jjResolutionWidth -- otherwise, either or both may be cut in half.
const int jjTeamScore[TEAM::Color]
  Each team's current score in team-based game modes and undefined value in other modes. Available indexes are BLUE, RED, GREEN and YELLOW.
float jjTexturedBGFadePositionX
float jjTexturedBGFadePositionY
  Where on the screen, with 0 as top/left and 1 as bottom/right, the textured background should fade to, if applicable (only Warp Horizon and Tunnel use Y, and only Tunnel uses X).
bool jjTexturedBGStars
  The layer 8 checkbox titled "Parallaxing stars background (Star Wars)" in JCS.
TEXTURE::Style jjTexturedBGStyle
  How the level's textured background should display itself. Options are WARPHORIZON, TUNNEL, MENU, and TILEMENU, defaulting to whichever is specified in layer 8's layer properties in JCS.
TEXTURE::Texture jjTexturedBGTexture
  Which 256x256 pixel (aka 8x8 tile) texture is used by the level. Defaults to LAYER8, meaning whatever the first 64(=8*8) tiles in layer 8 are. (If layer 8 has fewer than 64 tiles, this may cause JJ2 to crash.) The other options are listed in the appendix at the bottom of this file.
bool jjTexturedBGUsed
  Whether this level displays a textured background in place of layer 8 at all.
uint8 jjTileType[4096]
  Each tile's tile type: 0 for normal, 1 for translucent, 3 for invisible, and so on. Refer to your JCS.ini for the full list.
bool jjTriggers[32]
  The triggers, as set by the Trigger Zone and Trigger Crate events.
jjPLAYER@ jjTokenOwner
  In Roast Tag game mode, the player who is currently "it" or null if none. For the other special role in Roast Tag, see jjBottomFeeder.
bool jjUseLayer8Speeds
  Whether layer 8 should use its specified layer speeds. Works regardless of whether the layer is textured or not. False by default.
bool jjVerticalSplitscreen
  If there are exactly two local players, how the window is divided into their two subscreens.
bool jjWarpsTransmuteCoins
  If set to false, using a coin warp in Single Player mode will not turn all remaining coins into red and green gems.
float jjWaterChangeSpeed
  How fast water moves up or down when the water level is set (by event or function) with the "Instant" parameter set to false. Defaults to 1.
uint8 jjWaterLayer
  Which layer, 1-8, water is drawn in front of when visible. Defaults to 1. Set to 0 to make water invisible. Note that this is a purely visual setting, and putting water behind the sprite layer will not prevent players from swimming in it.
const float jjWaterLevel
  How high the water currently is, in pixels.
  This is a constant value; use the jjSetWaterLevel helper function instead for changing it.
WATERLIGHT::wl jjWaterLighting
  The current way that water and ambient lighting interact in the level. (Ambient lighting varies by local player and as such is a jjPLAYER property.) The following constants are permissible values:
  - WATERLIGHT::NONE: The default. When water is activated, the level will display at lighting 100, regardless of the current settings.
  - WATERLIGHT::GLOBAL: The entire level will be lit according to the current ambient lighting settings, both above and below the water line.
  - WATERLIGHT::LAGUNICUS: The current ambient lighting setting is ignored. Above the water, the level will display at lighting 100. Below the water, the level will display darker and darker depending on how far below the water line the player is.
const float jjWaterTarget
  The height the water is moving towards, in pixels. If the water level is set (by event or function) with the "Instant" parameter set to false, there will be a period in which jjWaterLevel and jjWaterTarget are two distinct values.
  This is a constant value; use the jjSetWaterLevel helper function instead for changing it.
jjWEAPON jjWeapons[WEAPON::Weapon]
jjWEAPON jjWeapons[9]
  Various properties of the nine different weapons available to a player; see the jjWEAPON section. Possible constants appear in the appendix below, or you may use simple 1-indexed numbers instead.
jjPLAYER@ p
  The current player; an alias of jjP, and the only property not to begin with the jj prefix, provided solely for convenience value.

## Global Functions

int jjAddObject(uint8 eventID, float xPixel, float yPixel, uint16 creatorID = 0, CREATOR::Type
creatorType = CREATOR::OBJECT, jjBEHAVIOR behavior = BEHAVIOR::DEFAULT)
  Adds and initiates an object of type eventID at xOrg xPixel and yOrg yPixel. Possible values for creatorType are CREATOR::OBJECT, CREATOR::LEVEL, and CREATOR::PLAYER. Useful values for eventID can be found in the appendix at the bottom of the page. Returns the object ID of the new object, or 0 if the function fails for whatever reason.
  The difference between `jjAddObject(1, 0, 0, CREATOR::OBJECT, 0, BEHAVIOR::BOUNCERBULLET);` and `jjObjects[jjAddObject(1, 0, 0)].behavior = BEHAVIOR::BOUNCERBULLET;` is that jjAddObject calls the object's behavior function as part of creating it. The first version will call BEHAVIOR::BOUNCERBULLET while the object's state is still STATE::START; the second version will call `jjObjectPresets[1].behavior` and only switch the object's behavior to BEHAVIOR::BOUNCERBULLET after it has already been initialized and its state likely changed to something else. The same distinction applies to setting the object's xOrg/yOrg, creatorType, and creatorID properties as parameters to the function or later on. See the Custom Objects section.
jjPARTICLE@ jjAddParticle(PARTICLE::Type type)
  Creates and returns a new particle object, or a null pointer if unsuccessful. See the jjPARTICLE documentation above for full details.
void jjAddParticlePixelExplosion(float xPixel, float yPixel, int curFrame, int direction, int mode)
  Creates an explosion of particles based on the shape and possibly colors of the specified curFrame. Use a mode value of 0 for a normal explosion, 1 for a fire explosion caused by a toaster or fire shield bullet, or 2 for an explosion caused by a special move. Values in the range of 15-255 will create fire explosions whose particles will use palette index equal to mode - this effect is currently used by weapons such as powered-up toaster and laser shield to create blue explosions. Value 14 creates a fire explosion whose particles use individually random colors. Values in the range of 3-13 and higher than 255 are undefined and - to preserve backward compatibility - shouldn't be used.
void jjAddParticleTileExplosion(uint16 xTile, uint16 yTile, uint16 tile, bool collapseSceneryStyle)
  Creates four fragments of a tile falling from a specified location, like when destroying a destructable scenery block. Does not produce a sound effect; use jjSample or jjSamplePriority for that instead. The fragments will continue to be drawn until they fall off the screen. If you want more control over the fragments' positions, speeds, etc., use jjAddParticle instead.
void jjAlert(const string &in text, bool sendToAll = false, STRING::Size size = STRING::SMALL)
  Writes text to the chatlogger window, and also displays it ingame for the local player. Uses size to determine size and positioning of text. If sendToAll is true and the function is called by the server, text will be sent to all clients as well.
void jjChat(const string &in text, bool teamchat = false)
  In online play, sends text to the server as a line of chat. If text is a command (e.g. "/spectate on" or "/ready"), it will be interpreted as such to the extent that the local player is allowed to use that command in the server.
  In offline play, JJ2+ will try to parse text as a command but will not display it as chat because there is no chat in offline mode. If you want to simulate chatting in a local game, use jjAlert instead.
void jjConsole(const string &in text, bool sendToAll = false)
  Writes text as a console message to the chatlogger window, and also displays it ingame for the local player. If sendToAll is true and the function is called by the server, text will be sent to all clients as well.
float jjCos(uint in)
  Returns the cosine of in with a range of 0.0-1.0 and a domain of 0-1023. Numbers outside the domain will be seemlessly moduloed. You may prefer AngelScript's native cos function.
void jjDebug(const string &in text, bool timestamp = false)
  Writes text to the chatlogger window (but not ingame), but only if `[General]AngelscriptDebug` equals True in plus.ini. If timestamp is true, adds a timestamp before the text.
void jjDeleteObject(int objectID)
  Permanently deletes an object. Like jjAddObject, this function is purely local in its scope.
void jjDrawPixel(float xPixel, float yPixel, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawRectangle(float xPixel, float yPixel, int width, int height, uint8 color, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawResizedSprite(float xPixel, float yPixel, uint8 setID, uint8 animation, uint8 frame, float xScale, float yScale, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawResizedSpriteFromCurFrame(float xPixel, float yPixel, uint sprite, float xScale, float yScale, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawRotatedSprite(float xPixel, float yPixel, uint8 setID, uint8 animation, uint8 frame, int angle, float xScale = 1, float yScale = 1, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawRotatedSpriteFromCurFrame(float xPixel, float yPixel, uint sprite, int angle, float xScale = 1, float yScale = 1, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawSprite(float xPixel, float yPixel, uint8 setID, uint8 animation, uint8 frame, int direction = 0, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawSpriteFromCurFrame(float xPixel, float yPixel, uint sprite, int direction = 0, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawString(float xPixel, float yPixel, const string &in text, uint8 STRING::SIZE size = STRING::SMALL, STRING::Mode mode = STRING::NORMAL, uint8 param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawString(float xPixel, float yPixel, const string &in text, uint8 STRING::SIZE size, const jjTEXTAPPEARANCE &in appearance, uint8 param1 = 0, SPRITE::Mode spriteMode = SPRITE::PALSHIFT, uint8 param2 = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawSwingingVineSpriteFromCurFrame(float xPixel, float yPixel, uint sprite, int length, int curvature, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
void jjDrawTile(float xPixel, float yPixel, uint16 tile, TILE::Quadrant tileQuadrant = TILE::ALLQUADRANTS, uint8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
  Global function versions of the jjCANVAS methods as applied to onDrawLayer# hooks, differing in that the jjCANVAS methods are executed instantly, whereas these functions create instructions for JJ2 to perform the drawing operations later on, at the proper time. For example, a swinging platform will call jjDrawSpriteFromCurFrame many times over, once for each of its chain links, in the middle of its behavior function, but the links won't actually get drawn to the screen until later in the game cycle. Native JJ2 code uses this method for everything but HUD graphics.
  The layerZ parameter specifies which layer, 1-8, the graphic should be drawn in front of as its Z-index. Unlike the jjCANVAS hooks, this can be used even for layers that don't have any tiles. JJ2 draws sprites exclusively(?) in front of layers 3, 4, and 5, but you're welcome to experiment.
  The layerXY parameter specifies which layer, 1-8, the graphic should be positioned relative to the top left corner of. JJ2 always, always does layer 4, but you can vary it up a bit. Unfortunately the game cycle is ordered so that the layers besides layer 4 may actually move around a little after the instruction is registered but before the graphic is drawn, so these drawing instructions will always be one frame behind. Here the jjCANVAS methods have a clear advantage.
  The playerID parameter specifies which player should see the drawn graphic, 0-31, or -1 for all of them (restricted only to players with true isLocal). Drawing for one player a time is used by JJ2+ to, for example, draw fastfire pickups as green/blue or normal/powered-up depending on the charCurr and powerup[1] values of each jjPLAYER viewing them. When spectating, sprites are drawn for the player ID of the spectator, not the spectatee.
void jjEnableEachASFunction()
  Resets all 256 bools in jjEnabledASFunctions to true.
int jjEventGet(uint16 xTile, uint16 yTile)
  Gets the Event ID at tile xTile,yTile, as seen in JCS.ini. This number can also be compared to the OBJECT or AREA constants listed in the appendix at the bottom of this file.
void jjEventSet(uint16 xTile, uint16 yTile, uint8 newEventID)
void jjEventSet(uint16 xTile, uint16 yTile, OBJECT::Object newEventID)
void jjEventSet(uint16 xTile, uint16 yTile, AREA::Area newEventID)
  Sets the event at tile xTile,yTile to newEventID. Possible OBJECT or AREA constants are listed in the appendix at the bottom of this file.
  Caution: this is a permanent change and will subsist even after death in offline play.
void jjGenerateSettableTileArea(uint8 layer, int xTile, int yTile, int width, int height)
  Makes all tiles that belong to layer and are in the range of the rectangle with its top left corner at xTile,yTile and specified width and height possible to use jjTileSet on without the potential consequence of changing other occurrences of the tile in the level due to tile cache.
int jjGetModOrder()
  Returns the current order in the currently playing module music, or -1 if the currently playing music is not a module, a module not handled by BASS or no music is playing.
int jjGetModRow()
  Returns the current row in the currently playing module music, or -1 if the currently playing music is not a module, a module not handled by BASS or no music is playing.
int jjGetModSpeed()
  Returns the "speed" parameter (ticks per row) of the currently playing module music, or -1 if the currently playing music is not a module, a module not handled by BASS or no music is playing.
int jjGetModTempo()
  Returns the tempo of the currently playing module music, or -1 if the currently playing music is not a module, a module not handled by BASS or no music is playing.
int jjGetStringWidth(const string &in text, STRING::Size size, const jjTEXTAPPEARANCE &in style)
  Returns width, in pixels, that text would have if it was drawn in specified size and style. If style allows multi-line text and text is multi-line, width of the longest line is returned.
bool jjIsValidCheat(const string &in text)
  Returns true if text is interpreted by the game as a cheat code.
void jjKillObject(int objectID)
  Permanently deletes an object, but first calls its native STATE::KILL code (if any). Probably functionally identical to jjDeleteObject in most cases, but might work a little better sometimes.
bool jjMaskedHLine(int xPixel, int lineLength, int yPixel, uint8 layer = 4)
  Returns true if any pixel from xPixel,yPixel to xPixel+lineLength,yPixel is masked in layer layer. Leaving the layer parameter out should be faster than setting it to 4.
bool jjMaskedPixel(int xPixel, int yPixel, uint8 layer = 4)
  Returns true if pixel xPixel,yPixel is masked in layer layer. Leaving the layer parameter out should be faster than setting it to 4.
int jjMaskedTopVLine(int xPixel, int yPixel, int lineLength, uint8 layer = 4)
  If any pixel from xPixel,yPixel to xPixel,yPixel+lineLength is masked in layer layer, returns the height of the topmost masked pixel relative to yPixel. (For example, if xPixel,yPixel+2 is masked but +1 and +0 weren't, the function returns 2.) If none of the pixels are masked, returns lineLength+1. Used for detecting inclines and the like. Leaving the layer parameter out should be faster than setting it to 4.
bool jjMaskedVLine(int xPixel, int yPixel, int lineLength, uint8 layer = 4)
  Returns true if any pixel from xPixel,yPixel to xPixel,yPixel+lineLength is masked in layer layer. Leaving the layer parameter out should be faster than setting it to 4.
bool jjMusicLoad(string filename, bool forceReload = false)
  Loads and starts playing a new music file, of any type supported by JJ2+. Returns false if the file cannot be found in either the main game folder or the cache subfolder, or if the specified music file is already playing and forceReload is false.
void jjMusicPause()
  Pauses the current music track. May not work with .mp3 files.
void jjMusicPlay()
  (Re)starts the current music track.
void jjMusicResume()
  Resumes the current music track, once paused. May not work with .mp3 files.
void jjMusicStop()
  Stops the current music track.
void jjNxt(string& filename = "", bool warp = false, bool fast = false)
  Ends the level and skips to the next one, or to filename if specified. Only works in Single Player and Cooperative.
int jjParameterGet(uint16 xTile, uint16 yTile, int8 offset, int8 length)
  Gets one of the parameters at tile xTile,yTile. Follow JCS.ini's lead in figuring out how to write the offset and length parameters.
  length is the simplest: use the exact same formatting JCS.ini does. To get the speed of a belt event, for instance, length should be -8. To get the number of blue gems in a gem crate, length should be 4. And so on.
  offset is calculated by adding the absolute values of every parameter on the tile prior to the one you want. The first (bottommost) parameter will always have offset 0. To get the parameter "Blue" in Gem Crate, offset should be 8 (4+4). To get the Y-Speed of a Rotating Rock, offset should be 12 (8+abs(-4)). And so on.
  Set length to 2 and offset to -4 to get the difficulty of an event (normal, easy, hard, multiplayer-only).
void jjParameterSet(uint16 xTile, uint16 yTile, int8 offset, int8 length, int newValue)
  Sets one of the parameters at tile xTile,yTile. length and offset work exactly as they do for jjParameterGet; the only change is newValue, which should be a valid number for the length setting. Trying to assign a negative number to an unsigned length parameter doesn't really make sense, for example, nor can you reasonably assign a newValue of 100 to a length of 3.
  Note that this is not quite as powerful as it may seem, since some objects read and process their parameters into memory when they are first created, rather than continually reading them again and again as the game continues. The function will however work fine for zones that affect the player, such as Warp or Sucker Tube or Wind, and it will also successfully set parameters for any such new objects created after the function is called.
  Caution: this is a permanent change and will subsist even after death in offline play.
void jjPrint(const string &in text, bool timestamp = false)
  Writes text to the chatlogger window but does not display it ingame. If timestamp is true, adds a timestamp before the text.
uint jjRandom()
  Provides a random number.
bool jjRegexIsValid(const string &in expression)
  Returns true if expression is a valid regular expression and false otherwise. This is the only regex function that doesn't cause debug errors when the input is an invalid expression and as such it should be always used before calling other regex functions when expression comes from an untrusted source (such as from user input rather than from a constant string in the script). Expressions will be parsed according to modified ECMAScript regular expression grammar.
bool jjRegexMatch(const string &in text, const string &in expression, bool ignoreCase = false)
bool jjRegexMatch(const string &in text, const string &in expression, array<string> &out results, bool ignoreCase = false)
  Returns true if expression is a valid regular expression that matches text entirely and false if no match is found. Where the second overload is used, results will contain match results in a standard order. If ignoreCase is true, the matching will be case insensitive. If expression is not a valid regular expression, a debug message will be printed to the chatlogger and the return value will be undefined. In future versions of JJ2+ this might have further consequences including complete script shutdown. For this reason, jjRegexIsValid should always be used to validate untrusted input. Note that this function only returns true if expression matches the entire string, whereas jjRegexSearch accepts substring matches.
string jjRegexReplace(const string &in text, const string &in expression, const string &in replacement, bool ignoreCase = false)
  Returns a string that is the result of replacement of all substrings of text that are matched by expression with replacement. If ignoreCase is true, the matching will be case insensitive. Capture results ($1, $2, etc., $0 always being the entire matched substring) can be successfully used in the replacement string. If expression is not a valid regular expression, a debug message will be printed to the chatlogger and the return value will be undefined. In future versions of JJ2+ this might have further consequences including complete script shutdown. For this reason, jjRegexIsValid should always be used to validate untrusted input.
bool jjRegexSearch(const string &in text, const string &in expression, bool ignoreCase = false)
bool jjRegexSearch(const string &in text, const string &in expression, array<string> &out results, bool ignoreCase = false)
  Returns true if expression is a valid regular expression that matches any substring of text and false if no match is found. Where the second overload is used, results will contain match results in a standard order. If ignoreCase is true, the matching will be case insensitive. If expression is not a valid regular expression, a debug message will be printed to the chatlogger and the return value will be undefined. In future versions of JJ2+ this might have further consequences including complete script shutdown. For this reason, jjRegexIsValid should always be used to validate untrusted input. Note that this function returns true if expression matches any character subsequence of text, whereas jjRegexMatch will only look for matches with the entire string.
void jjResetWaterGradient()
  Restores 16-bit water to its natural colors.
void jjSample(float xPixel, float yPixel, SOUND::Sample sample, int volume = 63, int frequency = 0)
  Plays a sound from anims.j2a at pixel xPixel, yPixel. Possible values for sample are listed in the appendix at the bottom of this file.
  volume ranges from 1-63, and 0 will default to 63. Higher values of frequency result in higher frequencies, or leaving it at 0 will use the sample's unique default frequency.
bool jjSampleIsLoaded(SOUND::Sample sample)
  Returns whether sample is loaded or not.
bool jjSampleLoad(SOUND::Sample sample, string &in filename)
  Attempts to load sample from a .wav file filename and returns true on success or false otherwise. If there is already a loaded sample corresponding to this SOUND::Sample constant, it will be overwritten.
int jjSampleLooped(float xPixel, float yPixel, SOUND::Sample sample, int channel, int volume = 63, int frequency = 0)
  Plays a looped sound from anims.j2a at pixel xPixel, yPixel. For every source of sound (e.g. a jjOBJ instance), channel should be 0 on the first call and in later calls should be replaced with the value returned from the previous call of this function.
  volume ranges from 1-63, and 0 will default to 63. Higher values of frequency result in higher frequencies, or leaving it at 0 will use the sample's unique default frequency.
void jjSamplePriority(SOUND::Sample sample)
  Plays a sound from anims.j2a, no matter what any local players' positions are. This is the function used to play the sugar rush jingle. Possible values for sample are listed in the appendix at the bottom of this file.
bool jjSendPacket(jjSTREAM &in packet, int toClientID = 0, uint toScriptModuleID = jjScriptModuleID)
  Sends packet from client to server (in case if you're a client) or from server to client (in case if you're a server). Returns true on success and false on failure. If toClientID is a positive value, the packet will be sent only to the client with the appropriate jjPLAYER::clientID. If it's negative, it will be sent to all clients with the exception of the one indicated by toClientID. Using the default value of 0 results in sending the packet to all clients. For a script to receive packet, you will need to declare an onReceive hook, which has the following signature:

      void onReceive(jjSTREAM &in packet, int fromClientID)

  Notice that packets can't be sent between two clients but only between the server and a client; if the function is called client-side, the toClientID argument is completely ignored. If a client is meant to send a packet to another client, they have to send the packet to the server first and the server should resend it to the other client from onReceive.
  If there are multiple distinct script modules running (two or more mutators, or one mutator and a level's primary script), the packet will only be received by the module whose jjScriptModuleID global value matches the toScriptModuleID parameter. By leaving this parameter as the default value, you can ensure that a packet sent from `foo.j2mut` will always be read by (the onReceive hook defined in) `foo.j2mut`, rather than another, simultaneously running module that wouldn't know what to do with the data. Passing 0 instead will send the packet to the level's primary script (if any), and is rarely a good idea. Other values are even more rarely a good idea.
void jjSetDarknessColor(jjPALCOLOR color = jjPALCOLOR(0, 0, 0))
  Sets the color of darkness used with ambient lighting.
void jjSetFadeColors(uint8 red, uint8 green, uint8 blue)
void jjSetFadeColors(uint8 paletteColorID = 207)
  Sets the fade colors of the level's textured background, as seen in the Layer properties window for layer 8 in JCS. Has no effect if there is no textured background.
  A simpler one (or zero!) parameter version of the function also exists to set the fade colors to the same RGB values as used by one of the entries in jjPalette. This defaults to 207, which is the last color of the most common textured background gradient and thus, not infrequently, the fade color used in 8-bit color.
void jjSetLayerXSpeed(uint8 layerID, float newspeed, bool newSpeedIsAnAutoSpeed)
void jjSetLayerYSpeed(uint8 layerID, float newspeed, bool newSpeedIsAnAutoSpeed)
  Changes the X or Y speed of a given layer. Unlike the basic arrays like jjLayerXSpeed and jjLayerYAutoSpeed, these functions will ensure that the layers remain in the same positions they were before their speeds were changed. Much more useful.
void jjSetModPosition(int order, int row, bool reset)
  Jumps to a specific row of a specific order in the currently playing module file. If reset is true, also stops all notes and resets the module's global volume, tempo, etc. to their original values.
  Calling this function with an invalid order or row number, or while BASS is not playing a module file, will have no effect.
void jjSetModSpeed(uint8 speed)
  Sets the "speed" of the currently playing module music file. Does nothing if BASS is not currently playing a module file. Note that the module may change its own speed, overwriting your change.
void jjSetModTempo(uint8 tempo)
  Sets the tempo of the currently playing module music file. Does nothing if BASS is not currently playing a module file. Note that the module may change its own tempo, overwriting your change.
void jjSetWaterGradient(uint8 red1, uint8 green1, uint8 blue1, uint8 red2, uint8 green2, uint8 blue2)
void jjSetWaterGradient()
  Changes the colors used by water in 16-bit color. If no parameters are included, the gradient will be generated from palette entries 176 and 207 instead, the most typical textured background colors (and most of the colors used by 8-bit water).
void jjSetWaterLevel(float yPixel, bool instant)
  Sets jjWaterTarget to yPixel. If instant is true, jjWaterLevel will also be set to yPixel; otherwise, it will move slowly up or down from its current height until it reaches its new target.
  Caution: this function is not identical to the Water Level event in JCS. The event measures in tiles, but this function measures in pixels. Multiply by thirty-two to get the same effect.
float jjSin(uint in)
  Returns the sine of in with a range of 0.0-1.0 and a domain of 0-1023. Numbers outside the domain will be seemlessly moduloed. This is the sine function used by JJ2 for spinning platforms and the like, though you may prefer AngelScript's native sin function.
void jjSlideModChannelVolume(int channel, float volume, int milliseconds)
  In currently playing module music, slide the volume of channel to volume over a chosen number of milliseconds. Does nothing if BASS is not currently playing a module file or channel does not exist. volume is 1.0 for the module's channel volume, 0.0 for silent. If you give a higher or lower value, the slide will stop when it reaches one of these boundaries. Volume slides continue while the music is paused. If you begin sliding the volume of a channel that is already sliding, the old slide will immediately stop.
void jjSpy(const string &in text)
  Prints text to the game's built-in spy window activated by running it with the -spy command line parameter, and also writes it to your jazz2.log file.
bool jjSwitchTrigger(uint8 id)
  Toggles jjTriggers[id] from true to false, or vice versa, like the "switch" parameter on the Trigger Zone and Trigger Crate events.
uint16 jjTileGet(uint8 layer, int xTile, int yTile)
  Returns the current tile at tile xTile,yTile in layer layer. If the tile is an animated tile, this function will return the tile ID for that animated tile instead of the current frame.
uint16 jjTileSet(uint8 layer, int xTile, int yTile, uint16 newTile)
  Sets the current tile at tile xTile,yTile in layer layer to be newTile. The same change will be applied to all instances of the same four-tile word that appear elsewhere in the level unless the tiles had had jjGenerateSettableTileArea used on them prior to the change.
void jjTriggerRock(uint8 id)
  Activates all Rotating Rock events with the "RockID" parameter set to id, exactly like the Trigger Rock event.
void jjUpdateTexturedBG()
  Forces JJ2+ to reconstruct the textured background from its relevant properties. This should be handled automatically now.

## Appendix: Tile IDS

Tile IDs are stored in uint16 variables, and while it's perfectly all right for you to figure them out on your own, JJ2+ does provide you with a few helpful constants.

There are a maximum of 4095 unique tiles in any given level, and you can get any ordinary tile's ID in JCS. The "1*" destruct block in the Tubelectric tileset, for example, is at position 5,31 within the tileset. Since those numbers are 1-indexed, you need to subtract 1 from each of them. Then multiply the Y position by 10, add it to the X position, and you've got the tile ID: 304. One row below it, the "2*" block is tile ID 314, and so on. If you've gotten a tile ID from jjTileGet and want to know which basic tile it is, you can bitwise AND the number with the constant TILE::RAWRANGE (=0xFFF): if `uint16 foo` is a vertically-flipped version of tile 715, then `foo & TILE::RAWRANGE` will equal 715 exactly.

Horizontally and vertically flipped tiles are marked by the 0x1000 and 0x2000 bits respectively, or more helpfully, TILE::HFLIPPED and TILE::VFLIPPED. Thus to get the tile ID for the horizontally flipped version of tile 443, type `443 + TILE::HFLIPPED`. (Or `|` instead of `+`, to be safe.) Note that JJ2 may crash if asked to horizontally flip a tile that was not placed horizontally flipped somewhere in the level in JCS; the same issue does not apply to vertically flipped tiles.

Similarly, to place or otherwise use animated tiles, add the TILE::ANIMATED constant (=0x8000). `0 + TILE::ANIMATED` will be the topmost animated tile in the list, `1 + TILE::ANIMATED` will be the second from the top, and so on. You can add TILE::HFLIPPED and/or TILE::VFLIPPED at the same time as TILE::ANIMATED—`1 + TILE::VFLIPPED + TILE::ANIMATED` is the second animated tile from the top, flipped vertically—and ANDing by TILE::RAWRANGE will get rid of the TILE::ANIMATED flag just like it does the other two.

(These constants all belong to the TILE::Flags enum, in case you need to know that for any arcane reason.)

## Appendix: Custom Objects

Among AngelScript's most powerful features is certainly the ability to define your own objects. behaviors. To begin with, it is important to have some understanding of how JJ2 handles its objects. Every active object contains a pointer to a function that defines its behavior. To take a simple example: the Pulze Light object sits in place, constantly adjusting its light property, and removes itself from memory when it goes too far offscreen or when the player dies in single player. Most objects have significantly more complicated behaviors than that, but they all come down to one thing: a function that is called by the object, every single tick. What AngelScript does is allow you to write your own object behaviors, either based on JJ2's native ones or else totally from scratch.

The starting point for any object customization is the jjOBJ property behavior. JJ2 (and by extension AngelScript) has a massive inventory of possible values, all grouped together for you in the BEHAVIOR namespace. Most of the behaviors correspond to individual JJ2 objects—BEHAVIOR::QUEEN for OBJECT::QUEEN, BEHAVIOR::CHESHIRE1 for OBJECT::CHESHIRE1, and so on—but there are also a lot of more generic behaviors that get recycled for multiple objects, such as BEHAVIOR::PICKUP (food, gems, ammo, coins, and so on), BEHAVIOR::WALKINGENEMY (lizards, hatters, doggy doggs, and several more), and BEHAVIOR::SHARD (various particle effects). To make the Norm Turtle enemy behave like its JJ1 counterpart, i.e. walk back and forth and never do anything else, we need only change its behavior from BEHAVIOR::NORMTURTLE to BEHAVIOR::WALKINGENEMY. By giving it a generic enemy-type playerHandling value, we can also remove its behavior of creating turtle shells when defeated.

    void onLevelLoad() {
    	jjObjectPresets[OBJECT::NORMTURTLE].behavior = BEHAVIOR::WALKINGENEMY;
    	jjObjectPresets[OBJECT::NORMTURTLE].playerHandling = HANDLING::ENEMY;
    }

Still, that's not very exciting. The generic WALKINGENEMY behavior is really easy to use because it only ever uses one animation, but what if you want something a little more complicated, for example, a Labrat? There are two steps involved here. First, define a behavior function, which is any function that fits the pattern `void func(jjOBJ@)`. This jjOBJ, naturally, is the object doing the behaving... if you have three objects in your level with the same behavior function, that function will be called three times per tick, pointing to each of the three objects in turn. Second, in onLevelLoad, set behavior to that function of yours instead of a BEHAVIOR::Behavior constant. For example:

    void onLevelLoad() {
    	jjObjectPresets[OBJECT::LABRAT].behavior = MyRat;
    }
    void MyRat(jjOBJ@ rat) {
    	rat.behave(BEHAVIOR::LABRAT);
    }

Those two steps alone are pretty pointless, though, since the end result will be that the object behaves exactly as it always did. Instead of JJ2 seeing that behavior is BEHAVIOR::LABRAT and calling the labrat behavior function, JJ2 will see that behavior is `MyRat` and then call that function, which will in turn tell JJ2 to call the labrat behavior function. A player would notice no difference whatsoever. However, behave does take an optional boolean parameter (defaults true), to specify whether JJ2 should actually follow any instructions within the native behavior function to draw the object. By setting this parameter to false, we gain the opportunity to draw the object however we like using AngelScript's various drawing functions, for example, tinted red:

    void onLevelLoad() {
    	jjObjectPresets[OBJECT::LABRAT].behavior = MyRat;
    }
    void MyRat(jjOBJ@ rat) {
    	rat.behave(BEHAVIOR::LABRAT, false);
    	jjDrawSpriteFromCurFrame(rat.xPos, rat.yPos, rat.curFrame, rat.direction, SPRITE::TINTED, 24);
    }

But AngelScript lets you do much more than just call the behave method and play around with sprites. Instead you can define an object's behavior completely from start (literally) to finish. The most important jjOBJ property to consider when defining a custom object behavior is state, since JJ2 objects are basically state machines. When an object is first created—barring bizarre jjObjectPresets fiddling—its state will equal STATE::START. Traditionally, objects take this opportunity to initialize a few properties not already set in jjObjectPresets, perhaps read some parameters from the event map, and then change their state to something else, e.g. STATE::IDLE or STATE::STILL or STATE::DELAYEDSTART. A red spring, for instance, learns during STATE::START whether it's supposed to be a ceiling spring or a floor spring, and never bothers checking to find out ever again. If you're defining a bullet object, changing state to something else (usually STATE::FLY) is mandatory, since bullets of STATE::START are not checked for collision with other objects/players; otherwise there's nothing in the game code that forces you to do it, but it certainly seems like it should be a good idea.

On the opposite side of an object's lifespan is STATE::KILL. Not all objects need to have this state, but anything that uses JJ2's normal shootable-object code will be set to STATE::KILL when the energy property reaches 0. In most cases, STATE::KILL is a sign to call jjOBJ::delete, although you may want to do other things as well, e.g. add an explosion. Bullets are a bit different, using STATE::EXPLODE instead, but the outcome is pretty much the same.

Somewhat related is STATE::DEACTIVATE, the only state besides STATE::START that is essentially guaranteed to apply to every single object, albeit only in Single Player. (In multiplayer, it can of course be invoked manually, but will never be triggered from the game itself.) STATE::DEACTIVATE occurs under one of two circumstances: the player dies, causing every object to deactivate, or the object was active but is now more than about thirty tiles distant from the player and thus no longer belongs in active memory. Objects whose deactivates property is set to false, e.g. Rotating Rock, are immune from the latter case, but all objects get STATE::DEACTIVATE when the player dies in SP. Like STATE::KILL and jjOBJ::delete, you can usually just get away with calling jjOBJ::deactivate, which deletes the object and (if it was created directly from the level map) makes a note that the object is no longer active and can be recreated later.

Finally, many objects will need some code for STATE::FREEZE. The usual pattern is to decrease the freeze property by one every tick, and once it hits 0, restore state to oldState. This is an also an opportunity to use the SPRITE::FROZEN mode for drawing sprites, although jjOBJ::draw will take care of that for you automatically, along with flashing the object white if it's recently been shot. Here, then, is some sample code for a very basic enemy that sits in place and does absolutely nothing but animate:

    void StationaryEnemy(jjOBJ@ obj) {
    	switch (obj.state) {
    		case STATE::START: //always used
    			obj.determineCurAnim(ANIM::SUCKER, 4);
    			obj.state = STATE::IDLE;
    			obj.playerHandling = HANDLING::ENEMY;
    			obj.bulletHandling = HANDLING::HURTBYBULLET;
    			obj.isTarget = true;
    			obj.triggersTNT = true;
    			obj.energy = 1;
    			obj.points = 300;
    		case STATE::IDLE: //arbitarily chosen state
    			obj.frameID = (jjGameTicks/5) & 7;
    			obj.determineCurFrame(); //remember to do this after changing frameID, since by the time you're writing your own behavior, JJ2 won't do it for you anymore
    			obj.draw();
    			break;
    								case STATE::FREEZE: //can be left out if object can't be shot, or if isFreezable equals false, or if there's no ice in the level, or if you don't mind object never unfreezing
    												if (--obj.freeze == 0) obj.state = obj.oldState;
    			//consider calling jjOBJ::unfreeze() here
    			obj.draw();
    			break;
    		case STATE::DEACTIVATE: //can be left out if level is MP-only
    			obj.deactivate();
    												break;
    		case STATE::KILL: //can be left out if not using normal object energy handling
    			obj.delete();
    			break;
    	}
    }

Naturally there's a lot more that an enemy can do—move around, change animations, fire bullets—but that's the basic structure right there. Draw the sprite to the screen somehow, remember to delete the object when it gets killed or deactivated, and pretty much everything else is optional or bonus. You don't even need to worry about its energy, since the HANDLING::ENEMY and HANDLING::HURTBYBULLET settings make JJ2 take care of all that stuff for you. Then there's the basic form of a bullet, which might look something like this:

    void onLevelLoad() {
    	jjObjectPresets[OBJECT::BLASTERBULLET].behavior = DullBullet; //for the sake of example, let's just use blaster's existing values for curAnim and xSpeed and so on
    }
    void DullBullet(jjOBJ@ obj) {
    	if (obj.state == STATE::START) {
    		obj.state = STATE::FLY;
    		if (obj.creatorType == CREATOR::PLAYER) obj.xSpeed = obj.xSpeed + obj.var[7] / 65536.0; //xSpeed of the player when firing the bullet
    	} else if (obj.state == STATE::DEACTIVATE) {
    		obj.delete();
    	} else if (obj.state == STATE::EXPLODE) {
    		obj.behavior = BEHAVIOR::EXPLOSION2;
    		obj.frameID = 0; //display the full .killAnim animation
    	} else {
    		obj.xSpeed = obj.xSpeed + obj.xAcc;
    		obj.ySpeed = obj.ySpeed + obj.yAcc;
    		if ((--obj.counterEnd == 0) || (jjMaskedPixel(obj.xPos + obj.xSpeed, obj.yPos + obj.ySpeed))) {
    			obj.state = STATE::EXPLODE;
    		} else {
    			obj.xPos = obj.xPos + obj.xSpeed;
    			obj.yPos = obj.yPos + obj.ySpeed;
    			obj.draw();
    		}
    	}
    }

The most important things about defining a bullet are a) changing the state from STATE::START and b) changing the state to STATE::EXPLODE, because those states are referenced by various bits of external code. Bullets with either of those two states will not be checked for collision with other objects or players. Moreover, setting the state to STATE::EXPLODE in an online server may potentially tell other clients that their own copies of that bullet object need to be destroyed.

To illustrate one way in which bullet behaviors may be made more complicated, using the following code instead will cause the bullet to recognize ricochet events:

    		if (--obj.counterEnd == 0) {
    			obj.state = STATE::EXPLODE;
    		} else if (
    			jjMaskedPixel(obj.xPos + obj.xSpeed, obj.yPos + obj.ySpeed) && ((jjEventAtLastMaskedPixel != AREA::RICOCHET) || !obj.ricochet())) {
    			obj.state = STATE::EXPLODE;
    		} else {
    			obj.xPos = obj.xPos + obj.xSpeed;
    			obj.yPos = obj.yPos + obj.ySpeed;
    			obj.draw();
    		}

Not all objects are enemies and bullets, of course, but you can get by for quite a while by pretending they are while making ever more inventive use of the various sprite-drawing functions. Still, what if you want to add a new pickup instead? For example, say you want the Fast Feet pickup to increase how high a player can jump. For that you'll need the most complicated hook function of them all: onObjectHit.

    void onLevelLoad() {
    	jjObjectPresets[OBJECT::FASTFEET].points = 100;
    	jjObjectPresets[OBJECT::FASTFEET].scriptedCollisions = true;
    }

    void onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
    	if (obj.eventID == OBJECT::FASTFEET) { //always a good idea to make sure you've got the right object
    		play.jumpStrength = play.jumpStrength - 1;
    		obj.behavior = BEHAVIOR::EXPLOSION2; //this is ESSENTIAL. just like enemies die by getting their states set to STATE::KILL, and bullets die by getting their states set to STATE::EXPLODE, pickups die by getting their behavior set to BEHAVIOR::EXPLOSION2. yes, sometimes a little consistency is in fact too much to ask for.
    		obj.scriptedCollisions = false; //or obj.playerHandling = HANDLING::EXPLOSION; or something like that
    		obj.frameID = 0;
    		//you should probably play a sound here too. pick one! it'll be an adventure!
    	}
    }

The short version of the story is that onObjectHit is what gets called for objects for which scriptedCollisions equals true, if their playerHandling value is HANDLING::PICKUP (called for collisions with players only), or HANDLING::SPECIAL (called for collisions with either players or bullets). The above case, HANDLING::PICKUP, is pretty straightforward: the player argument points to the jjPLAYER who collided with the pickup object, and the bullet and force arguments may be totally ignored. HANDLING::SPECIAL is unfortunately a lot more complicated.

| jjOBJ@ bullet | jjPLAYER@ player | int force
--- | --- | --- | ---
Collision | null | non-null | 0
Sugar rush | null | non-null | 1
Special move | null | non-null | -1
Frozen+running | null | non-null | -101
Bullet | non-null | maybe null | variable
Turtle shell | non-null, but objectID=0 | maybe null | 1
Blue+orange bird | non-null, but objectID=0 | maybe null | 4
TNT explosion | non-null, but objectID=0 | maybe null | variable

As you can see, the various collisions that may call onObjectHit for HANDLING::SPECIAL objects may be broken into two main types, or three if you want to consider bullets on their own. The first four categories result from a player rabbit directly colliding with the object, and the force parameter hinting at whether the object should consider taking damage from the collision or not. Of course, something like the Cheshire2 object doesn't pay any attention to force here; it's pretty much just for destructable objects like enemies or crates.

Bullets are the main alternate cause, and force should correspond to their animSpeed property. In this case, player may or may not be null, depending on whether the bullet was fired by a player (not null) or by a generator object/crate object/whatever (null). Or to put it in AngelScript terms, whether the bullet's creatorType property equals CREATOR::PLAYER.

The remaining three categories—turtle shells, impacts with the blue and orange bird's beak, and being caught in a TNT object's blast radius— all employ the curious strategy of setting bullet to `jjObjects[0]`. To distinguish them from true bullets, thus all you need to do is compare the jjOBJ's objectID property to 0, but since JJ2 never really reads any of object 0's properties, it's generally safe to treat such collisions exactly as if they were truly bullets: for example, setting the bullet jjOBJ's state to STATE::EXPLODE when that jjOBJ is object 0 is an action totally without consequences. Like bullets, turtle shell collisions may or may not set the player parameter, depending on whether they've been shot by a player or not prior to calling onObjectHit.

If you are scripting a multiplayer level and wish to ensure that the results of the collision are received
by other players in the server, and you are scripting a HANDLING::PICKUP object, change the object's behavior to BEHAVIOR::EXPLOSION2 upon collection. If the object is HANDLING::ENEMY or HANDLING::SPECIAL, changing its state to STATE::KILL, STATE::EXPLODE, or STATE::ACTION will be announced to other players; any other state change will remain local.

A fairly comprehensive onObjectHit definition for enemy-like objects can be found in `plusEnscripted.j2as`, which makes note of most of the edge cases that it's leaving out. Otherwise... good luck! Remember to look at all the options for the jjOBJ playerHandling and bulletHandling properties while figuring out exactly how you want your object to work, and as with everything else, don't be afraid to ask for help.

## Appendix: Metadata

The AngelScript library provides metadata as a very flexible system that JJ2+ is only beginning to take advantage of. Specifically, there are only two metadata options available so far:

[RFBULLET]
  Most standard bullet behaviors handle speed and acceleration pretty much the same way, but RFs need a lot of special treatment, and messing with them at all tends to make them not fly fast enough and stuff like that. If you write a behavior function that's supposed to serve as a wrapper around BEHAVIOR::RFBULLET (i.e. you set it as the behavior for one or more bullets in jjObjectPresets and it calls `jjOBJ::behave(BEHAVIOR::RFBULLET)`), make sure to prefix your behavior function with [RFBULLET] in order to receive that same special treatment and thus get the right speeds assigned at firing time.
[solid]
  Prefix a behavior function with [solid] and it will be counted as solid for the purposes of crates, barrels, etc. falling onto or being pushed into other objects in local games. This has no impact on whether players treat the object as solid, only whether crates and such do. The [solid] label is already effectively applied to BEHAVIOR::CRATE, BEHAVIOR::AMMO15, BEHAVIOR::MONITOR, and BEHAVIOR::BIGOBJECT.

## Appendix: Preprocessor Directives

A preprocessor directive is a line included in an Angelscript file that does not obey standard AS syntax rules and is in fact not processed in the course of ordinary code running but is instead preprocessed—evaluated before anything else in the script runs, even onLevelLoad. They each begin with a pound sign (#) and should take up an entire line of the file; AngelScript's handling of preprocessor directives is pretty flexible, but it's best to put them all at the top of the file, outside of any code blocks, each given its own line. Though all the below examples use double quotes (") to surround directive arguments, single quotes (') may be used instead if so desired, so long as the same symbol is used on both sides.

#include "<filename>"
  Similar to include directives/functions in other programming languages, #include loads another AngelScript file and adds all the code inside of it to the same module that the #include directive was written in. JJ2+ will treat everything in the included script exactly as if it were written directly in the including script, meaning that e.g. functions from one script may call functions from the other, but the same function name/return type may not be defined in both. A simple example use case might be a convenient single function to add a particle of a specific type at a specific location with specific initial speeds. Such a function would be useful enough that you might want to use it in multiple scripts, and this could be accomplished by placing it in its own file (`addparticle.asc`) and having those other scripts #include the file.
  It's worth emphasizing that #include adds all the code of the included file to the global scope, no matter where the directive appeared in the including file. Writing an #include directive inside of a namespace, for example, will not cause everything in the included file to be defined within that namespace.
  An included file may have any number of preprocessor directives of its own, including more #include directives. Any given file will only be #included at most once per script module, however, and JJ2+ will simply ignore attempts to the contrary; for example, if `foo.j2as` #includes both `bar.asc` and `baz.asc` but `baz.asc` also #includes `bar.asc`, then `bar.asc` will only be included the once. No error message is produced in this scenario.
#pragma require "<filename>"
  Unlike #include, the require directive is not interpreted by the AngelScript section of JJ2+ at all but instead is used in netcode. (As such, it is unnecessary in levels made exclusively for local play.) When an online server cycles to a new level or gets a new joiner, clients are forced to download the .j2l, .j2t, and (if any) .j2as file the server is hosting, unless they already have an identical version of that same file conveniently located on their harddrive. The same treatment is also applied to any files that are #pragma required, unless the server's host doesn't have the file to begin with.
  You should #pragma require any external file that your script uses unless you have a good reason not to. This includes tilesets or palettes used by jjPAL::load, custom sounds or animation files used by jjSampleLoad or jjANIMSET::load, and of course script files that you #include.
#pragma offer "<filename>"
  Offering files is the less forceful version of requiring them: the server will still attempt to send all clients any files that have been #pragma offered, but the clients may decline to accept those files by pressing Esc at the downloading screen. This is the same treatment that is given to music files (unless the server has turned `/uploadmusic` off), and music files (as loaded by jjMusicLoad) are basically the archetypal kind of file that should be offered (at best), not required, due to their comparatively large filesizes.
  (The overall order of files the client may attempt to download is, in case you're curious: the .j2l itself; the .j2t; any .j2as or .mut files; any #pragma required files; any #pragma offered files; the level's music.)
  If the same file appears in both a #pragma require and a #pragma offer, across any number of different script files, it will be required instead of offered.
#pragma name "<mutator name>"
  The #pragma name directive is only currently used by mutators, and must be included in the first ten lines of the file in order to be noticed. It serves no other purpose than to provide a human-readable name for JJ2+ to display in place of the mutator's filename in certain contexts, e.g. while listing active mutators.

## Appendix: Mutators

Mutators are special AngelScript files that differ from the ones described in the introduction in three key ways:

1. Instead of being connected to individual .j2l files, any mutator can be run in any level at all. (This is not always a good idea—for example, a mutator script designed solely for Treasure Hunt games wouldn't be much help run in a Race level—but it is allowed.) Mutators are enabled or disabled using the `/mutators` command in chat messages, and clients joining a server with one or more mutators running will download those scripts and run them just like they would an ordinary script file.
2. To signify this and to make for easier organization, mutators use the file extension .mut instead of .j2as.
3. Mutators may not define the onFunction# or onPlayerTimerEnd hook functions, because they are too closely tied to specific level designs. Mutators may define functions that happen to use those names, but they will not be called by JJ2 under normal circumstances. (Mutators can however use the jjPLAYER method timerFunction to make a player use a timer end function defined inside a mutator script, as well as access the global array jjEnabledASFunctions or the global function jjEnableEachASFunction.)

Otherwise, mutators have all the same options available to them that a level's primary script does, are run concurrently by both clients and servers, and may be written for any number of purposes. You could write a mutator that acts like an IRC bot and reacts to certain chat phrases, or a mutator to replace JJ2's normal health system with a new one of your devising, or a mutator to draw a minimap of players to some corner of the screen, or plenty of other things. Since any number of different mutators may be loaded at once, you should try to make each one do as few distinct tasks as possible.

To be specific about how exactly mutators work: each one is loaded by JJ2+'s AngelScript engine as a script module, meaning each has access to the same set of JJ2+-defined global variables, functions, classes, etc., but cannot access the other modules' locally defined variables or functions. Two mutators may each define a function `void foo()`, but since they live in separate modules, there will be no naming conflict. (The primary .j2as script of a level is also loaded as a distinct script module.)

Because mutators do not need to worry about naming conflicts with other mutators (or the primary script), individual hooks may be multiply defined across different modules. Three different mutators may all define a `void onMain()` function, for example, and JJ2+ will run all three of them every tick. Specifically, for any multiply defined hook, the version (if any) in the primary script will be run first, followed by the versions (if any) in each active mutator, in alphabetical order. It becomes the job of the server host (or remote admins) to pick a list of mutators that do not run code at cross purposes with one another.

For example, suppose that `foo.j2l` has a primary script `foo.j2as` with the function `void onPlayer(jjPLAYER@ p) { p.coins = 5; }`. The server is running `bar.mut`, containing `void onPlayer(jjPLAYER@ p) { ++p.coins; }`, and `baz.mut`, containing `void onPlayer(jjPLAYER@ p) { jjDrawString(p.xPos, p.yPos - 40, "" + p.coins); }`. The net result of the three script modules will be that the number 6 (5+1) will always be drawn above each local player's head.

Certain hooks—e.g. onLocalChat and onDrawHealth—have bool return values, where "true" means roughly "this script has performed all necessary actions relevant to this event," e.g. if onDrawHealth returns true then JJ2 will not run its normal health-drawing code but instead assumes the script is somehow doing the job of presenting that information to the player. If two different mutators each define onDrawHealth, and the first mutator's version returns true, the second mutator's version will still be run, but JJ2 will not draw the normal hearts no matter what value the second mutator's version returns. The return values are ORed together and it only matters that at least one of them (no matter which one) returns true.

Besides onFunction# and onPlayerTimerEnd, the only exception to the above rule is onReceive, which is only run at most once per jjSTREAM packet received. The global function jjSendPacket has an argument toScriptModuleID specifying which script module should receive the packet, defaulting to jjScriptModuleID, which is the index of the script module calling the function. The values of jjScriptModuleID match up with the order of modules that a multiply defined hook will be called in, so for example, in a server running `foo.j2as` and `baz.mut` and `bar.mut`, jjScriptModuleID will equal 0 within foo (primary script is always 0), 1 within bar, and 2 within baz (alphabetically later than bar). Therefore if jjScriptModuleID is passed as the value for toScriptModuleID, the packet will be received by the onReceive hook defined in the same module as jjSendPacket was called in—but if toScriptModuleID equals 0, the packet will be received by onReceive defined in the primary script instead. This is rarely a good idea but could be used by mutators designed to extend specific scripts.

Mutators can use all the same preprocessor directives that a primary script can, including or requiring or offering any supplementary files relevant to their execution. In fact, they are (currently) the only kind of script that can use `#pragma name`.

Even if you are not writing a mutator, it is important to be aware that they exist and might be run in parallel with your level's primary script (assuming you are creating a multiplayer level). For example, you might write a primary script like this to make chocolate bars give players an instant sugar rush no matter how many other food pickups they'd collected:

    void onLevelLoad() {
    	jjObjectPresets[OBJECT::CHOCOLATEBAR].scriptedCollisions = true;
    }
    void onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
    	player.startSugarRush();
    	obj.behavior = BEHAVIOR::EXPLOSION2;
    	obj.scriptedCollisions = false;
    	obj.frameID = 0;
    }

However, that would interact poorly with a mutator that set `jjObjectPresets[OBJECT::TOASTERAMMO3].scriptedCollisions = true;` for some other reason. Therefore it is best to code defensively, in this case by making sure that `(obj.eventID == OBJECT::CHOCOLATEBAR)`. Or it might be tempting to repurpose jjPLAYER::coins to keep track of some other player-related property in a level with no coins in it, but it's better to declare your own non-member variable for that, in order to avoid potential conflicts with a mutator using jjPLAYER::coins for something else. Certainly there will always be scripts that simply cannot functionally coexist with each other (a primary script where everyone has airboards; a mutator that disables all flight), but you might as well do your best to restrict such cases to times when the basic purposes of the scripts are in conflict, instead of just when one of them isn't coded carefully enough.

## Appendix: Constants

### WEAPON::Weapon

Weapons are named pretty intuitively, using the common names for each one instead of necessarily the JCS names or official manual names. The exceptions are Pepper Spray and Electro-Blaster, which are simply GUN8 and GUN9 (a la JCS) to allow for situations in which those constants refer to the Fireball and Blade Guns instead.

- BLASTER
- BOUNCER
- ICE
- SEEKER
- RF
- TOASTER
- TNT
- GUN8
- GUN9
- CURRENT

### STATE::State

These are the different modes, or states, that a jjOBJ object can be in at any given time. The important ones are START, which is the initial state of an object that's just been created; KILL, for when an object gets destroyed; DEACTIVATE, for when an object passes out of memory in single player mode; EXPLODE, for dying bullets; ACTION, for collisions between players/bullets and some more complicated objects; and FREEZE, for when an object gets shot with ice. The rest are largely arbitrary.

- START
- SLEEP
- WAKE
- KILL
- DEACTIVATE
- WALK
- JUMP
- FIRE
- FLY
- BOUNCE
- EXPLODE
- ROCKETFLY
- STILL
- FLOAT
- HIT
- SPRING
- ACTION
- DONE
- PUSH
- FALL
- FLOATFALL
- CIRCLE
- ATTACK
- FREEZE
- FADEIN
- FADEOUT
- HIDE
- TURN
- IDLE
- EXTRA
- STOP
- WAIT
- LAND
- DELAYEDSTART
- ROTATE
- DUCK

### AREA::Area

Indented constants are alternate aliases for the non-indented constants directly above them. They evaluate to exactly the same results—the only difference between, say, AREA::JAZZLEVELSTART and AREA::JAZZSTART is which you'd rather type.

- ONEWAY
- HURT
- VINE
- HOOK
- SLIDE
- HPOLE
- VPOLE
- FLYOFF
- RICOCHET
- BELTRIGHT
- BELTLEFT
- ACCBELTRIGHT
- ACCBELTLEFT
- STOPENEMY
- WINDLEFT
- WINDRIGHT
- EOL
- WARPEOL
- REVERTMORPH
- FLOATUP
- TRIGGERROCK
- DIMLIGHT
- SETLIGHT
- LIMITXSCROLL
- RESETLIGHT
- WARPSECRET
- ECHO
- ACTIVATEBOSS
- JAZZLEVELSTART
- JAZZSTART
- SPAZLEVELSTART
- SPAZSTART
- MPLEVELSTART
- MPSTART
- LORILEVELSTART
- LORISTART
- WARP
- WARPTARGET
- PATH
- AREAID
- NOFIREZONE
- TRIGGERZONE
- SUCKERTUBE
- TEXT
- WATERLEVEL
- MORPHFROG
- WATERBLOCK

### OBJECT::Object

Indented constants are alternate aliases for the non-indented constants directly above them. They evaluate to exactly the same results—the only difference between, say, OBJECT::SAVEPOST and OBJECT::CHECKPOINT is which you'd rather type.

- BLASTERBULLET
- BOUNCERBULLET
- ICEBULLET
- SEEKERBULLET
- RFBULLET
- TOASTERBULLET
- FIREBALLBULLET
- ELECTROBULLET
- BLASTERBULLETPU
- BOUNCERBULLETPU
- ICEBULLETPU
- SEEKERBULLETPU
- RFBULLETPU
- TOASTERBULLETPU
- FIREBALLBULLETPU
- ELECTROBULLETPU
- FIRESHIELDBULLET
- WATERSHIELDBULLET
- BUBBLESHIELDBULLET
- LIGHTNINGSHIELDBULLET
- PLASMASHIELDBULLET
- BULLET
- SMOKERING
- SHARD
- EXPLOSION
- BOUNCEONCE
- FLICKERGEM
- LASER
- UTERUSSPIKEBALL
- BIRD
- BUBBLE
- ICEAMMO3
- BOUNCERAMMO3
- SEEKERAMMO3
- RFAMMO3
- TOASTERAMMO3
- TNTAMMO3
- GUN8AMMO3
- GUN9AMMO3
- TURTLESHELL
- SWINGINGVINE
- BOMB
- SILVERCOIN
- GOLDCOIN
- GUNCRATE
- CARROTCRATE
- ONEUPCRATE
- GEMBARREL
- CARROTBARREL
- ONEUPBARREL
- BOMBCRATE
- ICEAMMO15
- BOUNCERAMMO15
- SEEKERAMMO15
- RFAMMO15
- TOASTERAMMO15
- TNT
- AIRBOARDGENERATOR
- FROZENSPRING
- FASTFIRE
- SPRINGCRATE
- REDGEM
- GREENGEM
- BLUEGEM
- PURPLEGEM
- SUPERGEM
- BIRDCAGE
- GUNBARREL
- GEMCRATE
- MORPH
- CARROT
- FULLENERGY
- FIRESHIELD
- WATERSHIELD
- BUBBLESHIELD
- LIGHTNINGSHIELD
- PLASMASHIELD
- FASTFEET
- ONEUP
- EXTRALIFE
- EXTRALIVE
- EOLPOST
- SAVEPOST
- CHECKPOINT
- BONUSPOST
- REDSPRING
- GREENSPRING
- BLUESPRING
- INVINCIBILITY
- EXTRATIME
- FREEZER
- FREEZEENEMIES
- HORREDSPRING
- HORGREENSPRING
- HORBLUESPRING
- BIRDMORPH
- TRIGGERCRATE
- FLYCARROT
- RECTREDGEM
- RECTGREENGEM
- RECTBLUEGEM
- TUFTURT
- TUFBOSS
- LABRAT
- DRAGON
- LIZARD
- BEE
- BUMBEE
- RAPIER
- SPARK
- BAT
- SUCKER
- CATERPILLAR
- CHESHIRE1
- CHESHIRE2
- HATTER
- BILSY
- SKELETON
- DOGGYDOGG
- NORMTURTLE
- HELMUT
- DEMON
- DRAGONFLY
- MONKEY
- FATCHICK
- FENCER
- FISH
- MOTH
- STEAM
- ROTATINGROCK
- BLASTERPOWERUP
- BOUNCERPOWERUP
- ICEPOWERUP
- SEEKERPOWERUP
- RFPOWERUP
- TOASTERPOWERUP
- LEFTPADDLE
- RIGHTPADDLE
- FIVEHUNDREDBUMP
- CARROTBUMP
- APPLE
- BANANA
- CHERRY
- ORANGE
- PEAR
- PRETZEL
- STRAWBERRY
- STEADYLIGHT
- PULZELIGHT
- PULSELIGHT
- FLICKERLIGHT
- QUEEN
- FLOATSUCKER
- BRIDGE
- LEMON
- LIME
- THING
- WATERMELON
- PEACH
- GRAPES
- LETTUCE
- EGGPLANT
- CUCUMB
- CUCUMBER
- COKE
- SOFTDRINK
- PEPSI
- SODAPOP
- MILK
- PIE
- CAKE
- DONUT
- CUPCAKE
- CHIPS
- CANDY
- CHOCBAR
- CHOCOLATEBAR
- ICECREAM
- BURGER
- PIZZA
- FRIES
- CHICKENLEG
- SANDWICH
- TACO
- WEENIE
- HAM
- CHEESE
- FLOATLIZARD
- STANDMONKEY
- DESTRUCTSCENERY
- DESTRUCTSCENERYBOMB
- TNTDESTRUCTSCENERY
- COLLAPSESCENERY
- STOMPSCENERY
- GEMSTOMP
- RAVEN
- TUBETURTLE
- GEMRING
- SMALLTREE
- AMBIENTSOUND
- UTERUS
- CRAB
- WITCH
- ROCKETTURTLE
- BUBBA
- DEVILDEVAN
- DEVANROBOT
- ROBOT
- CARROTUSPOLE
- PSYCHPOLE
- DIAMONDUSPOLE
- FRUITPLATFORM
- BOLLPLATFORM
- GRASSPLATFORM
- PINKPLATFORM
- SONICPLATFORM
- SPIKEPLATFORM
- SPIKEBOLL
- GENERATOR
- EVA
- BUBBLER
- TNTPOWERUP
- GUN8POWERUP
- GUN9POWERUP
- SPIKEBOLL3D
- SPRINGCORD
- BEES
- COPTER
- LASERSHIELD
- STOPWATCH
- JUNGLEPOLE
- WARP
- BIGROCK
- BIGBOX
- TRIGGERSCENERY
- BOLLY
- BUTTERFLY
- BEEBOY
- SNOW
- TWEEDLEBOSS
- AIRBOARD
- CTFBASE
- XMASNORMTURTLE
- XMASLIZARD
- XMASFLOATLIZARD
- XMASBILSY
- CAT
- PACMANGHOST

### BEHAVIOR::Behavior

Most of these constants share the same names as their corresponding OBJECT::Object constants—BEHAVIOR::SHARD for OBJECT::SHARD, BEHAVIOR::UTERUS for OBJECT::UTERUS, and so on—but there are a few exceptions. To save space, only the BEHAVIOR::Behavior values that do not align perfectly with OBJECT::Object values will be listed.

- OBJECT::BLASTERBULLET, OBJECT::ICEBULLET, OBJECT::FIREBALLBULLET, OBJECT::BLASTERBULLETPU, OBJECT::FIREBALLBULLETPU, OBJECT::FIRESHIELDBULLET, and OBJECT::PLASMASHIELDBULLET/LIGHTNINGSHIELDBULLET all use BEHAVIOR::BULLET.
- Both Seeker bullets use BEHAVIOR::SEEKERBULLET.
- Both RF bullets use BEHAVIOR::RFBULLET.
- Both Toaster bullets use BEHAVIOR::TOASTERBULLET.
- Both Pepper Spray bullets use BEHAVIOR::PEPPERBULLET.
- Both ElectroBlaster bullets use BEHAVIOR::ELECTROBULLET.
- Both pinball bumpers use BEHAVIOR::BUMP.
- Both pinball paddles use BEHAVIOR::PADDLE.
- Both Big Box and Big Rock use BEHAVIOR::BIGOBJECT.
- Tuf Turtle and Lizard both use BEHAVIOR::WALKINGENEMY. (A large number of other behaviors call it from within themselves, but those are the only two that use it directly.)
- Destruct, buttstomp, collapse, speed destruct, and TNT destruct scenery all use BEHAVIOR::DESTRUCTSCENERY.
- Rocket Turtles are divided into BEHAVIOR::ROCKETTURTLE and BEHAVIOR::ROCKETTURTLEPLUS. The second is used by rocket turtle bosses that have a non-zero Velocity parameter; the first by old rocket turtles that use the normal JJ2 code.
- Bolly bosses are divided into BEHAVIOR::BOLLYTOP and BEHAVIOR::BOLLYBOTTOM, the former being the value used by `jjObjectPresets[OBJECT::BOLLY]`.
- All swinging platforms (including the 2D spike boll) use BEHAVIOR::PLATFORM.
- All springs (including horizontal springs) use BEHAVIOR::SPRING.
- All +15 Ammo boxes use BEHAVIOR::AMMO15.
- All powerups, shields, and morph monitors use BEHAVIOR::MONITOR.
- All barrels and crates (including trigger crates) use BEHAVIOR::CRATE.
- All floating pickups (food, ammo, carrots, gems, fast feet, and so on) use BEHAVIOR::PICKUP, except for OBJECT/BEHAVIOR::FLICKERGEM and its close relative BEHAVIOR::DIAMONDSAREFOREVER.
- OBJECT::CTFBASE uses BEHAVIOR::FLAG, to emphasize which part does the actual moving around.
- Enemies exclusive to TSF, i.e. Xmas enemies, Cat and Pacman Ghost don't have their own behaviors and instead use those of, respectively: their original versions, Doggy Dog and Sparks.
- BEHAVIOR::INACTIVE exists for inactive objects, which definitionally don't have any actual behavior functions; it's possible to delete an object by setting its behavior to INACTIVE, but you shouldn't, since jjDeleteObject or jjOBJ::delete does some additional cleanup work. BEHAVIOR::DEFAULT is used for special purposes by jjAddObject and jjOBJ::behave, but otherwise doesn't do anything.
- Finally, the following names exist only as behaviors, not as dedicated objects: MONKEYBULLET, BILSYBULLET, BOLLYBULLET, WITCHBULLET, TUFBOSSBULLET, ROBOTSHARD, BONE, EXPLOSION2 (used by pickup objects once they've been collected), BURNING (creates lots of particles), AIRBOARDFALL, BIRDFEATHER, CORPSE..

### SOUND::Sample

Preview samples at the invaluable JJ2 Soundboard. All LORISOUNDS_* samples are naturally only available in TSF, but the X* and Z* samples (for HH98 and TSF enemies) will evaluate in 1.23 to their ordinary equivalents, e.g. BILSBOSS_THUNDER for XBILSY_THUNDER or DOG_WAF1 for ZDOG_WAF1.

- AMMO_BLUB1
- AMMO_BLUB2
- AMMO_BMP1
- AMMO_BMP2
- AMMO_BMP3
- AMMO_BMP4
- AMMO_BMP5
- AMMO_BMP6
- AMMO_BOEM1
- AMMO_BUL1
- AMMO_BULFL1
- AMMO_BULFL2
- AMMO_BULFL3
- AMMO_FIREGUN1A
- AMMO_FIREGUN2A
- AMMO_FUMP
- AMMO_GUN1
- AMMO_GUN2
- AMMO_GUN3PLOP
- AMMO_GUNFLP
- AMMO_GUNFLP1
- AMMO_GUNFLP2
- AMMO_GUNFLP3
- AMMO_GUNFLP4
- AMMO_GUNFLPL
- AMMO_GUNJAZZ
- AMMO_GUNVELOCITY
- AMMO_ICEGUN
- AMMO_ICEGUN2
- AMMO_ICEGUNPU
- AMMO_ICEPU1
- AMMO_ICEPU2
- AMMO_ICEPU3
- AMMO_ICEPU4
- AMMO_LASER
- AMMO_LASER2
- AMMO_LASER3
- AMMO_LAZRAYS
- AMMO_MISSILE
- AMMO_SPZBL1
- AMMO_SPZBL2
- AMMO_SPZBL3
- BAT_BATFLY1
- BILSBOSS_BILLAPPEAR
- BILSBOSS_FINGERSNAP
- BILSBOSS_FIRE
- BILSBOSS_FIRESTART
- BILSBOSS_SCARY3
- BILSBOSS_THUNDER
- BILSBOSS_ZIP
- BONUS_BONUS1
- BONUS_BONUSBLUB
- BUBBA_BUBBABOUNCE1
- BUBBA_BUBBABOUNCE2
- BUBBA_BUBBAEXPLO
- BUBBA_FROG2
- BUBBA_FROG3
- BUBBA_FROG4
- BUBBA_FROG5
- BUBBA_SNEEZE2
- BUBBA_TORNADOATTACK2
- BUMBEE_BEELOOP
- CATERPIL_RIDOE
- COMMON_AIRBOARD
- COMMON_AIRBTURN
- COMMON_AIRBTURN2
- COMMON_BASE1
- COMMON_BELL_FIRE
- COMMON_BELL_FIRE2
- COMMON_BENZIN1
- COMMON_BIRDFLY
- COMMON_BIRDFLY2
- COMMON_BLOKPLOP
- COMMON_BLUB1
- COMMON_BUBBLGN1
- COMMON_BURN
- COMMON_BURNIN
- COMMON_CANSPS
- COMMON_CLOCK
- COMMON_COIN
- COMMON_COLLAPS
- COMMON_CUP
- COMMON_DAMPED1
- COMMON_DOWN
- COMMON_DOWNFL2
- COMMON_DRINKSPAZZ1
- COMMON_DRINKSPAZZ2
- COMMON_DRINKSPAZZ3
- COMMON_DRINKSPAZZ4
- COMMON_EAT1
- COMMON_EAT2
- COMMON_EAT3
- COMMON_EAT4
- COMMON_ELECTRIC1
- COMMON_ELECTRIC2
- COMMON_ELECTRICHIT
- COMMON_EXPL_TNT
- COMMON_EXPSM1
- COMMON_FLAMER
- COMMON_FLAP
- COMMON_FOEW1
- COMMON_FOEW2
- COMMON_FOEW3
- COMMON_FOEW4
- COMMON_FOEW5
- COMMON_GEMSMSh2
- COMMON_GLASS2
- COMMON_GUNSM1
- COMMON_HARP1
- COMMON_HEAD
- COMMON_HELI1
- COMMON_HIBELL
- COMMON_HOLYFLUT
- COMMON_HORN1
- COMMON_ICECRUSH
- COMMON_IMPACT1
- COMMON_IMPACT2
- COMMON_IMPACT3
- COMMON_IMPACT4
- COMMON_IMPACT5
- COMMON_IMPACT6
- COMMON_IMPACT7
- COMMON_IMPACT8
- COMMON_IMPACT9
- COMMON_ITEMTRE
- COMMON_JUMP
- COMMON_JUMP2
- COMMON_LAND
- COMMON_LAND1
- COMMON_LAND2
- COMMON_LANDCAN1
- COMMON_LANDCAN2
- COMMON_LANDPOP
- COMMON_LOADJAZZ
- COMMON_LOADSPAZ
- COMMON_METALHIT
- COMMON_MONITOR
- COMMON_NOCOIN
- COMMON_PICKUP1
- COMMON_PICKUPW1
- COMMON_PISTOL1
- COMMON_PLOOP1
- COMMON_PLOP1
- COMMON_PLOP2
- COMMON_PLOP3
- COMMON_PLOP4
- COMMON_PLOPKORK
- COMMON_PREEXPL1
- COMMON_PREHELI
- COMMON_REVUP
- COMMON_RINGGUN
- COMMON_RINGGUN2
- COMMON_SHIELD1
- COMMON_SHIELD4
- COMMON_SHIELD_ELEC
- COMMON_SHLDOF3
- COMMON_SLIP
- COMMON_SMASH
- COMMON_SPLAT1
- COMMON_SPLAT2
- COMMON_SPLAT3
- COMMON_SPLAT4
- COMMON_SPLUT
- COMMON_SPRING1
- COMMON_STEAM
- COMMON_STEP
- COMMON_STRETCH
- COMMON_SWISh2
- COMMON_SWISh3
- COMMON_SWISH3
- COMMON_SWISH4
- COMMON_SWISH5
- COMMON_SWISH6
- COMMON_SWISH7
- COMMON_SWISH8
- COMMON_TELPORT1
- COMMON_TELPORT2
- COMMON_UP
- COMMON_WATER
- COMMON_WOOD1
- DEMON_RUN
- DEVILDEVAN_DRAGONFIRE
- DEVILDEVAN_FLAP
- DEVILDEVAN_FROG4
- DEVILDEVAN_JUMPUP
- DEVILDEVAN_LAUGH
- DEVILDEVAN_PHASER2
- DEVILDEVAN_STRECh3
- DEVILDEVAN_STRECHTAIL
- DEVILDEVAN_STRETCh2
- DEVILDEVAN_STRETCH3
- DEVILDEVAN_VANISh2
- DEVILDEVAN_WHISTLEDESCENDING2
- DEVILDEVAN_WINGSOUT
- DOG_AGRESSIV
- DOG_SNIF1
- DOG_WAF1
- DOG_WAF2
- DOG_WAF3
- DRAGFLY_BEELOOP
- ENDING_OHTHANK
- ENDTUNEJAZZ_TUNE
- ENDTUNELORI_CAKE
- ENDTUNESPAZ_TUNE
- EPICLOGO_EPIC1
- EPICLOGO_EPIC2
- EVA_KISS1
- EVA_KISS2
- EVA_KISS3
- EVA_KISS4
- FAN_FAN
- FATCHK_HIT1
- FATCHK_HIT2
- FATCHK_HIT3
- FENCER_FENCE1
- FROG_FROG
- FROG_FROG1
- FROG_FROG2
- FROG_FROG3
- FROG_FROG4
- FROG_FROG5
- FROG_JAZZ2FROG
- FROG_TONG
- GLOVE_HIT
- HATTER_CUP
- HATTER_HAT
- HATTER_PTOEI
- HATTER_SPLIN
- HATTER_SPLOUT
- INTRO_BLOW
- INTRO_BOEM1
- INTRO_BOEM2
- INTRO_BRAKE
- INTRO_END
- INTRO_GRAB
- INTRO_GREN1
- INTRO_GREN2
- INTRO_GREN3
- INTRO_GUNM0
- INTRO_GUNM1
- INTRO_GUNM2
- INTRO_HELI
- INTRO_HITSPAZ
- INTRO_HITTURT
- INTRO_IFEEL
- INTRO_INHALE
- INTRO_INSECT
- INTRO_KATROL
- INTRO_LAND
- INTRO_MONSTER
- INTRO_MONSTER2
- INTRO_ROCK
- INTRO_ROPE1
- INTRO_ROPE2
- INTRO_RUN
- INTRO_SHOT1
- INTRO_SHOTGRN
- INTRO_SKI
- INTRO_STRING
- INTRO_SWISh2
- INTRO_SWISh3
- INTRO_SWISH3
- INTRO_SWISH4
- INTRO_UHTURT
- INTRO_UP1
- INTRO_UP2
- INTRO_WIND_01
- JAZZSOUNDS_BALANCE
- JAZZSOUNDS_HEY1
- JAZZSOUNDS_HEY2
- JAZZSOUNDS_HEY3
- JAZZSOUNDS_HEY4
- JAZZSOUNDS_IDLE
- JAZZSOUNDS_JAZZV1
- JAZZSOUNDS_JAZZV2
- JAZZSOUNDS_JAZZV3
- JAZZSOUNDS_JAZZV4
- JAZZSOUNDS_JUMMY
- JAZZSOUNDS_PFOE
- LABRAT_BITE
- LABRAT_EYE2
- LABRAT_EYE3
- LABRAT_MOUSE1
- LABRAT_MOUSE2
- LABRAT_MOUSE3
- LIZARD_LIZ1
- LIZARD_LIZ2
- LIZARD_LIZ4
- LIZARD_LIZ6
- LORISOUNDS_DIE1
- LORISOUNDS_HURT0
- LORISOUNDS_HURT1
- LORISOUNDS_HURT2
- LORISOUNDS_HURT3
- LORISOUNDS_HURT4
- LORISOUNDS_HURT5
- LORISOUNDS_HURT6
- LORISOUNDS_HURT7
- LORISOUNDS_LORI1
- LORISOUNDS_LORI2
- LORISOUNDS_LORIBOOM
- LORISOUNDS_LORIFALL
- LORISOUNDS_LORIJUMP
- LORISOUNDS_LORIJUMP2
- LORISOUNDS_LORIJUMP3
- LORISOUNDS_LORIJUMP4
- LORISOUNDS_TOUCH
- LORISOUNDS_WEHOO
- MENUSOUNDS_SELECT0
- MENUSOUNDS_SELECT1
- MENUSOUNDS_SELECT2
- MENUSOUNDS_SELECT3
- MENUSOUNDS_SELECT4
- MENUSOUNDS_SELECT5
- MENUSOUNDS_SELECT6
- MENUSOUNDS_TYPE
- MENUSOUNDS_TYPEENTER
- MONKEY_SPLUT
- MONKEY_THROW
- MOTH_FLAPMOTH
- ORANGE_BOEML
- ORANGE_BOEMR
- ORANGE_BUBBELSL
- ORANGE_BUBBELSR
- ORANGE_GLAS1L
- ORANGE_GLAS1R
- ORANGE_GLAS2L
- ORANGE_GLAS2R
- ORANGE_MERGE
- ORANGE_SWEEP0L
- ORANGE_SWEEP0R
- ORANGE_SWEEP1L
- ORANGE_SWEEP1R
- ORANGE_SWEEP2L
- ORANGE_SWEEP2R
- P2_CRUNCH
- P2_FART
- P2_FOEW1
- P2_FOEW4
- P2_FOEW5
- P2_FROG1
- P2_FROG2
- P2_FROG3
- P2_FROG4
- P2_FROG5
- P2_KISS4
- P2_OPEN
- P2_PINCh2
- P2_PINCh3
- P2_PLOPSEQ1
- P2_PLOPSEQ2
- P2_PLOPSEQ3
- P2_PLOPSEQ4
- P2_POEP
- P2_PTOEI
- P2_SPLOUT
- P2_SPLUT
- P2_THROW
- P2_TONG
- PICKUPS_BOING_CHECK
- PICKUPS_HELI2
- PICKUPS_STRETCh2A
- PINBALL_BELL
- PINBALL_FLIP1
- PINBALL_FLIP2
- PINBALL_FLIP3
- PINBALL_FLIP4
- QUEEN_LADYUP
- QUEEN_SCREAM
- RAPIER_GOSTDIE
- RAPIER_GOSTLOOP
- RAPIER_GOSTOOOH
- RAPIER_GOSTRIP
- RAPIER_HITCHAR
- ROBOT_BIG1
- ROBOT_BIG2
- ROBOT_CAN1
- ROBOT_CAN2
- ROBOT_HYDRO
- ROBOT_HYDRO2
- ROBOT_HYDROFIL
- ROBOT_HYDROPUF
- ROBOT_IDLE1
- ROBOT_IDLE2
- ROBOT_JMPCAN1
- ROBOT_JMPCAN10
- ROBOT_JMPCAN2
- ROBOT_JMPCAN3
- ROBOT_JMPCAN4
- ROBOT_JMPCAN5
- ROBOT_JMPCAN6
- ROBOT_JMPCAN7
- ROBOT_JMPCAN8
- ROBOT_JMPCAN9
- ROBOT_METAL1
- ROBOT_METAL2
- ROBOT_METAL3
- ROBOT_METAL4
- ROBOT_METAL5
- ROBOT_OPEN
- ROBOT_OUT
- ROBOT_POEP
- ROBOT_POLE
- ROBOT_SHOOT
- ROBOT_STEP1
- ROBOT_STEP2
- ROBOT_STEP3
- ROCK_ROCK1
- RUSH_RUSH
- SCIENCE_PLOPKAOS
- SKELETON_BONE1
- SKELETON_BONE2
- SKELETON_BONE3
- SKELETON_BONE5
- SKELETON_BONE6
- SKELETON_BONE7
- SMALTREE_FALL
- SMALTREE_GROUND
- SMALTREE_HEAD
- SONCSHIP_METAL1
- SONCSHIP_MISSILE2
- SONCSHIP_SCRAPE
- SONCSHIP_SHIPLOOP
- SONCSHIP_TARGETLOCK
- SPAZSOUNDS_AUTSCh2
- SPAZSOUNDS_AUTSCh3
- SPAZSOUNDS_BIRDSIT
- SPAZSOUNDS_BURP
- SPAZSOUNDS_CHIRP
- SPAZSOUNDS_EATBIRD
- SPAZSOUNDS_HAHAHA
- SPAZSOUNDS_HAHAHA2
- SPAZSOUNDS_HAPPY
- SPAZSOUNDS_HIHI
- SPAZSOUNDS_HOHOHO1
- SPAZSOUNDS_HOOO
- SPAZSOUNDS_KARATE7
- SPAZSOUNDS_KARATE8
- SPAZSOUNDS_OHOH
- SPAZSOUNDS_OOOH
- SPAZSOUNDS_WOOHOO
- SPAZSOUNDS_YAHOO
- SPAZSOUNDS_YAHOO2
- SPRING_BOING_DOWN
- SPRING_SPRING1
- STEAM_STEAM
- STONED_STONED
- SUCKER_FART
- SUCKER_PINCh2
- SUCKER_PINCh3
- SUCKER_PINCH3
- SUCKER_PLOPSEQ1
- SUCKER_PLOPSEQ2
- SUCKER_PLOPSEQ3
- SUCKER_PLOPSEQ4
- SUCKER_UP
- TUFBOSS_CATCH
- TUFBOSS_RELEASE
- TUFBOSS_SWING
- TURTLE_BITE3
- TURTLE_HIDE
- TURTLE_HITSHELL
- TURTLE_IDLE1
- TURTLE_IDLE2
- TURTLE_NECK
- TURTLE_SPK1TURT
- TURTLE_SPK2TURT
- TURTLE_SPK3TURT
- TURTLE_SPK4TURT
- TURTLE_TURN
- UTERUS_CRABCLOSE
- UTERUS_CRABOPEN2
- UTERUS_SCISSORS1
- UTERUS_SCISSORS2
- UTERUS_SCISSORS3
- UTERUS_SCISSORS4
- UTERUS_SCISSORS5
- UTERUS_SCISSORS6
- UTERUS_SCISSORS7
- UTERUS_SCISSORS8
- UTERUS_SCREAM1
- UTERUS_STEP1
- UTERUS_STEP2
- WIND_WIND2A
- WITCH_LAUGH
- WITCH_MAGIC
- XBILSY_BILLAPPEAR
- XBILSY_FINGERSNAP
- XBILSY_FIRE
- XBILSY_FIRESTART
- XBILSY_SCARY3
- XBILSY_THUNDER
- XBILSY_ZIP
- XLIZARD_LIZ1
- XLIZARD_LIZ2
- XLIZARD_LIZ4
- XLIZARD_LIZ6
- XTURTLE_BITE3
- XTURTLE_HIDE
- XTURTLE_HITSHELL
- XTURTLE_IDLE1
- XTURTLE_IDLE2
- XTURTLE_NECK
- XTURTLE_SPK1TURT
- XTURTLE_SPK2TURT
- XTURTLE_SPK3TURT
- XTURTLE_SPK4TURT
- XTURTLE_TURN
- ZDOG_AGRESSIV
- ZDOG_SNIF1
- ZDOG_WAF1
- ZDOG_WAF2
- ZDOG_WAF3

### TEXTURE::Texture

With the exception of TEXTURE::LAYER8, all textures listed below were drawn from their respective tilesets (without asking permission from their artists).

TEXTURE::NORMAL is the default JJ2 background texture, drawn by Nick Stadler. | TEXTURE::BLADE, drawn by Blade. | TEXTURE::CORRUPTEDSANCTUARY, drawn by Disguise. | TEXTURE::DESOLATION, drawn by BlurredD. | TEXTURE::DIAMONDUSBETA, drawn by Pyromanus.
TEXTURE::ICTUBELECTRIC, drawn by P4ul. | TEXTURE::MEDIVO, drawn by Nick Stadler. | TEXTURE::MEZ02, drawn by Mez. | TEXTURE::MUCKAMOKNIGHT, drawn by Haze. | TEXTURE::PSYCH, drawn by Nick Stadler.
TEXTURE::RANEFORUSV, drawn by Violet CLM. | TEXTURE::WINDSTORMFORTRESS, drawn by BlurredD. | TEXTURE::WISETYNESS, drawn by Toxic Bunny. | TEXTURE::WTF, drawn by cooba. | TEXTURE::XARGON, drawn by Spaztic.

### SPRITE::Mode

Sprite modes dictate the result of the combination of three factors: the basic pixel being drawn (e.g. an individual color in a sprite, or the overall color of a rectangle); the background pixel it is being drawn onto, and a uint8 param value that dicates opacity, amount of palette shifting, or other such effects depending on the sprite mode in question. The simplest cases are modes like SPRITE::NORMAL, which draws the basic pixel without any modification, or SPRITE::TRANSLUCENT, which partially blends the basic pixel with the background pixel, but there are many others, and you are encouraged to try them all and decide for yourself which is best for your situation. (Hint: Usually SPRITE::NORMAL.)

- ALPHAMAP
  On the left, parameter value 24 is used; on the right it's 88. The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  ALPHAMAP is a special mode that draws a sprite in a single palette color whose index is determined by the parameter, however, it uses sprite data to determine translucency of each pixel - 0 is completely transparent and 255 is entirely opaque. This unique mode only makes sense to use with custom sprites designed with it in mind and it will not look good applied to regular JJ2 sprites. This mode was introduced by JJ2+.
- BLEND_NORMAL
  On the left, parameter value 192 is used; on the right it's 64. The difference between 8-bit and 16-bit color modes is not very noticable for constant values but more for transitions.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. All of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_DISSOLVE
  On the left, parameter value 192 is used; on the right it's 64. The conversion between 8-bit and 16-bit color modes is seamless.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_DARKEN
  The difference between 8-bit and 16-bit color modes is very noticeable but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_MULTIPLY
  The difference between 8-bit and 16-bit color modes is very noticeable but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_BURN
  The difference between 8-bit and 16-bit color modes is very noticeable but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_LIGHTEN
  The difference between 8-bit and 16-bit color modes is very noticeable but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_SCREEN
  The difference between 8-bit and 16-bit color modes is very noticeable but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_DODGE
  The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_OVERLAY
  The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_SOFTLIGHT
  The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_HARDLIGHT
  The difference between 8-bit and 16-bit color modes is noticeable but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_DIFFERENCE
  The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_EXCLUSION
  The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_COLOR
  The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_HUE
  The difference between 8-bit and 16-bit color modes is not very noticeable. A different background was used for the pictures on the right since the formula is ineffective when backgrounds with no saturation are used.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_SATURATION
  The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BLEND_LUMINANCE
  The conversion between 8-bit and 16-bit color modes is very nearly seamless.
  There are a wide variety of BLEND_ sprite modes, each matching the similarly named layer/object blend mode in your graphics program of choice. all of them draw the sprite using parameter value as the alpha channel, i.e. opacity.
- BRIGHTNESS
  On the left, parameter value 192 is used; on the right it's 64. The difference between 8-bit and 16-bit color modes is very small.
  BRIGHTNESS draws the sprite as dark or bright depending on parameter value. Parameter value 0 will draw the sprite in the darkness color (black by default but possible to modify by jjSetDarknessColor), value 128 will draw the sprite normally and value 255 will draw it white. Other values will yield expected transitions. This mode is used in JJ2 to draw dark text such as inactive menu items and certain objects such as 3D spike balls when they fade in the distance.
- FROZEN
  The difference between 8-bit and 16-bit color modes is very noticable but was always present in JJ2.
  FROZEN draws the sprite as frozen, ignoring the parameter. JJ2 uses this mode to draw frozen objects.
- GEM
  Devil Devan is drawn with parameter value 0, the gems with values 0-15. The difference between 8-bit and 16-bit color modes is very noticable but was always present in JJ2.
  GEM is a special sprite mode meant for drawing gem sprites but applicable to other sprites. The mode uses its parameter to determine what color scheme to use. Values 0-3 are values used by original JJ2 gems, values 4-12 are color schemes introduced by JJ2+, values of 13 and higher are undefined and their use is discouraged in order to preserve backward compatibility.
- INVISIBLE
  The effects are identical regardless of color mode and parameter value.
  INVISIBLE never draws anything.
- MENUPLAYER
  Results achieved by using this mode vary depending on fur of the chosen player. The sets of colors seen here are {40, 80, 72, 80} on the left and {72, 48, 72, 80} on the right. The conversion between 8-bit and 16-bit color modes is seamless.
  MENUPLAYER draws the sprite with respect to fur colors of the player whose ID is the same as the parameter; unlike the PLAYER mode, however, this mode is applicable to menu sprites. This mode was introduced by JJ2+ in order to draw large player characters at the end of each round.
- NEONGLOW
  On the left, parameter value 0 is used; on the right it's 4. The difference between 8-bit and 16-bit color modes is severe but inevitable due to the nature of this mode.
  NEONGLOW draws the sprite in a certain color using pixel brightness to determine its alpha channel, i.e. opacity. Parameter values 0-6 will result in, in this order, red, green, blue, magenta, yellow, cyan and white sprites. Value 7 is reserved for future use. For values higher than 7, the parameter's value modulo 8 will be used instead. It's unknown whether this mode was originally used for anything by JJ2. Its 8-bit version was introduced by JJ2+.
- NORMAL
  The conversion between 8-bit and 16-bit color modes is seamless.
  NORMAL draws the sprite without any modifications, ignoring the parameter. This mode is used to draw the vast majority JJ2 sprites.
- PALSHIFT
  On the left, parameter value 8 is used; on the right it's 16. The conversion between 8-bit and 16-bit color modes is seamless.
  PALSHIFT draws the sprite with every palette index increased by the value of the parameter (when this would mean the index becomes higher than 255, it has 256 subtracted from it). This mode is used by JJ2 to draw colored text strings.
- PLAYER
  Results achieved by using this mode vary depending on fur of the chosen player. The sets of colors seen here are {40, 80, 72, 80} on the left and {72, 48, 72, 80} on the right. The conversion between 8-bit and 16-bit color modes is seamless.
  PLAYER draws the sprite with respect to fur colors of the player whose ID is the same as the parameter, meaning that character sprites drawn in this mode will actually look like said player. All colors that don't belong to the default fur gradients (which are green, red, blue and yellow, i.e. palette indexes 16-47) stay unchanged when drawn by this mode. JJ2 uses this mode only to draw players, JJ2+ extends its effect to Fastfire pickups and the HUD blaster icon.
- RESIZED
  RESIZED draws the sprite scaled up or down depending on the value of the parameter without applying any other effects. This mode was made obsolete by functions such as jjDrawResizedSprite and jjCANVAS::drawResizedSprite and is no longer supported by JJ2+.
- SHADOW
  There is a visible brightness difference between 8-bit and 16-bit color modes but it was always present in JJ2.
  SHADOW draws the sprite black and translucent, ignoring the parameter. This mode is used by JJ2 to draw shadows cast by JJ2 logo and menu items in the main menu.
- SINGLECOLOR
  On the left, parameter value 15 is used; on the right it's 16. The conversion between 8-bit and 16-bit color modes is seamless.
  SINGLECOLOR draws the sprite entirely converted to a color with palette index specified by the parameter. JJ2 uses this mode to draw objects that have been recently hit so that they're all white.
- SINGLEHUE
  On the left, parameter value 16 is used; on the right it's 64. The conversion between 8-bit and 16-bit color modes is seamless.
  SINGLEHUE draws the sprite converting all its colors to a single palette range composed of 8 colors starting at the index specified by the parameter. For the default JJ2 palette this allows the range to contain a single gradient. This mode was introduced by JJ2+.
- TINTED
  On the left, parameter value 16 is used; on the right it's 24. The difference between 8-bit and 16-bit color modes is very noticable but was always present in JJ2.
  TINTED draws the sprite with all its colors tinted to the color of palette entry determined by the parameter. It's unknown whether this mode was originally used by JJ2.
- TRANSLUCENT
  The difference between 8-bit and 16-bit color modes is not very noticable and it was always present in JJ2.
  TRANSLUCENT draws the sprite as semi-transparent. This mode is used by JJ2 to draw objects such as Ice Ammo +3 pickups, Rapier enemies and bullet explosions.
- TRANSLUCENTCOLOR
  On the left, parameter value 16 is used; on the right it's 24. The difference between 8-bit and 16-bit color modes is not very noticable, like in all other translucent modes.
  TRANSLUCENTCOLOR draws the sprite as semi-transparent and converted to a single color defined by the parameter, so it works like a mix of SINGLECOLOR and TRANSLUCENT modes. This mode was introduced by JJ2+.
- TRANSLUCENTPALSHIFT
  On the left, parameter value 8 is used; on the right it's 16. The difference between 8-bit and 16-bit color modes is not very noticable, like in all other translucent modes.
  TRANSLUCENTPALSHIFT draws the sprite as semi-transparent with its palette entries shifted by an amount specified by the parameter, so it works like a mix of PALSHIFT and TRANSLUCENT modes. This mode was introduced by JJ2+.
- TRANSLUCENTPLAYER
  Results achieved by using this mode vary depending on fur of the chosen player. The sets of colors seen here are {40, 80, 72, 80} on the left and {72, 48, 72, 80} on the right. The difference between 8-bit and 16-bit color modes is not very noticable, like in all other translucent modes.
  TRANSLUCENTPLAYER draws the sprite as semi-transparent and with respect to fur colors of the player whose ID is the same as the parameter, so it works like a mix of PLAYER and TRANSLUCENT modes. This mode was introduced by JJ2+.
- TRANSLUCENTSINGLEHUE
  On the left, parameter value 16 is used; on the right it's 64. The difference between 8-bit and 16-bit color modes is not very noticable, like in all other translucent modes.
  TRANSLUCENTSINGLEHUE draws the sprite as semi-transparent with its palette entries converted to an 8-color range in the palette defined by the parameter, so it works like a mix of SINGLEHUE and TRANSLUCENT modes. This mode was introduced by JJ2+.

### ANIM::Set

Values used as indices to the jjAnimSets array, plus the setID parameters of the jjOBJ method determineCurAnim, the jjCANVAS method drawSprite, and the global function jjDrawSprite, as well as the jjPLAYER setID property. These are in the same order as they appear in Jazz Sprite Dynamite, so it should be totally straightforward to match the names to the numbers. Sets for HH98 or TSF-exclusive enemies, when used in 1.23, will evaluate to the values of their 1.23 equivalents, e.g. LIZARD for XLIZARD.

- AMMO
- BAT
- BEEBOY
- BEES
- BIGBOX
- BIGROCK
- BIGTREE
- BILSBOSS
- BIRD
- BIRD3D
- BOLLPLAT
- BONUS
- BOSS
- BRIDGE
- BUBBA
- BUMBEE
- BUTTERFLY
- CARROTPOLE
- CAT
- CAT2
- CATERPIL
- CHUCK
- COMMON
- CONTINUE
- DEMON
- DESTSCEN
- DEVAN
- DEVILDEVAN
- DIAMPOLE
- DOG
- DOOR
- DRAGFLY
- DRAGON
- EVA
- FACES
- FATCHK
- FENCER
- FISH
- FLAG
- FLARE
- FONT
- FROG
- FRUITPLAT
- GEMRING
- GLOVE
- GRASSPLAT
- HATTER
- HELMUT
- JAZZ
- JAZZ3D
- JUNGLEPOLE
- LABRAT
- LIZARD
- LORI
- LORI2
- MENU
- MENUFONT
- MONKEY
- MOTH
- PICKUPS
- PINBALL
- PINKPLAT
- PSYCHPOLE
- QUEEN
- RAPIER
- RAVEN
- ROBOT
- ROCK
- ROCKTURT
- SKELETON
- SMALTREE
- SNOW
- SONCSHIP
- SONICPLAT
- SPARK
- SPAZ
- SPAZ2
- SPAZ3D
- SPIKEBOLL
- SPIKEBOLL3D
- SPIKEPLAT
- SPRING
- STEAM
- SUCKER
- TUBETURT
- TUFBOSS
- TUFTUR
- TURTLE
- TWEEDLE
- UTERUS
- VINE
- WARP10
- WARP100
- WARP20
- WARP50
- WITCH
- XBILSY
- XLIZARD
- XTURTLE
- ZDOG
- ZSPARK

The above constants are all used for accessing animations in the anims.j2a file. JJ2+ comes with its own plus.j2a file, however, with its own set of constants listed below. Because plus.j2a, unlike anims.j2a, has the potential to change in future releases of JJ2+, you should be aware of the following guarantees:

While the order of anim sets within plus.j2a will not change, their indices might. For example, currently ANIM::PLUS_BETA immediately follows ANIM::PLUS_AMMO, but in some future release of JJ2+, another anim set ANIM::PLUS_BANHAMMER might be added between the two. Treat the ANIM::Set constants as magic numbers and do not subtract them from each other or perform any other such operations that might break in future releases.

Within an anim set in plus.j2a, the order of animations will not change, but their number might. If new animations are added to an existing anim set, they will be appended to the end, so `jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim]` will always be a leaf, but `jjAnimations[jjAnimSets[ANIM::PLUS_WARP].firstAnim + 2]` does not currently exist but might someday later.

Within an animation in plus.j2a, the number of frames may change. For example, `jjAnimations[jjAnimSets[ANIM::PLUS_COMMON].firstAnim + 6]` contains the stars used by sugar rushes, and its frameCount property equals 2. However, a future release of JJ2+ might add a third star and up frameCount to 3.

PLUS_AMMO
  Animations used by bullets and related code; loaded in every level. The first five animations deal with unfinished code and should be ignored for now; `firstAnim+5` is Lori's fastfire pickup/blaster icon, and `firstAnim+6` is the same but powered up.
PLUS_BETA
  Animations from beta versions of JJ2 that were replaced for the final release, as accessible through the JJ2+ menu item Plus->Beta Sprites. You can load this set if you want to use some or all of the animations regardless of whether the user has checked that menu item. The order of animations is 0: super gem; 1: gem; 2: rect gem; 3: sodas (two frames, 0=Coke and 1=Pepsi).
PLUS_COMMON
  All sorts of generally useful animations, loaded in every level. In order they are 0: Gun8 ammo+15 crate; 1: Gun9 ammo+15 crate; 2: Laser Shield monitor; 3: unfinished; 4: unfinished; 5: Lori blaster powerup monitor; 6: sugar rush stars; 7: Carrotade logo (pickup-sized).
PLUS_CONTINUE
  Continue animations for Lori, who was missing them in the regular 1.24 release. Same sorts/order of animations as in ANIM::CONTINUE, probably not too useful for general purposes
PLUS_FONT
  This is a special case animation that replaces ANIM::FONT to the point that JJ2+ will refuse to load ANIM::FONT from anims.j2a and will load ANIM::PLUS_FONT from plus.j2a instead. It's loaded in every level as ANIM::FONT, so there's not much point in you doing anything with it separately.
PLUS_REPLACEMENTS
  Another special case animation: PLUS_REPLACEMENTS is never loaded as a whole set, but instead individual animations are loaded from it to replace individual animations from anims.j2a under specific circumstances. You shouldn't need to access this one, because its contents will already be in game when they're needed; just in case, though, the first animation is the morph box animation with a yellow arrow (used in 1.24 but not 1.23), and the second and third animations are different sizes of the Jazz 2 logo using the menu palette.
PLUS_RETICLES
  Images used by mouse aim mode, specifically 1=WEAPON::NORMAL, 2=WEAPON::MISSILE, 3=WEAPON::POPCORN, 4=WEAPON::CAPPED, and 0 is the smaller target used for displaying fire angle as separate from cursor position. Although each of these animations is only one frame long, JJ2+ can play longer animations in their stead if you mess with the relevant jjANIMATION properties.
PLUS_SCENERY
  Useful animations for various graphical effects, not loaded by default. In order they are 0=Leaf (used by the Snow event if given the right parameters), 1=Fire, 2=Lava, 3=Bee hive. 4=Various destructible blocks (0: Blank; 1-2: Trigger; 3: Speed destruct; 4: Buttstomp; 5: Unknown; 6-9: Timed destruct; 10: Destruct; 11-19: Weapon-specific destruct; 20-23: Shield-specific destruct).
PLUS_WARP
  A pair of animations used by the Warp event when its ShowAnim parameter is set to 1.
CUSTOM[256]
  Finally, the CUSTOM array provides you with 256 slots in the jjAnimSets array that are guaranteed not to be used by any non-AngelScript code. You should generally use these when loading or creating your own animations unless you're specifically replacing a specific existing animation, e.g. replacing the Tuf Turtle enemy (who has only one animation and is therefore very easy to work with) with a slime creature or something. Unlike the other constants, the ANIM::CUSTOM values do not correspond to any animations in any .j2a file and should never be used in jjANIMSET::load.

### RABBIT::Anim

For the most part, the anims.j2a files in versions 1.23 and 1.24 of Jazz 2 are just the same, differing primarily in that 1.24's has a few extra anim sets, in particular ANIM::LORI and all the X* and Z* enemies. The other difference is that 1.24's removes a number of animations from the Jazz and Spaz anim sets that were not used in the final game, such as the animations for climbing onto a ledge. In practice this means that the animation for Jazz or Spaz on an airboard, for example, is the first animation in the set in 1.24, but the second in 1.23. Since you should try to make your code as version-independent as possible, this is kind of annoying.

The RABBIT::Anim enum solves this problem for you by providing a series of constants corresponding to the indices of each animation appearing in both 1.23 and 1.24, with their values adjusted internally depending on which version of JJ2 you're running. Thus RABBIT::AIRBOARD equals 0 in 1.24 and 1 in 1.23; RABBIT::HURT equals 29 in 1.24 and 32 in 1.23; and so on. The names are our best guesses for how they were originally named, working from some internal variable names as well as the fact that every animation in anims.j2a is ordered alphabetically.

To get the animation index of an actual rabbit player, suitable for comparing to RABBIT::Anim values, subtract the first animation of their anim set from their current animation. To see if a rabbit player `p` is currently standing still, for example, you can check `if (p.curAnim - jjAnimSets[p.setID].firstAnim == RABBIT::STAND)`, and that will work no matter whether the player is running 1.23 or 1.24, or playing as Jazz, Spaz, or Lori.

- AIRBOARD
- AIRBOARDTURN
- BUTTSTOMPLAND
- CORPSE
- DIE
- DIVE
- DIVEFIREQUIT
- DIVEFIRERIGHT
- DIVEUP
- EARBRACHIATE
- ENDOFLEVEL
- FALL
- FALLBUTTSTOMP
- FALLLAND
- FIRE
- FIREUP
- FIREUPQUIT
- FROG
- HANGFIREQUIT
- HANGFIREREST
- HANGFIREUP
- HANGIDLE1
- HANGIDLE2
- HANGINGFIREQUIT
- HANGINGFIRERIGHT
- HELICOPTER
- HELICOPTERFIREQUIT
- HELICOPTERFIRERIGHT
- HPOLE
- HURT
- IDLE1
- IDLE2
- IDLE3
- IDLE4
- IDLE5
- JUMPFIREQUIT
- JUMPFIRERIGHT
- JUMPING1
- JUMPING2
- JUMPING3
- LEDGEWIGGLE
- LIFT
- LIFTJUMP
- LIFTLAND
- LOOKUP
- LOOPY
- PUSH
- QUIT
- REV1
- REV2
- REV3
- RIGHTFALL
- RIGHTJUMP
- ROLLING
- RUN1
- RUN2
- RUN3
- SKID1
- SKID2
- SKID3
- SPRING
- STAND
- STATIONARYJUMP
- STATIONARYJUMPEND
- STATIONARYJUMPSTART
- STONED
- SWIMDOWN
- SWIMRIGHT
- SWIMTURN1
- SWIMTURN2
- SWIMUP
- SWINGINGVINE
- TELEPORT
- TELEPORTFALL
- TELEPORTFALLING
- TELEPORTFALLTELEPORT
- TELEPORTSTAND
- VPOLE

## Thanks

AngelScript is written and maintained by Andreas Jönsson of AngelCode.com. The JJ2+ developers owe him a great deal of thanks.