Downloads containing mo4a_3-1.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, ArcaneWeapons::FusionCannon::Weapon(), DefaultWeapons::Blaster(), WeaponVMega::Backfire::Weapon(), null, ArcaneWeapons::FusionCannon::Weapon(), null, ArcaneWeapons::MortarLauncher::Weapon()}); ///@MLLE-Generated
#include "MLLE-Include-1.5w.asc" ///@MLLE-Generated
#pragma require "mo4a_3-1-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "mo4a_3-1.j2l" ///@MLLE-Generated
#include "ArcaneWeapon4.asc" ///@MLLE-Generated
#pragma require "ArcaneWeapon4.asc" ///@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 "ArcaneWeapon9.asc" ///@MLLE-Generated
#pragma require "ArcaneWeapon9.asc" ///@MLLE-Generated
#include "Jazz1Enemies v05.asc"
#include "Resize v11.asc"
#include "TrueColor v13.asc"
#include "HH18savegems.asc"

int exit = 0;

bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
    canvas.drawResizedSprite(150, 585, ANIM::PICKUPS, 91, 4,0.7, 0.7, SPRITE::NORMAL);
    canvas.drawSprite(20, 585, ANIM::PICKUPS, 84, jjGameTicks>>2, -1, SPRITE::PALSHIFT, -8);
    canvas.drawString(170, 585, formatInt(exit%5, "1") + "/4", STRING::SMALL, STRING::NORMAL);
    canvas.drawString(30, 585, formatInt(player.coins%4, "1") + "/3", STRING::SMALL, STRING::NORMAL);
    return false;
}

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

 
void onLevelLoad() {
	gem::restorePlayerGems();
	jjLevelName = ("@@@@@@@@@Central Area");
	jjWaterLighting = WATERLIGHT::GLOBAL;
	jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckpointWrapper;
	jjObjectPresets[OBJECT::SAVEPOST].deactivates = false;

	Jazz1::MakeEnemy(OBJECT::HATTER, Jazz1::Enemies::Marbelara_Schwarzenguard, true).SetUsesJJ2StyleDeathAnimation(true).SetBulletFireSound(SOUND::INTRO_SHOT1).SetBulletExplosionSound(SOUND::COMMON_GUNSM1);
	Jazz1::MakeEnemy(OBJECT::DRAGONFLY, Jazz1::Enemies::Marbelara_Drageen).SetUsesJJ2StyleDeathAnimation(true);
	Jazz1::MakeEnemy(OBJECT::FLOATSUCKER, Jazz1::Enemies::Marbelara_Firebomb).SetUsesJJ2StyleDeathAnimation(true);
	Jazz1::MakeEnemy(OBJECT::NORMTURTLE, Jazz1::Enemies::Dreempipes_TerrapinSwimmer).SetUsesJJ2StyleDeathAnimation(true);

	jjObjectPresets[OBJECT::EVA].behavior = TimeMachine;
	jjObjectPresets[OBJECT::EVA].determineCurAnim(ANIM::FLAG, 1);
	jjObjectPresets[OBJECT::EVA].putOnGround(false);
	jjObjectPresets[OBJECT::EVA].scriptedCollisions = true;
	jjANIMATION@ tmAnim = jjAnimations[jjObjectPresets[OBJECT::EVA].curAnim];
	for (uint i = 0; i < tmAnim.frameCount; ++i)
		jjAnimFrames[tmAnim.firstFrame + i].coldSpotY = -44;
	for (uint i = 0; i < tmAnim.frameCount; ++i)
		jjAnimFrames[tmAnim.firstFrame + i].hotSpotY = -64;
	for (uint i = 0; i < tmAnim.frameCount; ++i)
		jjAnimFrames[tmAnim.firstFrame + i].hotSpotX = -54;

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

	jjObjectPresets[OBJECT::SANDWICH].behavior = Key2();
	jjObjectPresets[OBJECT::SANDWICH].scriptedCollisions = true;
	jjObjectPresets[OBJECT::SANDWICH].deactivates = false;

	jjObjectPresets[OBJECT::ICEAMMO15].behavior = FCannon();
	jjObjectPresets[OBJECT::ICEAMMO15].scriptedCollisions = true;
	jjObjectPresets[OBJECT::ICEAMMO15].playerHandling = HANDLING::SPECIAL;

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

	jjObjectPresets[OBJECT::WEENIE].behavior = RedSphere();
	jjObjectPresets[OBJECT::WEENIE].scriptedCollisions = true;
	jjObjectPresets[OBJECT::CHICKENLEG].behavior = BlueSphere();
	jjObjectPresets[OBJECT::CHICKENLEG].scriptedCollisions = true;
	jjObjectPresets[OBJECT::HAM].behavior = GreenSphere();
	jjObjectPresets[OBJECT::HAM].scriptedCollisions = true;
	jjObjectPresets[OBJECT::FRIES].behavior = PurpleSphere();
	jjObjectPresets[OBJECT::FRIES].scriptedCollisions = true;

	jjObjectPresets[OBJECT::STOPWATCH].behavior = Fastrun();
	jjObjectPresets[OBJECT::STOPWATCH].scriptedCollisions = true;

	jjObjectPresets[OBJECT::CHIPS].behavior = Paint();
	jjObjectPresets[OBJECT::CHIPS].scriptedCollisions = true;

	jjObjectPresets[OBJECT::BUTTERFLY].behavior = ButterThief();
	jjObjectPresets[OBJECT::BUTTERFLY].scriptedCollisions = true;
	jjObjectPresets[OBJECT::BUTTERFLY].playerHandling = HANDLING::SPECIAL;
	jjObjectPresets[OBJECT::BUTTERFLY].energy = 10;

	jjObjectPresets[OBJECT::BEEBOY].behavior = Tree;
	jjObjectPresets[OBJECT::BEEBOY].scriptedCollisions = true;
	jjObjectPresets[OBJECT::BEEBOY].bulletHandling = HANDLING::IGNOREBULLET;
	jjObjectPresets[OBJECT::BEEBOY].playerHandling = HANDLING::ENEMY;

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

	jjPIXELMAP pole(0, 26*32, 1*32, 3*32, 5);
	jjANIMFRAME@ frame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CARROTPOLE].firstAnim].firstFrame];
	pole.save(frame);
	frame.hotSpotX = -frame.width/2;
	frame.hotSpotY = -frame.height;

	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;
	jjObjectPresets[OBJECT::INVINCIBILITY].bulletHandling = HANDLING::IGNOREBULLET;
	jjObjectPresets[OBJECT::INVINCIBILITY].playerHandling = HANDLING::PLAYERBULLET;
	jjANIMATION@ BeAnim = jjAnimations[jjObjectPresets[OBJECT::INVINCIBILITY].curAnim];

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

}


