Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Find It Out (Single Player) | Superjazz | Single player | 9.1 |
#include "Fio_common.asc"
#include "Fio_drawing.asc"
#include "Fio_entities.asc"
#include "Fio_globals.asc"
#include "Fio_utils.asc"
const int GATE_FIRST_TRIGGER = 4;
const int GATE_LAST_TRIGGER = 28;
const string NEXT_LEVEL_FILENAME = "Fio1_y.j2l";
const string RACE_MUSIC_FILENAME = "fastrack.j2b";
const array<uint> BOMB_INTERVALS = {
jjDifficulty <= 0 ? 70 : jjDifficulty == 1 ? 60 : jjDifficulty == 2 ? 50 : 45, // Initial
jjDifficulty <= 0 ? 55 : jjDifficulty == 1 ? 50 : jjDifficulty == 2 ? 45 : 35, // Medium
jjDifficulty <= 0 ? 45 : jjDifficulty == 1 ? 40 : jjDifficulty == 2 ? 35 : 25, // Intense
jjDifficulty <= 0 ? 40 : jjDifficulty == 1 ? 35 : jjDifficulty == 2 ? 30 : 20 // Maximum
};
uint elapsed = 0; // Counter for all timed events
int endElapsed = 0; // Counter for playing sounds at the timer's end before killing
int nextGateTrigger = GATE_FIRST_TRIGGER;
bool hasBombRainTextBeenDisplayed = false;
bool hasGateTextBeenDisplayed = false;
bool hasPlayerReachedLevelEnd = false;
bool hasTimerEnded = false;
bool haveWallsCollapsedYet = false;
bool isEarthQuakeActive = false;
bool isFloorBreaking = false;
bool isGateOpening = false;
bool isOutroActive = false;
int startScore = 0;
array<Checkpoint@> fio1bCheckpoints = {
Checkpoint(0, TILE * 21, TILE * 36),
Checkpoint(1, TILE * 145, TILE * 17)
};
array<string> texts = {
"|What? A door appeared behind me by itself? What's going on?",
"|I'm in a dead end. What to do now?",
"|ARGH! BOMBS!",
"|Someone is trying to bury me down in this place. I need to get outta here before it blows up!",
"|This tunnel must have an exit!", // 4
"|WHO ON EARTH PUT THIS STONE HERE? I HAVE NO TIME FOR THIS!",
"|I guess the other side just started to collapse. If I am lucky I can still make it out this way.",
"|A big gate! A switch! Salvation!",
"|COME ON! DON'T BE SO SLOW TO OPEN!",
"|WHAT? MORE BOMBS? WHO THE HECK IS DOING THIS?" // 9
};
void controlActiveBombs() {
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.isActive && obj.eventID == OBJECT::APPLE) {
obj.counter--;
if ((play.xPos - obj.xPos) * (play.xPos - obj.xPos)
+ (play.yPos - obj.yPos) * (play.yPos - obj.yPos) < 16 * 16) {
play.hurt(1);
obj.counter = 0;
}
if (obj.counter <= 0) { // Bomb's death
int explosionId = jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos);
jjObjects[explosionId].determineCurAnim(ANIM::AMMO, 5);
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_EXPL_TNT);
jjDeleteObject(i);
}
}
}
}
void createBombInCeiling() {
jjAddObject(OBJECT::APPLE, TILE * (195.5 + jjRandom() % 8), 64);
}
int getTimerCountByDifficulty(int levelPart) {
if (levelPart == 3) {
return 350;
}
if (levelPart == 2) {
switch (jjDifficulty) {
case 0:
return 2100;
case 2:
return 1750;
case 3:
return 1610; // Not sure how realistic this is yet (Turbo)...
}
return 1960;
}
switch (jjDifficulty) {
case 0:
return 8400;
case 2:
return 7000;
case 3:
return 6300; // Not sure how realistic this is yet (Turbo)...
}
return 7700; // Medium
}
bool onCheat(string &in cheat) {
return fio::handleCheat(cheat, NEXT_LEVEL_FILENAME);
}
bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ canvas) {
fioDraw::animateHud();
fioDraw::drawHud(play, canvas);
return false;
}
bool onDrawLives(jjPLAYER@ play, jjCANVAS@ canvas) {
return true;
}
void onLevelLoad() {
initializeGlobals(fio1bCheckpoints, 0);
fioDraw::initializeDrawing(texts, array<string>(0));
setupBombs();
jjObjectPresets[OBJECT::BOMBCRATE].behavior = BEHAVIOR::CRATE; // Preserve default behavior for this level by overriding initializeGlobals
}
void onLevelReload() {
fioDraw::initializeDrawing(texts, array<string>(0));
setupBombs();
elapsed = 0;
endElapsed = 0;
// The game probably tries to set the current nextGateTrigger while the player is dying and respawning, but fails to do so or something,
// thus it seems that the nextGateTrigger variable needs to be rewinded back to the previous value to ensure all triggers get set after respawn
nextGateTrigger--;
isFloorBreaking = false;
isEarthQuakeActive = false;
isGateOpening = false;
isOutroActive = false;
hasTimerEnded = false;
haveWallsCollapsedYet = false;
jjEnabledASFunctions[0] =
jjEnabledASFunctions[1] =
jjEnabledASFunctions[2] =
jjEnabledASFunctions[5] =
jjEnabledASFunctions[6] =
jjEnabledASFunctions[7] =
jjEnabledASFunctions[8] =
jjEnabledASFunctions[11] =
jjEnabledASFunctions[13] =
jjEnabledASFunctions[14] = true;
if (!fio::handleLevelReload()) {
jjTriggers[1] = false;
}
// Always reset gate triggers since they are glitchy when reloading the level anyway and because it's a nice challenge anyway
for (int i = GATE_FIRST_TRIGGER; i <= GATE_LAST_TRIGGER; ++i) {
jjTriggers[i] = false;
}
nextGateTrigger = GATE_FIRST_TRIGGER;
}
void onFunction0() {
elapsed = 0;
isFloorBreaking = true;
jjTriggers[1] = true;
play.limitXScroll(12, 20);
}
void onFunction1() {
if (!checkpoints[0].isReached()) {
checkpoints[0].setReached();
fioDraw::doShowText(3);
}
jjMusicLoad(RACE_MUSIC_FILENAME);
jjAddObject(OBJECT::TNT, 32 * 22, 32 * 41);
play.limitXScroll(12, 220);
play.timerStart(getTimerCountByDifficulty(1));
}
void onFunction2() {
play.timerStop();
hasPlayerReachedLevelEnd = true;
hasTimerEnded = true;
fio::handleLevelCycle(NEXT_LEVEL_FILENAME);
}
void onFunction3() {
fioDraw::doShowText(4);
}
void onFunction4() {
fioDraw::doShowText(5);
}
void onFunction5() {
play.timerStop();
isFloorBreaking = false;
isEarthQuakeActive = true;
elapsed = 0;
}
void onFunction6() {
jjSamplePriority(SOUND::ROCK_ROCK1);
}
void onFunction7() {
jjSamplePriority(SOUND::ROCK_ROCK1);
}
void onFunction8() {
if (!checkpoints[1].isReached()) {
checkpoints[1].setReached();
}
play.timerStart(play.timerTime + getTimerCountByDifficulty(2));
}
void onFunction9(bool dark) {
if (dark) {
play.lighting = LIGHTING_DARK;
}
else {
play.lighting = LIGHTING_TWILIGHT;
}
}
void onFunction10() {
fioDraw::doShowText(7);
}
void onFunction11() {
play.timerStop();
isEarthQuakeActive = false; // Make sure isEarthQuakeActive and isFloorBreaking functionality is no longer running when elapsed is reset
isFloorBreaking = false;
elapsed = 0;
isGateOpening = true;
}
void onFunction12(bool twilight) {
if (twilight) {
play.lighting = LIGHTING_TWILIGHT;
}
else {
play.lighting = LIGHTING_STANDARD;
}
}
void onFunction13() {
play.cameraFreeze(TILE * 189, TILE * 9, false, true);
}
void onFunction14() {
jjTriggers[30] = true;
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.isActive && obj.eventID == OBJECT::PURPLEGEM) {
if ((play.yPos - obj.yOrg) < TILE * 15 && (play.xPos - obj.xOrg) < TILE * 2) {
obj.state = STATE::FLOATFALL;
}
}
}
}
void onFunction15() {
play.xPos -= TILE;
}
void onMain() {
fioDraw::controlHud();
for (int i = 1; i < jjObjectCount; ++i) {
jjOBJ@ obj = jjObjects[i];
if (obj.eventID == OBJECT::COLLAPSESCENERY
&& obj.state != STATE::DONE // STATE gets set to DONE after KILL by the game
&& abs(obj.xPos - play.xPos) < TILE * 8
&& abs(obj.yPos - play.yPos) < TILE * 8) {
obj.state = STATE::KILL;
}
}
}
void onPlayer(jjPLAYER@ play) {
fio::handlePlayer(play);
if (isFloorBreaking) {
if (elapsed == 50) fioDraw::doShowText(0);
if (elapsed == 470) fioDraw::doShowText(1);
if (elapsed == 650) jjAddObject(OBJECT::TNT, TILE * 14, TILE * 15);
if (elapsed == 680) fioDraw::doShowText(2);
if (elapsed == 700) jjAddObject(OBJECT::TNT, TILE * 19, TILE * 15);
if (elapsed == 750) jjAddObject(OBJECT::TNT, TILE * 24, TILE * 15);
if (elapsed == 800) jjAddObject(OBJECT::TNT, TILE * 29, TILE * 15);
if (elapsed < 801) {
elapsed++;
}
}
if (isEarthQuakeActive) {
if (elapsed == 1)
play.cameraFreeze(TILE * 135.75, TILE * 22, false, true);
if (elapsed == 2)
play.cameraFreeze(TILE * 135.5, TILE * 22, false, true);
if (elapsed == 4)
play.cameraFreeze(TILE * 135.25, TILE * 22, false, true);
if (elapsed == 8)
play.cameraFreeze(TILE * 135.5, TILE * 22, false, true);
if (elapsed == 16)
play.cameraFreeze(TILE * 135.75, TILE*22, false, true);
if (elapsed == 32)
play.cameraUnfreeze();
if (elapsed == 100) fioDraw::doShowText(6);
if (elapsed < 101) {
elapsed++;
}
}
if (isGateOpening) {
controlActiveBombs();
if (elapsed > 0 && elapsed % 100 == 0) {
jjTriggers[nextGateTrigger] = true;
if (nextGateTrigger < GATE_LAST_TRIGGER) {
nextGateTrigger++;
} else {
isGateOpening = false;
isOutroActive = true;
play.timerStart(play.timerTime + getTimerCountByDifficulty(3));
play.cameraUnfreeze(false);
jjAddParticleTileExplosion(203, 18, 236, true);
jjSamplePriority(SOUND::COMMON_DAMPED1);
}
}
if (elapsed == 225 && !hasGateTextBeenDisplayed) {
fioDraw::doShowText(8);
hasGateTextBeenDisplayed = true;
}
if (elapsed == 600 && !hasBombRainTextBeenDisplayed) {
fioDraw::doShowText(9);
hasBombRainTextBeenDisplayed = true;
}
if ((elapsed >= 500 && elapsed < 750 && elapsed % BOMB_INTERVALS[0] == 0)
|| (elapsed >= 750 && elapsed < 1000 && elapsed % BOMB_INTERVALS[1] == 0)
|| (elapsed >= 1000 && elapsed < 1500 && elapsed % BOMB_INTERVALS[2] == 0)
|| (elapsed >= 1500 && elapsed % BOMB_INTERVALS[3] == 0)) {
createBombInCeiling();
}
elapsed++;
} else if (isOutroActive) {
controlActiveBombs();
if (elapsed % BOMB_INTERVALS[3] == 0) {
createBombInCeiling();
}
}
if (hasTimerEnded) {
if (endElapsed > 0 && endElapsed < 70 && endElapsed % 5 == 0) {
jjSamplePriority(SOUND::ROCK_ROCK1);
for (int i = 0; i < 4; i++) {
float randomX = jjRandom() % jjSubscreenWidth;
float randomY = jjRandom() % jjSubscreenHeight;
float explosionX = play.xPos + TILE * (jjSubscreenWidth / TILE / 2) - randomX;
float explosionY = play.yPos + TILE * (jjSubscreenHeight / TILE / 2) - randomY;
jjObjects[jjAddObject(OBJECT::EXPLOSION, explosionX, explosionY)
].determineCurAnim(ANIM::AMMO, 2);
}
}
if (endElapsed == 1 && !haveWallsCollapsedYet) {
const int xOriginTile = int(play.xPos) / TILE - 15;
const int yOriginTile = int(play.yPos) / TILE - 10;
// This won't cover the whole surroundings of the player anyway, probably due to game memory limitations, but whatever
// At least most of the ceiling should get covered, so it should be good enough anyway
for (int y = yOriginTile; y < yOriginTile + 20; ++y) {
for (int x = xOriginTile; x < xOriginTile + 30; ++x) {
uint16 tileId = jjTileGet(4, x, y);
// Not to render air particles or drop the level end parts ;)
if (tileId > 0 && x < 209) {
jjAddParticleTileExplosion(x, y, jjTileGet(4, x, y), true);
}
}
}
jjSamplePriority(SOUND::COMMON_COLLAPS);
haveWallsCollapsedYet = true;
}
if (endElapsed == 70 && !hasPlayerReachedLevelEnd) { // This must not occur twice to prevent insta game over
fio::killPlayer();
}
if (endElapsed < 71) {
endElapsed++;
} else if (endElapsed == 71 && hasPlayerReachedLevelEnd) {
endElapsed = 0;
}
}
}
void onPlayerInput(jjPLAYER@ play) {
fio::controlPlayerInput(play, true);
}
void onPlayerTimerEnd() {
hasPlayerReachedLevelEnd = false;
hasTimerEnded = true;
endElapsed = 0;
}
void onRoast(jjPLAYER@ victim, jjPLAYER@ killer) {
fio::saveTriggerStates();
asPlay.savePlayerProperties(play);
}
void setupBombs() {
jjObjectPresets[OBJECT::APPLE].determineCurAnim(ANIM::AMMO,1);
jjObjectPresets[OBJECT::APPLE].lightType = LIGHT::BRIGHT;
jjObjectPresets[OBJECT::APPLE].objType = 3;
jjObjectPresets[OBJECT::APPLE].counter = 210;
jjObjectPresets[OBJECT::APPLE].animSpeed = 1;
jjObjectPresets[OBJECT::APPLE].light = 3;
jjObjectPresets[OBJECT::APPLE].state = STATE::FLOATFALL;
}
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.