Downloads containing mo4a_4-3.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, WeaponVMega::IceCloud::Weapon(), WeaponVMega::Voranj::Weapon(), WeaponVMega::Backfire::Weapon(), null, ArcaneWeapons::FusionCannon::Weapon(), null, ArcaneWeapons::MortarLauncher::Weapon()}); ///@MLLE-Generated
#include "MLLE-Include-1.5w.asc" ///@MLLE-Generated
#pragma require "mo4a_4-3.j2l" ///@MLLE-Generated
#include "ArcaneWeapon4.asc" ///@MLLE-Generated
#pragma require "ArcaneWeapon4.asc" ///@MLLE-Generated
#include "ArcaneWeapon9.asc" ///@MLLE-Generated
#pragma require "ArcaneWeapon9.asc" ///@MLLE-Generated
#include "WeaponVMega5.asc" ///@MLLE-Generated
#pragma require "WeaponVMega5.asc" ///@MLLE-Generated
#include "WeaponVMega8.asc" ///@MLLE-Generated
#pragma require "WeaponVMega8.asc" ///@MLLE-Generated
#include "WeaponVMega3.asc" ///@MLLE-Generated
#pragma require "WeaponVMega3.asc" ///@MLLE-Generated
#include "HH18savegems.asc"
#include "Jazz1Enemies v05.asc"
#include "Resize v11.asc"
#include "TrueColor v13.asc"
bool jump = false, coinchange = false, coinready = false;
int FireflyRed = 0, FireflyGreen = 0, FireflyBlu = 0, exit = 0;

bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
	if(jump == true)
    {canvas.drawSprite(100, 582, ANIM::PICKUPS, 33, jjGameTicks>>2, -1, SPRITE::PALSHIFT, 24);}
	if(coinchange == true && coinready == false)
    {canvas.drawSprite(150, 582, ANIM::PICKUPS, 37, jjGameTicks>>2, -1, SPRITE::BRIGHTNESS, 52);}
    canvas.drawSprite(20, 578, ANIM::PICKUPS, 0, jjGameTicks>>2, -1, SPRITE::NORMAL);
    canvas.drawString(30, 585, formatInt(exit%4, "1") + "/3", STRING::SMALL, STRING::NORMAL);
    return false;
}

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


