Downloads containing Fio2_a.j2as

Downloads
Name Author Game Mode Rating
JJ2+ Only: Find It Out (Single Player)Featured Download Superjazz Single player 8.7 Download file

File preview

#include "MLLE-Include-1.6.asc" ///@MLLE-Generated
#pragma require "Fio2_a-MLLE-Data-1.j2l" ///@MLLE-Generated
const bool MLLESetupSuccessful = MLLE::Setup();
#pragma require "wisetyness.j2t"
#pragma require "Fio2_a.j2l"
#pragma require "Boss2.j2b"
#include "Fio_common.asc"
#include "Fio_cutscene.asc"
#include "Fio_drawing.asc"
#include "Fio_entities.asc"
#include "Fio_globals.asc"
#include "Fio_utils.asc"

enum Cutscene { CUTSCENE_NONE, CUTSCENE_INTRO, CUTSCENE_HELL_KNIGHTS, CUTSCENE_BUBBA, CUTSCENE_OUTRO };

const float CAMERA_INITIAL_X = TILE * 54;
const float CAMERA_INITIAL_Y = TILE * 15;
const float DURATION_ANIMATION_FALL = CUTSCENE_SECOND * 3;
const float DURATION_ANIMATION_HURT = CUTSCENE_SECOND * 5;
const float DURATION_ANIMATION_TELEPORT = CUTSCENE_SECOND;
const float DURATION_CAMERA_FOLLOW = CUTSCENE_SECOND * 10 + 20;
const float DURATION_FADE_TOTAL = CUTSCENE_SECOND * 2;
const float DURATION_FADE_BLACKOUT = CUTSCENE_SECOND * 1;

const string NEXT_LEVEL_FILENAME = "Fio2_x.j2l";

const array<ArmoryItem@> ARMORY_ITEMS = {
	ArmoryItem(0, "||||Bouncer Power up@+ 20 ammo", 25, 52, ANIM::PICKUPS, 61, 0, @fio::sellArmoryItemBouncerPU),
	ArmoryItem(1, "||||+15 Seeker ammo", 30, 48, ANIM::AMMO, 37, 1, @fio::sellArmoryItemSeekerAmmo),
	ArmoryItem(2, "", 15, 44, ANIM::PICKUPS, 72, 5, @fio::sellArmoryItemInvincibility, @fio::canBuyInvincibility), // Text updated later when currentGameSession has been loaded
	ArmoryItem(3, "", 10, 56, ANIM::PICKUPS, 21, 0, @fio::sellArmoryItemPocketCarrot, @fio::canBuyPocketCarrot) // Text updated later when currentGameSession has been loaded
};

uint8 activeCutscene = uint8(CUTSCENE_NONE);

uint16 yellowSwitch = 0;
uint16 yellowBall = 0;
uint16 gateTile = 0;
uint16 redSwitch = 0;
uint16 redBall = 0;

uint elapsed = 0;

int hellKnightSummoningElapsed = 0;

bool areHellKnightsActive = false;
bool areHellKnightsBeingSummoned = false;
bool areHellKnightsDefeated = false;
bool hasHellKnightsArenaBeenReached = false;
bool isYellowBallCollected = false;
bool isYellowBallSet = false;
bool isBubbaRendered = false;
bool isFloorMotionActive = false;
bool isGateBeingOpened = false;
bool isPlayerRenderedInAir = false;
bool isRedBallCollected = false;
bool isRedBallSet = false;
bool wasCutsceneWatchedTillTheEndBubba = false;
bool wasCutsceneWatchedTillTheEndHellKnights = false;
bool wasHellKnightsLoreShown = false;

BossFromHell@ bubba;
HellKnight@ hellKnight1;
HellKnight@ hellKnight2;

array<Checkpoint@> fio2aCheckpoints = {
	Checkpoint(0, TILE * 12, TILE * 56),
	Checkpoint(1, TILE * 85, TILE * 84),
	Checkpoint(2, TILE * 9, TILE * 142),
	Checkpoint(3, TILE * 78, TILE * 183)
};

