const uint8 resetKey = 'R'[0]; //In AngelScript, this is the easiest way to obtain virtual-key codes of alphanumeric characters.
const array<CHAR::Char> chars = {CHAR::JAZZ, CHAR::SPAZ, CHAR::LORI};
enum textOffset {text_none, text_djSpeed, text_djCount, text_copterInvert, text_copterSpeed, text_copterDuration, text_jumpType, text_copterX, text_djX}
class Tpoint { //Registering a very simple point type that consists of a pair of integer coordinates.
	int x, y; //Position variables.
	Tpoint() {} //Default constructor.
	Tpoint(int X, int Y) {x = X; y = Y;} //Constructor from coordinate values.
}
array<Tpoint> minusSpriteCoords, plusSpriteCoords, swapSpriteCoords, textCoords;
void onLevelLoad() {
	for (uint i = 0; i < chars.length(); i++)
		reset(chars[i]);
	for (int i = 0; i < jjLayerHeight[4]; i++) {
		for (int j = 0; j < jjLayerWidth[4]; j++) {
			if (jjEventGet(j, i) == AREA::TEXT) {
				if (jjParameterGet(j, i, 9, 1) == 1) {
					switch (jjParameterGet(j, i, 0, 8)) {
						case 0:
							minusSpriteCoords.insertLast(Tpoint((j << 5) + 9, (i << 5) + 8));
							break;
						case 1:
							plusSpriteCoords.insertLast(Tpoint((j << 5) + 10, (i << 5) + 7));
							break;
						case 2:
							swapSpriteCoords.insertLast(Tpoint((j << 5) + 9, (i << 5) + 8));
							break;
					}
				} else {
					uint id = jjParameterGet(j, i, 0, 8);
					if (textCoords.length() <= id)
						textCoords.resize(id + 1);
					textCoords[id] = Tpoint((j << 5) + 15, (i << 5) - 16);
				}
			}
		}
	}
}
void onDrawLayer4(jjPLAYER@ player, jjCANVAS@ canvas) {
	for (uint i = 0; i < minusSpriteCoords.length(); i++) {
		const Tpoint@ point = minusSpriteCoords[i];
		canvas.drawSprite(point.x, point.y, ANIM::FONT, 1, 13, SPRITE::FLIPNONE, SPRITE::PALSHIFT, 216);
	}
	for (uint i = 0; i < plusSpriteCoords.length(); i++) {
		const Tpoint@ point = plusSpriteCoords[i];
		canvas.drawSprite(point.x, point.y, ANIM::FONT, 1, 11, SPRITE::FLIPNONE, SPRITE::PALSHIFT, 224);
	}
	for (uint i = 0; i < swapSpriteCoords.length(); i++) {
		const Tpoint@ point = swapSpriteCoords[i];
		canvas.drawSprite(point.x, point.y, ANIM::FONT, 1, 32, SPRITE::FLIPNONE, SPRITE::PALSHIFT, 24);
	}
	jjCHARACTER@ chara = jjCharacters[player.charCurr];
	for (uint i = 0; i < textCoords.length(); i++) {
		string text = "";
		switch (i) {
			case text_djSpeed:
				text = chara.airJump == AIR::DOUBLEJUMP ? formatFloat(abs(chara.doubleJumpYSpeed), "", 0, 1) : "N/A";
				break;
			case text_djCount:
				if (chara.airJump == AIR::DOUBLEJUMP)
					text = chara.doubleJumpCountMax <= 10 ? formatInt(chara.doubleJumpCountMax, "") + " jump(s)" : "Unlimited jumps";
				else
					text = "N/A";
				break;
			case text_copterInvert:
				if (chara.airJump == AIR::HELICOPTER)
					text = chara.helicopterYSpeed > 0 ? "Descending" : (chara.helicopterYSpeed < 0 ? "Ascending" : "Constant height");
				else 
					text = "N/A";
				break;
			case text_copterSpeed:
				text = chara.airJump == AIR::HELICOPTER ? formatFloat(chara.helicopterYSpeed, "+", 0, 1) : "N/A";
				break;
			case text_copterDuration:
				if (chara.airJump == AIR::HELICOPTER)
					text = chara.helicopterDurationMax <= 350 ? formatFloat(chara.helicopterDurationMax / 70.f, "", 0, 1) + " second(s)" : "Unlimited time";
				else
					text = "N/A";
				break;
			case text_jumpType:
				text = chara.airJump == AIR::DOUBLEJUMP ? "Double jump" : "Copter ears";
				break;
			case text_copterX:
				text = chara.airJump == AIR::HELICOPTER ? formatFloat(chara.helicopterXSpeed, "+", 0, 1) : "N/A";
				break;
			case text_djX:
				text = chara.airJump == AIR::DOUBLEJUMP ? formatFloat(chara.doubleJumpXSpeed, "+", 0, 1) : "N/A";
				break;
		}
		const Tpoint@ point = textCoords[i];
		jjTEXTAPPEARANCE style;
		style.xAmp = style.yAmp = 0;
		style.align = STRING::CENTER;
		canvas.drawString(point.x, point.y, text, STRING::SMALL, style, 0, SPRITE::NEONGLOW, 6);
	}
}
void reset(CHAR::Char char) {
	jjCHARACTER@ ch = jjCharacters[char];
	ch.airJump = char == CHAR::SPAZ ? AIR::DOUBLEJUMP : AIR::HELICOPTER;
	ch.helicopterDurationMax = 9999999;
	ch.helicopterYSpeed = 1;
	ch.helicopterXSpeed = 0;
	ch.doubleJumpCountMax = 1;
	ch.doubleJumpYSpeed = -8;
	ch.doubleJumpXSpeed = 0;
}