void onLevelLoad() {
	gem::restorePlayerGems();
	jjObjectPresets[OBJECT::TUFTURT].behavior = Yeti;
	jjObjectPresets[OBJECT::TUFTURT].determineCurAnim(ANIM::TUFBOSS, 4);
	jjObjectPresets[OBJECT::TUFTURT].playerHandling = HANDLING::ENEMY;
	jjObjectPresets[OBJECT::TUFTURT].bulletHandling = HANDLING::HURTBYBULLET;
	jjObjectPresets[OBJECT::TUFTURT].energy = 3;
	jjWaterLighting = WATERLIGHT::GLOBAL;
	jjLevelName = ("@@@@@@@@@Mt. Everest");
	jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckpointWrapper;
	jjObjectPresets[OBJECT::SAVEPOST].deactivates = false;

	jjObjectPresets[OBJECT::FATCHICK].behavior = DemonGhost();
	jjObjectPresets[OBJECT::FATCHICK].scriptedCollisions = true;

	jjObjectPresets[OBJECT::GRASSPLATFORM].xSpeed = 5;
	jjObjectPresets[OBJECT::GRASSPLATFORM].scriptedCollisions = true;
	jjObjectPresets[OBJECT::GRASSPLATFORM].behavior = PlatformH;

	jjObjectPresets[OBJECT::SONICPLATFORM].xSpeed = 5;
	jjObjectPresets[OBJECT::SONICPLATFORM].scriptedCollisions = true;
	jjObjectPresets[OBJECT::SONICPLATFORM].behavior = FlyingSpring();	
	jjObjectPresets[OBJECT::SONICPLATFORM].determineCurAnim(ANIM::SPRING,6);	

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

	jjObjectPresets[OBJECT::PURPLEGEM].behavior = MovingPurpleGem();
	jjObjectPresets[OBJECT::PURPLEGEM].scriptedCollisions = true;

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

	Jazz1::MakeEnemy(OBJECT::SUCKER, Jazz1::Enemies::Holidaius_HandVertical).SetDirection(Jazz1::Directions::Right);
	Jazz1::MakeEnemy(OBJECT::RAVEN, Jazz1::Enemies::Holidaius_Devil).SetUsesJJ2StyleDeathAnimation(true).SetDeathSound(SOUND::BILSBOSS_THUNDER);
	Jazz1::MakeEnemy(OBJECT::FLOATSUCKER, Jazz1::Enemies::Holidaius_HandVertical).SetDirection(Jazz1::Directions::Left);
	Jazz1::MakeEnemy(OBJECT::SKELETON, Jazz1::Enemies::Megairbase_Doofusguard).SetUsesJJ2StyleDeathAnimation(true).SetBulletFireSound(SOUND::INTRO_SHOTGRN).SetBulletExplosionSound(SOUND::COMMON_GUNSM1);

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

	jjObjectPresets[OBJECT::WEENIE].behavior = BluSw();
	jjObjectPresets[OBJECT::WEENIE].scriptedCollisions = true;

	jjObjectPresets[OBJECT::LEMON].behavior = RedFF;
	jjObjectPresets[OBJECT::LEMON].scriptedCollisions = true;

	jjObjectPresets[OBJECT::BANANA].behavior = BluFF;
	jjObjectPresets[OBJECT::BANANA].scriptedCollisions = true;

	jjObjectPresets[OBJECT::TURTLESHELL].behavior = GreenFF;
	jjObjectPresets[OBJECT::TURTLESHELL].scriptedCollisions = true;

	jjObjectPresets[OBJECT::FASTFEET].behavior = FakeCoin();
	jjObjectPresets[OBJECT::FASTFEET].scriptedCollisions = true;

	jjObjectPresets[OBJECT::CHICKENLEG].behavior = WallJump();
	jjObjectPresets[OBJECT::CHICKENLEG].scriptedCollisions = true;

	jjObjectPresets[OBJECT::BOLLPLATFORM].behavior = Ice();	
	jjObjectPresets[OBJECT::BOLLPLATFORM].scriptedCollisions = true;

	jjObjectPresets[OBJECT::FRUITPLATFORM].behavior = IceR();	
	jjObjectPresets[OBJECT::FRUITPLATFORM].scriptedCollisions = true;

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

	jjObjectPresets[OBJECT::HAM].determineCurAnim(ANIM::PICKUPS, 78);
	jjObjectPresets[OBJECT::HAM].scriptedCollisions = true;	
	jjObjectPresets[OBJECT::HAM].playerHandling = HANDLING::PLAYERBULLET;	

	jjObjectPresets[OBJECT::MILK].energy = 100;
	jjObjectPresets[OBJECT::MILK].behavior = SpittingBubba;
	jjObjectPresets[OBJECT::MILK].points = 1000;
	jjObjectPresets[OBJECT::MILK].xSpeed = 1;
	jjObjectPresets[OBJECT::MILK].state = STATE::WALK;
	jjObjectPresets[OBJECT::MILK].deactivates = false;
	jjObjectPresets[OBJECT::MILK].scriptedCollisions = true;
	jjObjectPresets[OBJECT::MILK].isTarget = true;
	jjObjectPresets[OBJECT::MILK].playerHandling = HANDLING::ENEMY;

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

}

// Credit: from Stone Abbys by Bloody_Body
	jjOBJ@ spawnedObject;
