Downloads containing primpXmas.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Jingle JumbleFeatured Download Primpy Single player 9.3 Download file

File preview

const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
#include "MLLE-Include-1.6.asc" ///@MLLE-Generated
#pragma require "primpXmas-MLLE-Data-2.j2l" ///@MLLE-Generated
#pragma require "primpXmas-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "Hellfire.j2t" ///@MLLE-Generated
#pragma require "primpXmas.j2l" ///@MLLE-Generated
#pragma require "SExmas.j2a"
#pragma require "xmasBoss.pal"
#include "PickupTracker.asc"
#include "HH17Enemies.asc"

const float PI = 3.141592f;
uint fallLoopCount, teleCooldown, firstRemainingGiftID, secondRemainingGiftID, thirdRemainingGiftID;
string plrCharacterStr;
bool bossActivatedOnce, isBossActive, cutscene, blasterPoweredUp, noTeleArea;
float bossPalOpacity;
jjPAL bossPalette;
array<jjLAYER@> layers, bossLayers;
array<bool> SavedTriggers(32, false);

class Collectible {
	int x, y;
	bool found = false;
	Collectible(){}
	Collectible(int newX, int newY, bool newFound) {
		x = newX;
		y = newY;
		found = newFound;
	}
}
array<Collectible> collectibleData;

array<string> fallLoopMessages = {
		"#|||~I can't wait to meet Santa...",
		"...",
		"#|||~Santa sure lives deep underground...",
		"#|||~Wait... underground? Oh no...",
		"#|||~This isn't Santa... it's Satan!",
		"#||||~[???]@@Sure is! Name's Bilsy!",
		"#||||~[Bilsy]@@Thanks for bringing Santa's lost gifts@right to me!",
		"#|||~You're not stealing these presents@on my watch!"
};

array<string> postBossMessages = {
		"#||||~[Bilsy]@@Hold it! This fight was fun but@you're misunderstanding the situation.",
		"#||||~[Bilsy]@@I was assigned to@wait for you to recover the gifts@and deliver them to Santa.",
		"#|||~Santa employs demons to aid him in his work?",
		"#||||~[Bilsy]@@How else do you think he could manage to@give gifts to everyone on every planet?",
		"#||||~[Bilsy]@@Also, notice the hat and cape I'm wearing?@He makes us wear these silly work uniforms...",
		"#|||~When you put it that way, it makes@more sense... Sorry for earlier.",
		"#||||~[Bilsy]@@No biggie.@Anyhoo, thanks for gathering the lost gifts.",
		"#||||~[Bilsy]@@Toodles! And happy holidays!",
};

array<string> getNPCMessages(float x, float y)
{ 
	if (x == 4527 && y == 3471) return {
		"Hi there, " + plrCharacterStr + "!@",
		"Thank you for agreeing to help Santa with@"
		"finding his lost gifts.@",
		"When you find at least " + PickupTracker::minAmount + " gifts,@"
		"the hole to your left will open.@",
		"Jump in it and deliver the gifts.@",
		"Good luck!"
		};
	if (x == 335 && y == 1039) return { 
		"Do you think Santa is hot?@",
		"Just kidding... Hahaha..."}; 
	if (x == 2607 && y == 2063) return { 
		"Santa? Really? He isn't real.@",
		"Science will bring my presents."};
	if (x == 5295 && y == 3119) return { 
		"The roundy, floaty, glowy gifts are Santa's.@",
		"The gifts that look like this one are for you!"};
	if (x == 3119 && y == 5231) return { 
		"Brrr... It's awfully cold in this cave...@",
		"Icicles have started growing in a place@"
		"where no rabbit should have icicles..."};
	if (x == 7151 && y == 5583) return { 
		"When you eat tons of sweets,@"
		"you become super strong.@",
		"When I eat tons of sweets,@"
		"I get a tummy ache.@",
		"Life is so unfair."};
	if (x == 7951 && y == 719) return { 
		"I just ate some frozen chicken with my@reindeer girlfriend. Yum." };
	if (x == 2991 && y == 687) return { "Pepperoni secret." };
	return { "" };
}

