/*
	This example script illustrates some of the new scripting options available in JJ2+ v5.2,
	specifically jjWEAPON::comesFromBirds, upside-down birdcages, jjLAYER (and accompanying
	global functions), and jjBEHAVIORINTERFACE. This should not be taken as a full list of
	new features present in 5.2, or even a full list of options relevant to these particular
	new features; the user is, as ever, recommended to read plus-angelscript.html for more.
*/

//To begin with, this Sign class will be used for drawing text to the screen. JJ2's standard text display code (using the Text event or the jjPLAYER::showText method) is easier to use and would have required less code to setup, but this is somewhat easier on the reader on account of it not leaving the screen after a few seconds.
//Very little of this Sign-related code is new to 5.2 in particular, so you can skip it if you're just looking for the juicy stuff, but otherwise, it can't hurt to (re)familiarize yourself with classes and strings and jjCANVAS.
jjTEXTAPPEARANCE SignTextAppearance = STRING::NORMAL;
class Sign {
	private int xPos, yPos; //These pixel-based positions will be generated from tile-based positions in the constructor by multiplying by 32
	private string text;
	private uint widthOfText;
	Sign(){} //AngelScript requires any class that appears in any array to have an explicit default constructor, even if it's never called
	Sign(int xTile, int yTile, const string &in t) {
		xPos = xTile * 32; //Since this is a constant operation, it could strictly be performed in the draw method instead of the constructor, but this way involves fewer multiplication instructions
		yPos = yTile * 32; //
		text = t;
		SignTextAppearance.newline = STRING::SPECIALSIGN; //Causes the drawString method to interpret instances of the \n character as signals to drop down to a new line, similar to the special effect of the @ character in the STRING::SPIN appearance.
		SignTextAppearance.spacing = -2; //int jjTEXTAPPEARANCE::spacing is new in 5.2, and this particular value is equivalent to prefixing the string with "§2". Make sure to check out bool jjTEXTAPPEARANCE::monospace too, though it didn't end up getting used in this level.
		widthOfText = jjGetStringWidth(text, STRING::SMALL, SignTextAppearance); //Used for determining how large of a dark rectangle should be drawn behind the text. A matching heightOfText value could of course be generated by counting the number of newline characters--for example, "heightOfText = text.split("\n").length * 20;"--but here the rectangles are constant height instead to limit the temptation to ramble on and on.
	}
	void draw(jjCANVAS@ layer, uint8 textIntensity) const { //Because this method will be called from an onDraw method, it's important to have a jjCANVAS@ passed among the arguments.
		layer.drawRectangle(xPos, yPos - 16, widthOfText + 8, 86, 0, SPRITE::TRANSLUCENT);
		layer.drawString(xPos, yPos, text, STRING::SMALL, SignTextAppearance, 0, SPRITE::BLEND_LUMINANCE, textIntensity);
	}
}
const array<Sign> Signs = { //This is what an array looks like! Yay. Below are constructors! Yay. You can read all this text, but it's probably easier to read in the level where it doesn't have those newline characters to worry about.
	Sign(7, 12, "Welcome to Jazz Jackrabbit 2 Plus beta version 5. 2!\nThis level demonstrates some of the new features\nthat 5. 2 makes available to scriptwriters."),
	Sign(27, 11, "Quite a lot has been changed about companion birds.\nFor one thing, they work online now! But that's\nhard to demonstrate in this example level, so\nhere's a bird cage hanging upside-down instead."),
	Sign(50, 8, "If the script allows it,\nred companion birds can now fire\nany weapon that you can! Try cycling\nthrough your weapons to try this."),
	Sign(63, 6, "Now let's think about eyecandy! Aren't those pillars\nand chains in front of you pretty annoying?\nWell, 5. 2 adds a new function \"jjLayerOrderSet\"\nthat can move them BEHIND layer 4!"),
	Sign(75, 9, "But that's not all. First, let's set the stage\nwith a peaceful textured background\nusing the Diamondus Day palette..."),
	Sign(95, 9, "The pair of functions \"jjTilesFromTileset\" and\n\"jjLayersFromLevel\" can be used to add all-new tiles\nand layers. Touch that sign to see a level with \nNINE layers, three of them from Diamondus!"), //Why nine? In a nutshell, there wasn't much use for Layer 3 in this level, so it got left out of all the jjLayerOrderSet calls. And Layer 7 gets excluded because it's a Castle-themed background and is replaced with the TEXTURE::NORMAL textured background in Layer 8. So six remaining layers rom Plus52Scripting.j2l plus three new layers from Diam1.j2l equals nine total.
	Sign(115, 9, "You can add foreground layers too.\nThis layer requires some recoloring,\nso be sure to check the script\nto see how it's done."),
	Sign(131, 6, "Of course, there's no rule that the layers have to come\nfrom different tilesets. You could make a separate .j2l using the same\ntileset, just for an easy way to define another eight or so background layers.\nOr construct some layers entirely in the script!"),
	Sign(140, 10, "Anyway, that's enough about layers for now. Let's talk\nabout object behaviors. They used to look like this:\n\nvoid MyEnemy (jjOBJ@ obj){ ... }"),
	Sign(141, 13, "void onLevelLoad() {\n    jjObjectPresets[OBJECT::HELMUT].behavior = MyEnemy;\n}"),
	Sign(158, 10, "That's still allowed. But you can do this too:\nclass MyEnemy : jjBEHAVIORINTERFACE {\n    void onBehave(jjOBJ@ obj){ ... }\n}"),
	Sign(159, 13, "void onLevelLoad() {\n    jjObjectPresets[OBJECT::HELMUT].behavior = MyEnemy();\n}"),
	Sign(174, 7, "What's the appeal? Well, it depends on how well\nyou understand classes. One important point is\nthis allows you to name your own properties,\ninstead of just using jjOBJ's for everything."),
	Sign(175, 11, "jjOBJ has a lot of properties, but they're almost\nall ints and floats, and you run the risk of breaking\nstuff. So these apples over here have string\nproperties in their jjBEHAVIORINTERFACE classes:"), //The string class was chosen for this example level because it's easy to demonstrate and understand, but you can also have properties that are handles, arrays, etc. One common usage might be to define a Point class and then have one of your behavior classes define an array of Points to keep track of where an object has recently been, in order to draw some sort of trail effect.
	Sign(199, 6, "Another important point: classes implementing jjBEHAVIORINTERFACE\ncan define other special hook methods besides onBehave.\nThe most useful is probably onObjectHit, which lets you define\nthe results of a player (or sometimes bullet) colliding with the object."),
	Sign(200, 9, "In older versions of JJ2+, onObjectHit was a\nglobal function, and you had to be very careful while\ndetermining which object got hit. By making it a method,\nyour code becomes much safer and easier to read."),
	Sign(201, 12, "This \"1 UP\" pickup actually gives you FIVE lives.\nAll the normal JJ2 code for collecting a pickup\nhas been replicated in its onObjectHit method.\n(Feel free to copy this!)"),
	Sign(216, 8, "Similarly, this Flower enemy uses HANDLING::SPECIAL and HANDLING::DETECTBULLET\ninstead of HANDLING::ENEMY and HANDLING::HURTBYBULLET, but its onObjectHit\nhas been written to fully replicate those standard handlings.\nShoot it, freeze it, stomp it, whatever!"), //Feel free to copy this too.
	Sign(238, 9, "Finally, a major benefit of jjBEHAVIORINTERFACE is inheritance.\nThe RedFlower class inherits its onBehave method from Flower,\nbut overrides its speed, onDraw, and onObjectHit. Interacting with\nthe RedFlower in any way, even shooting it, hurts you."),
	Sign(255, 8, "That's all we have time for today.\nJJ2+ 5. 2 is mostly a bug fix update,\nbut there are some exciting new scripting possibilities."),
	Sign(256, 11, "And check the docs to read about\nzlib (de)compression, more jjSTREAM options,\nunix time checking, remote admin privileges,\nmonospaced strings, and more.") //True! Do this!
};
//The Sign class instances, as stored in the Signs array, will be drawn directly to the screen as part of the onDrawLayer4 hook method. Note that later in this script file, we're going to be changing the list of layers around quite a bit; however, onDrawLayer4 is always tied to "Layer 4" in JCS, the same layer that the player and all the other objects run around on. Likewise, an onDrawLayer3 hook is always tied to whatever you put in "Layer 3" in JCS, etc. So far no code has been written to allow defining onDrawLayer-type hooks for any jjLAYER objects other than the original eight in the jjLayers array... sorry! Maybe in 5.5.
void onDrawLayer4(jjPLAYER@, jjCANVAS@ layer) { //We don't care about which player happens to be viewing layer 4 for the purposes of this function, so the argument can be left unnamed.
	const uint8 textIntensity = 200 + int(jjSin(jjGameTicks * 16) * 50); //Ranges from roughly 150 to 250. This number will be passed as the "param" value for SPRITE::BLEND_LUMINANCE, which (like all other BLEND_* modes) uses "param" to specify the sprite's opacity, where 255 is fully opaque and 0 is invisible.
	for (uint signID = 0; signID < Signs.length; ++signID) //Bog-standard code for looping through an array, in lieu of actual ForEach support in AngelScript.
		Signs[signID].draw(layer, textIntensity);
}