array<jjLAYER@> defaultLayers;
array<jjLAYER@> introLayers;
array<uint16> floorTiles;

array<string> texts = {
	"|Ugh...that wasn't nice. Now where am I? What just happened? What is this place?", // 0
	"|Is this my destiny? Where are the paradise islands?",
	"|That looks like a switch that requires some kind of a round object as a key. A yellow one.",
	"|Hmm, there's a yellow ball below the burrow of spikes. But how to get there safely?",
	"|There we go! But what's down there? Guess there's only one way to find out!", // 4
	"|And how to open that door? There must be a hidden switch!",
	"|That red figure just keeps following me.",
	"|The torches look like they could be stepped on.",
	"|The top of that wall seems more fragile than the rest of it. I guess I need to take a leap of faith here to get past it.",
	"|Got it!", // 9
	"||Ha ha ha! My flesh may be dead, but I still live on in the darkness! And that is where you are going with me!",
	"||||Lore: Hell knights are demonic creatures that burn those who get too close. Their rings of doom slowly dissolve living creatures.",
	"|Now where to find the ball for this red one?", 
	"|Alrighty! But what's that thing shining behind the wall?" // 13
};

array<string> cutsceneTextsBubba = {
	"||I have been waiting for you mortal...",
	"||...to feast on the eternal darkness with me from now on!"
};

array<string> cutsceneTextsHellKnights = {
	"||Welcome...fallen one...to my lair of pain!",
	"||I am...well...actually...that does not matter...for you.",
	"||Because...you are going to DIE!"
};

array<string> cutsceneTextsIntro = {
	"||||The Grey Fort collapses right behind you, as you barely make it through the gate in time.",
	"||||But as soon as you pass the gate, you are surrounded by a bright light...",
	"||||...and you begin suffering terrible pain and torment. Suddenly, everything is different."
};

array<string> cutsceneTextsOutro = {
	"||Ha ha ha! My flesh may be dead, but I still live on in the darkness! And that is where you are going with me!"
};

array<string> questTexts = {
	fio::getQuestText(7, 10, 7000),
	fio::getQuestTextComplete(7000),
	fio::getQuestTextPerfect(3000)
};

bool areHellKnightsDead() {
	return @hellKnight1 !is null && hellKnight1.isDead
			&& @hellKnight2 !is null && hellKnight2.isDead;
}

void createInterBoss() {
	@hellKnight1 = HellKnight(jjObjects[jjAddObject(OBJECT::EVA, 75.5*32, 83.5*32)], jjDifficulty * 10 + 15);
	@hellKnight2 = HellKnight(jjObjects[jjAddObject(OBJECT::EVA, 79.5*32, 80.5*32)], jjDifficulty * 10 + 15);
}

void endCutsceneBubba() {
	fioCut::endCutscene(TILE * 86.5, TILE * 159);
	activeCutscene = CUTSCENE_NONE;
	fioCut::clearAnimationChains();
	fioUtils::releasePlayer();
	staticPlayerRenderState = STATE_STATIC_PLAYER_RENDER_OFF;
	@bubba = BossFromHell(fio::getActiveObjectFromLevel(OBJECT::BUBBA), jjDifficulty * 50 + 50);
	bubba.obj.state = STATE::DELAYEDSTART;
	isCustomBossActivated = true;
	isBubbaRendered = false;
	jjMusicLoad(BOSS_THEME_FILENAME, false, true);
	fioDraw::textIndex = -1;
	fio::preserveAmmoForBossBattle();
}