void onPlayerInput(jjPLAYER@) { //Nothing player-specific is done here so the parameter is left anonymous. This is good practice for unused parameters.
	if (jjKey[resetKey]) {
		for (uint i = 0; i < chars.length(); i++)
			reset(chars[i]);
	}
}
void onFunction0(jjPLAYER@ player, uint8 offset) {
	jjCHARACTER@ chara = jjCharacters[player.charCurr];
	switch (offset) {
		case text_djSpeed:
			if (chara.airJump == AIR::DOUBLEJUMP and (chara.doubleJumpYSpeed = chara.doubleJumpYSpeed + 1) > 0)
				chara.doubleJumpYSpeed = 0;
			break;
		case text_djCount:
			if (chara.airJump == AIR::DOUBLEJUMP and chara.doubleJumpCountMax > 0) {
				if (chara.doubleJumpCountMax > 10)
					chara.doubleJumpCountMax = 10;
				else
					chara.doubleJumpCountMax--;
			}
			break;
		case text_copterSpeed:
			if (chara.airJump == AIR::HELICOPTER)
				chara.helicopterYSpeed = chara.helicopterYSpeed - 0.5f;
			break;
		case text_copterDuration:
			if (chara.airJump == AIR::HELICOPTER and chara.helicopterDurationMax > 0) {
				if (chara.helicopterDurationMax > 350)
					chara.helicopterDurationMax = 350;
				else
					chara.helicopterDurationMax -= 35;
			}
			break;
		case text_copterX:
			if (chara.airJump == AIR::HELICOPTER)
				chara.helicopterXSpeed = chara.helicopterXSpeed - 2;
			break;
		case text_djX:
			if (chara.airJump == AIR::DOUBLEJUMP)
				chara.doubleJumpXSpeed = chara.doubleJumpXSpeed - 2.5f;
			break;
	}
}
void onFunction1(jjPLAYER@ player, uint8 offset) {
	jjCHARACTER@ chara = jjCharacters[player.charCurr];
	switch (offset) {
		case text_djSpeed:
			if (chara.airJump == AIR::DOUBLEJUMP)
				chara.doubleJumpYSpeed = chara.doubleJumpYSpeed - 1;
			break;
		case text_djCount:
			if (chara.airJump == AIR::DOUBLEJUMP)
				if (++chara.doubleJumpCountMax > 10)
					chara.doubleJumpCountMax = 9999999;
			break;
		case text_copterSpeed:
			if (chara.airJump == AIR::HELICOPTER)
				chara.helicopterYSpeed = chara.helicopterYSpeed + 0.5f;
			break;
		case text_copterDuration:
			if (chara.airJump == AIR::HELICOPTER) {
				if ((chara.helicopterDurationMax += 35) > 350)
					chara.helicopterDurationMax = 9999999;
			}
			break;
		case text_copterX:
			if (chara.airJump == AIR::HELICOPTER)
				chara.helicopterXSpeed = chara.helicopterXSpeed + 2;
			break;
		case text_djX:
			if (chara.airJump == AIR::DOUBLEJUMP)
				chara.doubleJumpXSpeed = chara.doubleJumpXSpeed + 2.5f;
			break;
	}
}
void onFunction2(jjPLAYER@ player, uint8 offset) {
	jjCHARACTER@ chara = jjCharacters[player.charCurr];
	switch (offset) {
		case text_copterInvert:
			if (chara.airJump == AIR::HELICOPTER)
				chara.helicopterYSpeed = -chara.helicopterYSpeed;
			break;
		case text_jumpType:
			chara.airJump = chara.airJump == AIR::DOUBLEJUMP ? AIR::HELICOPTER : AIR::DOUBLEJUMP;
			break;
	}
}