void onLevelLoad() { //Various types of setup will be done here as always.
	jjUseLayer8Speeds = true;

	jjObjectPresets[OBJECT::BIRDCAGE].direction = SPRITE::FLIPV; //This causes ALL bird cages to be drawn upside-down and stick to the ceiling instead of the floor. (The same thing can be done with all crate, barrel, monitor, etc. objects.) In a situation where you wanted SOME bird cages, but not ALL, to have reversed gravity, you'd probably want to write a behavior that checks a custom event parameter before turning the jjOBJ back over to BEHAVIOR::BIRDCAGE, but since there's only the one cage in this level, a change to the jjObjectPresets entry is all that's required.
	for (int weaponID = WEAPON::BLASTER; weaponID <= WEAPON::GUN9; ++weaponID) {
		jjWEAPON@ weaponProfile = jjWeapons[weaponID];
		weaponProfile.replenishes = true; //Ensures that you start the level with full ammo for all nine weapons. However, weaponProfile.infinite is left false (except for WEAPON::BLASTER), meaning that you can see that the bird firing bullets does not deplete your ammo count!
		weaponProfile.comesFromBirds = weaponProfile.comesFromBirdsPowerup = true; //These properties, new in 5.2, allow red companion birds to fire bullets of weapons other than blaster. If only the first property is true and your current weapon is powered-up, the bird will shoot the unpowered-up version instead. Making only the second property true has no effect and the bird will simply default to unpowered-up blaster.
	}
	
	jjObjectPresets[OBJECT::ONEUP].behavior = FiveUp(); //FiveUp is a class that implements jjBEHAVIORINTERFACE, and as such, instances of it can be assigned to jjBEHAVIOR properties, in particular jjOBJ::behavior. Every object of eventID OBJECT::ONEUP will thus have as its behavior this particular instance of the FiveUp class.
	jjObjectPresets[OBJECT::ONEUP].scriptedCollisions = true; //Needed in order for FiveUp::onObjectHit to be called.
	
	//Below is an anonymous function (http://www.angelcode.com/angelscript/sdk/docs/manual/doc_script_anonfunc.html) with the effect that every object of eventID OBJECT::APPLE gets its own unique instance of the AppleWithString class. Contrast this to FiveUp, above, which only has one instance total throughout the duration of the level but potentially multiple references to that one instance.
	//The primary benefit of this setup is that the values of the class's properties may potentially vary across instances (and therefore across jjOBJs). They are therefore analogous to a jjOBJ property like jjOBJ::counter or jjOBJ::xPos, rather than to a global property like ::SignTextAppearance defined above.
	//Note that while an anonymous function is used here, any sort of code--a full jjVOIDFUNCOBJ, a method delegate, or even another jjBEHAVIORINTERFACE instance--that achieves the same goal of assigning each object a new AppleWithString instance would work just as well. Anonymous functions are simply the quickest and cleanest way of doing the job.
	jjObjectPresets[OBJECT::APPLE].behavior = function(obj){obj.behavior = AppleWithString();};
	
	jjObjectPresets[OBJECT::APPLE].deactivates = false;	//These lines aren't particularly important, but are just here to ensure that AppleWithString's destructor is only called when the jjOBJ with the AppleWithString instance in its jjOBJ::behavior property is eaten (jjOBJ::delete), rather than when it gets too far away from the player (jjOBJ::deactivate).
	jjObjectPresets[OBJECT::GENERATOR].deactivates = false;	//
	
	//Flower (and its child class RedFlower) is, like FiveUp and AppleWithString, a class that implements jjBEHAVIORINTERFACE. Unlike them, it has an explicit non-argumentless constructor, one that takes a jjOBJ (here entries in the jjObjectPresets array) and assigns it all the necessary properties to be an enemy object. All those property assignments to jjObjectPresets[OBJECT::PEACH] and jjObjectPresets[OBJECT::PEAR] could certainly be done here in onLevelLoad instead, but this is arguably a bit cleaner, since it keeps all the Flower-related jjOBJ properties inside the methods of the Flower class.
	Flower(jjObjectPresets[OBJECT::PEACH]);
	RedFlower(jjObjectPresets[OBJECT::PEAR]);
}

