Downloads containing Fio5_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

const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
#include "MLLE-Include-1.6.asc" ///@MLLE-Generated
#pragma require "Fio5_a-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "Carrot1.j2t" ///@MLLE-Generated
#pragma require "Fio5_a.j2l" ///@MLLE-Generated
#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_INTERLUDE };

const float FRAME_RATE_INTERLUDE_RABBIT = 2;
const float INTERLUDE_CAMERA_X = TILE * 21;
const float INTERLUDE_CAMERA_Y = TILE * 44;
const float INTERLUDE_X = TILE * 23;
const float INTERLUDE_Y = TILE * 48 - 8;
const float INTRO_END_X = TILE * 168; // Not an actual cutscene
const float INTRO_END_Y = TILE * 85;
const float PLATFORM_OFFSET_X = -7;
const float PLATFORM_OFFSET_Y = 17;
const float RABBIT_RUN_DURATION = CUTSCENE_SECOND * 2;
const float RABBIT_STILL_DURATION = CUTSCENE_SECOND * 18;

const string NEXT_LEVEL_FILENAME = "Fio5_x.j2l";

const array<ArmoryItem@> ARMORY_ITEMS = {
	ArmoryItem(0, "||||Bouncer Power up@+ 20 ammo", 15, 52, ANIM::PICKUPS, 61, 0, @fio::sellArmoryItemBouncerPU),
	ArmoryItem(1, "||||Toaster Power up@+20 ammo", 15, 48, ANIM::PICKUPS, 65, 0, @fio::sellArmoryItemToasterPU),
	ArmoryItem(2, "||||Pepperspray Power up@+20 ammo", 20, 48, ANIM::PICKUPS, 66, 0, @fio::sellArmoryItemPeppersprayPU),
	ArmoryItem(3, "||||Freezer Power up@+20 ammo", 10, 48, ANIM::PICKUPS, 62, 0, @fio::sellArmoryItemFreezerPU),
	ArmoryItem(4, "||||RF Power up@+20 ammo", 25, 48, ANIM::PICKUPS, 64, 0, @fio::sellArmoryItemRFPU),
	ArmoryItem(5, "||||+15 Seeker ammo", 20, 48, ANIM::AMMO, 37, 1, @fio::sellArmoryItemSeekerAmmo),
	ArmoryItem(6, "", 15, 44, ANIM::PICKUPS, 72, 5, @fio::sellArmoryItemInvincibility, @fio::canBuyInvincibility), // Text updated later when currentGameSession has been loaded
	ArmoryItem(7, "", 10, 56, ANIM::PICKUPS, 21, 0, @fio::sellArmoryItemPocketCarrot, @fio::canBuyPocketCarrot) // Text updated later when currentGameSession has been loaded
};

uint8 activeCutscene = uint8(CUTSCENE_NONE);

// Ensure that the player cannot skip the cutscene at the exact moment when the player is warped away from the portal, so that the player position is messed up...
bool hasPlayerWarpedFromPortal = false;

bool hasTriggerCrateBottomBeenDestroyed = false;
bool hasTriggerCrateInWallBeenDestroyed = false;

ANIM::Set playerAnimSet;

CharacterWithMindStone@ character;