float getPickupY(const jjOBJ@ obj) {
	bool floating = obj.state == STATE::FLOAT || obj.behavior != BEHAVIOR::PICKUP;
	int arg = (((obj.objectID << 3) + jjGameTicks) << (floating && obj.yPos > jjWaterLevel ? 1 : 4)) + (floating ? int(obj.xPos) << 4 : 0);
	return obj.yPos + jjSin(arg) * 0.5f;
}

float getAngle(int x1, int x2, int y1, int y2) {
	return atan2(y2 - y1, x2 - x1);
}

void drawArrow(jjPLAYER@ player, Collectible collectible, uint8 color) {
	
	float angle = getAngle(int(player.xPos), collectible.x * 32 + 15, int(player.yPos), collectible.y * 32 + 15);
	jjDrawRotatedSpriteFromCurFrame(
						player.xPos + cos(angle) * 32,
						player.yPos + sin(angle) * 32,
						jjAnimations[jjAnimSets[ANIM::FLAG]], int(-(angle / (2 * PI)) * 1024 - 55), 1, 1, SPRITE::SINGLEHUE, color, -1);
}

void applyBossPalette() {	
	if (bossPalOpacity < 1.f && cutscene) {
		jjPalette.reset();
		jjPalette.copyFrom(1, 254, 1, bossPalette, bossPalOpacity);
		jjPalette.apply();
		if (!bossActivatedOnce) {
			if (jjGameTicks % 20 == 0)
				bossPalOpacity += 0.01f;
		}
		else {
			if (jjGameTicks % 2 == 0)
				bossPalOpacity += 0.01f;
		}
	}
}

void applyBossLight(jjPLAYER@ player) {
	if (player.lighting > 10 && cutscene) {
		if (!bossActivatedOnce) {
			if (jjGameTicks % 70 == 0)
				player.lighting = player.lighting - 1;
		}
		else {
			if (jjGameTicks % 10 == 0)
				player.lighting = player.lighting - 1;
		}
	}
}

string getPlrCharacterStr() { return jjPlayers[0].charCurr == CHAR::JAZZ ? "Jazz" : (jjPlayers[0].charCurr == CHAR::SPAZ ? "Spaz" : (jjPlayers[0].charCurr == CHAR::LORI ? "Lori" : "little critter")); }

void CheckpointWrapper(jjOBJ@ obj) {
	if (obj.state == STATE::STOP) {
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame);
	} else if (obj.state == STATE::DEACTIVATE) {
		obj.deactivate();
	} else {
		obj.behave(BEHAVIOR::CHECKPOINT);
		if (obj.state == STATE::DONE) {
			obj.state = STATE::STOP;
		for (uint i = 0; i < 32; ++i)
			SavedTriggers[i] = jjTriggers[i];
		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;
        }
      }
    }
  }
}

void findRemainingGifts() {
	uint8 amount = 0;
	for (uint i = 0; i < collectibleData.length(); ++i) {
		if (collectibleData[i].found == false) {
			if (amount == 0)
				firstRemainingGiftID = i;
			else if (amount == 1)
				secondRemainingGiftID = i;
			else
				thirdRemainingGiftID = i;
			amount++;
		}
		if (amount == 3)
			break;
	}
}

void markCollectibleAsFound(int xOrg, int yOrg) {
	for (uint i = 0; i < collectibleData.length(); ++i) {
		if (collectibleData[i].x == xOrg && collectibleData[i].y == yOrg) {
			collectibleData[i].found = true;
			return;
		}
	}
}

void processHellbats() {
	for (uint i = 0; i < 5; i++) {
		jjANIMATION@ animBat = jjAnimations[jjAnimSets[ANIM::BAT] + i];
		for (uint j = 0; j < animBat.frameCount; j++) {
			jjANIMFRAME@ frame = jjAnimFrames[animBat + j];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 88 - 57 && sprite[x,y] <= 94 - 57) sprite[x,y] -= 8;
				}
			}
			sprite.save(frame);
		}
	}
}