void onFunction0() { //The first sign, responsible for taking the visible foreground layers (the giant pillars and the chains) and moving them behind layer 4.
	jjLayers[1].setXSpeed(0.67,false); //Those layers are set up in JCS to move faster than Layer 4, though, so it's important to reduce their x speeds here or they'd look really weird in the background. Calling jjLAYER::setXSpeed instead of editing jjLAYER::xSpeed means that the layers will magically remain in the same positions they were before the speed change, which is always helpful.
	jjLayers[2].setXSpeed(0.59,false); //jjSetLayerXSpeed(2, 0.59, false); would have the same effect, but this way emphasizes the existence of the jjLAYER class and its various helpful properties and methods.
	jjLayerOrderSet(array<jjLAYER@> = {jjLayers[4], jjLayers[1], jjLayers[2], jjLayers[5], jjLayers[6], jjLayers[7], jjLayers[8]}); //It should be clear how this function works. Note that the presence of jjLayers[4] somewhere in the array is absolutely mandatory--the presence of jjLayers[8] is not mandatory but is still recommended, on account of its various forms of special treatment by the game.
}
void onFunction1() { //The second sign: show a textured background instead of all the Castle pillars and bricks and things. Other than the last line, none of this code is new in 5.2, but custom palettes and textured backgrounds are probably among the commonest scripting tricks, so these are important skills to keep around.
	jjPAL diamColors;
	if (diamColors.load("Diam1.j2t")) { //The user really, really ought to have Diam1.j2t, but it can't hurt to make absolutely sure.
		jjPalette.copyFrom(128, 16, 112, diamColors);
		jjPalette.copyFrom(144, 64, 144, diamColors);
		jjPalette.apply();
		
		jjTexturedBGTexture = TEXTURE::NORMAL;
		jjTexturedBGUsed = true;
		jjSetFadeColors(); //Defaults to effectively jjSetFadeColors(jjPalette.color[207]). One minor thing that's new in 5.2 is that several color-related functions have new jjPALCOLOR overloads, so look around for the "Updated" label in plus-angelscript.html.
	} else {
		jjAlert("|ERROR: Diam1.j2t not found!", false, STRING::MEDIUM);
	}
	
	jjLayerOrderSet(array<jjLAYER@> = {jjLayers[4], jjLayers[8]}); //All you really need in a level: something to walk around on, and a background to do it in front of. The jjTexturedBGUsed property (and likewise jjTexturedBGTexture and jjSetFadeColors) refers specifically to (the use of) jjLayers[8], so a handle to that jjLAYER has to be included in this array or else there'd be no background at all, textured or otherwise.
}
void onFunction2() { //The third sign: import three layers from Diamondus and put them in the background between a couple of layers of pillars.
	const int oldTileCount = jjTileCount; //This is how many tiles are currently defined in the level, which may or may not be the same number as how many tiles are defined in the .j2t file used by this level. As it happens, in this case it is NOT--the level started out with 660 tiles from Castle1.j2t, but a line of code in the Flower class below adds eight new tiles before onFunction2 gets called, meaning that jjTileCount is actually 668 at this point. But you shouldn't need to keep track of that, so the jjTileCount property does it for you.
	if (jjTilesFromTileset("Diam1.j2t", 480, 240)) { //The second argument to jjTilesFromTileset is "firstTileID", the first tileID in the tileset that should be added to the current level; the third argument is "tileCount", or how many tiles, starting from that tileID, should be added. So if firstTileID was 8 and tileCount was 6, then tiles 8,9,10,11,12,13 would be copied from Diam1.j2t and added to this level. Here a series of 240 tiles starting at tileID 480 is copied, not because every one of those 240 tiles is wanted, but because all the tiles that ARE wanted--Diamondus's hills and trees--are all within that range. You can confirm this for yourself by checking in JCS, but remember to watch out for JCS's 1-indexing.
		array<jjLAYER@> diamLayers = jjLayersFromLevel( //It's generally best to call jjLayersFromLevel AFTER calling jjTilesFromTileset if the level you're copying one or more layers from uses a different tileset, for some combination of semantic and code-based reasons.
			"Diam1.j2l", //Level filename
			array<uint> = {5,6,7}, //Which (1-indexed) layers you want to copy from the other level, in this case all Diam1's background layers except for its textured background in its layer 8 (which was already replicated using TEXTURE::NORMAL in onFunction1 above). Note that this argument must always be a uint array, even if you only want a single layer.
			oldTileCount - 480 //This is a number that is added to every non-zero, non-animated tileID present in every copied layer, and serves to make them use the same tile images/masks that they did in their original contexts, despite those images/masks having strictly different tileIDs. The first Diamondus background tree tile is tileID 480, but those tiles are appended to the end of the castle tileset, meaning that instead of 480 the background layers should use 668, and instead of 481 they should use 669, and so on. This is a difference of 668-480==188, and so that number is what must be added to the layers' tileIDs.
		);
		if (diamLayers.length > 0) { //Specifically, its length should be 3.
			for (uint diamLayerID = 0; diamLayerID < diamLayers.length; ++diamLayerID)
				diamLayers[diamLayerID].yOffset = 64; //Diam1.j2l is a much taller level than Plus52Scripting.j2l, so some offsets are applied here just for the sake of making the trees and mountains more visible in this particular context. This loop is not otherwise a necessary part of the layer-importing process, particularly if the levels are the same size or the imported layer uses speeds 1,1.
			jjLayerOrderSet(array<jjLAYER@> = {jjLayers[4], jjLayers[1], jjLayers[2], jjLayers[5], diamLayers[0], diamLayers[1], jjLayers[6], diamLayers[2], jjLayers[8]}); //A combination of original layers (from jjLayers) and script-generated layers (from diamLayers), because they're all jjLAYER objects. The order is as arbitrary as ever, again the only requirement being that jjLayers[4] appear somewhere in the array, and that no null handles are included.
			
			 //See disussion of this arrow in the function immediately below.
			fadeInLayers = diamLayers;
			fadeInLayers.insertLast(jjLayers[1]);
			fadeInLayers.insertLast(jjLayers[2]);
			fadeInLayers.insertLast(jjLayers[5]);
			fadeInLayers.insertLast(jjLayers[6]);
			for (uint fadeInLayerID = 0; fadeInLayerID < fadeInLayers.length; ++fadeInLayerID)
				fadeInLayers[fadeInLayerID].spriteMode = SPRITE::BLEND_NORMAL;
		} else {
			jjAlert("|ERROR: Diam1.j2l not found!", false, STRING::MEDIUM); //Why would someone have Diam1.j2t but not Diam1.j2l? That makes no sense. But these two functions do provide the possibility of error checking--jjTilesFromTileset returns false if the file isnt found, and jjLayersFromLevel returns an array with zero length--so you may want to take advantage of it.
		}
	} else {
		jjAlert("|ERROR: Diam1.j2t not found!", false, STRING::MEDIUM);
	}
}