void SpittingBubba(jjOBJ@ enemy) {
					if ( enemy.energy == 0)
					{enemy.state = STATE::KILL;
							p.activateBoss(false);}
	switch (enemy.state) {
		case STATE::WALK:
		enemy.behave(BEHAVIOR::WALKINGENEMY);
		enemy.determineCurAnim(ANIM::BUBBA, 5);	
										
			 if (enemy.findNearestPlayer(64*800) > -1 && ++enemy.counter > 36) enemy.state = STATE::IDLE;
				if (enemy.energy == 0)
					enemy.state = STATE::KILL;
			break;
			case STATE::IDLE:
			enemy.frameID = enemy.counter/5;
			enemy.determineCurFrame();
			
			switch (jjRandom() & 3) {
				
				case 0:  
				case 1:
				
					enemy.state = STATE::ACTION;
					enemy.determineCurAnim(ANIM::BUBBA, 0);
					enemy.counter = 0;
					break;
					
				case 2:
				
					enemy.state = STATE::WALK;
					enemy.determineCurAnim(ANIM::BUBBA, 5);
					enemy.counter = 0;
					
					break;
					
					}
			break;
			
			
		case STATE::ACTION:
		if (++enemy.counter < 36) {
			enemy.frameID = enemy.counter/5;
			enemy.determineCurFrame();
			enemy.direction = (jjLocalPlayers[0].xPos < enemy.xPos) ? -1 : 1;
				   if (enemy.justHit == 0)
			if (enemy.counter == 25 ) {
			//if difficulty is easy Bubba spits once per every "state::action", if normal- twice, it hard or turbo- three times.
		jjOBJ@ bullet = jjObjects[enemy.fireBullet(OBJECT::LIGHTNINGSHIELDBULLET)];
				bullet.determineCurAnim(ANIM::DEVAN, 0);
				bullet.killAnim = bullet.determineCurAnim(ANIM::AMMO, 3, false);
				bullet.playerHandling = HANDLING::ENEMYBULLET;
				bullet.state = STATE::FLY;
				bullet.xPos = enemy.xPos;
				bullet.yPos = bullet.yPos - 16; 
				if (p.curAnim - jjAnimSets[p.setID].firstAnim == RABBIT::HURT)
				{p.freeze(true);}
				bullet.animSpeed = 2;
				jjSample(enemy.xPos, enemy.yPos, SOUND::BUBBA_SNEEZE2);
		}

		

		} else {
			enemy.frameID = enemy.counter = 0;
			enemy.determineCurAnim(ANIM::BUBBA, 5);
			enemy.state = STATE::IDLE;
		}
		break;
	
			
		case STATE::FREEZE:
			if (enemy.freeze > 0) {
				enemy.draw();
				enemy.freeze -= 4;
			}
			if (enemy.freeze < 4) {
				enemy.unfreeze(0);
				enemy.state = enemy.oldState;
			}
			break; 
	
			
		case STATE::KILL:
			jjAddObject(OBJECT::CHICKENLEG, 4586, 1802);
			enemy.delete();
			break;
			}
				   if (enemy.justHit == 0)
		{    jjDrawSpriteFromCurFrame(enemy.xPos, enemy.yPos, enemy.curFrame, enemy.direction, SPRITE::PALSHIFT, 40);}
else 
    jjDrawSpriteFromCurFrame(enemy.xPos, enemy.yPos, enemy.curFrame, enemy.direction, SPRITE::BRIGHTNESS, 255);
		
		
	}

class DemonGhost: jjBEHAVIORINTERFACE {	

	void onBehave(jjOBJ@ obj) {

			obj.behave(BEHAVIOR::WALKINGENEMY, false);
			obj.bulletHandling = HANDLING::IGNOREBULLET;
			obj.playerHandling = HANDLING::SPECIAL;
			obj.determineCurAnim(ANIM::DEMON, 2);
			jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TRANSLUCENTSINGLEHUE, 72);
			obj.xSpeed = 3*obj.direction;
			if (jjMaskedVLine(obj.xSpeed > 0 ? obj.xPos + 16 : obj.xPos - 16, obj.yPos, 1))
			{obj.direction = obj.xSpeed = -obj.xSpeed;}
	}

	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ p, int force) {		
		if(p !is null && force >= -1)
			p.hurt();
		return true;
	}
}


class MovingPurpleGem : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.lightType = LIGHT::LASER;
		obj.light = 200;
		++obj.counter;
		obj.deactivates = false;
		if(obj.xPos < 110*32 && obj.findNearestPlayer(300*300) > -1)
		{obj.xPos = obj.xPos + 8;} 
		if((obj.xPos > 110*32 && obj.findNearestPlayer(300*300) > -1) || p.xPos > 116*32)
		{obj.xPos = 360; obj.yPos = 46*32;} 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		obj.draw();
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		p.gems[GEM::PURPLE] = 1; 
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PICKUP1, 1000);

		return true;
	}
}

class PlatinCoin : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		if(p.coins >= 3)
		{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		obj.determineCurAnim(ANIM::PICKUPS, 84);  
		obj.light = 100;
		obj.lightType = LIGHT::LASER;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -8);
}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		p.coins += 1; 
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_COIN, 1000);
		jump = false;
		jjAddObject(OBJECT::CHICKENLEG, 6154, 1834);
		return true;
	}
}

class FreezeJazz: jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.determineCurAnim(ANIM::AMMO, 8);

		obj.behave(BEHAVIOR::PICKUP, false);
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::INVISIBLE);	
	}

	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
	if(bullet is null) {
		play.freeze(true);
		play.hurt();
		obj.behave(BEHAVIOR::EXPLOSION2);
		obj.delete();
	}	
	return true;
	}
}


class Ice: jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::INACTIVE, true);
		obj.draw();
		jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::BOLLPLATFORM].curAnim];
		anim.frameCount = 1;
		jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
		jjPIXELMAP plat(0, 62*32, 1*32, 2*32, 5);
		plat.save(frame);
		frame.hotSpotX = -12;
		frame.hotSpotY = -17;
	}	
	
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		if (bullet is null && (force == -1 || force == 1)) {
			jjAddObject(OBJECT::ICEAMMO3, obj.xPos, obj.yPos-40);
			jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ICECRUSH, 1000);
			obj.delete();
			}
		if(bullet !is null)
			{obj.bulletHandling == HANDLING::DETECTBULLET;
			obj.causesRicochet = true;}
		return true;
	}
}