void endCutsceneHellKnights() {
	fioCut::endCutscene(TILE * 73, TILE * 87);
	activeCutscene = CUTSCENE_NONE;
	isPlayerHiddenAndUnableToMove = true;
	staticPlayerRenderState = STATE_STATIC_PLAYER_RENDER_IDLE;
	forceMoveRight = false;
	jjTileSet(3, 76, 86, 0);
	jjTileSet(3, 76, 87, 0);
	jjTriggers[9] = true;
	hellKnightSummoningElapsed = 0;
	areHellKnightsBeingSummoned = true;
	createInterBoss();
	
	if (!wasHellKnightsLoreShown) {
		wasHellKnightsLoreShown = true;
		fioDraw::doShowText(11);
	}
}

void endCutsceneIntro() {
	fioCut::endCutscene(TILE * 12, TILE * 39);
	activeCutscene = CUTSCENE_NONE;
	fioUtils::releasePlayer();
}

int getPointRewardByDifficultyAndBoss(bool isFinalBoss) {
	if (isFinalBoss) {
		if (jjDifficulty >= 3) return 40000;
		if (jjDifficulty == 2) return 10000;
		if (jjDifficulty == 1) return 2000;
		return 1000;
	}
	// Hell knights
	if (jjDifficulty >= 3) return 20000;
	if (jjDifficulty == 2) return 5000;
	if (jjDifficulty == 1) return 1000;
	return 500;
}

void initializeIntro() {
	fioCut::initializeCutscene(@processTickEvents, cutsceneTextsIntro);
	initializeIntroAnimationChain();
	fioCut::createEventFade(DURATION_FADE_TOTAL, DURATION_FADE_BLACKOUT, false, true);
	fioCut::createEventCameraScroll(DURATION_CAMERA_FOLLOW, TILE * 34, TILE * 13, TILE * 79, TILE * 13);
	fioCut::setTickEventsProcessed(true);
	play.noFire = true;
	play.cameraFreeze(CAMERA_INITIAL_X, CAMERA_INITIAL_Y, true, true);
	play.warpToID(10, true);
	activeCutscene = CUTSCENE_INTRO;
}

void initializeIntroAnimationChain() {
	const ANIM::Set playerAnimSet = fio::getAnimSetForPlayer(jjLocalPlayers[0]);
	
	// Duration, xOrigin, yOrigin, xDestination, yDestination, angle, scaleX, scaleY, animSet, animationId,
	// startingFrame, finalFrame, frameRate, repetitions (optional)
	// REMINDER: DON'T FORGET TO REMOVE THE TRAILING COMMA, SINCE OTHERWISE AS WILL INSERT A NULL HANDLE AFTER THE LAST ACTUAL OBJECT ELEMENT
	
	const array<fioCut::Animation@> animationsIntroRabbit = {
		fioCut::Animation(DURATION_CAMERA_FOLLOW, TILE * 34, TILE * 15.5, TILE * 80, TILE * 15.5,
				0, 1, 1, playerAnimSet, RABBIT::RUN1, 0, 7, FRAME_RATE_INTRO_RABBIT),
				
		fioCut::Animation(15, TILE * 80, TILE * 15.5, TILE * 81, TILE * 15.5,
				0, 1, 1, playerAnimSet, RABBIT::SKID3, 0, 7, 4, 1),
				
		fioCut::Animation(DURATION_ANIMATION_HURT, TILE * 81, TILE * 15.5, TILE * 81, TILE * 15.5,
				0, 1, 1, playerAnimSet, RABBIT::HURT, 0, 0, 2),
				
		fioCut::Animation(CUTSCENE_SECOND - 10, TILE * 81, TILE * 15.5, TILE * 81, TILE * 15.5,
				0, 1, 1, playerAnimSet, RABBIT::HURT, 0, 8, 2, 1),
				
		fioCut::Animation(10, TILE * 81, TILE * 15.5, TILE * 81, TILE * 15.5,
				0, 1, 1, playerAnimSet, RABBIT::TELEPORTFALLTELEPORT, 2, 6, 2, 1),
				
		fioCut::EmptyAnimation(10),
				
		fioCut::Animation(10, TILE * 94, TILE * 35, TILE * 94, TILE * 35,
				0, 1, 1, playerAnimSet, RABBIT::TELEPORTFALL, 0, 7, 2, 1),
				
		fioCut::Animation(DURATION_ANIMATION_FALL, TILE * 94, TILE * 35, TILE * 94, TILE * 35,
				0, 1, 1, playerAnimSet, RABBIT::TELEPORTFALLING, 0, 4, 2),
				
		fioCut::Animation(10, TILE * 94, TILE * 35, TILE * 94, TILE * 35,
				0, 1, 1, playerAnimSet, RABBIT::TELEPORTFALLTELEPORT, 0, 6, 2, 1)
	};
	fioCut::createAnimationChain(animationsIntroRabbit);
}