//This level is supposedly all about 5.2 scriping options, but version 5.3 introduced the properties jjLAYER::spriteMode and jjLAYER::spriteParam, which are so closely tied to what we're showing in this level that they seem worth highlighting here anyway. By setting a layer's spriteMode to anything other than SPRITE::NORMAL, we can cause all (or nearly all--see again the full documentation for details) tiles in it to be drawn in certain unusual ways. SPRITE::BLEND_NORMAL, as set in onFunction2 above, is pretty straightforward in that it draws the tiles' normal colors but with a custom opacity as specified by jjLAYER::spriteParam. In this case, onFunction2 inserted seven new layers into the layer order and gave them all the SPRITE::BLEND_NORMAL spriteMode, but the default spriteParam is 0, meaning they'd be invisible. This onMain() function then spends the next 51 (255/5) gameticks bringing their opacity back up to fully opaque (255).
array<jjLAYER@> fadeInLayers(0);
void onMain() {
	for (uint fadeInLayerID = 0; fadeInLayerID < fadeInLayers.length; ++fadeInLayerID) {
		jjLAYER@ layer = fadeInLayers[fadeInLayerID];
		if (layer.spriteParam < 255)
			layer.spriteParam += 5;
	}
}

void onFunction3() { //The fourth sign: import one layer from Psych and put it in the foreground.
	const int oldTileCount = jjTileCount; //Same purpose as in onFunction2
	array<uint8> psychColorMap(256, 0); //This time an extra step gets added: RECOLORING the tiles. This requires an array<uint8> of length 256 (or longer, but there's no point to that) to be passed to jjTilesFromTileset--if the array is too small, or if a null handle (default) is passed instead, no recoloring will happen at all. 
	for (int i = 0; i < 8; ++i) //In Psych, the foreground vine uses (besides transparency) only eight color indices, all fortunately right next to each other.
		psychColorMap[96 + i] = 164 + i; //Specifically, the color indices 96,97,98,99,100,101,102,103... but those colors are brown in our combination-Castle-and-Diamondus jjPalette. So instead they'll get shifted over to 164,165,166,167,168,169,170,171. Every other entry in the array can, in this case, be left at 0 because none of those other colors are used by the tiles, but to play it safe, you might want to run an (i<256) loop and set myColorMap[i] = i; before doing any actual recoloring work. Another option would be to load the tileset's palette in a jjPAL object and then have your loop set myColorMap[i] = jjPalette.findNearestColor(myPalObject.color[i]);, which would result in mapping every single color in the imported tileset to the most similar color in jjPalette.
	jjTilesFromTileset(
		"Psych2.j2t", //All three Psych tilesets include these same tiles in the same place, but since we'll be taking the layer from Psych2.j2l, it feels only proper to use the matching .j2t file. Naturally, if you ever want to import tiles into a multiplayer level and your source is a non-official tileset, remember to force clients to download that .j2t file using #pragma require.
		376, 12, //Grabs all the tiles used by the Psych foreground vine, plus eight other irrelevant tiles besides. Note that jjTileCount needn't always be a multiple of 10.
		psychColorMap //The color map that got assembled in the i<8 loop above. If it's not clear by now, every palette index in the original tiles is replaced with the number at that index in the color map, e.g. pixels color 96 in the Psych tileset will use color psychColorMap[96] (which happens to equal 164) instead. This is not a context in which you should play with tile transparency, so ideally, the first entry in the color map should always be 0 and no non-0 colors will be changed to 0 in the mapping process.
	);
	jjLAYER@ psychLayer = jjLayersFromLevel("Psych2.j2l", array<uint> = {2}, oldTileCount - 376)[0]; //This is just the same as the Diamondus example in onFunction3, but since we only want a single layer this time, the "psychLayer" property can get away with being a jjLAYER@ instead of an array<jjLAYER@>.
	psychLayer.spriteMode = SPRITE::TRANSLUCENTTILE; //Make the vine tiles transparent--this is actually 5.3 code again, not 5.2, but who's counting?
	//The next three lines straightforwardly insert the Psych vine layer into the very front of the layer order, no matter which calls to jjLayerOrderSet were or were not made before this. Whether this will ever be a useful pattern is up to you, but just in case: yes, there does exist a jjLayerOrderGet function.
	array<jjLAYER@> oldLayerOrder = jjLayerOrderGet();
	oldLayerOrder.insertAt(0, psychLayer);
	jjLayerOrderSet(oldLayerOrder);
}

