Downloads containing mo4a_1-2.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Mystery of the Four... chandie Single player 6.6 Download file

File preview

const bool MLLESetupSuccessful = MLLE::Setup(array<MLLEWeaponApply@> = {null, null, DefaultWeapons::Blaster(), DefaultWeapons::Blaster(), WeaponVMega::Backfire::Weapon(), null, null, null, DefaultWeapons::Blaster()}); ///@MLLE-Generated
#include "MLLE-Include-1.5w.asc" ///@MLLE-Generated
#pragma require "mo4a_1-2-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "mo4a_1-2.j2l" ///@MLLE-Generated
#include "WeaponVMega5.asc" ///@MLLE-Generated
#pragma require "WeaponVMega5.asc" ///@MLLE-Generated
#include "MLLE-DefaultWeapons.asc" ///@MLLE-Generated
#pragma require "MLLE-DefaultWeapons.asc" ///@MLLE-Generated
#include "Jazz1Enemies v05.asc"
#include "Resize v11.asc"
#include "TrueColor v13.asc"
#include "HH18savegems.asc"
#pragma require "kangaroo.j2a"

const OBJECT::Object JillEventID = OBJECT::FENCER;
int LaserFrog = 0, exit = 0, frogexit = 0;
bool crate2 =false, crate3 = false, crate5 = false, crate6 = false, control = true;

bool froggy = false, doorunblocked = false, startrush = false, readytorush = false, womensaved = false;

void onPlayer(jjPLAYER@ play) {
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::ORANGE].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP fruit(0, 31*32, 1*32, 1*32, 5);
	fruit.save(frame);
for (int i = 0; i < 1024; i++) { //loop through the global array jjParticles[1024]
    jjPARTICLE@ particle = jjParticles[i];
    if (particle.type == PARTICLE::RAIN) {
      particle.xSpeed = 0; //make rain fall straight down
      particle.ySpeed = play.ySpeed < 0? 10 : int(10 + play.ySpeed); //the rain speed accounts for differences in the player speed, and so won't appear to fall more slowly when the player is falling
    }
  }
	if(jjTriggers[15] == false)
		{jjEnabledASFunctions[2] = true;}
	if(jjTriggers[14] == false)
		{jjEnabledASFunctions[8] = true;}
	if(jjTriggers[13] == false)
		{jjEnabledASFunctions[7] = true;}
	if(jjTriggers[12] == false)
		{jjEnabledASFunctions[6] = true;}
	if(jjTriggers[1] == false)
		{jjEnabledASFunctions[5] = true;}
	if(jjTriggers[10] == false)
		{jjEnabledASFunctions[4] = true;}

	frogexit = 0;
	if(jjTriggers[2] == true)
		{frogexit += 1;
		crate2 = true;}
	if(jjTriggers[3] == true)
		{frogexit += 1;
		crate3 = true;}
	if(jjTriggers[5] == true)
		{frogexit += 1;
		crate5 = true;}
	if(jjTriggers[6] == true)
		{frogexit += 1;
		crate6 = true;}

	exit = 0;
	if(jjTriggers[15] == true)
		{exit += 1;}
	if(jjTriggers[14] == true)
		{exit += 1;}
	if(jjTriggers[13] == true)
		{exit += 1;}
	if(jjTriggers[12] == true)
		{exit += 1;}
	if(jjTriggers[1] == true)
		{exit += 1;}
	if(jjTriggers[10] == true)
		{exit += 1;}
	gem::trackPlayerGems(p);
	gem::upgradeHealth(p);

	if(womensaved == false && jjTriggers[10] == true && jjTriggers[1] == true && jjTriggers[12] == true && jjTriggers[13] == true && jjTriggers[14] == true && jjTriggers[15] == true)
		{p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Coin unlocked.");
		womensaved = true;}

	if(p.food == 100 && jjKey[0x52] == false && startrush == false)
		{p.showText("@@@@@@@@@@@@@@@@||||||||Press 'R' when you need to use Sugar Rush!", STRING::MEDIUM);
		p.startSugarRush(0);
		startrush = true;
		readytorush = true;}

	if(readytorush == true)
		{p.food = 100;}

	if(p.food == 100 && jjKey[0x52])
		{p.startSugarRush(1400);
		p.food = 0;
		readytorush = false;
		startrush = false;
	}

	for (int i = 1; i < jjObjectCount; i++) {
	jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::SILVERCOIN && p.coins >= 2) {
		o.state = STATE::KILL;
		}

	}

	for (int i = 1; i < jjObjectCount; i++) {
		jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::SEEKERAMMO3 && p.ammo[WEAPON::SEEKER] < 1) {
			o.state = STATE::KILL;}
	}
	for (int i = 1; i < jjObjectCount; i++) {
		jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::RFAMMO3 && p.ammo[WEAPON::RF] < 1) {
			o.state = STATE::KILL;}
	}
	for (int i = 1; i < jjObjectCount; i++) {
		jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::TNTAMMO3 && p.ammo[WEAPON::TNT] < 1) {
			o.state = STATE::KILL;}
	}
	for (int i = 1; i < jjObjectCount; i++) {
		jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::GUN9AMMO3 && p.ammo[WEAPON::GUN9] < 1) {
			o.state = STATE::KILL;}
	}
	for (int i = 1; i < jjObjectCount; i++) {
		jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::SEEKERPOWERUP && p.ammo[WEAPON::SEEKER] < 1) {
			o.state = STATE::KILL;}
	}
	for (int i = 1; i < jjObjectCount; i++) {
		jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::RFPOWERUP && p.ammo[WEAPON::RF] < 1) {
			o.state = STATE::KILL;}
	}
	for (int i = 1; i < jjObjectCount; i++) {
		jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::TNTPOWERUP && p.ammo[WEAPON::TNT] < 1) {
			o.state = STATE::KILL;}
	}
	for (int i = 1; i < jjObjectCount; i++) {
		jjOBJ@ o = jjObjects[i];
		if (o.isActive && o.eventID == OBJECT::GUN9POWERUP && p.ammo[WEAPON::GUN9] < 1) {
			o.state = STATE::KILL;}
	}
 		if (doorunblocked == false && jjTriggers[2]==true && jjTriggers[3]==true && jjTriggers[5]==true && jjTriggers[6]==true) {
		p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Cage door's key activated.");
		doorunblocked = true;}

	if(p.yPos<4*32 && p.xPos>95*32 && p.xPos<99*32 && p.coins < 2)
		{p.testForCoins(2);}
	if(p.yPos<4*32 && p.xPos>95*32 && p.xPos<99*32 && p.coins == 2)
		{jjNxt("mo4a_1-3_save", false, true);
		gem::saveGemData();}

	if(p.charCurr == CHAR::FROG) {
		p.furSet(64, 64, 64, 64);
	}
	if(p.charCurr != CHAR::FROG) {
		p.furSet(0, 0, 0, 0);
	}

	if(froggy == true) {
	jjTriggers[20] = true;
	}
		
	if(p.coins==0)
	{p.coins +=1;}

		if(control==false)
		{p.keyLeft = false;
		p.keyRight = false;
		p.keyDown = false;}

		if(p.idle > 100)
		{p.cameraUnfreeze(true);
		control=true;}
		else if (p.idle > 30 && (p.keyLeft || p.keyRight || p.keyJump || p.keyFire || p.keyFire))
		{p.cameraUnfreeze(true);
		control=true;}


	if(play.keyUp == true && p.xPos>225*32 && p.xPos<228*32 && p.yPos<51*32 && p.yPos>49*32 && jjTriggers[2]==false){
	jjTriggers[2] = true;
	p.cameraFreeze(208*32, 48*32, true, false);
	control = false;
	jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2);}

	if(play.keyUp == true && p.xPos<3*32 && p.yPos<4*32 && jjTriggers[7]==false){
	jjTriggers[7] = true;
	p.cameraFreeze(49*32, 12*32, true, false);
	control = false;
	jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2);}	

	if(play.keyUp == true && p.xPos>5*32 && p.xPos<8*32 && p.yPos>56*32 && p.yPos<59*32 && jjTriggers[8]==false){
	jjTriggers[8] = true;
	p.cameraFreeze(8*32, 38*32, true, false);
	control = false;
	jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2);}

	if(play.keyUp == true && p.xPos>248*32 && p.xPos<251*32 && p.yPos<57*32 && p.yPos>54*32 && jjTriggers[9]==false){
	jjTriggers[9] = true;
	jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2);}

	if(play.keyUp == true && p.xPos>248*32 && p.xPos<251*32 && p.yPos<25*32 && p.yPos>22*32 && jjTriggers[11]==false){
	jjTriggers[11] = true;
	jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2);}

	if(play.yPos > jjWaterLevel && play.charCurr == CHAR::FROG && p.keyJump == true)
	{play.jumpStrength = -10;}
	if(play.yPos > jjWaterLevel && play.charCurr != CHAR::FROG)
	{play.health = 0;}
	play.lightType = LIGHT::NONE;
	
	if(LaserFrog < jjGameTicks)		
		{p.invisibility = false;}
	if (LaserFrog > jjGameTicks) {
			p.invisibility = false;

	if(play.keyFire) {
			p.invisibility = true;
			p.lightType = LIGHT::LASER;
			jjDrawSprite(p.xPos, p.yPos, p.setID, RABBIT::EARBRACHIATE, p.curFrame, p.direction, SPRITE::PLAYER); 
	if(jjRandom() & 9 == 0){
			jjOBJ@ obj = jjObjects[OBJECT::LASER];
			jjAddObject(OBJECT::LASER, -p.xPos, p.yPos);
					obj.behave(BEHAVIOR::BULLET);
					obj.determineCurAnim(ANIM::AMMO, 60);
					obj.playerHandling = HANDLING::PLAYERBULLET;
					obj.bulletHandling = HANDLING::IGNOREBULLET;
					}

}
  }


}


