Downloads containing Fio1_b.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 "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;
}