Downloads containing mo4a_3-1_revisited1.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_revisited1-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "mo4a_3-1_revisited1.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"

bool pass = false;

bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {

if(jjTriggers[10]==true)
    {canvas.drawSprite(100, 581, ANIM::PICKUPS,80, jjGameTicks>>2, 1, SPRITE::NORMAL);}

if(pass==false)
    {canvas.drawSprite(100, 585, ANIM::PICKUPS, 0, jjGameTicks>>2, 1, SPRITE::NORMAL);}
    canvas.drawSprite(20, 585, ANIM::PICKUPS, 84, jjGameTicks>>2, -1, SPRITE::PALSHIFT, -8);
    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);
	Jazz1::MakeEnemy(OBJECT::SUCKER, Jazz1::Enemies::Megairbase_Doofusguard).SetUsesJJ2StyleDeathAnimation(true).SetBulletFireSound(SOUND::INTRO_SHOTGRN).SetBulletExplosionSound(SOUND::COMMON_GUNSM1);

	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::WEENIE].behavior = RedSphere();
	jjObjectPresets[OBJECT::WEENIE].scriptedCollisions = true;
	jjObjectPresets[OBJECT::CHICKENLEG].behavior = BlueSphere();
	jjObjectPresets[OBJECT::CHICKENLEG].scriptedCollisions = true;
	jjObjectPresets[OBJECT::BURGER].behavior = YellowSphere();
	jjObjectPresets[OBJECT::BURGER].scriptedCollisions = true;
	jjObjectPresets[OBJECT::HAM].behavior = GreenSphere();
	jjObjectPresets[OBJECT::HAM].scriptedCollisions = true;
	jjObjectPresets[OBJECT::FRIES].behavior = PurpleSphere();
	jjObjectPresets[OBJECT::FRIES].scriptedCollisions = true;

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

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

	jjObjectPresets[OBJECT::STOPWATCH].behavior = Fastrun();
	jjObjectPresets[OBJECT::STOPWATCH].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::GOLDCOIN].behavior = Coin();
	jjObjectPresets[OBJECT::GOLDCOIN].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;

}

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("@@@@@@@@@@@@@@@@@@@@@@@@@The Grand Library", STRING::MEDIUM);
	jjTriggers[11] = false;
	jjEnabledASFunctions[4] = false;
	pass = true;
	jjMusicLoad("mo4a_Rock and Lava.ogg");}

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

void onFunction6(jjPLAYER@ p) {
	if(thieffly == false)
	{p.showText("@@A key must be somewhere@around here...");}
	thieffly = true;}

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::EXTRALIFE].curAnim];
	anim.frameCount = 1; 
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP pass(0*32, 20*32, 1*32, 1*32, 5);
	pass.save(frame);
	frame.hotSpotY = -17;
}
bool buyammo = false, doorunblocked = false, startrush = false, readytorush = false;
void onPlayer(jjPLAYER@ p) {
	gem::trackPlayerGems(p);
	gem::upgradeHealth(p);
	p.lightType = LIGHT::NONE;
	if(p.coins == 0)
	{p.coins += 3;}
 		
	if (doorunblocked == false && jjTriggers[1]==true && jjTriggers[2]==true && jjTriggers[3]==true && jjTriggers[4]==true && jjTriggers[4]==true) {
		p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Spheres collected.");
		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;
	}


	if(leveldone == true && p.xPos > 188*32 && p.yPos >= 40*32 && p.coins <3)
	{p.testForCoins(3);
	p.yPos = p.yPos - 63;
	p.xPos = p.xPos - 63;}
	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 ButterThief : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		if(thieffly == false)
		{obj.playerHandling = HANDLING::PLAYERBULLET;}
		if(thieffly == true && jjTriggers[10]==false)
		{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;
	}

}

class Coin : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
	obj.behave(BEHAVIOR::PICKUP, false);
	if(jjTriggers[1] == true && jjTriggers[2] == true && jjTriggers[3] == true && jjTriggers[4] == true && jjTriggers[5] == 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@ play, int force) { 
	if(jjTriggers[1] == true && jjTriggers[2] == true && jjTriggers[3] == true && jjTriggers[4] == true && jjTriggers[5] == true)
	{obj.scriptedCollisions = false;}
	else {p.showText("@@Collect 5 Spheres first.");}
	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) {
		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;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}

class BlueSphere : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		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;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}

class YellowSphere : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.lightType = LIGHT::FLICKER;
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::BURGER].curAnim];
	anim.frameCount = 1;
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP sphere(0, 23*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[3] = true;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}

class GreenSphere : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		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;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}

class PurpleSphere : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		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;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);

		return true; 
	}
}

void onLevelReload()  {
	gem::restorePlayerGems();
	jjLocalPlayers[0].lives++;
	jjWaterLighting = WATERLIGHT::GLOBAL;
	buyammo == false;
	jjTriggers[9] = false;
  	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(54, 19, "Have you seen my\nLibrary Pass yet?"),
	Sign(146, 22, "What? You have a PASS?\nOh... well... Anyway..."),
	Sign(117, 37, "No sign of the\nFreezer I guess..."),
	Sign(132, 11, "No entrance to the\nbuildings with powerups..."),	
	Sign(189, 27, "The portrait upstairs belongs to Thrasymakhos,\nour ex leader who has gone missing lately."),
	Sign(171, 13, "Press P if you'd like to\nbuy Fusion Cannon for 50 Gems."),

};

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