array<string> cutsceneTextsInterlude = {
	"|Woah! This portal landed me straight to the cell of Nicholas by the looks of it. I wonder why the coincidence.",
	"|NICHOLAS? ARE YOU HERE? I CANNOT HEAR YOU IN MY HEAD ANYMORE!",
	"|Nope, he's probably not here. What about the greater mindstone? Would that have some energy left in it?",
	"", // Empty for visuality
	"|NICHOLAS! CAN YOU HEAR ME NOW? I NEED TO KNOW WHERE YOU ARE!",
	"|||At last! It seems that you have made it to my cell! The demons have moved me away from my cell and my powers have weakened even more recently, so I probably lost contact to your mindstone...",
	"|||But since you made it that far, not all hope is lost yet! But you must act quickly! The demons may be plotting something.",
	"|||The demons took me to another heavily guarded cell located far to the east. You must head on here and break through the guards!",
	"|To the east? How do I tell where east is in a place like this?",
	"|||I think you must have used the same portal to reach my cell that they used to move out my soul. So you probably have seen some of the area already.",
	"|||I haven't been here for long yet, and the demons must have opened several gates while moving my soul.",
	"|||If I focus with the energy I have left, I should be able to teleport you closer to my location.",
	"|||From there the road should be quite straightforward, but you may face some locked gates and demon resistance.",
	"|||With any luck some of the gates could still be open if you just hurry. I will try to move you with my powers as much as I can.",
	"|Alright Nicholas. Do what you can and I'll be on my way there."
};

array<string> questTexts = {
	fio::getQuestText(5, 7, 5000),
	fio::getQuestTextComplete(5000),
	fio::getQuestTextPerfect(2000)
};

array<string> texts = {
	"|Phew...Hopefully I will never have to face that one again. What an experience...",
	"|Ugh, this seems like another warm and familiar place. Now what's next? Should I find Nicholas from this maze? Great...",
	"|...I wonder where is this tube taking me to. This place seems like a huge prison.",
	"|Now how do I break free this time?",
	"|Hmm, I wonder if I can use that switch below the floor for opening this wall?", // 4th
	"|Nope. Seems like this wall can be opened only from the other side, but how to get there? I wonder what else did the switch open though.",
	"ENTERING THE AREA OF THE CURSED--BEWARE OR YOU MAY FALL--",
	"|Oh? A Portal to the area of the cursed ones? How lucky must I be to find a portal straight to Nicholas!",
	"|WOAH! That's a big boom! And now I realize where I am again! But where to find the route to Nicholas?",
	"|I only hope I can find Nicholas in time before the demons do anything to him.", // 9th
	"|The demons seem to have unleashed some kind of hell hounds to patrol for runaways...",
	"|Hey, this gate wasn't open before. This must be the way the demons have used to transport Nicholas away from his cell."
};

array<Platform@> platforms;

array<Checkpoint@> fio5aCheckpoints = {
	Checkpoint(0, INTRO_END_X, INTRO_END_Y),
	Checkpoint(1, TILE * 124, TILE * 127),
	Checkpoint(2, TILE * 115, TILE * 87),
	Checkpoint(3, TILE * 75, TILE * 9),
	Checkpoint(4, TILE * 27, TILE * 80),
	Checkpoint(5, TILE * 124, TILE * 127)
};

void endCutsceneInterlude() {
	fioCut::endCutscene(TILE * 27, TILE * 80);
	play.lighting = LIGHTING_TWILIGHT;
	activeCutscene = CUTSCENE_NONE;
	fioUtils::releasePlayer();
}

void initiatePlatformMovement() {
	for (uint i = 0; i < platforms.length(); i++) {
		platforms[i].obj.state = STATE::FADEIN;
	}
}

void initializeInterlude() {
	fioCut::initializeCutscene(@processTickEvents, cutsceneTextsInterlude);
	fioCut::setTickEventsProcessed(true);
	play.lighting = 255;
	play.noFire = true;
	isPlayerUnableToMove = true;
	activeCutscene = CUTSCENE_INTERLUDE;
}