class IceR: jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::INACTIVE, true);
		obj.draw();
		obj.beSolid();
		jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::FRUITPLATFORM].curAnim];
		anim.frameCount = 1;
		jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
		jjPIXELMAP plat(0, 60*32, 1*32, 2*32, 5);
		plat.save(frame);
		frame.hotSpotX = -12;
		frame.hotSpotY = -17;
		
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		if (bullet is null && (force == -1 || force == 1)) {
			jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ICECRUSH, 1000);
			jjAddObject(OBJECT::ICEAMMO3, obj.xPos+40, obj.yPos+40);
			obj.behavior = BEHAVIOR::BURNING;
			}
		if(bullet !is null)
			{obj.bulletHandling == HANDLING::DETECTBULLET;
			obj.causesRicochet = true;}
		return true;
	}
}


class FlyingSpring : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		float YSpeed = 22;
		obj.behave(BEHAVIOR::BUTTERFLY,false);
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 40);

		jjPARTICLE@ particle = jjAddParticle(PARTICLE::STAR);
		particle.snow.frame = jjRandom() & 987;
		particle.xPos = obj.xPos;
		particle.yPos = obj.yPos+14;
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int) {
		if(bullet !is null) {
		obj.causesRicochet = true;
		}
		else
		player.direction = player.ySpeed = -42;
		obj.scriptedCollisions = true;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_SPRING1);
		return true;
	}
}

void PlatformH (jjOBJ@ obj) {

	switch (obj.state) {
		case STATE::START:
			obj.light = 10;
			obj.lightType = LIGHT::NORMAL;
			obj.direction = obj.xSpeed = -2;
			obj.determineCurAnim(ANIM::DESTSCEN, 4);   
			obj.determineCurFrame();
			obj.playerHandling = HANDLING::SPECIAL;
			obj.state = STATE::FLY;
			obj.beSolid();
		case STATE::FLY:
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.beSolid();
			obj.deactivates = false;
			if (jjMaskedVLine(obj.xSpeed > 0 ? obj.xPos + 16 : obj.xPos - 16, obj.yPos, 1)) {
				obj.direction = obj.xSpeed = -obj.xSpeed;
			}
			jjDrawTile(obj.xPos - 17, obj.yPos - 32, 527);

			break;

	}
}

class GreenSw: jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PLATFORM, false);
		obj.determineCurAnim(ANIM::SONICPLAT, 0);
		obj.determineCurFrame();
		++obj.counter; 
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -56);
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		if(p.keyFire){
		FireflyGreen = jjGameTicks + 26*61;
		p.timerStart(1500);		
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
		}	
		return true; 
	}
}


class RedSw: jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PLATFORM, false);
		obj.determineCurAnim(ANIM::SONICPLAT, 0);
		obj.determineCurFrame();
		obj.frameID = ++obj.counter/6;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -48);
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		if(p.keyFire){
		FireflyRed = jjGameTicks + 26*61;
		p.timerStart(1500);		
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
		}	
		return true; 
	}
}

class BluSw : jjBEHAVIORINTERFACE {

	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PLATFORM, false);
		obj.determineCurAnim(ANIM::SONICPLAT, 0);
		obj.determineCurFrame();
		++obj.counter; 
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -40);
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		if(p.keyFire){
		FireflyBlu = jjGameTicks + 26*61;
		p.timerStart(1500);		
		jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
		}	
		return true; 
	}
}