class AppleWithString : jjBEHAVIORINTERFACE { //Only (instances of) classes that implement jjBEHAVIORINTERFACE may be assigned to jjBEHAVIOR properties.
	string name = "        "; //Only the length (eight characters, arbitrarily) is important, since each individual character will be changed in the constructor below
	AppleWithString() { //This constructor is called every time the anonymous function that is jjObjectPresets[OBJECT::APPLE]'s behavior is run
		for (uint i = 0; i < name.length; ++i)
			name[i] = "A"[0] + (jjRandom()%26); //Random strings of capital letters--in AngelScript, "A"[0] and the like are the easiest way to obtain virtual-key codes of alphanumeric characters.
	}
	void onBehave(jjOBJ@ obj) { //In order for a class to implement jjBEHAVIORINTERFACE, it must define a "void onBehave(jjOBJ@ obj)" method. Otherwise the script will fail to compile. This method is called every game tick for every jjOBJ with (an instance of) this class as its jjOBJ::behavior, and that jjOBJ will be passed as the method's argument.
		obj.behave(BEHAVIOR::PICKUP);
		jjDrawString(obj.xPos, obj.yPos, name);
	}
	~AppleWithString() { //Because every apple has its own AppleWithString instance for its behavior, upon that apple's destruction, there are no more references in memory to that instance and it may be deleted, at which point its destructor is called. This shouldn't be relied on too much, however--see plus-angelscript.html for more details and a cautionary link.
		jjAlert("You ate apple " + name + "!");
	}
}