bool isBubbaDead() {
	return @bubba !is null && bubba.energy <= 0;
}

bool onCheat(string &in cheat) {
	return fio::handleCheat(cheat, NEXT_LEVEL_FILENAME);
}

bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ canvas) {
	fioDraw::animateHud();
	fioDraw::drawHud(play, canvas, activeCutscene != CUTSCENE_NONE);
	
	if (activeCutscene != CUTSCENE_NONE) {
		fioCut::drawCutscene(canvas, centeredText);
	}
	
	if (isPlayerInArmory) {
		fioDraw::drawArmoryInterface(canvas);
	}
	
	return activeCutscene != CUTSCENE_NONE;
}

void onDrawLayer4(jjPLAYER@ play, jjCANVAS@ canvas) {
	fioCut::renderAnimations(canvas);
	fio::renderCommon(play, canvas);
	if (!isYellowBallCollected) canvas.drawTile(50 * TILE, 61 * TILE, 143);
	if (!isRedBallCollected) canvas.drawTile(51 * TILE, 182 * TILE, 140);
	if (isPlayerRenderedInAir) {
		fioDraw::drawFrameAtPlayerPos(play, canvas, fio::getAnimSetForPlayer(play), RABBIT::FALL, 0);
	}
	if (isBubbaRendered) canvas.drawSprite(TILE * 96, TILE * 159.5, ANIM::BUBBA, 2, 8, -1);
	fioDraw::drawArmoryAtPos(canvas, TILE * 3.5, TILE * 142.75); // Offset with +0.5 xTiles and +0.75 yTiles
}

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

bool onDrawScore(jjPLAYER@ play, jjCANVAS@ canvas) {
	return activeCutscene != CUTSCENE_NONE;
}

void onFunction0() {
	if (!checkpoints[0].isReached()) {
		checkpoints[0].setReached();
	}
	play.cameraUnfreeze();
	fioDraw::doShowText(0);
}

void onFunction1() {
	fioDraw::doShowOptionalQuest(0);
}

void onFunction2() {
	if (isYellowBallCollected && !isYellowBallSet) {
		jjGenerateSettableTileArea(4, 31, 62, 1, 1);
		jjTileSet(4, 31, 62, 133);
		fioDraw::doShowText(4);
		isYellowBallSet = true;
		isFloorMotionActive = true;
		elapsed = 0;
	}
}

void onFunction3() {
	fioDraw::doShowText(3);
}

void onFunction4() {
	isYellowBallCollected = true;
	jjEnabledASFunctions[2] = true;
	fioDraw::doShowText(9);
}

void onFunction5() {
	fioDraw::doShowText(5);
}

void onFunction7() {
	// Ole varovainen, ettet vahingossa passaa sisään funktiota, jossa päivitetään playerin xSpeediä, koska jos playerin on tarkoitus
	// pysyä paikoillaan, tuo xSpeedin lisäys aiheuttaa playerin nykimisen cutscenen aikana...
	if (!areHellKnightsActive && !areHellKnightsDefeated) {
		if (!wasCutsceneWatchedTillTheEndHellKnights) {
			fioCut::initializeCutscene(@processTickEvents, cutsceneTextsHellKnights);
			activeCutscene = CUTSCENE_HELL_KNIGHTS;
		} else {
			endCutsceneHellKnights();
		}
		
		jjTriggers[11] = true;
	}
}