void initializeInterludeAnimationChain() {
	// 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@> animationsInterludeRabbit = {
		fioCut::Animation(10,
				TILE * 13, TILE * 40,
				TILE * 13, TILE * 40,
				0, 1, 1, playerAnimSet, RABBIT::TELEPORTFALL, 0, 7, 2, 1),
		fioCut::Animation(25,
				TILE * 13, TILE * 40,
				TILE * 13, INTERLUDE_Y,
				0, 1, 1, playerAnimSet, RABBIT::TELEPORTFALLING, 0, 4, 2),
		fioCut::Animation(10,
				TILE * 13, INTERLUDE_Y,
				TILE * 13, INTERLUDE_Y,
				0, 1, 1, playerAnimSet, RABBIT::BUTTSTOMPLAND, 0, 6, 2, 1),
		fioCut::Animation(RABBIT_STILL_DURATION,
				TILE * 13, INTERLUDE_Y,
				TILE * 13, INTERLUDE_Y,
				0, 1, 1, playerAnimSet, character.idleAnimation, character.idleFrame, character.idleFrame, 1),
		fioCut::Animation(RABBIT_RUN_DURATION,
				TILE * 13, INTERLUDE_Y,
				INTERLUDE_X, INTERLUDE_Y,
				0, 1, 1, playerAnimSet, RABBIT::RUN1, 0, 7, FRAME_RATE_INTERLUDE_RABBIT),
		fioCut::Animation(CUTSCENE_SECOND * 77 - 10,
				INTERLUDE_X, INTERLUDE_Y,
				INTERLUDE_X, INTERLUDE_Y,
				0, 1, 1, playerAnimSet, character.idleAnimation, character.idleFrame, character.idleFrame, 1),
		fioCut::Animation(10,
				INTERLUDE_X, INTERLUDE_Y,
				INTERLUDE_X, INTERLUDE_Y,
				0, 1, 1, playerAnimSet, RABBIT::TELEPORTFALLTELEPORT, 2, 6, 2, 1)
	};
	fioCut::createAnimationChain(animationsInterludeRabbit);
}

void initializePlatforms() {
	platforms = array<Platform@>(0);
	// The x argument should represent the location of the left edge of the platform
	// and the y argument should represent the top part of the platform
	array<array<Node@>> nodeSets = {
		{
			Node(TILE * 218 + PLATFORM_OFFSET_X, TILE * 116 + PLATFORM_OFFSET_Y),
			Node(TILE * 232 + PLATFORM_OFFSET_X, TILE * 116 + PLATFORM_OFFSET_Y),
			Node(TILE * 232 + PLATFORM_OFFSET_X, TILE * 131 + PLATFORM_OFFSET_Y),
			Node(TILE * 245 + PLATFORM_OFFSET_X, TILE * 131 + PLATFORM_OFFSET_Y),
			Node(TILE * 245 + PLATFORM_OFFSET_X, TILE * 133 + PLATFORM_OFFSET_Y),
			Node(TILE * 218 + PLATFORM_OFFSET_X, TILE * 133 + PLATFORM_OFFSET_Y)
		},
		{
			Node(TILE * 245 + PLATFORM_OFFSET_X, TILE * 131 + PLATFORM_OFFSET_Y),
			Node(TILE * 245 + PLATFORM_OFFSET_X, TILE * 133 + PLATFORM_OFFSET_Y),
			Node(TILE * 218 + PLATFORM_OFFSET_X, TILE * 133 + PLATFORM_OFFSET_Y),
			Node(TILE * 218 + PLATFORM_OFFSET_X, TILE * 116 + PLATFORM_OFFSET_Y),
			Node(TILE * 232 + PLATFORM_OFFSET_X, TILE * 116 + PLATFORM_OFFSET_Y),
			Node(TILE * 232 + PLATFORM_OFFSET_X, TILE * 131 + PLATFORM_OFFSET_Y)
		},
		{
			Node(TILE * 139 + PLATFORM_OFFSET_X, TILE * 25 + PLATFORM_OFFSET_Y),
			Node(TILE * 145 + PLATFORM_OFFSET_X, TILE * 19 + PLATFORM_OFFSET_Y)
		},
		{
			Node(TILE * 139 + PLATFORM_OFFSET_X, TILE * 12 + PLATFORM_OFFSET_Y),
			Node(TILE * 145 + PLATFORM_OFFSET_X, TILE * 18 + PLATFORM_OFFSET_Y)
		},
		{
			Node(TILE * 137 + PLATFORM_OFFSET_X, TILE * 12 + PLATFORM_OFFSET_Y),
			Node(TILE * 128 + PLATFORM_OFFSET_X, TILE * 12 + PLATFORM_OFFSET_Y)
		},
		{
			Node(TILE * 47 + PLATFORM_OFFSET_X, TILE * 87 + PLATFORM_OFFSET_Y),
			Node(TILE * 47 + PLATFORM_OFFSET_X, TILE * 102 + PLATFORM_OFFSET_Y)
		},
		{ // Top level platforms
			Node(TILE * 105 + PLATFORM_OFFSET_X, TILE * 15 + PLATFORM_OFFSET_Y),
			Node(TILE * 94 + PLATFORM_OFFSET_X, TILE * 15 + PLATFORM_OFFSET_Y)
		},
		{
			Node(TILE * 80 + PLATFORM_OFFSET_X, TILE * 15 + PLATFORM_OFFSET_Y),
			Node(TILE * 91 + PLATFORM_OFFSET_X, TILE * 15 + PLATFORM_OFFSET_Y)
		}
	};
	
	array<float> platformSpeeds = {
		2.5,
		2.5,
		2.0,
		2.0,
		1.5,
		2.5,
		2.0,
		2.0
	};
	
	array<uint16> tileIds = {
		10, 13
	};
	
	for (uint i = 0; i < nodeSets.length(); i++) {
		platforms.insertLast(
				Platform(
						jjObjects[jjAddObject(OBJECT::PINKPLATFORM, nodeSets[i][0].x, nodeSets[i][0].y)],
						nodeSets[i],
						16,
						platformSpeeds[i],
						tileIds
				)
		);
	}
}