void processIcebats() {
	for (uint i = 0; i < 5; i++) {
		jjANIMATION@ animBat = jjAnimations[jjAnimSets[ANIM::BAT] + i];
		for (uint j = 0; j < animBat.frameCount; j++) {
			jjANIMFRAME@ frame = jjAnimFrames[animBat + j];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 88 - 65 && sprite[x,y] <= 94 - 65) sprite[x,y] += 8;
				}
			}
			sprite.save(frame);
		}
	}
}

void GiftPickup() {
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::LEMON].curAnim];
	anim.frameCount = 1;
	
	jjAnimSets[ANIM::CUSTOM[1]].load(2, "SExmas.j2a");
	
	jjObjectPresets[OBJECT::LEMON].determineCurAnim(ANIM::CUSTOM[1], 0);
	jjObjectPresets[OBJECT::LEMON].points = 1000;
	jjObjectPresets[OBJECT::LEMON].scriptedCollisions = true;
	jjObjectPresets[OBJECT::LEMON].behavior = CustomPickup();
}

class CustomPickup : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
		if(obj.state == STATE::FLOATFALL)
			obj.state = STATE::FLOAT;
		obj.yPos = getPickupY(obj);
		if (obj.var[0] == 0)
			obj.var[0] = 1 + jjRandom() % 3;
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ player, int) {
		obj.behavior = BEHAVIOR::EXPLOSION2; 
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_HIBELL, 63);
		player.gems[GEM::RED] = player.gems[GEM::RED] + 1;
		jjEventSet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0);
		markCollectibleAsFound(int((obj.xOrg - 15) / 32), int((obj.yOrg - 15) / 32));
		return true;
	}
	void onDraw(jjOBJ@ obj) {
		jjDrawSpriteFromCurFrame(obj.xPos + 2, obj.yPos + 2, obj.curFrame, obj.direction, SPRITE::SHADOW, 0, 5); 
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame + obj.var[0] - 1, obj.direction, SPRITE::NORMAL);
	}
}

class NPC : jjBEHAVIORINTERFACE {
	bool debounce = true;
	float lastXPos = -1.f, lastYPos = -1.f;
	