class FiveUp : jjBEHAVIORINTERFACE {
	//Unlike AppleWithString, FiveUp doesn't need to do any special setup, so no explicit constructor is defined. This is basically no different from writing FiveUp(){}.
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP); //Obviously more could be done here--preventing the pickup from falling when shot, for instance, by always resetting its position and speed properties--but for this example level, all we want to change is what happens when this pickup gets _collected_.
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { //"bullet" will always be null, because this is a PICKUP; "force" doesn't usually matter, because in normal JJ2 you can collect pickups regardless of whether you're using a special attack at the time.
		//Results specific to this "FiveUp" kind of pickup: increment the player's lives, and play a sound.
		//(If you want to make a level with _multiple_ custom pickups, you should probably create a generic "abstract class MyPickup : jjBEHAVIORINTERFACE" class that does all this but calls a method "myPickupResult" or something instead of the next two lines, and then have other classes (used for various custom pickups) inherit from that class and override the "myPickupResult" method. You might also consider making the sound played (here SOUND::COMMON_HARP1) a class property that also gets overridden by those inheriting classes. Just suggestions.)
		player.lives += 5;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_HARP1);
		//Results common to ALL pickups being collected:
		obj.frameID = 0;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		
		givePlayerPointsForObject(player, obj); //The code surrounding onObjectHit for HANDLING::PICKUP objects _automatically_ increases the player's score and generates a particle if the object's behavior is set to BEHAVIOR::EXPLOSION2, but this function (defined lower down in the script) replicates that too, in case you want to customize your object handling even more. Otherwise you don't actually need to do this stuff.
		return true; //See plus-angelscript.html for an explanation of jjBEHAVIORINTERFACE::onObjectHit's return value, or else return true all the time and don't worry about it.
	}
}