void RedFF (jjOBJ@ obj) {
			obj.determineCurAnim(ANIM::BEEBOY, 0);   
			obj.determineCurFrame();
			obj.frameID = ++obj.counter/6;
	switch (obj.state) {
		case STATE::START:
			obj.direction = obj.ySpeed = 2;
			obj.state = STATE::ACTION;
		case STATE::ACTION:
			obj.energy = 1;
			if(jjKey[0x57] && FireflyRed > jjGameTicks) {
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = -2;}
			if(jjKey[0x53] && FireflyRed > jjGameTicks) {
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 2;}
			if(jjKey[0x41] && FireflyRed > jjGameTicks){
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.direction = obj.xSpeed = -2;}
			if(jjKey[0x44] && FireflyRed > jjGameTicks){
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.direction = obj.xSpeed = 2;}
			if((jjKey[0x30] || jjKey[0x60]) && FireflyRed > jjGameTicks)
			{obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 0.3;
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.xSpeed = 0.3*obj.direction;
			p.cameraFreeze(obj.xPos, obj.yPos, true, true);
			jjOBJ@ o = jjObjects[obj.fireBullet(OBJECT::FIREBALLBULLET)];
					o.behavior = BEHAVIOR::BULLET;
					o.determineCurAnim(ANIM::AMMO, 14);
					o.killAnim = jjObjectPresets[OBJECT::BOUNCERBULLET].killAnim;
					o.playerHandling = HANDLING::PLAYERBULLET;
					o.bulletHandling = HANDLING::HURTBYBULLET;
					o.lightType = LIGHT::POINT;
					o.xPos = obj.xPos +6*obj.direction;
					o.xAcc = .9;
					int xs = (jjRandom() & 9);
					o.xSpeed = 1.2;
					int ys = (jjRandom() & 6);
					o.ySpeed = ys - 3;}
			else if(FireflyRed > jjGameTicks) {
			p.xSpeed = 0;
			p.cameraFreeze(obj.xPos, obj.yPos, true, true);
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 0.3;
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.xSpeed = 0.3*obj.direction;
			p.idle = 0;
}
		if (jjMaskedVLine(obj.xSpeed > 0 ? obj.xPos + 16 : obj.xPos - 16, obj.yPos, 1) || FireflyRed == jjGameTicks +1*61 || jjKey[0x5A]) {
			obj.behavior = BEHAVIOR::EXPLOSION2;
			obj.delete();
			p.cameraUnfreeze();
			FireflyRed = 0;
			p.timerStop();
			p.yPos = p.yPos -40;
			}
			obj.deactivates = false;
			break;
	}

			jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::SINGLEHUE, 40);
}

void GreenFF (jjOBJ@ obj) {
			obj.determineCurAnim(ANIM::BEEBOY, 0);   
			obj.determineCurFrame();
			obj.frameID = ++obj.counter/6;

	switch (obj.state) {
		case STATE::START:
			obj.state = STATE::ACTION;
			obj.direction = obj.ySpeed = -2;	
		case STATE::ACTION:
			obj.energy = 1;
			if(jjKey[0x57] && FireflyGreen > jjGameTicks) {
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = -2;}
			if(jjKey[0x53] && FireflyGreen > jjGameTicks) {
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 2;}
			if(jjKey[0x41] && FireflyGreen > jjGameTicks){
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.direction = obj.xSpeed = -2;}
			if(jjKey[0x44] && FireflyGreen > jjGameTicks){
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.direction = obj.xSpeed = 2;}
			if((jjKey[0x30] || jjKey[0x60]) && FireflyGreen > jjGameTicks)
			{obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 0.3;
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.xSpeed = -0.3*obj.direction;
			p.cameraFreeze(obj.xPos, obj.yPos, true, true);
			jjOBJ@ o = jjObjects[obj.fireBullet(OBJECT::FIREBALLBULLET)];
					o.behavior = BEHAVIOR::BULLET;
					o.determineCurAnim(ANIM::AMMO, 11);
					o.killAnim = jjObjectPresets[OBJECT::BOUNCERBULLET].killAnim;
					o.playerHandling = HANDLING::PLAYERBULLET;
					o.bulletHandling = HANDLING::HURTBYBULLET;
					o.lightType = LIGHT::POINT;
					o.xPos = obj.xPos +6*obj.direction;
					o.xAcc = .9;
					int xs = (jjRandom() & 9);
					o.xSpeed = 1.2;
					int ys = (jjRandom() & 6);
					o.ySpeed = ys - 3;}
			else if(FireflyGreen > jjGameTicks) {
			p.xSpeed = 0;
			p.cameraFreeze(obj.xPos, obj.yPos, true, true);
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 0.3*obj.direction;
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.xSpeed = 0.3*obj.direction;
			p.idle = 0;
}
		if (jjMaskedVLine(obj.xSpeed > 0 ? obj.xPos + 16 : obj.xPos - 16, obj.yPos, 1) || FireflyGreen == jjGameTicks +1*61 || jjKey[0x5A]) {
			obj.behavior = BEHAVIOR::EXPLOSION2;
			obj.delete();
			p.cameraUnfreeze();
			FireflyGreen = 0;
			p.timerStop();
			p.yPos = p.yPos -40;
			}

			obj.deactivates = false;
			break;
	}
			{jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::SINGLEHUE, 16);}

}