class PlatinCoin : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(p.coins >= 1)
		{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		if(jjTriggers[1] == true && jjTriggers[2] == true && jjTriggers[4] == true && jjTriggers[5] == 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@ play, int force) { 
		if(jjTriggers[1] == true && jjTriggers[2] == true && jjTriggers[4] == true && jjTriggers[5] == true)
			{p.coins += 1; 
			obj.behavior = BEHAVIOR::EXPLOSION2;
			jjSample(obj.xPos, obj.yPos, SOUND::COMMON_COIN, 1000);}
		else {if(spheretext == false) {
				p.showText("@@Collect the spheres first.");
				spheretext = true;}
		}
	return true;
	}

}

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();
}

int slope = 0;
int SpeedUpUntil = 0;

class FCannon : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(p.ammo[WEAPON::TNT] > 1)
		{obj.delete();}
		obj.behave(BEHAVIOR::MONITOR);	
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) { 
		if(bullet !is null && play !is null)
			{obj.behave(BEHAVIOR::EXPLOSION2);
			jjAddObject(OBJECT::TNTAMMO3, 186*32, 13*32);
			jjAddObject(OBJECT::TNTAMMO3, 187*32, 13*32);
			jjAddObject(OBJECT::TNTAMMO3, 188*32, 13*32);
			jjAddObject(OBJECT::TNTAMMO3, 186*32, 12*32);			
			jjAddObject(OBJECT::TNTAMMO3, 187*32, 12*32);
			jjAddObject(OBJECT::TNTAMMO3, 188*32, 12*32);
			jjSample(p.xPos, p.yPos, SOUND::COMMON_HARP1, 1000);}
		return true;
	}
	
}