void onFunction8() {
	jjTriggers[9] = true;
}

void onFunction9() {
	fioDraw::doShowText(2);
}

void onFunction10(bool show) {
	if (show) {
		play.noFire = true;
		isPlayerInArmory = true;
	}
	else {
		play.noFire = false;
		isPlayerInArmory = false;
	}
}

void onFunction11() {
	if (!checkpoints[2].isReached()) {
		checkpoints[2].setReached();
	}
}

void onFunction12() {
	jjTriggers[17] = true;
	fioDraw::doShowText(6);
}

void onFunction13() {
	fioDraw::doShowText(7);
}

void onFunction14() {
	fioDraw::doShowText(8);
}

void onFunction15() {
	isRedBallCollected = true;
	jjEnabledASFunctions[16] = true;
	fioDraw::doShowText(9);
}

void onFunction16() {
	if (isRedBallCollected && !isRedBallSet) {
		jjGenerateSettableTileArea(4, 74, 183, 1, 1);
		jjTileSet(4, 74, 183, 130);
		jjTriggers[25] = true;
		isRedBallSet = true;
		checkpoints[3].setReached();
		fioDraw::doShowText(13);
	} else if (!isRedBallCollected) {
		fioDraw::doShowText(12);
	}
}

void onFunction17() {
	fioDraw::doShowText(1);
}

void onFunction18() {
	if (!areHellKnightsActive && !areHellKnightsDefeated) {
		forceMoveRight = true;
		fioCut::pace = fioCut::NORMAL_PACE; // Dirty hack for forceMoveRight when a cutscene may not be initialized, if the cutscene was watched till the end
		play.noFire = true;
		fio::killFollowingEnemies();
	}
}

void onFunction19(bool darker) {
	if (darker) {
		play.lighting = LIGHTING_TWILIGHT;
	}
	else {
		play.lighting = LIGHTING_STANDARD;
	}
}

void onFunction20(bool darker) {
	if (darker) {
		play.lighting = LIGHTING_DARK;
	}
	else {
		play.lighting = LIGHTING_TWILIGHT;
	}
}

void onFunction21() {
	play.warpToTile(85, 159);
}

void onFunction22() {
	forceMoveRight = true;
	fioCut::pace = fioCut::NORMAL_PACE; // Dirty hack for forceMoveRight when a cutscene may not be initialized, if the cutscene was watched till the end
	isBubbaRendered = true;
	play.noFire = true;
}

void onFunction23() {
	forceMoveRight = false;
	
	if (!wasCutsceneWatchedTillTheEndBubba) {
		isPlayerHiddenAndUnableToMove = true;
		staticPlayerRenderState = STATE_STATIC_PLAYER_RENDER_IDLE;
		fioCut::initializeCutscene(null, cutsceneTextsBubba);
		activeCutscene = CUTSCENE_BUBBA;
	} else {
		endCutsceneBubba();
	}
}

void onLevelBegin() {
	initializeIntro();
}