void onLevelLoad() {
jjPIXELMAP rain(32,32);
  for (uint x = 0; x < rain.width; ++x) {
    for (uint y = 0; y < rain.height; ++y) {
      if (x == 16) { //draw in the middle of the tile, xPixel 16
        if (y <= 24) rain[x,y] = 75; //if at yPixel 24 or less, use color 75
        else rain[x,y] = 74; //use color 74 for yPixels 25-32
      } else {
        rain[x,y] = 0;
      }
    }
  }
 
  jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::COMMON].firstAnim + 2];
  for (uint frameID = 0; frameID < anim.frameCount; ++frameID) {
    jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame + frameID];
    rain.save(frame);
    frame.hotSpotX = -frame.width/2;
    frame.hotSpotY = -frame.height;
  }

	jjWaterLighting = WATERLIGHT::GLOBAL;
	gem::restorePlayerGems();
	jjLevelName = ("@@@@@@@@@Sacred Woods");
	jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckpointWrapper;
	jjObjectPresets[OBJECT::SAVEPOST].deactivates = false;

	jjObjectPresets[OBJECT::FISH].determineCurAnim(ANIM::FROG, 2);
	jjObjectPresets[OBJECT::FISH].scriptedCollisions = true;
	jjObjectPresets[OBJECT::FISH].direction = 1;
	jjObjectPresets[OBJECT::FISH].playerHandling = HANDLING::SPECIAL;
	jjObjectPresets[OBJECT::FISH].bulletHandling = HANDLING::IGNOREBULLET;
	jjObjectPresets[OBJECT::FISH].putOnGround();
	jjObjectPresets[OBJECT::FISH].behavior = FrogWait;

	Jazz1::MakeEnemy(OBJECT::HATTER, Jazz1::Enemies::Marbelara_Schwarzenguard, true).SetUsesJJ2StyleDeathAnimation(true).SetBulletFireSound(SOUND::INTRO_SHOT1).SetBulletExplosionSound(SOUND::COMMON_GUNSM1);

	jjObjectPresets[OBJECT::AIRBOARD].behavior = Key();
	jjObjectPresets[OBJECT::AIRBOARD].scriptedCollisions = true;

	jjObjectPresets[OBJECT::SEEKERPOWERUP].behavior = Switch1();
	jjObjectPresets[OBJECT::SEEKERPOWERUP].scriptedCollisions = true;

	jjObjectPresets[OBJECT::TOASTERPOWERUP].behavior = Switch2();
	jjObjectPresets[OBJECT::TOASTERPOWERUP].scriptedCollisions = true;

	jjObjectPresets[OBJECT::BOUNCERPOWERUP].behavior = Switch3();
	jjObjectPresets[OBJECT::BOUNCERPOWERUP].scriptedCollisions = true;

	jjObjectPresets[OBJECT::BLASTERPOWERUP].behavior = Switch4();
	jjObjectPresets[OBJECT::BLASTERPOWERUP].scriptedCollisions = true;

	jjObjectPresets[OBJECT::EXTRATIME].behavior = Laser();
	jjObjectPresets[OBJECT::EXTRATIME].scriptedCollisions = true;

	jjObjectPresets[OBJECT::SILVERCOIN].behavior = PlatinCoin();
	jjObjectPresets[OBJECT::SILVERCOIN].scriptedCollisions = true;

	jjObjectPresets[OBJECT::TRIGGERCRATE].deactivates = false;

	jjObjectPresets[OBJECT::FLYCARROT].determineCurAnim(ANIM::PLUS_WARP, 0);
	jjObjectPresets[OBJECT::FLYCARROT].behavior = Bonus;
	jjObjectPresets[OBJECT::FLYCARROT].scriptedCollisions = true;
	jjANIMATION@ BAnim = jjAnimations[jjObjectPresets[OBJECT::FLYCARROT].curAnim];
	for (uint i = 0; i < BAnim.frameCount; ++i)
		jjAnimFrames[BAnim.firstFrame + i].hotSpotY = -85;
	for (uint i = 0; i < BAnim.frameCount; ++i)
		jjAnimFrames[BAnim.firstFrame + i].hotSpotX = 20;

	jjObjectPresets[OBJECT::INVINCIBILITY].determineCurAnim(ANIM::PLUS_WARP, 1);
	jjObjectPresets[OBJECT::INVINCIBILITY].behavior = Bonuseye;
	jjObjectPresets[OBJECT::INVINCIBILITY].scriptedCollisions = true;
	jjANIMATION@ BeAnim = jjAnimations[jjObjectPresets[OBJECT::INVINCIBILITY].curAnim];

	for (uint i = 0; i < BeAnim.frameCount; ++i)
		jjAnimFrames[BeAnim.firstFrame + i].hotSpotY = 32;
	for (uint i = 0; i < BeAnim.frameCount; ++i)
		jjAnimFrames[BeAnim.firstFrame + i].hotSpotX = 26;