void onFunction0(jjPLAYER@ play) {
	jjTriggers[26] = true;
	jjSetWaterLevel(1984, false);
	jjSample(p.xPos, p.yPos, SOUND::ROBOT_HYDROPUF);}

void onFunction1(jjPLAYER@ p) {
	slope = jjGameTicks + 2*61;}

void onFunction2(jjPLAYER@ p) {
	slope = 0;}

void onFunction3(jjPLAYER@ p) {
	p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@Atlantius Harbour", STRING::MEDIUM);
	jjEnabledASFunctions[3] = false;}

void onFunction4(jjPLAYER@ p) {
	p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@Grand Library", STRING::MEDIUM);
	jjEnabledASFunctions[4] = false;}

void onFunction5(jjPLAYER@ p) {
	p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@Underwater City", STRING::MEDIUM);
	jjEnabledASFunctions[5] = false;}

void onMain() {
gem::deleteCollectedGems();
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); 
}
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::PEACH].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP fruit(0, 20*32, 1*32, 1*32, 5);
	fruit.save(frame);
	frame.hotSpotX = -frame.width / 2;
}
bool buyammo = false, doorunblocked = false, startrush = false, readytorush = false, spheretext = false, control = true;

void onPlayer(jjPLAYER@ p) {
	gem::trackPlayerGems(p);
	gem::upgradeHealth(p);
	p.lightType = LIGHT::NONE;

jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::WEENIE].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP sphere(0, 21*32, 1*32, 1*32, 5);
	sphere.save(frame);

	exit = 0;
	if(jjTriggers[1] == true)
		{exit += 1;}
	if(jjTriggers[2] == true)
		{exit += 1;}
	if(jjTriggers[4] == true)
		{exit += 1;}
	if(jjTriggers[5] == true)
		{exit += 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 > 5 && (p.keyLeft || p.keyRight || p.keyJump || p.keyFire))
		{p.cameraUnfreeze(true);
		control=true;}

 		
	if (doorunblocked == false && jjTriggers[1]==true && jjTriggers[2]==true && jjTriggers[4]==true && jjTriggers[5]==true) {
		p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Spheres collected.");
		jjTriggers[6] = true;
		doorunblocked = 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 >= 1) {
		o.state = STATE::KILL;
		}

	}


	if(p.xPos<8*32 && p.yPos<20*32 && p.coins < 1)
		{p.testForCoins(1);}
	if(p.xPos<8*32 && p.yPos<20*32 && p.coins >= 1)
		{jjNxt("mo4a_3-2_save", false, true);
		gem::saveGemData();}
	if(leveldone == true && p.xPos > 188*32 && p.yPos >= 40*32 && p.coins <3)
	{p.testForCoins(3);}
	if(leveldone == true && p.xPos > 188*32 && p.yPos >= 40*32 && p.coins >=3)
	{jjNxt("mo4a_3-4_save", false, true);
	gem::saveGemData();}

	if(p.ammo[WEAPON::TNT] < 1 && p.xPos>168*32 && p.xPos<171*32 && p.yPos<16*32 && p.yPos>13*32 && jjKey[0x50] && p.gems[GEM::RED]<50 && buyammo == false)
		{p.testForGems(50, GEM::RED);}
	if(p.ammo[WEAPON::TNT] < 1 && p.xPos>168*32 && p.xPos<171*32 && p.yPos<16*32 && p.yPos>13*32 && jjKey[0x50] && p.gems[GEM::RED]>=50 && buyammo == false)
	{p.testForGems(50, GEM::RED);
	jjTriggers[9]=true;
	buyammo = true;}
	if(p.ammo[WEAPON::TNT] >= 1 && p.xPos>168*32 && p.xPos<171*32 && p.yPos<16*32 && p.yPos>13*32 && jjKey[0x50] && buyammo == false)
		{p.showText("@@You already have the weapon.");}