void onLevelLoad() {
	initializeGlobals(fio2aCheckpoints, 7, 7000, 3000);
	
	fioDraw::initializeDrawing(texts, questTexts, true);
	fioCut::clearAnimationChains();
	
	// Re-assignment via static declaration
	armoryItems = ARMORY_ITEMS;
	
	// Global indice
	armoryItemIndexInvincibility = 2;
	armoryItemIndexPocketCarrot = 3;
	
	armoryItems[armoryItemIndexInvincibility].text = fio::getInvincibilityItemText();
	armoryItems[armoryItemIndexPocketCarrot].text = fio::getPocketCarrotItemText();
	
	floorTiles.insertLast(jjTileGet(4, 26, 63));
	floorTiles.insertLast(jjTileGet(4, 27, 63));
	floorTiles.insertLast(jjTileGet(4, 30, 63));
	yellowSwitch = jjTileGet(4, 31, 62);
	yellowBall = jjTileGet(4, 50, 60);
	redSwitch = jjTileGet(4, 84, 84);
	redBall = jjTileGet(4, 76, 79);
	gateTile = jjTileGet(4, 31, 76);
	
	jjAnimSets[ANIM::BUBBA].load();
	
	jjGenerateSettableTileArea(3, 76, 86, 1, 2);
	jjGenerateSettableTileArea(4, 26, 63, 5, 1);
	jjGenerateSettableTileArea(4, 31, 76, 1, 5);
}

void onLevelReload() {
	MLLE::ReapplyPalette();
	reloadGlobals();
	fioDraw::initializeDrawing(texts, questTexts, true);
	fioCut::clearAnimationChains();
	
	if (fio::handleLevelReload()) {
		activeCutscene = CUTSCENE_NONE;
	} else {
		initializeIntro();
	}

	elapsed = 0;
	isPlayerHiddenAndUnableToMove = false;
	staticPlayerRenderState = STATE_STATIC_PLAYER_RENDER_OFF;
	forceMoveRight = false;
	
	if (areHellKnightsDefeated) {
		jjLayers[3].generateSettableTileArea();
		jjTriggers[10] = true;
		jjTriggers[11] = true;
		jjTileSet(3, 76, 86, 0);
		jjTileSet(3, 76, 87, 0);
	} else {
		// Just in case
		@hellKnight1 = null;
		@hellKnight2 = null;
		areHellKnightsActive = false;
		hasHellKnightsArenaBeenReached = false;
		jjEnabledASFunctions[7] = true;
		jjEnabledASFunctions[18] = true;
		jjTriggers[9] = false;
		jjTriggers[11] = false;
		jjTileSet(3, 76, 86, 165 | TILE::HFLIPPED);
		jjTileSet(3, 76, 87, 175 | TILE::HFLIPPED);
	}
	
	jjEnabledASFunctions[2] = true;
	jjEnabledASFunctions[8] = true;
	jjEnabledASFunctions[21] = true;
	jjEnabledASFunctions[22] = true;
	jjEnabledASFunctions[23] = true;
	
	jjTriggers[11] = false;
	
	if (isBubbaDead()) {
		fio::handleLevelCycle(NEXT_LEVEL_FILENAME, true); // Just in case you mess up the outro by jjk or something :-)
	}
}

void onMain() {
	fio::controlPressedKeys();
	fioDraw::controlHud();
}