jjEventSet(197, 11, JillEventID);
	
	//call a couple Kangaroo functions
	Kangaroo::MakeEventJoey(OBJECT::MONKEY); //default parameters
	Kangaroo::MakeEventJoey(OBJECT::STANDMONKEY, 0, 5, 5, 12, 5, 256); //custom parameters
	Kangaroo::MakeEventJill(
		JillEventID
	).deactivates = false; //note that both MakeEventJoey and MakeEventJill return the jjObjectPresets entry of the eventID passed to them

}

void Bonuseye(jjOBJ@ obj){
	obj.direction = -1;
	obj.behave(BEHAVIOR::PICKUP, false);
	obj.draw();
}
void Bonus(jjOBJ@ obj){
	obj.putOnGround();
	obj.direction = -1;
	obj.behave(BEHAVIOR::PICKUP, false);
	obj.draw();
}

void FrogWait(jjOBJ@ obj) {

		obj.behave(BEHAVIOR::FISH, false);
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::SINGLEHUE, 64);
}

bool cointext = true;

class PlatinCoin : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(p.coins >= 2)
		{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		if(jjTriggers[10] == true && jjTriggers[1] == true && jjTriggers[12] == true && jjTriggers[13] == true && jjTriggers[14] == true && jjTriggers[15] == true)
			{jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -8);}
		else jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TRANSLUCENTPALSHIFT, -8);
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		if(jjTriggers[10] == true && jjTriggers[1] == true && jjTriggers[12] == true && jjTriggers[13] == true && jjTriggers[14] == true && jjTriggers[15] == true)
		{p.coins += 1; 
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_COIN, 1000);}
		else if(cointext == true)
		{p.showText("@@Save the women to@unlock the token.");
		cointext=false;}

		return true;
	}
}

bool keytext = true;

class Key : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(jjTriggers[0]==true)
			{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::AIRBOARD].curAnim];
		anim.frameCount = 1;
		jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
		jjPIXELMAP pump(0, 59*32, 1*32, 1*32, 5);
		pump.save(frame);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
	if(jjTriggers[2] == true && jjTriggers[3] == true && jjTriggers[5] == true && jjTriggers[6] == true)
		{jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);}
	else {jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TRANSLUCENT);}

	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
	if(jjTriggers[2] == true && jjTriggers[3] == true && jjTriggers[5] == true && jjTriggers[6] == true)
	{
		jjTriggers[0]=true;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
	}
	if(keytext==true) {
		p.showText("@@@Find 4 metal crates@to activate this key.");
		keytext = false;}
		return true;
	}
}

void onFunction0(jjPLAYER@ play) {
if(p.charCurr == CHAR::FROG) {
play.boss=jjAddObject(OBJECT::FISH, 103*32, 51*32);
froggy = true;
p.xPos = 104*32;
p.yPos = 60*32;
p.cameraFreeze(99*32, 57*32, true, false);
//control = false;
}
}

void onFunction1(jjPLAYER@ play) {
jjTriggers[20] = true;
p.revertMorph(false);
p.cameraUnfreeze();
froggy = true;
}