void BluFF (jjOBJ@ obj) {
			obj.determineCurAnim(ANIM::BEEBOY, 0);   
			obj.determineCurFrame();
			obj.determineCurFrame();
			obj.frameID = ++obj.counter/6;
	switch (obj.state) {
		case STATE::START:
			obj.direction = obj.ySpeed = 2;
			obj.state = STATE::ACTION;
		case STATE::ACTION:
			obj.energy = 1;
			if(jjKey[0x57] && FireflyBlu > jjGameTicks) {
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = -2;}
			if(jjKey[0x53] && FireflyBlu > jjGameTicks) {
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 2;}
			if(jjKey[0x41] && FireflyBlu > jjGameTicks){
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.direction = obj.xSpeed = -2;}
			if(jjKey[0x44] && FireflyBlu > jjGameTicks){
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.direction = obj.xSpeed = 2;}
			if((jjKey[0x30] || jjKey[0x60]) && FireflyBlu > jjGameTicks)
			{obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 0.3;
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.xSpeed = 0.3*obj.direction;
			p.cameraFreeze(obj.xPos, obj.yPos, true, true);
			jjOBJ@ o = jjObjects[obj.fireBullet(OBJECT::FIREBALLBULLET)];
					o.behavior = BEHAVIOR::BULLET;
					o.determineCurAnim(ANIM::DEVILDEVAN, 17);
					o.killAnim = jjObjectPresets[OBJECT::BOUNCERBULLET].killAnim;
					o.playerHandling = HANDLING::PLAYERBULLET;
					o.bulletHandling = HANDLING::HURTBYBULLET;
					o.lightType = LIGHT::POINT;
					o.xPos = obj.xPos +6*obj.direction;
					o.xAcc = .9;
					int xs = (jjRandom() & 9);
					o.xSpeed = 1.2;
					int ys = (jjRandom() & 6);
					o.ySpeed = ys - 3;}
			else if(FireflyBlu > jjGameTicks) {
			p.xSpeed = 0;
			p.cameraFreeze(obj.xPos, obj.yPos, true, true);
			obj.yPos = obj.yPos + obj.ySpeed;
			obj.ySpeed = 0.3;
			obj.xPos = obj.xPos + obj.xSpeed;
			obj.xSpeed = 0.3*obj.direction;
			p.idle = 0;
}
		if (jjMaskedVLine(obj.xSpeed > 0 ? obj.xPos + 16 : obj.xPos - 16, obj.yPos, 1) || FireflyBlu == jjGameTicks +1*61 || jjKey[0x5A]) {
			obj.behavior = BEHAVIOR::EXPLOSION2;
			obj.delete();
			p.cameraUnfreeze();
			FireflyBlu = 0;
			p.timerStop();
			p.yPos = p.yPos -40;
			}
			obj.deactivates = false;
			break;
	}

			{jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::SINGLEHUE, 32);}

}

void onFunction0(jjPLAYER@ p)
{if(jump == false && p.coins < 3)
		p.activateBoss();
		p.boss = jjAddObject(OBJECT::MILK, 139*32, 53*32);
		jjOBJ@ boss = jjObjects[p.boss];
		p.cameraFreeze(137*32, 53*32, true, false);
		jjEnabledASFunctions[0] = false;
}

void onFunction4(jjPLAYER@ p)
{p.cameraUnfreeze();}

void onFunction5(jjPLAYER@ p) {
	if(jjTriggers[1] == true && jjTriggers[2] == true && jjTriggers[3] == true && jjTriggers[4] == false) {
		jjTriggers[4]=true;
		jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2, 1000);}
	else
		{p.showText("@@To open the gate@three switches must be activated.");}
}

void onFunction6(jjPLAYER@ p) {
		if(coinchange == true) {
		jjAddObject(OBJECT::GOLDCOIN, 6128, 1932);
		p.showText("@@Yay!");
		jjSample(p.xPos, p.yPos, SOUND::WITCH_MAGIC, 3000);
		coinready = true;
		coinchange = false;}
}


class FakeCoin : jjBEHAVIORINTERFACE {
	
	void onBehave(jjOBJ@ obj) {
		if(p.coins == 3)
		{obj.delete();}
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.determineCurAnim(ANIM::PICKUPS, 37);
		++obj.counter; 
		obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::BRIGHTNESS, 52);}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
		coinchange = true;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_COIN, 1000);

		return true;
	}
}

bool walltext = false;

class WallJump : jjBEHAVIORINTERFACE {
	
	void onBehave(jjOBJ@ obj) {
		if(jump==false) {
		obj.light = 100;
		obj.lightType = LIGHT::LASER;
		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);}
		if(jump==true) {obj.delete();}
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { 
	if(p.xPos < 167*32) {
		jump = true;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PICKUP1, 1000);
		}
	if(p.xPos > 167*32) {	
		jump = false;
		if(walltext == false)
		{p.showText("@@I gave the shoe to return the favour.@I'm not looking for trouble.");}
		walltext = true;
	}
		return true;
	}
}