if(p.keyUp && slope > jjGameTicks){
	p.yPos = p.yPos - 4;
	p.invisibility = true;
	jjDrawSprite(p.xPos, p.yPos, p.setID, RABBIT::HPOLE, p.curFrame, p.direction, SPRITE::PLAYER);
	}
if(p.keyUp == false && slope > jjGameTicks){
	p.invisibility = false;
}
if(slope < jjGameTicks){
	p.invisibility = false;
}

  if (SpeedUpUntil > jjGameTicks) {
	p.invincibility = 10;
	p.keyRun = true;
	if(p.keyRight)
	p.xSpeed = 30;
	if(p.keyLeft)
	p.xSpeed = -30;
  }

}
bool thieffly= false;
class Paint : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::CHIPS].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP paint(0, 102*32, 2*32, 3*32, 1);
	paint.save(frame);
	frame.hotSpotX = -15;
	frame.hotSpotY = -47;
	obj.behave(BEHAVIOR::BUMP,false);
	obj.bulletHandling = HANDLING::IGNOREBULLET;
	obj.draw();
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
	if(p.keyUp==true || bullet !is null)
	{thieffly = true;
	obj.behavior = BEHAVIOR::EXPLOSION2;}
	return true;

	}
}
class ButterThief : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		if(thieffly == false)
		{obj.playerHandling = HANDLING::PLAYERBULLET;}
		if(thieffly == true)
		{obj.behave(BEHAVIOR::BUTTERFLY);
		obj.lightType = LIGHT::LASER;
		obj.light = 100;
		obj.playerHandling = HANDLING::ENEMY;
		jjDrawTile(obj.xPos - 17, obj.yPos + 2, 870);
				if(obj.energy == 0)
				{obj.state = STATE::KILL;
				obj.behavior = BEHAVIOR::EXPLOSION;
				jjAddObject(OBJECT::SANDWICH, obj.xPos, obj.yPos-32);
				}
		}
	}
	
}

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

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

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

}

	bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ play, int force) {
	play.timerStart(800);
		SpeedUpUntil = jjGameTicks + 13 * 61;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		obj.frameID = 0;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PICKUP1, 6000);

		return true;
	}

}



void TimeMachine(jjOBJ@ obj){
	obj.behave(BEHAVIOR::EVA, false);
			jjDrawResizedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, 1, SPRITE::PALSHIFT, 8);

}

void Tree(jjOBJ@ enemy) {
jjPLAYER@ play = jjLocalPlayers[0];
		if (enemy.xPos > play.xPos)
				enemy.xPos = enemy.xPos - (2+jjDifficulty/4); 
		else if (enemy.xPos < play.xPos) 
				enemy.xPos = enemy.xPos + (2+jjDifficulty/4);
		if (enemy.yPos > play.yPos)
				enemy.yPos = enemy.yPos - (2+jjDifficulty/4); 
		else if (enemy.yPos < play.yPos) 
				enemy.yPos = enemy.yPos + (2+jjDifficulty/4);
if (jjMaskedVLine(enemy.xSpeed > 0 ? enemy.xPos + 16 : enemy.xPos - 16, enemy.yPos, 1)) {
				enemy.delete();
		  int explosionID = jjAddObject(OBJECT::BLASTERBULLET, enemy.xPos, enemy.yPos, enemy.creator, CREATOR::OBJECT);
		jjObjects[explosionID].determineCurAnim(ANIM::AMMO, 4, true);
		jjObjects[explosionID].state = STATE::EXPLODE;
			}
		enemy.determineCurAnim(ANIM::WITCH, 3);
		enemy.determineCurFrame();
		enemy.frameID = enemy.counter/8;
		//enemy.draw();
		++enemy.counter;
		enemy.counter += 1; 
		 if (enemy.counter >200)
		{enemy.delete();
		  int explosionID = jjAddObject(OBJECT::BLASTERBULLET, enemy.xPos, enemy.yPos, enemy.creator, CREATOR::OBJECT);
		jjObjects[explosionID].determineCurAnim(ANIM::AMMO, 4, true);
		jjObjects[explosionID].state = STATE::EXPLODE;
		}
	jjDrawSpriteFromCurFrame(enemy.xPos, enemy.yPos, enemy.curFrame, enemy.direction, SPRITE::PALSHIFT, 24);

}