const array<SOUND::Sample> ButtstompSounds = {SOUND::COMMON_SPLAT1, SOUND::COMMON_SPLAT2, SOUND::COMMON_SPLAT3, SOUND::COMMON_SPLAT4};
class Flower : jjBEHAVIORINTERFACE {
	float XSpeed = 2;
	Flower(jjOBJ@ objectPreset) {
		jjANIMSET@ flowerAnimSet = jjAnimSets[ANIM::CUSTOM[0]];
		if (flowerAnimSet.firstAnim == 0) { //Animation not loaded yet
			//Once again we go through the motions of jjTilesFromTileset, but this time there is no accompanying jjLayersFromLevel call--instead, the eight tiles taken from Carrot1.j2t are one-by-one loaded into jjPIXELMAP objects and saved into jjANIMFRAME objects. If you're not sure how this all works, consult plusPixelMap.j2l/j2as instead.
			const int firstFlowerTileID = jjTileCount;
			const int flowerFrameCount = 8;
			jjTilesFromTileset("Carrot1.j2t", 692, flowerFrameCount); //appended to tileset
			flowerAnimSet.allocate(array<uint> = {flowerFrameCount}); //only one animation needed in this set
			const int firstFlowerFrameID = jjAnimations[flowerAnimSet.firstAnim].firstFrame;
			for (int flowerFrameID = 0; flowerFrameID < flowerFrameCount; ++flowerFrameID) {
				jjPIXELMAP flowerImage(firstFlowerTileID + flowerFrameID);
				jjANIMFRAME@ flowerFrame = jjAnimFrames[firstFlowerFrameID + flowerFrameID];
				flowerImage.save(flowerFrame);
				flowerFrame.hotSpotX = -16; //middle of image
				flowerFrame.hotSpotY = -24;
			}
		}
		//This class's constructor takes a jjOBJ@, specifically a handle to a jjObjectPresets entry, though it could just as easily be used on an individual game object from jjObjects instead. The following lines assign that (preset) object sufficient properties to make it be treated as an enemy by various bits of external code.
		objectPreset.behavior = this;
		objectPreset.scriptedCollisions = true;
		objectPreset.playerHandling = HANDLING::SPECIAL; //In combination with the previous line: players or bullets coming into contact with this enemy call its onObjectHit method.
		objectPreset.bulletHandling = HANDLING::DETECTBULLET; //Bullets are not automatically destroyed upon contact with this enemy, meaning the onObjectHit method must do it instead.
		objectPreset.isTarget = true; //Attracts seeker missiles and bird attacks.
		objectPreset.isFreezable = true; //Processes ice bullets separately from the onObjectHit method.
		objectPreset.triggersTNT = true; //TNT goes off when near this enemy.
		objectPreset.deactivates = true; //This enemy is deleted if it gets too far away from the player in local SP.
		objectPreset.causesRicochet = false; //Bullets don't automatically bounce off of this enemy.
		objectPreset.energy = 10; //How much damage this enemy can take.
		objectPreset.points = 100; //How many points the player gets for killing this enemy.
		objectPreset.determineCurAnim(ANIM::CUSTOM[0], 0);
		objectPreset.direction = 1;
	}
	void onBehave(jjOBJ@ obj) { //A very simple behavior function, covering all the main states (and one neutral state, here STATE::WALK) used by most game objects. The code for STATE::FREEZE, STATE::KILL, and STATE::DEACTIVATE is in fact so likely to be identical across many different objects that this is a very promising area for you to practice your skills with class inheritance.
		if (obj.state == STATE::START) {
			obj.state = STATE::WALK;
			obj.putOnGround();
		}
		if (obj.state == STATE::WALK) { //This code is written just to make the enemy look more interesting, but it is NOT a sufficient walking enemy behavior and should not be copied verbatim into your own scripts, because it doesn't account for walls, only cliffs. (Besides, JJ2 comes with a billion enemies that do nothing but walk back and forth already--surely you should be writing something more interesting than that?)
			if (jjMaskedVLine(int(obj.xPos + XSpeed*obj.direction), int(obj.yPos), 32)) //floor in front of me
				obj.xPos += XSpeed*obj.direction;
			else //no floor
				obj.direction = -obj.direction; //turn around!
			//animate
			obj.frameID = jjGameTicks >> 2;
			obj.determineCurFrame();
		} else if (obj.state == STATE::FREEZE) {
			if (obj.freeze-- <= 1) {
				obj.freeze = 0;
				obj.state = obj.oldState;
			}
		} else if (obj.state == STATE::KILL) {
			obj.delete();
		} else if (obj.state == STATE::DEACTIVATE) {
			obj.deactivate();
		}
	}
	void onDraw(jjOBJ@ obj) { //You can generally put any code you'd put in onDraw inside onBehave instead, but this is more semantic. (See plus-angelscript for a few more details about the divide.)
		obj.draw();
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { //As described in the signs in-level, this is a nearly 100% faithful recreation of standard enemy collision code.
		if (bullet !is null) {
			//recreation of HANDLING::HURTBYBULLET with HANDLING::ENEMY
			if (obj.causesRicochet) {
				if ((bullet.var[6] & 6) == 0) //not fire-based, not a laser beam
					bullet.ricochet();
				else if ((bullet.var[6] & 4) == 0) //not a laser beam
					bullet.delete();
			} else if ((bullet.var[6] & 16) == 0) //not a fireball
				bullet.state = STATE::EXPLODE;
			if (obj.freeze > 0 && force < 3)
				force = 3;
			obj.energy -= force;
			obj.justHit = 5; //flash white for 5 ticks--jjOBJ::justHit is automatically deincremented by the JJ2 engine, so individual behavior functions don't need to worry about doing that.
			if (obj.energy <= 0) { //killed
				obj.energy = 0;
				obj.state = STATE::KILL;
				if (obj.freeze > 0)
					obj.unfreeze(0);
				else if ((bullet.var[6] & 2) == 0) //not fire-based
					obj.particlePixelExplosion(0);
				else {
					jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BURN);
					if ((bullet.var[6] & 4) != 0) //laser beam
						obj.particlePixelExplosion(72); //gray
					else
						obj.particlePixelExplosion(((bullet.var[6] & 8) != 0) ? 32 : 40); //powered-up (blue) or not (orange)
				}
				if (player !is null) {
					obj.grantPickup(player, (uint(bullet.curAnim) == jjAnimSets[ANIM::AMMO].firstAnim + 17) ? 5 : 10);
					givePlayerPointsForObject(player, obj);
				}
			} else
				obj.freeze = 0;
		} else { //recreation of HANDLING::ENEMY; player guaranteed to be non-null
			if (force != 0) { //attacking via special attack, e.g. buttstomp
				obj.energy -= 4; //constant amount of damage for special attacks
				if (obj.energy <= 0) { //killed
					obj.energy = 0;
					givePlayerPointsForObject(player, obj);
					obj.state = STATE::KILL;
				} else { //only wounded
					obj.justHit = 5;
				}
				
				if (obj.freeze > 0) {
					obj.unfreeze(1);
				} else {
					if (obj.energy == 0)
						obj.particlePixelExplosion(2);
					jjSample(obj.xPos, obj.yPos, ButtstompSounds[jjRandom() % ButtstompSounds.length]);
				}
				
				if (force > 0) { //buttstomp or sugar rush
					player.buttstomp = 50; //landing
					player.ySpeed = player.ySpeed / -2 - 8;
					player.yAcc = 0;
					player.extendInvincibility(-70);
				} else if (force == -101) { //running into frozen enemy
					player.xAcc = 0;
					player.xSpeed /= -2;
					player.ySpeed = -6;
					player.extendInvincibility(-10);
				}
			} else { //not attacking
				player.hurt();
			}
		}
		return true;
	}
}
class RedFlower : Flower { //As described in the signs in-level, RedFlower inherits from Flower and changes various visible aspects of it but not its actual onBehave method.
	RedFlower(jjOBJ@ objectPreset) {
		super(objectPreset); //Run all the code in the Flower constructor function with this jjOBJ@ as its argument, resulting in this jjObjectPresets entry too getting all those enemy-like property values assigned to it.
		XSpeed = 3; //Was 2 on Flower, but this class is faster.
	}
	void onDraw(jjOBJ@ obj) override { //The "override" keyword is not actually required by AngelScript, but it's nice to write it whenever you can, simply to make sure you that got the method name right.
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, (obj.freeze == 0) ? SPRITE::TINTED : SPRITE::FROZEN, 24);
	}
	bool onObjectHit(jjOBJ@, jjOBJ@, jjPLAYER@ player, int) override {
		if (player !is null)
			player.hurt(); //This is a pretty ridiculous example, just to show a minimal case of inheritance... there are sure to be more useful things you can think of.
		return true;
	}
}