void Yeti(jjOBJ@ enemy) {
	switch (enemy.state) {
		case STATE::START:
		enemy.state = STATE::ACTION;
			break;
		case STATE::ACTION:

		enemy.putOnGround(false);
				if (jjLocalPlayers[0].xPos < enemy.xPos) enemy.direction = -1;
				else enemy.direction = 1;
			if ((jjRandom() & 1) == 0) {
				enemy.state = STATE::ATTACK;
				enemy.frameID = 0;
				enemy.counter = 0;
				jjSample(enemy.xPos, enemy.yPos, SOUND::COMMON_SWISH8);
			} else {
				if (++enemy.counter > 6) {
					enemy.counter = 0;
					if (++enemy.frameID >= 11)
						enemy.frameID = 0;

				}
			}
			break;
		case STATE::ATTACK:

			if (++enemy.counter > 4) {
				enemy.counter = 0;
				if (++enemy.frameID >= 11) {
				jjObjectPresets[OBJECT::TUFTURT].determineCurAnim(ANIM::TUFBOSS, 2);
					enemy.state = STATE::ACTION;

				} else if (enemy.frameID >= 0 && enemy.frameID <= 11) {

					enemy.determineCurAnim(ANIM::TUFBOSS, 1);
					jjOBJ@ obj = jjObjects[enemy.fireBullet(OBJECT::BLASTERBULLET)];
					obj.behavior = BEHAVIOR::BULLET;
					obj.determineCurAnim(ANIM::AMMO, 30);
					obj.killAnim = jjObjectPresets[OBJECT::BOUNCERBULLET].killAnim;
					obj.playerHandling = HANDLING::ENEMYBULLET;
					obj.bulletHandling = HANDLING::IGNOREBULLET;
					obj.lightType = LIGHT::POINT;
					obj.xAcc = .9;
					int xs = (jjRandom() & 9);
					obj.xSpeed = 1.2;
					int ys = (jjRandom() & 6);
					obj.ySpeed = ys - 3;
					obj.counterEnd = 55;
					if(p.xPos == obj.xPos && p.yPos == obj.yPos)
					{p.freeze();}
				}
				enemy.determineCurFrame();
			}
			break;
		case STATE::DEACTIVATE:
		case STATE::KILL:
			enemy.delete();
			break;
}
	if(enemy.justHit == 0)
	{jjDrawSpriteFromCurFrame(enemy.xPos, enemy.yPos, enemy.curFrame, enemy.direction, SPRITE::PALSHIFT, 24);}
else enemy.draw();
	
}

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

void onLevelReload() {
	MLLE::Palette.apply();
	gem::restorePlayerGems();
	jjLocalPlayers[0].lives++;
	jjWaterLighting = WATERLIGHT::GLOBAL;
  	for (uint i = 0; i < 32; ++i)
	  	jjTriggers[i] = SavedTriggers[i];
	if(p.coins >= 3)
		{jjAddObject(OBJECT::CHICKENLEG, 6154, 1834);}
	if(jump == false && p.coins < 3)
		{jjEnabledASFunctions[0] = true;}
}

class PlayerWallJumpProperties {
  uint Stage = 0;
  bool FacingRight;
  int LastSlidingTick = 0;
  bool PressingJumpOneTickAgo = false;
  PlayerWallJumpProperties(){}
}
array<PlayerWallJumpProperties> PlayerWallJumpPropertiesArray(jjLocalPlayerCount);

bool doorunblocked = false, startrush = false, readytorush = false, control = true;

void onPlayer(jjPLAYER@ play) {
	gem::trackPlayerGems(p);
	gem::upgradeHealth(p);
	if(p.coins == 0)
	{p.coins += 2;}
	p.lightType = LIGHT::NONE;

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

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

		if(p.idle > 100 && p.xPos < 223*32)
		{p.cameraUnfreeze(true);
		control=true;}
		else if (p.idle > 5 && (p.keyLeft || p.keyRight || p.keyJump || p.keyFire))
		{p.cameraUnfreeze(true);
		control=true;}

	exit = 0;
	if(jjTriggers[1] == true)
		{exit += 1;}
	if(jjTriggers[2] == true)
		{exit += 1;}
	if(jjTriggers[3] == true)
		{exit += 1;}


	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::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(p.yPos > jjWaterLevel)
	{play.hurt();}

 		if (doorunblocked == false && jjTriggers[1] == true && jjTriggers[2] == true && jjTriggers[3] == true) {
		p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Switches unlocked.");
		p.cameraFreeze(69*32 ,57*32, true, false);
		doorunblocked = true;}

	if(p.coins<3 && p.xPos < 11*32 && p.xPos > 5*32 && p.yPos > 62*32)
	{p.testForCoins(3);
	p.yPos = 58*32;
	p.xPos = 6*32;}
	if(p.coins>=3 && p.xPos < 11*32 && p.xPos > 5*32 && p.yPos > 62*32)
	{jjNxt(false, true);}

	if(p.keyUp && jjTriggers[1] == false && p.yPos >6*32 && p.yPos <8*32 && p.xPos > 123*32 && p.xPos <126*32)
		{jjTriggers[1] = true;
		jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2);}
	if(p.keyUp && jjTriggers[2] == false && p.yPos >20*32 && p.xPos > 51*32 && p.xPos <53*32)
		{jjTriggers[2] = true;
		jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2);}
	if(p.keyUp && jjTriggers[3] == false && p.yPos >30*32 && p.yPos < 32*32 && p.xPos >203*32)
		{jjTriggers[3] = true;
		jjSample(p.xPos, p.yPos, SOUND::INTRO_MONSTER2);}


	if (FireflyRed > jjGameTicks || FireflyGreen > jjGameTicks || FireflyBlu > jjGameTicks) {
	p.keyRight = p.keyLeft = p.keyJump = p.keyRun = p.keyFire = false;
	}