	void onBehave(jjOBJ@ obj) {
		int drawParam = ((int(obj.xPos) + int(obj.yPos)) % 5) * 8 + 16;
		jjDrawSprite((obj.direction == -1) ? (obj.xPos + 15) : (obj.xPos - 15), obj.yPos, ANIM::PLUS_WARP, 0, jjGameTicks >> 3, obj.direction, SPRITE::SINGLEHUE, drawParam);
		jjDrawSprite((obj.direction == -1) ? (obj.xPos + 15) : (obj.xPos - 15), obj.yPos, ANIM::PLUS_WARP, 1, jjGameTicks >> 3, obj.direction, SPRITE::NORMAL);
		
		int playerID_direction = obj.findNearestPlayer(100000);
		if (playerID_direction >= 0)
		{
			if (obj.xPos > jjPlayers[playerID_direction].xPos)
				obj.direction = -1;
			else obj.direction = 1;
		}
		
		// NPCs should be at least 2 tiles away from one another, otherwise expect your ears to bleed.
		int playerID_collision = obj.findNearestPlayer(2000);
		if (playerID_collision >= 0)
		{
			jjPLAYER@ player = jjPlayers[playerID_direction];
			if (obj.var[0] == 0 || (debounce == true && (obj.xPos != lastXPos || obj.yPos != lastYPos)))
			{
				int sound = jjGameTicks % 3;
				switch (sound) {
					case 0:
						jjSample(obj.xPos, obj.yPos, SOUND::SPAZSOUNDS_HAHAHA, 0);
						break;
					case 1:
						jjSample(obj.xPos, obj.yPos, SOUND::SPAZSOUNDS_HAHAHA2, 0);
						break;
					case 2:
						jjSample(obj.xPos, obj.yPos, SOUND::SPAZSOUNDS_HIHI, 0);
						break;
				}
				array<string> messages = getNPCMessages(obj.xPos, obj.yPos);
				string msg = "@@@@[Rabbit]@@";
				
				for (uint i = 0; i < messages.length(); ++i) {
					msg = msg + messages[i] + "@";
					//player.showText("@", STRING::MEDIUM);
				}
				
				player.showText(msg, STRING::MEDIUM);
				obj.var[0] = 450;
				debounce = true;
				lastXPos = obj.xPos;
				lastYPos = obj.yPos;
			}
		}
		
		if (obj.var[0] > 0)
			obj.var[0] = obj.var[0] - 1;
		if (obj.var[0] == 0 && debounce == true && obj.xPos != lastXPos && obj.yPos != lastYPos) {
			debounce = false;
		}
	}
}

 class CustomBilsy: jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		if (obj.state != STATE::EXTRA) {
			obj.behave(BEHAVIOR::BILSY);
		}
		else
		{
			if (uint(obj.var[0]) < (postBossMessages.length() - 1) * 400) {
				if (obj.var[0] % 400 == 0)
					jjLocalPlayers[0].showText("@@@@" + postBossMessages[obj.var[0] / 400], STRING::MEDIUM);
				obj.var[0] = obj.var[0] + 1;
				
				jjDrawSprite(obj.xPos, obj.yPos, ANIM::XBILSY, 4, jjGameTicks >> 3, (obj.xPos > jjPlayers[0].xPos) ? -1 : 1, SPRITE::NORMAL);
			}
			else if (obj.var[1] <= 16 * 2) {
				jjDrawSprite(obj.xPos, obj.yPos, ANIM::XBILSY, 2, obj.var[1] / 2, jjLocalPlayers[0].direction, SPRITE::NORMAL);
				obj.var[1] = obj.var[1] + 1;
			}
			else { 
				jjNxt();
				obj.state = STATE::KILL;
			}
		}
    }
	
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
		if (obj.state != STATE::EXTRA && obj.state != STATE::KILL) {
			obj.scriptedCollisions = false;
			if (bullet is null)
				player.objectHit(obj, force, HANDLING::SPECIAL);
			else
				bullet.objectHit(obj, HANDLING::ENEMY);
			obj.scriptedCollisions = true;
			
			if (obj.energy <= 5) {
				obj.energy = 5;
				obj.state = STATE::EXTRA;
				player.boss = -1;
				player.invincibility = -50000;
			}
		}
		return true;
	}
}