void onPlayer(jjPLAYER@ play) {
	fio::handlePlayer(play);
	fio::controlQuest();
	
	if (isCustomBossActivated && isBubbaDead()) {
		bubba.obj.particlePixelExplosion(1);
		bubba.obj.state = STATE::KILL;
		bubba.obj.delete();
		isCustomBossActivated = false;
		play.noFire = true;
		play.invisibility = true;
		play.invincibility = 7000; // Should suffice
		isPlayerHiddenAndUnableToMove = true;
		isPlayerRenderedInAir = true;
		fio::rewardPoints(getPointRewardByDifficultyAndBoss(true));
		fioCut::initializeCutscene(@processTickEvents, cutsceneTextsOutro);
		activeCutscene = CUTSCENE_OUTRO;
		removeSpikeBollsFromLevel();
	}
	
	if (isPlayerRenderedInAir) {
		play.xPos = TILE * 91;
		play.yPos = TILE * 158;
	}
	
	if (activeCutscene != CUTSCENE_NONE) {
		fioCut::run();
		if (!fioCut::isTickEventsProcessed()) {
			processTickEvents(play);
			fioCut::setTickEventsProcessed(true);
		}
	}
	
	if (isFloorMotionActive) {
		if (elapsed == 10) jjTileSet(4, 26, 63, 2 * 10 + 2);
		if (elapsed == 20) jjTileSet(4, 27, 63, 0);
		if (elapsed == 30) jjTileSet(4, 28, 63, 0);
		if (elapsed == 40) jjTileSet(4, 29, 63, 0);
		if (elapsed == 50) jjTileSet(4, 30, 63, 2 * 10);
		if (elapsed <= 51) elapsed++;
	}
	
	if (jjTriggers[6]) {
		if (!isGateBeingOpened) {
			isFloorMotionActive = false;
			elapsed = 0;
			isGateBeingOpened = true;
		}
	}
	
	if (isGateBeingOpened) {
		if (elapsed == 10) jjTileSet(4, 31, 80, 0);
		if (elapsed == 20) jjTileSet(4, 31, 79, 0);
		if (elapsed == 30) jjTileSet(4, 31, 78, 0);
		if (elapsed == 40) jjTileSet(4, 31, 77, 0);
		if (elapsed == 50) jjTileSet(4, 31, 76, 0);
		if (elapsed <= 51) elapsed++;
	}
	
	if (areHellKnightsActive && areHellKnightsDead()) {
		areHellKnightsActive = false;
		areHellKnightsDefeated = true;
		jjTriggers[10] = true;
		jjTriggers[11] = false;
		fio::rewardPoints(getPointRewardByDifficultyAndBoss(false));
		
		if (!checkpoints[1].isReached()) {
			checkpoints[1].setReached();
		}
	}
	
	if (areHellKnightsBeingSummoned) {
		if (hellKnightSummoningElapsed < 350) {
			hellKnightSummoningElapsed++;
		} else {
			fioUtils::releasePlayer();
			staticPlayerRenderState = STATE_STATIC_PLAYER_RENDER_OFF;
			areHellKnightsBeingSummoned = false;
			areHellKnightsActive = true;
			hellKnight1.appearElapsed = 350;
			hellKnight2.appearElapsed = 350;
		}
	}
	
	if (!hasHellKnightsArenaBeenReached && activeCutscene == CUTSCENE_HELL_KNIGHTS && play.xPos >= TILE * 73) {
		hasHellKnightsArenaBeenReached = true;
		forceMoveRight = false;
		isPlayerHiddenAndUnableToMove = true;
		staticPlayerRenderState = STATE_STATIC_PLAYER_RENDER_IDLE;
	}
}

void onPlayerInput(jjPLAYER@ play) {
	fio::controlArmoryInput(play);
	fio::controlPlayerInput(play, activeCutscene != CUTSCENE_NONE);
	if (activeCutscene != CUTSCENE_NONE) {
		fioCut::controlPlayerInput(play);
		if (fioCut::isCutsceneSkipInitiatedAfterDelay(play)) {
			fioCut::setCutsceneSkipInitiated();
			if (activeCutscene == CUTSCENE_INTRO) {
				endCutsceneIntro();
			} else if (activeCutscene == CUTSCENE_HELL_KNIGHTS) {
				endCutsceneHellKnights();
			} else if (activeCutscene == CUTSCENE_BUBBA) {
				endCutsceneBubba();
			} else if (activeCutscene == CUTSCENE_OUTRO) {
				fioUtils::releasePlayer();
				isPlayerRenderedInAir = false;
				fio::handleLevelCycle(NEXT_LEVEL_FILENAME, true, true);
				// Don't set activeCutscene to CUTSCENE_NONE so that the cutscene engine can process the level cycle event properly
			}
		}
	}
}

void onRoast(jjPLAYER@ victim, jjPLAYER@ killer) {
	fio::saveTriggerStates();
	asPlay.savePlayerProperties(play);
}

