Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Find It Out (Single Player) | Superjazz | Single player | 9.1 |
const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
#include "MLLE-Include-1.6.asc" ///@MLLE-Generated
#pragma require "Fio5_b-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "Inferno1.j2t" ///@MLLE-Generated
#pragma require "Fio5_b.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 };
funcdef void EFFECT_FUNC();
// Requires ANIM::WITCH to be loaded or a Witch event present in the level
// Level-based implementation with custom texts displayed
class PossessedRock : jjBEHAVIORINTERFACE {
float xOrg;
float yOrg;
float transitionElapsed = 0;
float transitionLength = 70;
float transitionSpeedX;
float transitionSpeedY;
PossessedRock(jjOBJ@ obj) {
xOrg = obj.xOrg;
yOrg = obj.yOrg;
obj.behavior = this;
obj.deactivates = false;
obj.playerHandling = HANDLING::SPECIAL;
obj.scriptedCollisions = true;
}
void onBehave(jjOBJ@ obj) override {
if (obj.freeze > 0 && !hasRockFreezeTextBeenShown) {
hasRockFreezeTextBeenShown = true;
}
switch (obj.playerHandling) {
case HANDLING::SPECIAL:
obj.behave(BEHAVIOR::BIGOBJECT);
break;
case HANDLING::SPECIALDONE:
obj.behave(BEHAVIOR::EVA);
if (transitionElapsed > 0) {
transitionElapsed--;
moveTowardsOrigin(obj);
} else {
obj.freeze = 0;
obj.playerHandling = HANDLING::SPECIAL;
// Just to double check
obj.xPos = xOrg;
obj.yPos = yOrg;
}
break;
}
}
void onDraw(jjOBJ@ obj) {
if (obj.playerHandling == HANDLING::SPECIALDONE) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::BLEND_DISSOLVE, 128);
jjDrawRotatedSprite(obj.xPos + 20, obj.yPos - 36, ANIM::WITCH, 1, 55, 512, 2, 2, SPRITE::BLEND_DISSOLVE, 128);
} else if (obj.freeze <= 0) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TINTED, 25);
jjDrawRotatedSprite(obj.xPos + 20, obj.yPos - 36, ANIM::WITCH, 1, 55, 512, 2, 2);
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (@player !is null && player.charCurr == CHAR::FROG) {
jjCharacters[CHAR::FROG].canHurt = true;
// If hit by a frog tongue
if (player.getObjectHitForce(obj) == -1) {
if (!hasRockReturnTextBeenShown) {
fioDraw::doShowText(9);
hasRockReturnTextBeenShown = true;
}
// Set the object's platform behavior to 0 to ensure that the game no longer thinks that the player is standing on the rock when the behavior changes
// (and walk on air, because it seems that the platform property is used to determine that)
obj.clearPlatform();
obj.playerHandling = HANDLING::SPECIALDONE;
transitionElapsed = transitionLength;
float xDistance = xOrg - obj.xPos;
float yDistance = yOrg - obj.yPos;
transitionSpeedX = xDistance / transitionLength;
transitionSpeedY = yDistance / transitionLength;
}
jjCharacters[CHAR::FROG].canHurt = false;
}
return true;
}
// Required to preserve the object stacking behavior on top of each other with custom behavior classes
bool onIsSolid(jjOBJ@ obj) {
return true;
}
private void moveTowardsOrigin(jjOBJ@ obj) {
obj.xPos += transitionSpeedX;
obj.yPos += transitionSpeedY;
}
}
class SummoningFire {
int x;
int y;
int elapsed;
SummoningFire(int x, int y) {
this.x = x;
this.y = y;
elapsed = 0;
}
bool process(jjPLAYER@ play) {
// Offset player location by 16 pixels
if (abs(play.xPos - float(x + 16)) < 32 && abs(play.yPos - float(y)) < 32) {
play.hurt(1);
}
if (elapsed < SUMMONING_FIRE_DURATION) {
elapsed++;
return false;
}
return true;
}
void draw(jjCANVAS@ canvas) {
canvas.drawTile(x, y, 71 + TILE::ANIMATED);
}
}
class SummoningRock {
int x;
int y;
SummoningRock(int x, int y) {
this.x = x + SUMMONING_ROCK_OFFSET;
this.y = y + SUMMONING_ROCK_OFFSET;
}
bool process() {
if (y < SUMMONING_FLOOR_FIRST_Y + SUMMONING_ROCK_OFFSET) {
y += SUMMONING_ROCK_SPEED_Y;
return false;
}
int frame = int(jjAnimations[jjAnimSets[ANIM::ROCK].firstAnim].firstFrame);
jjAddParticlePixelExplosion(float(this.x), float(this.y), frame, 0, 0);
jjSample(float(this.x), float(this.y), SOUND::DEVILDEVAN_DRAGONFIRE);
return true;
}
void draw(jjCANVAS@ canvas) {
canvas.drawRotatedSprite(x, y, ANIM::ROCK, 0, 0, jjGameTicks % 1024 * 24, 0.5, 0.5, SPRITE::TINTED, 24);
}
}
class SwitchStone {
int x;
int y;
bool isOn = false;
EFFECT_FUNC@ effectFunc;
SwitchStone(int x, int y, EFFECT_FUNC@ effectFunc) {
this.x = x;
this.y = y;
@this.effectFunc = effectFunc;
}
void draw(jjCANVAS@ canvas) {
canvas.drawTile(x, y + STONE_OFFSET_Y, isOn ? 65 : 66);
canvas.drawTile(x, y - 32 + STONE_OFFSET_Y, isOn ? 55 : 56);
}
void switchOnIfPlayerInRangeAndInterludeWatched(jjPLAYER@ play) {
// Offset player location by 16 pixels
if (wasInterludeWatched && !isOn && abs(play.xPos - float(x + 16)) < 24 && abs(play.yPos - float(y)) < 32) {
isOn = true;
jjSamplePriority(SOUND::COMMON_BASE1);
effectFunc();
}
}
}
const float DURATION_FADE_TOTAL = CUTSCENE_SECOND * 1;
const float DURATION_FADE_BLACKOUT = CUTSCENE_SECOND * 0.5;
const float FRAME_RATE_INTERLUDE_RABBIT = 2;
const float INTERLUDE_X = TILE * 63;
const float INTERLUDE_Y = TILE * 85;
const float PLATFORM_OFFSET_X = -7;
const float PLATFORM_OFFSET_Y = 17;
const float RABBIT_FRIGHTENED_DURATION = CUTSCENE_SECOND * 20;
const float RABBIT_STILL_DURATION = CUTSCENE_SECOND * 7 - 15;
const int FLOOR_MOTION_INTERVAL = 5;
const int FLOOR_MOTION_LENGTH = 15;
const int FLOOR_MOTION_START_X = 63;
const int STONE_OFFSET_Y = 8;
const int SUMMONING_FIRE_DURATION = 140;
const int SUMMONING_FLOOR_FIRST_Y = TILE * 85;
const int SUMMONING_ROCK_SPEED_Y = 8;
const int SUMMONING_ROCK_OFFSET = 16;
const uint SUMMONING_RANGE_MAX_Y = 68;
const uint SUMMONING_RANGE_MIN_Y = 52;
const string NEXT_LEVEL_FILENAME = "Fio5_y.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
};
const array<Checkpoint@> FIO5_B_CHECKPOINTS = {
Checkpoint(0, INTERLUDE_X, INTERLUDE_Y),
Checkpoint(1, TILE * 63, TILE * 91),
Checkpoint(2, TILE * 63, TILE * 49),
Checkpoint(3, TILE * 21, TILE * 17)
};
const array<OBJECT::Object> SUMMONING_ENEMIES = {
OBJECT::DEMON,
OBJECT::DOGGYDOGG,
OBJECT::DRAGON,
OBJECT::HELMUT,
OBJECT::SKELETON,
OBJECT::SUCKER
};
const array<SwitchStone@> SWITCH_STONES = {
SwitchStone(TILE * 51, TILE * 85, @summonEnemiesFromTheSky),
SwitchStone(TILE * 57, TILE * 85, @summonEnemiesFromTheSky),
SwitchStone(TILE * 69, TILE * 85, @summonEnemiesFromTheSky),
SwitchStone(TILE * 75, TILE * 85, @summonEnemiesFromTheSky),
SwitchStone(TILE * 49, TILE * 91, @summonHellKnightsPart),
SwitchStone(TILE * 77, TILE * 91, @summonHellKnightsPart)
};
bool areHellKnightsActive = false;
bool areHellKnightsBeingSummoned = false;
bool areHellKnightsDefeated = false;
bool hasHellKnightsTextBeenShown = false;
bool hasPlatformTextBeenShown = false;
bool hasRockFreezeTextBeenShown = false;
bool hasRockReturnTextBeenShown = false;
bool hasSwitchStoneTextBeenDisplayed = false;
bool isFloorMotionActive = false;
bool isPlayerWaitingToBeWarpedAway = false;
bool isUpperSummoningFloorDefeated = false;
bool wasInterludeWatched = false;
int floorMotionOffsetX = 1;
int hellKnightSummoningElapsed = 0;
int playerWarpDelayElapsed = 0;
uint8 activeCutscene = uint8(CUTSCENE_NONE);
ANIM::Set playerAnimSet;
CharacterWithMindStone@ character;
HellKnight@ hellKnight1;
HellKnight@ hellKnight2;
array<uint> SUMMONING_RANGES_X = { 50, 57, 63, 69, 76 };
array<string> cutsceneTextsInterlude = {
"||Greetings wanderer! Searching for someone? You'll find him from my lair! But before that, I want to test you a little more, to be sure that you're really worth it.",
"|WHAT? WHO IS SPEAKING? SHOW YOURSELF!",
"|That was no voice of Nicholas for sure. I may be facing a greater evil who wants to stop me right here where I am.",
"|What is this room full of stones that look like my mindstone anyway?"
};
array<string> texts = {
"|I really wonder why the demons decided to move Nicholas away from his cell. Why this move out of sudden?",
"|I would really like to know what the demons use to move prisoners, while I have to creep through canyons full of traps like these.",
"|Of course it's a TRAP! Or even a game? I guess my only choice is to kill all of these minions.",
"", // Unused after migration to cutscene texts
"", // 4th // Unused after migration to cutscene texts
"|What? Platforms outta nowhere? I can't get anywhere with those...",
"|What's happening now? Oh...it's these ones again!",
"||Let's see how far can you run from my minions under a mutation spell! Hah hah!",
"||Well! I've never met someone like you! I hope you can still push me around a bit. Stick your tongue into my eyes and I will go back.",
"||Oof! Now that was a WET tongue!", // 9th
"|Phew, finally back to my own form. How many more games do I have to play to find Nicholas and get outta here?"
};
array<jjOBJ@> summonedEnemies;
array<Platform@> platforms;
array<SummoningFire@> summoningFires;
array<SummoningRock@> summoningRocks;
bool areHellKnightsDead() {
return @hellKnight1 !is null && hellKnight1.isDead
&& @hellKnight2 !is null && hellKnight2.isDead;
}
bool areAllUpperFloorSwitchStonesTurnedOn() {
for (uint i = 0; i < 4; i++) {
if (!SWITCH_STONES[i].isOn) {
return false;
}
}
return true;
}
void controlSummoningFloors() {
if (!jjTriggers[14]
&& areAllUpperFloorSwitchStonesTurnedOn()
&& summoningFires.length() == 0
&& summoningRocks.length() == 0
&& isFirstSummoningFloorClearOfSummonedEnemies()) {
jjTileSet(4, 63, 86, 0); // Start with the middle tile
isFloorMotionActive = true;
isUpperSummoningFloorDefeated = true;
if (!checkpoints[1].isReached()) {
checkpoints[1].setReached();
}
}
}
void controlSwitchStones(jjPLAYER@ play) {
for (uint i = 0; i < SWITCH_STONES.length(); i++) {
SWITCH_STONES[i].switchOnIfPlayerInRangeAndInterludeWatched(play);
}
}
void createHellKnights() {
// Make sure the total health doesn't exceed 127 as that's the limitation with obj.energy here (which will break the boss)
@hellKnight1 = HellKnight(jjObjects[jjAddObject(OBJECT::EVA, TILE * 58, TILE * 87)], jjDifficulty * 25 + 45);
@hellKnight2 = HellKnight(jjObjects[jjAddObject(OBJECT::EVA, TILE * 69, TILE * 87)], jjDifficulty * 25 + 45);
}
void drawSummoningFires(jjCANVAS@ canvas) {
for (uint i = 0; i < summoningFires.length(); i++) {
summoningFires[i].draw(canvas);
}
}
void drawSummoningRocks(jjCANVAS@ canvas) {
for (uint i = 0; i < summoningRocks.length(); i++) {
summoningRocks[i].draw(canvas);
}
}
void drawSwitchStones(jjCANVAS@ canvas) {
for (uint i = 0; i < SWITCH_STONES.length(); i++) {
SWITCH_STONES[i].draw(canvas);
}
}
void endCutsceneInterlude() {
wasInterludeWatched = true;
fioCut::endCutscene(play.xPos, play.yPos);
play.lighting = LIGHTING_TWILIGHT;
activeCutscene = CUTSCENE_NONE;
fioUtils::releasePlayer();
}
int getPointRewardByDifficulty() {
if (jjDifficulty >= 3) return 30000;
if (jjDifficulty == 2) return 10000;
if (jjDifficulty == 1) return 1000;
return 500;
}
RABBIT::Anim getRabbitAnimForFrightenedState() {
if (play.charCurr == CHAR::LORI || play.charCurr == CHAR::SPAZ) {
return RABBIT::DIVE;
}
// Default Jazz
return RABBIT::ENDOFLEVEL;
}
int getRandomUnusedLocationFromRange(uint range, array<uint> usedLocations) {
uint random = jjRandom() % range;
while (usedLocations.find(random) >= 0) {
random = jjRandom() % range;
}
return random;
}
void initiatePlatformMovement() {
for (uint i = 0; i < platforms.length(); i++) {
platforms[i].obj.state = STATE::FADEIN;
}
}
void initializeHellKnights() {
hellKnightSummoningElapsed = 0;
createHellKnights();
areHellKnightsBeingSummoned = true;
isPlayerUnableToMove = true;
play.cameraFreeze(TILE * 63.5, TILE * 90, true, false);
if (!hasHellKnightsTextBeenShown) {
fioDraw::doShowText(6);
hasHellKnightsTextBeenShown = true;
}
}
void initializeInterlude() {
fioCut::initializeCutscene(@processTickEvents, cutsceneTextsInterlude);
fioCut::setTickEventsProcessed(true);
play.noFire = 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(RABBIT_STILL_DURATION,
play.xPos, play.yPos,
play.xPos, play.yPos,
0, 1, 1, playerAnimSet, character.idleAnimation, character.idleFrame, character.idleFrame, 1),
fioCut::Animation(RABBIT_FRIGHTENED_DURATION,
play.xPos, play.yPos,
play.xPos, play.yPos,
0, 1, 1, playerAnimSet, getRabbitAnimForFrightenedState(), 0, 0, 1)
};
fioCut::createAnimationChain(animationsInterludeRabbit);
}
void initializePossessedRocks() {
PossessedRock(jjObjects[jjAddObject(OBJECT::BIGROCK, TILE * 81.5, TILE * 37.75)]);
PossessedRock(jjObjects[jjAddObject(OBJECT::BIGROCK, TILE * 81.5, TILE * 55.75)]);
PossessedRock(jjObjects[jjAddObject(OBJECT::BIGROCK, TILE * 99.5, TILE * 37.75)]);
PossessedRock(jjObjects[jjAddObject(OBJECT::BIGROCK, TILE * 99.5, TILE * 55.75)]);
PossessedRock(jjObjects[jjAddObject(OBJECT::BIGROCK, TILE * 129.5, TILE * 73.75)]);
PossessedRock(jjObjects[jjAddObject(OBJECT::BIGROCK, TILE * 68.5, TILE * 19.75)]);
PossessedRock(jjObjects[jjAddObject(OBJECT::BIGROCK, TILE * 111.5, TILE * 19.75)]);
PossessedRock(jjObjects[jjAddObject(OBJECT::BIGROCK, TILE * 135.5, TILE * 19.75)]);
}
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 * 20 + PLATFORM_OFFSET_X, TILE * 66 + PLATFORM_OFFSET_Y),
Node(TILE * 33 + PLATFORM_OFFSET_X, TILE * 66 + PLATFORM_OFFSET_Y),
Node(TILE * 33 + PLATFORM_OFFSET_X, TILE * 120 + PLATFORM_OFFSET_Y),
Node(TILE * 20 + PLATFORM_OFFSET_X, TILE * 120 + PLATFORM_OFFSET_Y)
},
{
Node(TILE * 33 + PLATFORM_OFFSET_X, TILE * 120 + PLATFORM_OFFSET_Y),
Node(TILE * 20 + PLATFORM_OFFSET_X, TILE * 120 + PLATFORM_OFFSET_Y),
Node(TILE * 20 + PLATFORM_OFFSET_X, TILE * 66 + PLATFORM_OFFSET_Y),
Node(TILE * 33 + PLATFORM_OFFSET_X, TILE * 66 + PLATFORM_OFFSET_Y)
},
{
Node(TILE * 38 + PLATFORM_OFFSET_X, TILE * 122 + PLATFORM_OFFSET_Y),
Node(TILE * 51 + PLATFORM_OFFSET_X, TILE * 122 + PLATFORM_OFFSET_Y)
},
{
Node(TILE * 84 + PLATFORM_OFFSET_X, TILE * 123 + PLATFORM_OFFSET_Y),
Node(TILE * 97 + PLATFORM_OFFSET_X, TILE * 123 + PLATFORM_OFFSET_Y)
},
{
Node(TILE * 101 + PLATFORM_OFFSET_X, TILE * 123 + PLATFORM_OFFSET_Y),
Node(TILE * 105 + PLATFORM_OFFSET_X, TILE * 126 + PLATFORM_OFFSET_Y),
Node(TILE * 109 + PLATFORM_OFFSET_X, TILE * 123 + PLATFORM_OFFSET_Y),
Node(TILE * 104 + PLATFORM_OFFSET_X, TILE * 126 + PLATFORM_OFFSET_Y)
},
{
Node(TILE * 117 + PLATFORM_OFFSET_X, TILE * 115 + PLATFORM_OFFSET_Y),
Node(TILE * 114 + PLATFORM_OFFSET_X, TILE * 119 + PLATFORM_OFFSET_Y),
Node(TILE * 109 + PLATFORM_OFFSET_X, TILE * 123 + PLATFORM_OFFSET_Y),
Node(TILE * 113 + PLATFORM_OFFSET_X, TILE * 119 + PLATFORM_OFFSET_Y)
},
{
Node(TILE * 121 + PLATFORM_OFFSET_X, TILE * 115 + PLATFORM_OFFSET_Y),
Node(TILE * 141 + PLATFORM_OFFSET_X, TILE * 115 + PLATFORM_OFFSET_Y)
},
{
Node(TILE * 145 + PLATFORM_OFFSET_X, TILE * 115 + PLATFORM_OFFSET_Y),
Node(TILE * 161 + PLATFORM_OFFSET_X, TILE * 113 + PLATFORM_OFFSET_Y),
Node(TILE * 162 + PLATFORM_OFFSET_X, TILE * 115 + PLATFORM_OFFSET_Y),
Node(TILE * 147 + PLATFORM_OFFSET_X, TILE * 117 + PLATFORM_OFFSET_Y)
}
};
array<float> platformSpeeds = {
2.0,
2.0,
2.0,
2.0,
2.0,
2.0,
2.5,
2.5
};
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
)
);
}
}
bool isFirstSummoningFloorClearOfSummonedEnemies() {
return summonedEnemies.length() == 0;
}
// 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);
drawSwitchStones(canvas);
drawSummoningFires(canvas);
drawSummoningRocks(canvas);
fioDraw::drawArmoryAtPos(canvas, TILE * 177.5, TILE * 19.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;
}
void onFunction1() {
fioDraw::doShowText(0);
}
void onFunction2() {
fioDraw::doShowText(10);
}
void onFunction3() {
fioDraw::doShowText(1);
}
void onFunction4() {
isPlayerUnableToMove = true;
play.warpToID(1);
}
void onFunction5() {
play.lighting = LIGHTING_TWILIGHT;
if (!checkpoints[0].isReached()) {
checkpoints[0].setReached();
initializeInterlude();
}
}
void onFunction6() {
fioDraw::doShowText(7);
}
// onFunction7() obsolete
void onFunction8() {
fioDraw::doShowText(8);
}
void onFunction9() {
if (!checkpoints[3].isReached()) {
checkpoints[3].setReached();
}
play.revertMorph();
play.warpToID(3);
}
// onFunction10() obsolete
void onFunction11(bool show) {
if (show) {
play.noFire = true;
isPlayerInArmory = true;
} else {
play.noFire = false;
isPlayerInArmory = false;
}
}
void onFunction12() {
fio::handleLevelCycle(NEXT_LEVEL_FILENAME);
}
// Required for each level
void onLevelBegin() {
@character = fio::getCharacterWithMindStoneForPlayer(play);
initializePlatforms();
initiatePlatformMovement();
initializePossessedRocks();
if (jjDifficulty == 0) {
jjTriggers[25] = true;
}
}
// Required for each level
void onLevelLoad() {
// NOTE TO SELF: Make sure to initialize globals (custom object preset behaviors) in onLevelLoad instead of onLevelBegin, since apparently
// some events/objects may already be placed in the level prior to running that hook, but initializing things here should ensure that all objects get
// the custom behavior presets set
initializeGlobals(FIO5_B_CHECKPOINTS);
fioDraw::initializeDrawing(texts, array<string>(0), 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();
jjAnimSets[ANIM::WITCH].load();
jjGenerateSettableTileArea(4, 49, 86, 29, 1);
if (jjDifficulty <= 0) {
jjTriggers[13] = true;
}
}
// Required for each level
void onLevelReload() {
MLLE::ReapplyPalette();
reloadGlobals();
fioDraw::initializeDrawing(texts, array<string>(0), true);
initializePlatforms();
initializePossessedRocks();
initiatePlatformMovement();
fio::handleLevelReload();
if (!isUpperSummoningFloorDefeated) {
for (uint i = 0; i < 4; i++) {
SWITCH_STONES[i].isOn = false;
}
}
if (!areHellKnightsDefeated) {
for (uint i = 4; i < 6; i++) {
SWITCH_STONES[i].isOn = false;
}
jjTriggers[15] = false;
}
// Just to ensure that the arrays does not go into some broken state if the player dies during the summoning floor fight
summonedEnemies = array<jjOBJ@>(0);
summoningFires = array<SummoningFire@>(0);
summoningRocks = array<SummoningRock@>(0);
}
// Required for each level
void onMain() {
fio::controlPressedKeys();
fioDraw::controlHud();
if (!isUpperSummoningFloorDefeated) {
controlSummoningFloors();
processSummonedEnemies();
processSummoningFires();
processSummoningRocks();
}
}
// Required for each level
void onPlayer(jjPLAYER@ play) {
fio::handlePlayer(play);
if (activeCutscene != CUTSCENE_NONE) {
fioCut::run();
if (!fioCut::isTickEventsProcessed()) {
processTickEvents(play);
fioCut::setTickEventsProcessed(true);
}
}
controlSwitchStones(play);
// TODO: Check wtf is wrong with these...Sometimes randomly doesn't seem to work...Add some debug lines with timestamp true...
// Voisit harkita esim. jonkin "secret" tilen laittamista areenan seinään, josta saisi tarvittaessa debug datat ulos kun logiikka kusee ja sama varuiksi yläkertaan...
if (areHellKnightsActive && areHellKnightsDead()) {
areHellKnightsActive = false;
areHellKnightsDefeated = true;
jjTriggers[15] = false;
isPlayerWaitingToBeWarpedAway = true;
playerWarpDelayElapsed = 0;
fio::rewardPoints(getPointRewardByDifficulty());
if (!checkpoints[2].isReached()) {
checkpoints[2].setReached();
}
}
if (areHellKnightsBeingSummoned) {
if (hellKnightSummoningElapsed < 350) {
hellKnightSummoningElapsed++;
} else {
fioUtils::releasePlayer();
areHellKnightsBeingSummoned = false;
areHellKnightsActive = true;
hellKnight1.appearElapsed = 350;
hellKnight2.appearElapsed = 350;
play.cameraUnfreeze(false);
}
}
if (isFloorMotionActive) {
if (jjGameTicks % FLOOR_MOTION_INTERVAL == 0 && floorMotionOffsetX < FLOOR_MOTION_LENGTH) {
jjTileSet(4, 63 + floorMotionOffsetX, 86, 0);
jjTileSet(4, 63 - floorMotionOffsetX, 86, 0);
floorMotionOffsetX++;
} else if (floorMotionOffsetX >= FLOOR_MOTION_LENGTH) {
isFloorMotionActive = false;
}
}
if (isPlayerWaitingToBeWarpedAway) {
if (playerWarpDelayElapsed < 105) {
playerWarpDelayElapsed++;
} else {
isPlayerWaitingToBeWarpedAway = false;
play.warpToTile(63, 49);
}
}
}
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_INTERLUDE) {
endCutsceneInterlude();
}
}
}
}
void onRoast(jjPLAYER@ victim, jjPLAYER@ killer) {
fio::saveTriggerStates();
asPlay.savePlayerProperties(play);
}
void processCutsceneInterlude(jjPLAYER@ play) {
switch(uint(fioCut::getElapsedCutscene())) {
case CUTSCENE_SECOND:
isPlayerHiddenAndUnableToMove = true;
initializeInterludeAnimationChain();
fioCut::startTextSliding();
break;
case CUTSCENE_SECOND * 26 + 10:
endCutsceneInterlude();
fio::increaseCutscenesWatchedIfFastForwardWasNotUsed(fioCut::wasFastForwardUsed);
break;
}
}
void processTickEvents(jjPLAYER@ play) {
switch (activeCutscene) {
case CUTSCENE_INTERLUDE:
processCutsceneInterlude(play);
break;
}
}
void processSummonedEnemies() {
uint j = 0;
uint summonedEnemiesLength = summonedEnemies.length();
for (uint i = 0; i < summonedEnemiesLength; i++) {
if (summonedEnemies[i].isActive && summonedEnemies[i].state != STATE::KILL) {
@summonedEnemies[j] = summonedEnemies[i];
j++;
}
}
summonedEnemies.removeRange(j, summonedEnemiesLength - j);
}
void processSummoningFires() {
uint j = 0;
uint summoningFiresLength = summoningFires.length();
for (uint i = 0; i < summoningFiresLength; i++) {
if (!summoningFires[i].process(play)) {
@summoningFires[j] = summoningFires[i];
j++;
}
}
summoningFires.removeRange(j, summoningFiresLength - j);
}
void processSummoningRocks() {
uint j = 0;
uint summoningRocksLength = summoningRocks.length();
for (uint i = 0; i < summoningRocksLength; i++) {
if (!summoningRocks[i].process()) {
@summoningRocks[j] = summoningRocks[i];
j++;
} else {
if (!hasSwitchStoneTextBeenDisplayed) {
fioDraw::doShowText(2);
hasSwitchStoneTextBeenDisplayed = true;
}
summoningFires.insertLast(
SummoningFire(
summoningRocks[i].x - SUMMONING_ROCK_OFFSET,
summoningRocks[i].y - SUMMONING_ROCK_OFFSET
)
);
jjOBJ@ enemyObj = jjObjects[
jjAddObject(
SUMMONING_ENEMIES[jjRandom() % SUMMONING_ENEMIES.length()],
float(summoningRocks[i].x),
float(summoningRocks[i].y)
)
];
summonedEnemies.insertLast(enemyObj);
enemyObj.direction = play.xPos < enemyObj.xPos ? -1 : 1;
enemyObj.points = 0;
// Dragons cannot walk :-)
if (enemyObj.eventID != OBJECT::DRAGON) {
enemyObj.state = STATE::WALK;
}
}
}
summoningRocks.removeRange(j, summoningRocksLength - j);
}
void summonEnemiesFromTheSky() {
array<uint> usedYLocations = array<uint>(0);
// Hacky hack :)
for (uint i = 0; i < SUMMONING_RANGES_X.length() - 1; i++) {
uint amount = jjDifficulty * 1 + 1;
array<uint> usedXLocations = array<uint>(0);
for (uint j = 0; j < amount; j++) {
uint randomX = getRandomUnusedLocationFromRange(SUMMONING_RANGES_X[i + 1] - SUMMONING_RANGES_X[i], usedXLocations);
uint randomY = getRandomUnusedLocationFromRange((SUMMONING_RANGE_MAX_Y - SUMMONING_RANGE_MIN_Y), usedYLocations);
usedXLocations.insertLast(randomX);
usedYLocations.insertLast(randomY);
summoningRocks.insertLast(
SummoningRock(TILE * (SUMMONING_RANGES_X[i] + randomX), TILE * (SUMMONING_RANGE_MIN_Y + randomY))
);
}
}
}
void summonHellKnightsPart() {
if (!jjTriggers[15]) {
jjTriggers[15] = true;
if (!hasPlatformTextBeenShown) {
fioDraw::doShowText(5);
hasPlatformTextBeenShown = true;
}
} else {
initializeHellKnights();
}
}
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.