class Key : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::AIRBOARD].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP doorkey(0, 29*32, 1*32, 1*32, 5);
	doorkey.save(frame);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
		jjTriggers[0] = true;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}
bool leveldone = false;
class Key2 : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::SANDWICH].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP doorkey(0, 29*32, 1*32, 1*32, 5);
	doorkey.save(frame);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
		jjTriggers[10] = true;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
		leveldone = true;
		return true; 
	}
}

class RedSphere : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(jjTriggers[1] == true)
			{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.lightType = LIGHT::FLICKER;
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::WEENIE].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP sphere(0, 21*32, 1*32, 1*32, 5);
	sphere.save(frame);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
		jjTriggers[1] = true;
		p.cameraFreeze(89*32,28*32,true, false);
		exit += 1;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}

class BlueSphere : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(jjTriggers[2] == true)
			{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.lightType = LIGHT::FLICKER;
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::CHICKENLEG].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP sphere(0, 22*32, 1*32, 1*32, 5);
	sphere.save(frame);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
		jjTriggers[2] = true;
		p.cameraFreeze(89*32,23*32,true, false);
		exit += 1;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}


class GreenSphere : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(jjTriggers[4] == true)
			{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.lightType = LIGHT::FLICKER;
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::HAM].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP sphere(0, 24*32, 1*32, 1*32, 5);
	sphere.save(frame);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
		jjTriggers[4] = true;
		exit += 1;
		p.cameraFreeze(89*32,20*32,true, false);
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}

class PurpleSphere : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(jjTriggers[5] == true)
			{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.lightType = LIGHT::FLICKER;
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::FRIES].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP sphere(0, 25*32, 1*32, 1*32, 5);
	sphere.save(frame);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
		jjTriggers[5] = true;
		exit += 1;
		p.cameraFreeze(89*32,15*32,true, false);
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}

void onLevelReload()  {
	gem::restorePlayerGems();
	jjLocalPlayers[0].lives++;
	buyammo == false;
	jjTriggers[9] = false;
	jjWaterLighting = WATERLIGHT::GLOBAL;
  	for (uint i = 0; i < 32; ++i)
	  	jjTriggers[i] = SavedTriggers[i];
}

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(16, 20, "Sorry sir. Ships are not\nbe sailing today."),
	Sign(54, 19, "I've lost my Library Pass last week\nduring my visit to Poseidon Island."),
	Sign(146, 22, "Those who have the LIBRARY PASS shall\nexperience the wisdom of THE GRAND LIBRARY."),
	Sign(79, 37, "Poseidon Island would be a nice place to visit.\nBut the harbour's closed due to a volcanic erruption on the island."),
	Sign(1, 17, "Looking for a ship?\nI can help."),
	Sign(117, 37, "Detectors show that the artifact\nis very close around here!."),
	Sign(132, 11, "No entrance to the\nbuildings with powerups..."),
	Sign(171, 13, "Press P if you'd like to\nbuy Fusion Cannon for 50 Gems."),
	Sign(118, 13, "Don't you ever try to get in the library.\nIt's dangerous. And nothing special about it."),
};

void onDrawLayer3(jjPLAYER@, jjCANVAS@ layer) { 
	if(jjKey[0x54]){
	const uint8 textIntensity = 200 + int(jjSin(jjGameTicks * 16) * 50); 
	for (uint signID = 0; signID < Signs.length; ++signID) 
		Signs[signID].draw(layer, textIntensity);
}
}

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;
        }
      }
    }
  }
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
	return MLLE::WeaponHook.drawAmmo(player, canvas);
}