void onFunction2(jjPLAYER@ play) {
	if(p.charCurr != CHAR::FROG && jjTriggers[15] == false) {
		play.activateBoss();
		jjMusicLoad("mo4a_Doc's Cave.ogg");
		jjEnabledASFunctions[2] = false;
	}
}

void onFunction3(jjPLAYER@ play) {
if(froggy == false)
p.morphTo(CHAR::FROG, false);
}

void onFunction4(jjPLAYER@ p) {
	p.showText("@@You are free to go now.");
	jjTriggers[10] = true;
	exit += 1;
	jjSample(p.xPos, p.yPos, SOUND::LORISOUNDS_LORIFALL);
}
void onFunction5(jjPLAYER@ p) {
	p.showText("@@You are free to go now.");
	jjSample(p.xPos, p.yPos, SOUND::LORISOUNDS_LORIFALL);
	exit += 1;
	jjTriggers[1] = true;
}
void onFunction6(jjPLAYER@ p) {
	p.showText("@@You are free to go now.");
	jjSample(p.xPos, p.yPos, SOUND::LORISOUNDS_LORIFALL);
	exit += 1;
	jjTriggers[12] = true;
}
void onFunction7(jjPLAYER@ p) {
	p.showText("@@You are free to go now.");
	jjSample(p.xPos, p.yPos, SOUND::LORISOUNDS_LORIFALL);
	exit += 1;
	jjTriggers[13] = true;
}
void onFunction8(jjPLAYER@ p) {
	p.showText("@@You are free to go now.");
	jjSample(p.xPos, p.yPos, SOUND::LORISOUNDS_LORIFALL);
	exit += 1;
	jjTriggers[14] = true;
}

class Laser : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {

obj.behave(BEHAVIOR::PICKUP, false);
obj.determineCurAnim(ANIM::AMMO, 58);
++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;

		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 56);

}

	bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ play, int force) {

		LaserFrog= jjGameTicks + 5 * 61;
		p.timerStart(300);
		obj.behavior = BEHAVIOR::EXPLOSION2;
		obj.frameID = 0;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PICKUP1, 6000);

		return true;
	}

}




class Switch1: jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.draw();



jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::SEEKERPOWERUP].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP sw(0, 30*32, 1*32, 1*32, 5);
	sw.save(frame);
	frame.hotSpotY = 17;
	frame.hotSpotX = -17;
}

bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) { 

obj.playerHandling = HANDLING::ENEMY;
obj.scriptedCollisions = false;
		jjSwitchTrigger(8);
jjSample(p.xPos, p.yPos, SOUND::COMMON_EXPSM1, 3000);
		obj.frameID = 0;
		obj.behavior = BEHAVIOR::EXPLOSION2;

		return true;  
	} 
}

class Switch2: jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.draw();



jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::TOASTERPOWERUP].curAnim];
	anim.frameCount = 1; //The Bee enemy's animation has several frames, but the tileset only has material for one. Editing jjANIMATION::frameCount to 1 will ensure that the animation never extends beyond the one frame we're providing.	
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP pump(0, 30*32, 1*32, 1*32, 5);
	pump.save(frame);
	frame.hotSpotY = 17;
	frame.hotSpotX = -17;
}

bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) { 

obj.playerHandling = HANDLING::ENEMY;
obj.scriptedCollisions = false;
		jjSwitchTrigger(7);
jjSample(p.xPos, p.yPos, SOUND::COMMON_EXPSM1, 3000);
		obj.frameID = 0;
		obj.behavior = BEHAVIOR::EXPLOSION2;

		return true;  
	} 
}

class Switch3: jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.draw();



jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::BOUNCERPOWERUP].curAnim];
	anim.frameCount = 1; //The Bee enemy's animation has several frames, but the tileset only has material for one. Editing jjANIMATION::frameCount to 1 will ensure that the animation never extends beyond the one frame we're providing.	
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP pump(0, 30*32, 1*32, 1*32, 5);
	pump.save(frame);
	frame.hotSpotY = 17;
	frame.hotSpotX = -15;
}

bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) { 

obj.playerHandling = HANDLING::ENEMY;
obj.scriptedCollisions = false;
		jjSwitchTrigger(9);
jjSample(p.xPos, p.yPos, SOUND::COMMON_EXPSM1, 3000);
		obj.frameID = 0;
		obj.behavior = BEHAVIOR::EXPLOSION2;

		return true;  
	} 
}

class Switch4: jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.draw();



jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::BLASTERPOWERUP].curAnim];
	anim.frameCount = 1; //The Bee enemy's animation has several frames, but the tileset only has material for one. Editing jjANIMATION::frameCount to 1 will ensure that the animation never extends beyond the one frame we're providing.	
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP pump(0, 30*32, 1*32, 1*32, 5);
	pump.save(frame);
	frame.hotSpotY = 17;
	frame.hotSpotX = -17;
}

bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) { 

obj.playerHandling = HANDLING::ENEMY;
obj.scriptedCollisions = false;
		jjSwitchTrigger(11);
jjSample(p.xPos, p.yPos, SOUND::COMMON_EXPSM1, 3000);
		obj.frameID = 0;
		obj.behavior = BEHAVIOR::EXPLOSION;

		return true;  
	} 
}



void onMain() {
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::BURGER].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP woman(0, 32*32, 1*32, 1*32, 5);
	woman.save(frame);
	frame.hotSpotY = -27;


gem::deleteCollectedGems();
if(jjTriggers[20]==true){
if(jjKey[9] && jjKey[0x51]) {
p.morphTo(CHAR::JAZZ, false); 
}
if(jjKey[9] && jjKey[0x57]) {
p.morphTo(CHAR::SPAZ, false); 
}
if(jjKey[9] && jjKey[0x45]) {
p.morphTo(CHAR::LORI, false); 
}
}
}