void processCutsceneBubba(jjPLAYER@ play) {
	switch(uint(fioCut::getElapsedCutscene())) {
		case 1:
			fioCut::startTextSliding();
			break;
		case CUTSCENE_SECOND * 9:
			fio::increaseCutscenesWatchedIfFastForwardWasNotUsed(fioCut::wasFastForwardUsed);
			wasCutsceneWatchedTillTheEndBubba = true;
			endCutsceneBubba();
			break;
	}
}

void processCutsceneHellKnights(jjPLAYER@ play) {
	switch(uint(fioCut::getElapsedCutscene())) {
		case 1:
			fioCut::startTextSliding();
			break;
		case CUTSCENE_SECOND * 10:
			jjTileSet(3, 76, 86, 0);
			jjTileSet(3, 76, 87, 0);
			jjTriggers[9] = true;
			break;
		case CUTSCENE_SECOND * 13:
			fio::increaseCutscenesWatchedIfFastForwardWasNotUsed(fioCut::wasFastForwardUsed);
			wasCutsceneWatchedTillTheEndHellKnights = true;
			endCutsceneHellKnights();
			break;
	}
}

void processCutsceneIntro(jjPLAYER@ play) {
	switch(uint(fioCut::getElapsedCutscene())) {
		case CUTSCENE_SECOND * 1:
			fioCut::startTextSliding();
			break;
		case CUTSCENE_SECOND * 10:
			fioCut::createEventCameraScroll(CUTSCENE_SECOND, TILE * 79, TILE * 13, TILE * 81, TILE * 13);
			play.xPos = TILE * 74;
			play.yPos = TILE * 28;
			break;
		case CUTSCENE_SECOND * 17:
			jjSamplePriority(SOUND::COMMON_TELPORT1);
			break;
		case CUTSCENE_SECOND * 17 + 10:
			fioCut::createEventLayerOffset(CUTSCENE_SECOND * 4, 6,
					jjLayers[6].xOffset, jjLayers[6].yOffset - 100,
					jjLayers[6].xOffset, jjLayers[6].yOffset);
			fioCut::createEventLayerOffset(CUTSCENE_SECOND * 4, 7,
					jjLayers[7].xOffset, jjLayers[7].yOffset - 24,
					jjLayers[7].xOffset, jjLayers[7].yOffset);
			play.cameraFreeze(TILE * 94, TILE * 35, true, true);
			jjSamplePriority(SOUND::COMMON_TELPORT2);
			break;
		case CUTSCENE_SECOND * 21 - 10:
			jjSamplePriority(SOUND::COMMON_TELPORT1);
			break;
		case CUTSCENE_SECOND * 21:
			fio::increaseCutscenesWatchedIfFastForwardWasNotUsed(fioCut::wasFastForwardUsed);
			endCutsceneIntro();
	}
}

void processCutsceneOutro(jjPLAYER@ play) {
	switch(uint(fioCut::getElapsedCutscene())) {
		case 1:
			fioCut::startTextSliding();
			break;
		case CUTSCENE_SECOND * 6:
			fioUtils::releasePlayer();
			isPlayerRenderedInAir = false;
			fio::increaseCutscenesWatchedIfFastForwardWasNotUsed(fioCut::wasFastForwardUsed);
			fio::handleLevelCycle(NEXT_LEVEL_FILENAME, true);
			break;
	}
}

void processTickEvents(jjPLAYER@ play) {
	switch (activeCutscene) {
		case CUTSCENE_INTRO:
			processCutsceneIntro(play);
			break;
		case CUTSCENE_HELL_KNIGHTS:
			processCutsceneHellKnights(play);
			break;
		case CUTSCENE_BUBBA:
			processCutsceneBubba(play);
			break;
		case CUTSCENE_OUTRO:
			processCutsceneOutro(play);
			break;
	}	
}

void removeSpikeBollsFromLevel() {
	for (int i = 1; i < jjObjectCount; i++) {
		if (jjObjects[i].eventID == OBJECT::SPIKEBOLL3D) {
			jjObjects[i].delete();
		}
	}
}