// Required for each level
bool onCheat(string &in cheat) {
	return fio::handleCheat(cheat, NEXT_LEVEL_FILENAME);
}

// Required for each level
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) {
	if (activeCutscene != CUTSCENE_NONE) {
		fioCut::renderAnimations(canvas);
	}
	fio::renderCommon(play, canvas);
	fioDraw::drawArmoryAtPos(canvas, TILE * 240.5, TILE * 55.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() {
	play.lighting = LIGHTING_TWILIGHT;
	fioDraw::doShowText(0);
}

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

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

void onFunction3() {
	play.lighting = LIGHTING_TWILIGHT;
	jjTriggers[1] = false;
	if (!checkpoints[0].isReached()) {
		checkpoints[0].setReached();
		fioDraw::doShowText(3);
	}
}

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

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

void onFunction6() {
	play.lighting = LIGHTING_TWILIGHT;
}

void onFunction7() {
	play.lighting = LIGHTING_TWILIGHT;
	if (!checkpoints[2].isReached()) {
		checkpoints[2].setReached();
	}
}

void onFunction8() {
	play.lighting = LIGHTING_TWILIGHT;
	if (!checkpoints[3].isReached()) {
		checkpoints[3].setReached();
	}
}

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

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

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

void onFunction12() {
	fioDraw::doShowText(9);
}

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

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

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

void onFunction16() {
	fio::handleLevelCycle(NEXT_LEVEL_FILENAME);
}

// Required for each level
void onLevelBegin() {
	@character = fio::getCharacterWithMindStoneForPlayer(play);
	initializePlatforms();
	initiatePlatformMovement();
	
	if (jjDifficulty == 0) {
		jjTriggers[25] = true;
	}
}

// Required for each level
void onLevelLoad() {
	initializeGlobals(fio5aCheckpoints, 5);
	fioDraw::initializeDrawing(texts, questTexts, true);
	playerAnimSet = fio::getAnimSetForPlayer(jjLocalPlayers[0]);
	
	// Re-assignment via static declaration
	armoryItems = ARMORY_ITEMS;
	
	// Global indice
	armoryItemIndexInvincibility = 6;
	armoryItemIndexPocketCarrot = 7;
	
	armoryItems[armoryItemIndexInvincibility].text = fio::getInvincibilityItemText();
	armoryItems[armoryItemIndexPocketCarrot].text = fio::getPocketCarrotItemText();
	
	// Just to make sure that each one is a unique instance, although I'm not using class level properties anymore
	jjObjectPresets[OBJECT::DESTRUCTSCENERY].behavior = function(obj) { obj.behavior = DestructSceneryWithHealthBar(); };
	
	if (jjDifficulty == 0) {
		jjTriggers[25] = true;
	}
}

// Required for each level
void onLevelReload() {
	MLLE::ReapplyPalette();
	reloadGlobals();
	fioDraw::initializeDrawing(texts, questTexts, true);
	
	initializePlatforms();
	initiatePlatformMovement();
	
	fio::handleLevelReload();
}

// Required for each level
void onMain() {
	fio::controlPressedKeys();
	fioDraw::controlHud();
	
	if (!hasTriggerCrateBottomBeenDestroyed && jjTriggers[8]) {
		hasTriggerCrateBottomBeenDestroyed = true;
		fioDraw::doShowText(5);
		if (!checkpoints[1].isReached()) {
			checkpoints[1].setReached();
		}
	}
	if (!hasTriggerCrateInWallBeenDestroyed && jjTriggers[26]) {
		hasTriggerCrateInWallBeenDestroyed = true;
		fioDraw::doShowText(8);
		if (!checkpoints[5].isReached()) {
			checkpoints[5].setReached();
		}
	}
}

// Required for each level
void onPlayer(jjPLAYER@ play) {
	fio::handlePlayer(play);
	fio::controlQuest();
	
	if (activeCutscene != CUTSCENE_NONE) {
		fioCut::run();
		if (!fioCut::isTickEventsProcessed()) {
			processTickEvents(play);
			fioCut::setTickEventsProcessed(true);
		}
	}
}

void onPlayerInput(jjPLAYER@ play) {
	fio::controlArmoryInput(play);
	fio::controlPlayerInput(play, activeCutscene != CUTSCENE_NONE);
	if (activeCutscene != CUTSCENE_NONE) {
		fioCut::controlPlayerInput(play);
		if (fioCut::isCutsceneSkipInitiatedAfterDelay(play) && hasPlayerWarpedFromPortal) {
			fioCut::setCutsceneSkipInitiated();
			if (activeCutscene == CUTSCENE_INTERLUDE) {
				endCutsceneInterlude();
			}
		}
	}
}

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

void processCutsceneInterlude(jjPLAYER@ play) {
	switch(uint(fioCut::getElapsedCutscene())) {
		case CUTSCENE_SECOND:
			play.warpToID(1);
			break;
		case CUTSCENE_SECOND * 2:
			fioCut::createEventCameraScroll(CUTSCENE_SECOND * 40, INTERLUDE_CAMERA_X, INTERLUDE_CAMERA_Y,
					INTERLUDE_CAMERA_X, INTERLUDE_CAMERA_Y);
			initializeInterludeAnimationChain();
			hasPlayerWarpedFromPortal = true;
			isPlayerHiddenAndUnableToMove = true;
			play.lighting = LIGHTING_TWILIGHT;
			break;
		case CUTSCENE_SECOND * 4:
			fioCut::startTextSliding();
			break;
		case CUTSCENE_SECOND * 100:
			play.lighting = 255;
			break;
		case CUTSCENE_SECOND * 101:
			fio::increaseCutscenesWatchedIfFastForwardWasNotUsed(fioCut::wasFastForwardUsed);
			endCutsceneInterlude();
			break;
	}
}

// Ei kannata tehdä introsta cutsceneä, koska joutuu rakentamaan sucker tubet yms itse uudestaan, mielummin vain pakottaa pelaajan katsomaan sen kerran,
// koska sen jälkeen kuitenkin tulee checkpoint

void processTickEvents(jjPLAYER@ play) {
	switch (activeCutscene) {
		case CUTSCENE_INTERLUDE:
			processCutsceneInterlude(play);
			break;
	}
}