void onLevelLoad() {
	layers = jjLayerOrderGet();
	bossLayers = layers;
	layers.removeAt(3);
	for (int i = 0; i < 5; ++i) {
		bossLayers.insertAt(7, bossLayers[bossLayers.length() - 1]);
		bossLayers.removeLast();
	}
	bossLayers.removeRange(bossLayers.length() - 9, 9); 
	jjLayerOrderSet(layers);
	
	fallLoopCount = 0;
	bossActivatedOnce = false;
	isBossActive = false;
	cutscene = false;
	noTeleArea = false;
	blasterPoweredUp = false;
	teleCooldown = 150;
	bossPalette.load("xmasBoss.pal");
	bossPalOpacity = 0.f;
	
	plrCharacterStr = getPlrCharacterStr();
	
	for (int x = 0; x < jjLayerWidth[4]; ++x) {
		for (int y = jjLayerHeight[4] - 1; y >= 0; --y) {
			uint8 eventID = jjEventGet(x, y);
			if (eventID == OBJECT::LEMON) {
				collectibleData.insertLast(Collectible(x, y, false));
			}
		}
	}
		
	jjAnimSets[ANIM::PLUS_WARP].load();
	jjAnimSets[ANIM::BUBBA].load();
	uint src = jjAnimSets[ANIM::CUSTOM[255]].load(0, "SExmas.j2a");
	uint dest = jjAnimSets[ANIM::PICKUPS];
	for (int i = 0; i < 95; i++) {
		const jjANIMATION@ anim = jjAnimations[src + i];
		if (anim.frameCount != 0)
			jjAnimations[dest + i] = anim;
	}
	
	HH17::setEnemy(OBJECT::BAT);
	HH17::setEnemy(OBJECT::DRAGON);
	HH17::setEnemy(OBJECT::MONKEY);
	
	jjObjectPresets[OBJECT::STRAWBERRY].behavior = NPC();
	jjObjectPresets[OBJECT::STRAWBERRY].playerHandling = HANDLING::PARTICLE;
	jjObjectPresets[OBJECT::STRAWBERRY].bulletHandling = HANDLING::DESTROYBULLET;
	jjObjectPresets[OBJECT::STRAWBERRY].scriptedCollisions = true;
	
	jjObjectPresets[OBJECT::XMASBILSY].behavior = CustomBilsy();
	jjObjectPresets[OBJECT::XMASBILSY].deactivates = false;
	jjObjectPresets[OBJECT::XMASBILSY].scriptedCollisions = true;
	
	jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckpointWrapper;
	jjObjectPresets[OBJECT::SAVEPOST].deactivates = false;
	
	GiftPickup();
	PickupTracker::restorePlayerPickups();
	
	jjPIXELMAP rockImage(0,0, 96,96, 5);
	jjANIMFRAME@ rockFrame = jjAnimFrames[jjObjectPresets[OBJECT::ROTATINGROCK].curFrame];
	rockImage.save(rockFrame);
	rockFrame.hotSpotY -= 8;
	
	uint giftAnimID = jjAnimSets[ANIM::CUSTOM[0]].allocate(array<uint> = {3,1,1,1,1, 3,1,1,1,1, 3,1,1,1,1});
	for (uint color = 0; color < 3; ++color, ++giftAnimID) {
		const uint y = (3 + color) * 32;
		for (uint state = 0; state < 3; ++state) {
			jjANIMFRAME@ boxFrame = jjAnimFrames[jjAnimations[giftAnimID] + state];
			jjPIXELMAP(state * 64 + 13, y, 48,32, 5).save(boxFrame);
			boxFrame.hotSpotX = -24;
			boxFrame.hotSpotY = 0;
		}
		for (uint shard = 0; shard < 4; ++shard) {
			jjANIMFRAME@ shardFrame = jjAnimFrames[jjAnimations[++giftAnimID]];
			jjPIXELMAP((6 + shard) * 32, y, 32,32, 5).save(shardFrame);
			shardFrame.hotSpotX = shardFrame.hotSpotY = -16;
		}
	}
	
	jjObjectPresets[OBJECT::GUNCRATE].behavior = 
	jjObjectPresets[OBJECT::CARROTCRATE].behavior = 
	jjObjectPresets[OBJECT::ONEUPCRATE].behavior = 
	jjObjectPresets[OBJECT::BOMBCRATE].behavior = 
	jjObjectPresets[OBJECT::SPRINGCRATE].behavior = 
	jjObjectPresets[OBJECT::GEMCRATE].behavior = 
	jjObjectPresets[OBJECT::BOUNCERAMMO15].behavior = 
	jjObjectPresets[OBJECT::ICEAMMO15].behavior = 
	jjObjectPresets[OBJECT::SEEKERAMMO15].behavior = 
	jjObjectPresets[OBJECT::RFAMMO15].behavior = 
	jjObjectPresets[OBJECT::TOASTERAMMO15].behavior = 
		Gift;
		
	jjObjectPresets[OBJECT::DESTRUCTSCENERY].behavior = Lamp;
}