/*
// *Kangaroo by VioletCLM.* I wanted to use a jjAddObject instead of jjNxt after the defeat of Jill. So I had to carry the contents of the ASC folder here and make a small arrangement. Sorry about that Violet.
API (all expected to be called in onLevelLoad):
	jjOBJ@ Kangaroo::MakeEventJoey(uint8 eventID, int minX = 0, int maxX = 4, int minY = 6, int maxY = 12, int jumpDelay = 35, int minDistance = 224)
		Assign a specific event slot to the Joey enemy, e.g. replacing another enemy type or a food object or something. If you assign multiple event slots, you can get Joey enemies with different parameters.
	jjOBJ@ Kangaroo::MakeEventJill(uint8 eventID, uint8 spawn = 0, bool secondStage = false, int textID = -1)
		Assign a specific event slot to the Jill boss.
			"spawn", if non-zero, is an eventID that the boss will occasionally create from her pouch. This is assumed to be a Joey enemy but may be other objects as well.
			The "secondStage" bool causes Jill to turn red and jump faster after she has lost three-quarters of her health.
			If "textID" is 0-16, defeating Jill will display that text ID.
	void Kangaroo::OnJillDefeat(JILLCALLBACKFUNC@ callback = null)
		Three seconds after defeating Jill, this function will be called. The JILLCALLBACKFUNC pattern is the same as the behavior pattern: a void-returning function taking a jjOBJ@ as its only argument. If "callback" is left null, or if OnJillDefeat is never called, defeating a Jill will simply end the level.
	void Kangaroo::Joey(jjOBJ@ obj)
		The behavior function for the Joey enemy
	void Kangaroo::Jill(jjOBJ@ obj)
		The behavior function for the Jill boss
*/




namespace Kangaroo {
	namespace Private {
		void jillDefeatedDefaultAction(jjOBJ@ o) {
			p.showText("@@You are free to go now.");
			jjTriggers[15] = true;
			jjAddObject(OBJECT::SAVEPOST, 204*32, 13*32);
			exit += 1;
			jjSample(p.xPos, p.yPos, SOUND::LORISOUNDS_LORIFALL);
			jjMusicLoad("mo4a_The Golden City.ogg");
			p.bossActivated = false;
		}
		JILLCALLBACKFUNC@ jillCallback = jillDefeatedDefaultAction;
		
		bool animsLoaded = false;
		uint customAnimID = 0;
		void loadAnims() {
			if (!animsLoaded) {
				animsLoaded = true;
				while (jjAnimSets[ANIM::CUSTOM[customAnimID]] != 0)
					++customAnimID;
				customAnimID = ANIM::CUSTOM[customAnimID];
				jjAnimSets[customAnimID].load(0, "kangaroo.j2a");
				if (!jjSampleIsLoaded(SOUND::BUBBA_BUBBABOUNCE1))
					jjAnimSets[ANIM::BUBBA].load();
			}
		}
		
		void applyGenericEnemySettingsToPreset(jjOBJ@ preset) {
			preset.playerHandling = HANDLING::ENEMY;
			preset.bulletHandling = HANDLING::HURTBYBULLET;
			preset.causesRicochet = false;
			preset.isBlastable = false;
			preset.triggersTNT = true;
			preset.isFreezable = true;
			preset.isTarget = true;
			preset.scriptedCollisions = false;
			preset.direction = 1;
			preset.freeze = 0;
		}
		
		void putKangarooOnGround(jjOBJ@ obj, int width, int height) {
			while (!jjMaskedHLine(int(obj.xPos) - width/2, width, int(obj.yPos) + height/2))
				obj.yPos += 1;
		}
		