// Wall Jump snippet by VioletCLM
PlayerWallJumpProperties@ wallJumpProperties = @PlayerWallJumpPropertiesArray[play.localPlayerID];
  const bool pressingJump = play.keyJump;
  if (jump == true &&
    pressingJump &&
    !wallJumpProperties.PressingJumpOneTickAgo &&
    wallJumpProperties.Stage < 2 &&
    (jjGameTicks - wallJumpProperties.LastSlidingTick) < 10 //10 = number of ticks after technically letting go of the wall during which you can still start the jump. Higher numbers are more generous.
  ) {
    wallJumpProperties.Stage = 2;
    jjSample(play.xPos, play.yPos, SOUND::COMMON_JUMP);
    play.ySpeed = play.jumpStrength;
    play.helicopter = 0;
    play.doubleJumpCount = 0;
  } else if (wallJumpProperties.Stage > 1) {
    if (++wallJumpProperties.Stage == 20)
      wallJumpProperties.Stage = 0;
    else {
      play.keyRight = wallJumpProperties.FacingRight;
      play.keyLeft = !wallJumpProperties.FacingRight;
      play.keyJump = true;
      play.helicopter = 0;
    }
  } else {
    const auto anim = play.curAnim - jjAnimSets[play.setID];
    const int xPosToTest = int(play.xPos) + (play.keyRight ? 13 : -13);
    uint16 tileIDToTest;
    if (jump == true &&
      (anim == RABBIT::FALL || anim == RABBIT::HELICOPTER) &&
      (play.keyRight || play.keyLeft) &&
      (jjMaskedPixel(xPosToTest, int(play.yPos)-8) && jjMaskedPixel(xPosToTest, int(play.yPos)+8))
      //put any additional conditions, e.g. events or tiles you can't slide on, here.
    ) {
      play.helicopter = 0;
      wallJumpProperties.Stage = 1;
      wallJumpProperties.FacingRight = play.keyLeft;
      play.ySpeed = 0.25; //modify as desired if you don't like this sliding speed
      play.keyJump = false;
      wallJumpProperties.LastSlidingTick = jjGameTicks;
    } else if (wallJumpProperties.Stage == 1)
      wallJumpProperties.Stage = 0;
  }
  if (play.invisibility = wallJumpProperties.Stage == 1) { //this works for SP, but for MP you'd probably instead want to give everyone SPRITE::INVISIBLE all the time and draw non-local players in an onMain loop.
    if (play.blink == 0 || ((jjRenderFrame >> 2) & 1) == 1) {
      const int vDir = !play.antiGrav ? 1 : -1;
      jjDrawRotatedSprite(play.xPos - play.direction * 3, play.yPos, play.setID, RABBIT::SKID1, jjGameTicks>>4, 256 * play.direction * vDir, -play.direction, vDir, SPRITE::PLAYER, play.playerID);
    }
  }
  wallJumpProperties.PressingJumpOneTickAgo = pressingJump;
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
	return MLLE::WeaponHook.drawAmmo(player, canvas);
}

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(17, 59, "The demons have destroyed the last token.\n Maybe the Alchemist can help you..."),
	Sign(144, 55, "Try to avoid Demon Ghosts.\nThey are invincible."),
	Sign(193, 60, "Bring me something coppery I'll turn it\ninto anything! But I'll take your shoe."),
	Sign(160, 31, "Psst! Looking for a token?\nYou won't find any!"),
	Sign(96, 48, "Neither stand on thin ice\nnor try swimming. You might freeze."),
	Sign(36, 58, "With some pink shoes on\nyou can wall jump."),


};

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