void onMain() {
	HH17::handleEnemyProjectiles();
	PickupTracker::deleteGemDrops();
	
	if (PickupTracker::foundAllMax && !blasterPoweredUp) {
		jjLocalPlayers[0].powerup[WEAPON::BLASTER] = true;
		blasterPoweredUp = true;
	}
}

void onPlayer(jjPLAYER@ player)
{
	if (PickupTracker::pickupsCollected == PickupTracker::maxAmount - 3 && firstRemainingGiftID == 0 && secondRemainingGiftID == 0 && thirdRemainingGiftID == 0) {
		findRemainingGifts();
	}
	if (!cutscene && !isBossActive && PickupTracker::pickupsCollected >= PickupTracker::maxAmount - 3 && firstRemainingGiftID != secondRemainingGiftID && firstRemainingGiftID != thirdRemainingGiftID) {
		if (collectibleData[firstRemainingGiftID].found == false)
			drawArrow(player, collectibleData[firstRemainingGiftID], 16);
		if (collectibleData[secondRemainingGiftID].found == false)
			drawArrow(player, collectibleData[secondRemainingGiftID], 24);
		if (collectibleData[thirdRemainingGiftID].found == false)
			drawArrow(player, collectibleData[thirdRemainingGiftID], 32);
	}
	
	if (isBossActive && player.boss > -1 && player.buttstomp < 121 && jjDifficultyOrig > 0)
		jjDifficulty = 3;
	else jjDifficulty = jjDifficultyOrig;
	
	if (isBossActive && player.boss > -1 && jjDifficultyOrig > 0 && player.invincibility < -30)
		player.invincibility = -30;

	if (cutscene)
	{
		player.keyLeft = player.keyRight = player.keyUp = player.keyDown = player.keyFire = player.keyJump = player.keyRun = false;
		player.idle = 0;
		player.xSpeed = 0;
	}
	PickupTracker::trackPlayerPickups(player);
	PickupTracker::onFoundAllPickups(player, PickupTracker::FoundAllReward::Unlock);
	
	if (teleCooldown > 0 && PickupTracker::foundAllMin)
		teleCooldown--;
	if (jjKey[84] && teleCooldown == 0 && !cutscene && !isBossActive && !noTeleArea) // Pressed T
	{	
		player.warpToID(3);
		teleCooldown = 150;
	}
	
	applyBossPalette();
	applyBossLight(player);
}	

void onLevelReload()
{
	jjLayerOrderSet(layers);
	jjDifficulty = jjDifficultyOrig;
	cutscene = false;
	noTeleArea = false;
	isBossActive = false;
	blasterPoweredUp = false;
	fallLoopCount = 0;
	teleCooldown = 150;
	bossPalOpacity = 0.f;
	
	for (uint i = 0; i < 32; ++i)
		jjTriggers[i] = SavedTriggers[i];
		
	PickupTracker::restorePlayerPickups();
	jjLocalPlayers[0].lives++;
	jjMusicLoad("kaze_icelevel.ogg");
	if (PickupTracker::foundAllMin)
		jjTriggers[11] = true;
		
	plrCharacterStr = getPlrCharacterStr();

	HH17::processEnemyColors();
	processIcebats();
}

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

bool onDrawScore(jjPLAYER@ player, jjCANVAS@ canvas) {
	PickupTracker::drawCounter(player, canvas, "gifts");
	if (PickupTracker::foundAllMin && !cutscene && !isBossActive && !noTeleArea)
		canvas.drawString(20, jjResolutionHeight - 20, "|(Press 'T' to teleport to the exit)", STRING::SMALL, STRING::NORMAL);
		
	return false;
}