bool givePlayerPointsForObject(jjPLAYER@ player, jjOBJ@ obj) { //This will probably be made a jjOBJ method as part of the real JJ2+ API eventually, because it shows up all the time in the native code, but that hasn't happened yet, so here you go. Increases the player's jjPLAYER::score to match the object's jjOBJ::points, and creates a string particle with that number which flies up to the top left corner of the screen.
	if (player is null)
		return false;
	if (obj.points != 0 && (jjGameMode == GAME::SP || jjGameMode == GAME::COOP)) {
		player.score += obj.points;
		jjPARTICLE@ particle = jjAddParticle(PARTICLE::STRING);
		if (particle !is null) {
			particle.xPos = obj.xPos;
			particle.yPos = obj.yPos;
			particle.xSpeed = (-32768 - int(jjRandom() & 0x3FFF)) / 65536.f;
			particle.ySpeed = (-65536 - int(jjRandom() & 0x7FFF)) / 65536.f;
			particle.string.text = formatInt(obj.points);
		}
		obj.points = 0; //Ensures that JJ2 won't end up increasing the player's score TWICE, since a call to (the native version of) this function is actually part of the standard pickup-handling code and would otherwise therefore get you in trouble with the FiveUp onObjectHit code.
		return true;
	}
	return false;
}