		uint firstGloveAnimationFrame;
		const jjANIMFRAME@ roundExplosionFrame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::AMMO] + 5] + 2];;
		
		void doGloveAt(int x, int y) {
			for (int i = 0; i < jjLocalPlayerCount; ++i) {
				jjPLAYER@ localPlayer = jjLocalPlayers[i];
				if (localPlayer.blink == 0 && roundExplosionFrame.doesCollide(x, y, 0, jjAnimFrames[localPlayer.curFrame], int(localPlayer.xPos), int(localPlayer.yPos), localPlayer.direction))
					localPlayer.hurt();
			}
			for (int i = jjObjectCount - 1; i > 0; --i) {
				jjOBJ@ obj = jjObjects[i];
				if (obj.playerHandling == HANDLING::PLAYERBULLET && obj.state != STATE::EXPLODE && roundExplosionFrame.doesCollide(x, y, 0, jjAnimFrames[obj.curFrame], int(obj.xPos), int(obj.yPos), obj.direction)) {
					obj.ricochet();
					//obj.playerHandling = HANDLING::ENEMYBULLET;
				}
			}
		}
	}
	enum KangarooVariables {
		kvWIDTH = 0, kvHEIGHT, kvMINX, kvMAXX, kvMINY, kvMAXY, kvJUMPDELAY, kvMINDISTANCE, kvGLOVE1FRAME, kvGLOVE2FRAME, kvSECONDSTAGE
	}
	jjOBJ@ MakeEventJoey(uint8 eventID, int minX = 0, int maxX = 4, int minY = 6, int maxY = 12, int jumpDelay = 35, int minDistance = 224) {
		Kangaroo::Private::loadAnims();
		
		jjOBJ@ preset = jjObjectPresets[eventID];
		preset.behavior = Joey;
		preset.determineCurAnim(Kangaroo::Private::customAnimID, 0);
		preset.frameID = 0; preset.determineCurFrame();
		
		Kangaroo::Private::applyGenericEnemySettingsToPreset(preset);
		
		preset.deactivates = true;
		preset.energy = 1;
		preset.points = 200;
		preset.yAcc = 0.33f;
		preset.counter = 0;
		preset.var[kvWIDTH] = 12;
		preset.var[kvHEIGHT] = 28;
		preset.var[kvMINX] = minX;
		preset.var[kvMAXX] = maxX;
		preset.var[kvMINY] = minY;
		preset.var[kvMAXY] = maxY;
		preset.var[kvJUMPDELAY] = jumpDelay;
		preset.var[kvMINDISTANCE] = minDistance;
		
		return preset;
	}
	
	funcdef void JILLCALLBACKFUNC(jjOBJ@);
	jjOBJ@ MakeEventJill(uint8 eventID, uint8 spawn = 0, bool secondStage = false, int textID = -1) {
		Kangaroo::Private::loadAnims();
		if (jjAnimSets[ANIM::GLOVE] == 0)
			jjAnimSets[ANIM::GLOVE].load();
		Kangaroo::Private::firstGloveAnimationFrame = jjAnimations[jjAnimSets[ANIM::GLOVE] + 3];
		
		jjOBJ@ preset = jjObjectPresets[eventID];
		preset.behavior = Jill;
		preset.determineCurAnim(Kangaroo::Private::customAnimID, 1);
		preset.frameID = 0; preset.determineCurFrame();
		
		Kangaroo::Private::applyGenericEnemySettingsToPreset(preset);
		
		preset.doesHurt = spawn;
		preset.yAcc = 0.16f;
		preset.energy = 100;
		preset.points = 5000;
		preset.counterEnd = 210; //death wait
		preset.special = textID;
		preset.playerHandling = HANDLING::DYING; //no initial collision damage
		preset.var[kvWIDTH] = 32;
		preset.var[kvHEIGHT] = 98;
		preset.var[kvMINX] = 2;
		preset.var[kvMAXX] = 4;
		preset.var[kvMINY] = 5;
		preset.var[kvMAXY] = 10;
		preset.var[kvJUMPDELAY] = 140;
		preset.var[kvMINDISTANCE] = 400;
		preset.var[kvGLOVE1FRAME] = 0;
		preset.var[kvGLOVE2FRAME] = 0;
		preset.var[kvSECONDSTAGE] = secondStage ? 1 : 0;
		
		return preset;
	}
	void OnJillDefeat(JILLCALLBACKFUNC@ callback = null) {
		if (callback !is null)
			@Kangaroo::Private::jillCallback = callback;
	}
	
	
	void Joey(jjOBJ@ obj) {
		const int width = obj.var[kvWIDTH];
		const int height = obj.var[kvHEIGHT];
		switch (obj.state) {
			case STATE::START:
				obj.state = STATE::IDLE;
				Kangaroo::Private::putKangarooOnGround(obj, width, height);
			case STATE::IDLE:
				if (obj.counter == 0 || --obj.counter == 0) {
					const int nearestPlayerID = obj.findNearestPlayer(int(pow(obj.var[kvMINDISTANCE], 2)));
					if (nearestPlayerID >= 0) {
						jjPLAYER@ nearestPlayer = jjPlayers[nearestPlayerID];
						obj.xSpeed = (nearestPlayer.xPos - obj.xPos) / 20.0f;
						obj.direction = (obj.xSpeed >= 0) ? 1 : -1;
						
						float xSpeed = abs(obj.xSpeed);
						if (xSpeed > obj.var[kvMAXX]) xSpeed = obj.var[kvMAXX];
						else if (xSpeed < obj.var[kvMINX]) xSpeed = obj.var[kvMINX];
						obj.xSpeed = xSpeed * obj.direction;
						
						float ySpeed = abs((nearestPlayer.yPos - obj.yPos) / 20.0f);
						if (ySpeed > obj.var[kvMAXY]) ySpeed = obj.var[kvMAXY];
						else if (ySpeed < obj.var[kvMINY]) ySpeed = obj.var[kvMINY];
						obj.ySpeed = -ySpeed;
						
						obj.state = STATE::JUMP;
						obj.counter = obj.var[kvJUMPDELAY];
						jjSample(obj.xPos, obj.yPos, ((jjRandom() & 1) == 0) ? SOUND::BUBBA_BUBBABOUNCE1 : SOUND::BUBBA_BUBBABOUNCE2);
					} else
						obj.direction = (obj.xPos > jjLocalPlayers[0].xPos) ? -1 : 1;
				}
				break;
			case STATE::FREEZE:
				if (obj.freeze > 0)
					--obj.freeze;
				if (obj.freeze <= 0) {
					obj.state = obj.oldState;
					obj.unfreeze(0);
				}
				break;
			case STATE::JUMP:{
				obj.yPos += (obj.ySpeed += obj.yAcc);
				const int newXPos = int(obj.xPos + obj.xSpeed) + (width * obj.direction)/2;
				if ((newXPos < 0) || (newXPos > jjLayerWidth[4]*32) || jjMaskedVLine(newXPos, int(obj.yPos - height/2), height)) {
					obj.xSpeed = -obj.xSpeed;
					obj.direction = -obj.direction;
				} 
				obj.xPos += obj.xSpeed;
				int newYPos = int(obj.yPos + obj.ySpeed);
				if (obj.ySpeed < 0) {
					if ((newYPos < 0) || jjMaskedHLine(int(obj.xPos) - width/2, width, newYPos - height/2)) {
						obj.ySpeed = obj.yAcc;
						obj.frameID = 2;
					} else obj.frameID = 1;
				}
				if (obj.ySpeed > 0) {
					if ((newYPos > jjLayerHeight[4]*32) || jjMaskedHLine(int(obj.xPos) - width/2, width, newYPos + height/2)) {
						obj.state = STATE::IDLE;
						obj.ySpeed = 0;
						obj.frameID = 0;
						Kangaroo::Private::putKangarooOnGround(obj, width, height);
					} else obj.frameID = 2;
				}
				obj.determineCurFrame();
				break;
			} case STATE::DEACTIVATE:
				obj.deactivate();
				return;
			case STATE::KILL:
				obj.delete();
				return;
		}
		obj.draw();
	}
	void Jill(jjOBJ@ obj) {
		switch (obj.state) {
			case STATE::START:
				obj.state = STATE::DELAYEDSTART;
			case STATE::DELAYEDSTART:
				for (int i = 0; i < jjLocalPlayerCount; ++i) {
					jjPLAYER@ localPlayer = jjLocalPlayers[i];
					if (localPlayer.bossActivated) {
						localPlayer.boss = obj.objectID;
						obj.state = STATE::START;
					}
				}
				if (obj.state == STATE::START) {
					obj.playerHandling = HANDLING::ENEMY;
					break;
				}
				return;
			case STATE::KILL:
				if (obj.special >= 0) //textID
					jjLocalPlayers[0].showText(obj.special, 0);
				obj.playerHandling = HANDLING::DYING;
				obj.state = STATE::DONE;
			case STATE::DONE:
				if (--obj.counterEnd == 0) {
					obj.delete();
					Kangaroo::Private::jillCallback(obj);
				}
				return;
			default:
				break;
		}
		
		int oldState = obj.state;
		obj.behave(Joey, false);
		obj.frameID = 0;
		obj.determineCurFrame();
		
		const int direction = obj.direction;
		const bool secondStage = (obj.var[kvSECONDSTAGE] != 0 && obj.energy < 25);
		if (secondStage) {
			if (obj.var[kvSECONDSTAGE] == 1) {
				obj.var[kvSECONDSTAGE] = 2;
				obj.var[kvMINX] = 3;
				obj.var[kvMAXX] = 5;
				obj.var[kvJUMPDELAY] = 50;
				obj.var[kvMINDISTANCE] = 600;
			}
		}
		if (obj.doesHurt != 0 && (jjRandom() & 255) == 0) {
			jjOBJ@ spawn = jjObjects[jjAddObject(obj.doesHurt, obj.xPos, obj.yPos + 11, obj.objectID, CREATOR::OBJECT, BEHAVIOR::INACTIVE)];
			jjBEHAVIOR originalBehavior = jjObjectPresets[obj.doesHurt].behavior;
			if (originalBehavior == Joey) {
				spawn.direction = obj.direction;
				spawn.xSpeed = spawn.direction * (1 + (jjRandom() & 3));
				spawn.ySpeed = -6;
				spawn.state = STATE::JUMP;
			}
			spawn.behavior = originalBehavior;
		}
		
		SPRITE::Mode mode = SPRITE::NORMAL;
		uint8 param = 15;
		SPRITE::Mode modeDark = SPRITE::BRIGHTNESS;
		uint8 paramDark = 96;
		if (obj.justHit != 0) {
			mode = modeDark = SPRITE::SINGLECOLOR;
			paramDark = param;
			const int nearestPlayerID = obj.findNearestPlayer(64);
			for (int i = 0; i < jjLocalPlayerCount; ++i) {
				jjPLAYER@ localPlayer = jjLocalPlayers[i];
				if (localPlayer.specialMove != 0) {
					localPlayer.specialMove = 0;
					localPlayer.ySpeed -= 1;
					localPlayer.extendInvincibility(-35);
				}
			}
			
		} else if (obj.state == STATE::FREEZE) {
			mode = modeDark = SPRITE::FROZEN;
		} else if (secondStage) {
			mode = modeDark = SPRITE::TINTED;
			param = 25;
			paramDark = 29;
		}
		
		int armAngle = obj.age;
		if (obj.state != STATE::FREEZE) {
			const int nearestPlayerID = obj.findNearestPlayer(1000000);
			if (nearestPlayerID >= 0) {
				jjPLAYER@ nearestPlayer = jjPlayers[nearestPlayerID];
				const float deltaX = nearestPlayer.xPos - obj.xPos;
				const float deltaY = nearestPlayer.yPos - obj.yPos;
				armAngle = int(atan2(
				    (direction == 1) ? deltaY : -deltaY,
				    abs(deltaX)
				) * -512.0 * 0.318309886142228);
			}
			obj.age = armAngle;
		}
		const float armSin = jjSin(armAngle);
		const float armCos = jjCos(armAngle);
		int tailAngle = int(obj.ySpeed*-16)*direction;
		const int tailX = int(obj.xPos) - 32 * direction;
		const int tailY = int(obj.yPos) + 23;
		const int gloveLength = (12 + 29 + 29) * obj.direction;
		const int legAngle = int(obj.ySpeed*8)*direction;
		const int arm1X = int(obj.xPos) - 2 * direction;
		const int arm1Y = int(obj.yPos) - 7;
		const int arm2X = int(obj.xPos) + 4 * direction;
		const int arm2Y = int(obj.yPos - 11);
		//if (tailAngle > 0) tailAngle = 0;
		
		if (obj.ySpeed < 0) {
			obj.animSpeed = jjSampleLooped(obj.xPos, obj.yPos, SOUND::COMMON_FLAMER, obj.animSpeed);
			if (obj.state != STATE::FREEZE && (jjRandom() & 1) == 0) {
				jjPARTICLE@ part = jjAddParticle(PARTICLE::FIRE);
				if (part !is null) {
					part.xSpeed = jjSin(tailAngle) * 2;
					part.ySpeed = jjCos(tailAngle) * 2;
					part.xPos = tailX + part.xSpeed * 8;
					part.yPos = tailY + part.ySpeed * 8;
					part.xSpeed += abs(obj.xSpeed) * -obj.direction;
				}
			}
			if (oldState == STATE::IDLE) {
				obj.var[kvGLOVE1FRAME] = 1;
				jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PISTOL1);
				obj.var[kvGLOVE2FRAME] = 0;
			}
		}
		{
			int oldGloveFrame = obj.var[kvGLOVE1FRAME];
			if (oldGloveFrame > 0 && oldGloveFrame < 12 && (jjGameTicks & 3) == 1)
				obj.var[kvGLOVE1FRAME] = oldGloveFrame + 1;
			
			oldGloveFrame = obj.var[kvGLOVE2FRAME];
			if (obj.state == STATE::IDLE && oldGloveFrame == 0) {
				obj.var[kvGLOVE2FRAME] = 1;
				jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PISTOL1);
			}
			if (oldGloveFrame > 0 && oldGloveFrame < 12 && (jjGameTicks & 3) == 1)
				obj.var[kvGLOVE2FRAME] = oldGloveFrame + 1;
		}
		
		const int glove2FrameID = Kangaroo::Private::firstGloveAnimationFrame + (obj.var[kvGLOVE2FRAME] + 3) % 12;
		jjDrawRotatedSpriteFromCurFrame(
			arm2X + 12*armSin + gloveLength*armCos,
			arm2Y + 12*armCos - gloveLength*armSin,
			 glove2FrameID,
			armAngle - 256 * obj.direction,
			direction, 2, modeDark, paramDark
		); //glove
		if (obj.state != STATE::FREEZE) {
			const int glove2Length = gloveLength + (jjAnimFrames[glove2FrameID].height - 30) * 2 * obj.direction;
			Kangaroo::Private::doGloveAt(
				int(arm2X + 12*armSin + glove2Length*armCos),
				int(arm2Y + 12*armCos - glove2Length*armSin)
			);
		}
		jjDrawRotatedSpriteFromCurFrame(arm2X, arm2Y, obj.curFrame + 1, armAngle, direction, 1, modeDark, paramDark); //back arm
		
		jjDrawRotatedSpriteFromCurFrame(obj.xPos - 24 * direction, obj.yPos + 24, obj.curFrame + 2, legAngle, direction, 1, modeDark, paramDark); //back leg
		jjDrawRotatedSpriteFromCurFrame(tailX, tailY, obj.curFrame + 3, tailAngle, direction, 1, mode, param); //tail
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, direction, mode, param); //body
		jjDrawRotatedSpriteFromCurFrame(obj.xPos - 30 * direction, obj.yPos + 28, obj.curFrame + 2, legAngle, direction, 1, mode, param); //leg
		
		jjDrawRotatedSpriteFromCurFrame(arm1X, arm1Y, obj.curFrame + 1, armAngle, direction, 1, mode, param); //arm
		const int glove1FrameID = Kangaroo::Private::firstGloveAnimationFrame + (obj.var[kvGLOVE1FRAME] + 3) % 12;
		jjDrawRotatedSpriteFromCurFrame(
			arm1X + 12*armSin + gloveLength*armCos,
			arm1Y + 12*armCos - gloveLength*armSin,
			 glove1FrameID,
			armAngle - 256 * obj.direction,
			direction, 2, mode, param
		); //glove
		if (obj.state != STATE::FREEZE) {
			const int glove1Length = gloveLength + (jjAnimFrames[glove1FrameID].height - 30) * 2 * obj.direction;
			Kangaroo::Private::doGloveAt(
				int(arm2X + 12*armSin + glove1Length*armCos),
				int(arm2Y + 12*armCos - glove1Length*armSin)
			);
		}
	}
}

bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
    canvas.drawString(30, 585, formatInt(exit%7, "1") + "/6", STRING::SMALL, STRING::NORMAL);
    canvas.drawSprite(20, 585, ANIM::PICKUPS, 11, jjGameTicks>>2, -1, SPRITE::NORMAL);
    canvas.drawString(100, 585, formatInt(frogexit%5, "1") + "/4", STRING::SMALL, STRING::NORMAL);
    canvas.drawResizedSprite(90, 585, ANIM::PICKUPS, 52, 4,0.7, 0.7, SPRITE::NORMAL);
    return false;
}

bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {     return true;}


void onLevelReload() {

	if(crate2 == true)
		{jjAddObject(OBJECT::TNT, 1*32, 35*32);}
	if(crate3 == true)
		{jjAddObject(OBJECT::TNT, 49*32, 12*32);}
	if(crate5 == true)
		{jjAddObject(OBJECT::TNT, 208*32, 48*32);}
	if(crate6 == true)
		{jjAddObject(OBJECT::TNT, 250*32, 14*32);}



	jjEnabledASFunctions[3] = true;
	jjEnabledASFunctions[2] = true;
	jjMusicLoad("mo4a_The Golden City.ogg");
	gem::restorePlayerGems();
	jjLocalPlayers[0].lives++;
	jjWaterLighting = WATERLIGHT::GLOBAL;
  	for (uint i = 0; i < 32; ++i)
	  	jjTriggers[i] = SavedTriggers[i];
}