void onFunction0(jjPLAYER@ player) {
	cutscene = true;
	jjLayerOrderSet(bossLayers);
	jjMusicStop();
	if (!bossActivatedOnce) {			
		if (fallLoopCount < 5 * fallLoopMessages.length())
		{
			player.warpToID(0, true);			
			if (fallLoopCount % 5 == 0) {
				player.showText( "@@@@@" + fallLoopMessages[uint(fallLoopCount / 5)], STRING::MEDIUM);
			}
			fallLoopCount++;
		}
	}
	else
	{
		if (fallLoopCount == 1)
			player.showText( "@@@@@" + "#|||~I'm gonna kick you in the@deviled eggs this time!", STRING::MEDIUM);
		if (fallLoopCount < 5)
		{
			player.warpToID(0, true);
			fallLoopCount++;
		}
	}
}

void onFunction1(jjPLAYER@ player) {
	processHellbats();
	player.activateBoss();
	bossActivatedOnce = true;
	cutscene = false;
	isBossActive = true;
	jjMusicLoad("kaze_boss.ogg", false, true);
	player.limitXScroll(115, 25);
}

void onFunction2(jjPLAYER@ player) {
	if (jjDifficulty >= 2)
		player.hurt();
	if (jjTriggers[12] == false)
		player.warpToID(1);
	else player.warpToID(2);
}

void onFunction3(jjPLAYER@ player) {
	noTeleArea = true;
}

void onFunction4(jjPLAYER@ player) {
	noTeleArea = false;
}

bool onCheat(string &in cheat) {
	jjPLAYER@ Player = jjLocalPlayers[0];
	if ((cheat == "jjk" || cheat == "jjkill") && Player.health <= 0) { ; }
	else if (cheat == "jjgems") {
		jjLocalPlayers[0].gems[GEM::RED] = jjLocalPlayers[0].gems[GEM::RED] + 5;
	}
	else if (cheat == "jjmorph") {
		jjLocalPlayers[0].morph(false, false);
		plrCharacterStr = getPlrCharacterStr();
	}
	else return false;
	jjAlert(cheat, false, STRING::MEDIUM);
	return true;
}

[SOLID]
void Gift(jjOBJ@ obj) {
	if (obj.state == STATE::START) {
		obj.determineCurAnim(ANIM::CUSTOM[0], (jjRandom() % 3) * 5);
		obj.frameID = 0;
		obj.determineCurFrame();
		obj.behave(BEHAVIOR::CRATE, false);
	} else if (obj.state == STATE::DONE) {
		//obj.beSolid();
		obj.draw();
	} else if (obj.state == STATE::ACTION) {
		obj.behave(BEHAVIOR::CRATE, false);
		if (jjGameConnection == GAME::LOCAL) {
			obj.state = STATE::DONE;
			obj.creatorType = CREATOR::OBJECT;
			obj.behavior = Gift;
			obj.playerHandling = HANDLING::SPECIALDONE;
			obj.bulletHandling = HANDLING::IGNOREBULLET;
			obj.curFrame = jjAnimations[obj.curAnim] + 2;
			obj.justHit = 0;
			obj.draw();
			uint shardID = 0;
			for (uint objectID = jjObjectCount; --objectID != 0;) {
				jjOBJ@ shard = jjObjects[objectID];
				if (shard.eventID == OBJECT::SHARD && shard.isActive && shard.xOrg == obj.xPos && shard.yOrg == obj.yPos)
					shard.curAnim = obj.curAnim + 1 + (shardID++ & 3);
			}
		} else {
			obj.delete();
		}
	} else {
		obj.behave(BEHAVIOR::CRATE);
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos - 32, obj.curFrame + 1, 1);
	}
}

void Lamp(jjOBJ@ obj) {
	if (obj.state == STATE::START) {
		obj.behave(BEHAVIOR::DESTRUCTSCENERY, false);
		if ((jjTileGet(4, uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5) & TILE::RAWRANGE) == 361) {
			obj.lightType = LIGHT::NORMAL;
			obj.light = 11;
		}
	} else {
		obj.behave(BEHAVIOR::DESTRUCTSCENERY, false);
		if (obj.state == STATE::DONE) {
			obj.lightType = LIGHT::NONE;
			obj.behavior = BEHAVIOR::DESTRUCTSCENERY;
		}
	}
}