array<bool> SavedTriggers(32, false);
//Extendable Checkpoints by VioletCLM
void CheckpointWrapper(jjOBJ@ obj) {
  if (obj.state == STATE::STOP) { //don't do anything anymore
    jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
  } else if (obj.state == STATE::DEACTIVATE) { //due to death
    obj.deactivate();
  } else {
    obj.behave(BEHAVIOR::CHECKPOINT);
	jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
    if (obj.state == STATE::DONE) { //triggered by the player hitting it
      obj.state = STATE::STOP;
      //save the current state of some properties
      for (uint i = 0; i < 32; ++i)
        SavedTriggers[i] = jjTriggers[i];

      //OPTIONAL: this loop makes checkpoints reusable, so only the most recent checkpoint you touched is ever active
      for (int i = jjObjectCount; --i > 0;) {
        jjOBJ@ obj2 = jjObjects[i];
        if (obj2.eventID == OBJECT::CHECKPOINT && i != obj.objectID && obj2.isActive) {
          obj2.state = STATE::SLEEP;
          obj2.var[0] = 0;
        }
      }
    }
  }
}

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, 55, 0, SPRITE::TRANSLUCENT);
		layer.drawString(xPos, yPos, text, STRING::SMALL, SignTextAppearance, 0, SPRITE::BLEND_HARDLIGHT, textIntensity);
	}
}
const array<Sign> Signs = { 
	Sign(54, 38, "Rabbits cannot swim.\nHow about frogs?"),
	Sign(100, 56, "HEY! GET ME OUTTA HERE\nWITH THAT KEY OVER THERE!"),
};

void onDrawLayer3(jjPLAYER@, jjCANVAS@ layer) { 
	if((jjKey[0x54] && p.charCurr == CHAR::FROG)||(p.charCurr == CHAR::FROG && p.xPos >90*32 && p.xPos<113*32 && p.yPos>34*32)){
	const uint8 textIntensity = 200 + int(jjSin(jjGameTicks * 16) * 50); 
	for (uint signID = 0; signID < Signs.length; ++signID) 
		Signs[signID].draw(layer, textIntensity);
}
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
	return MLLE::WeaponHook.drawAmmo(player, canvas);
}