Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Find It Out (Single Player) | Superjazz | Single player | 9.1 |
/* Common global values for all levels */
#include "Fio_entities.asc"
#include "Fio_utils.asc"
// Custom states for more detail besides obj.state
enum StateAttack { STATE_ATTACK_CHARGE, STATE_ATTACK_FLY, STATE_ATTACK_RETURN };
enum StateBall { STATE_BALL_APPEAR, STATE_BALL_DEFAULT, STATE_BALL_FALL };
enum StateHellKnight { STATE_HELL_KNIGHT_APPEAR, STATE_HELL_KNIGHT_ATTACK_EXPAND_RING,
STATE_HELL_KNIGHT_ATTACK_FOCUS_RING, STATE_HELL_KNIGHT_IDLE, STATE_HELL_KNIGHT_DIE };
enum StateStaticPlayerRender {
STATE_STATIC_PLAYER_RENDER_OFF,
STATE_STATIC_PLAYER_RENDER_IDLE,
STATE_STATIC_PLAYER_RENDER_FOCUSED,
STATE_STATIC_PLAYER_RENDER_FRIGHTENED,
STATE_STATIC_PLAYER_RENDER_FALLING
};
enum StateRabbit { STATE_RABBIT_CONFUSED, STATE_RABBIT_DEFAULT, STATE_RABBIT_FALL,
STATE_RABBIT_FRIGHTENED, STATE_RABBIT_HURT, STATE_RABBIT_RUN, STATE_RABBIT_SKID,
STATE_RABBIT_TELEIN, STATE_RABBIT_TELEOUT, STATE_RABBIT_WAITING };
const bool isDebugModeOn = false;
const float BOSS_HEALTH_BAR_OFFSET_X = 40.f;
const float DESTRUCT_SCENERY_HEALTH_BAR_OFFSET_X = 20.f;
const float DESTRUCT_SCENERY_HEALTH_BAR_OFFSET_Y = 20.f;
const float DEFAULT_GEM_SCALE = 1.f;
const int ARMORY_ITEM_MOTION_DURATION = 35;
const int ARROW_UP_ANGLE = 190;
const int BOSS_HEALTH_BAR_LENGTH = 80;
const int DESTRUCT_SCENERY_HEALTH_BAR_LENGTH = 40.f;
const int FRAME_RATE_INTRO_RABBIT = 2;
const int INVINCIBILITY_DURATION = SECOND * 10;
const int LIGHTING_DARK = 60;
const int LIGHTING_STANDARD = 100;
const int LIGHTING_TWILIGHT = 80;
const int MAXIMUM_AMMO = 99;
const int MAXIMUM_HEADER_OPACITY = 255;
const int MINIMUM_HEADER_OPACITY = 80; // For some weird reason the real opacity resets to minimum at somewhere around value 96 o_o
const int PLAYER_POINT_LOSS_FOR_DEATH = 1000;
const uint8 CUSTOM_ENEMY_JUST_HIT_LENGTH = 5;
const uint8 HUD_BAR_BACKGROUND_COLOR = 47;
const uint8 HUD_BAR_BORDER_COLOR = 24;
const uint8 KEY_CODE_ARROW_DOWN = 40;
const uint8 KEY_CODE_ARROW_UP = 38;
const uint8 KEY_CODE_P = 80;
const uint8 KEY_CODE_ENTER = 13;
const uint8 KEY_CODE_I = 73;
const uint8 KEY_CODE_SPACE = 32;
const uint8 MAX_INVINCIBILITIES = 1; // Max 1 to balance boss fights
const uint8 MAX_POCKET_CARROTS = 3; // Should not be too many to avoid health stacking
const uint8 MINDSTONE_COMMUNICATION_BACKGROUND_COLOR = 31;
const uint CYCLE_DELAY_FINISH = 35;
const uint CUTSCENE_SECOND = 35;
const uint SECOND = 70;
const uint TILE = 32;
const string BOSS_THEME_FILENAME = "Boss2.j2b";
const string KEY_MOUSE_LEFT_CLICK = "left mouse";
const string GAME_SESSIONS_FILENAME = "Fio_game_sessions.asdat";
const string RESPAWN_WARNING_TEXT = "||||Welcome back to life! However, Although your lives are infinite, beware of dying too much, "
+ "for every respawn from now on will cost you ||||||" + PLAYER_POINT_LOSS_FOR_DEATH + "|| points, down to score 0.";
float playerXPosOnHold;
float playerYPosOnHold;
int armoryItemIndexInvincibility = -1;
int armoryItemIndexPocketCarrot = -1;
int armoryItemsMotionElapsed = 0; // Upper level counter to ensure that player cannot change selected armory item during motion
int idlePlayerDirection = 0;
int questGemsInLevel;
int questRewardPoints;
int questPerfectRewardPoints;
int totalGemsInLevel;
uint selectedArmoryItem = 0;
// Quick hack for rendering static falling animation
uint8 playerFallingFrame = 0;
array<uint16> mindstoneCommunicationTileIds;
bool areArmoryItemsInMotion = false;
bool bonusRewardGiven = false;
bool isCustomBossActivated = false;
bool forceMoveRight = false;
bool hasLevelCycleBeenInitiated = false; // To ensure that the process won't get called multiple tiles, e.g. from multiple text event functions
bool isPlayerInArmory = false;
bool isPlayerHiddenAndUnableToMove = false;
bool isPlayerUnableToMove = false;
bool keyFireReleased = true;
bool perfectBonusRewardGiven = false;
bool playerPropertiesReinitialized = false;
bool shouldStorePlayerEquipment = true; // To be set to false when the player enters a boss fight to preserve ammo upon death
bool wasEquipmentPreserveAlertDisplayed = false;
array<bool> activeTriggers(32, false);
array<Key> keys(256);
array<ArmoryItem@> armoryItems;
array<Checkpoint@> checkpoints;
array<GameSession@> gameSessions;
jjPLAYER@ play = jjLocalPlayers[0];
jjTEXTAPPEARANCE centeredText();
StateStaticPlayerRender staticPlayerRenderState = STATE_STATIC_PLAYER_RENDER_OFF;
GameSession@ currentGameSession;
Player@ asPlay;
void calculateTotalCoinAmountFromLevelForDebug() {
jjLAYER@ spriteLayer = jjLayers[4];
int count = 0;
for (int y = 0; y < spriteLayer.height; ++y) {
// No tileWidth checkbox used for sprite layers so far
for (int x = 0; x < spriteLayer.width; ++x) {
int eventID = jjEventGet(uint16(x), uint16(y));
if (eventID == OBJECT::SILVERCOIN) {
count++;
} else if (eventID == OBJECT::GOLDCOIN) {
count += 5;
}
}
}
jjAlert("total coin count: " + count);
}
int calculateTotalGemAmountFromLevel() {
jjLAYER@ spriteLayer = jjLayers[4];
int count = 0;
for (int y = 0; y < spriteLayer.height; ++y) {
// No tileWidth checkbox used for sprite layers so far
for (int x = 0; x < spriteLayer.width; ++x) {
int eventID = jjEventGet(uint16(x), uint16(y));
if (eventID == OBJECT::PURPLEGEM) {
count++;
} else if (eventID == OBJECT::GEMBARREL || eventID == OBJECT::GEMCRATE) {
if (isDebugModeOn) {
jjAlert("Barrel or crate gem count: " + (jjParameterGet(x, y, 12, 4)));
}
count += jjParameterGet(x, y, 12, 4);
}
}
}
if (isDebugModeOn) {
jjAlert("total purple gem count: " + count);
}
return count;
}
int getRandomUnusedGameId() {
if (gameSessions.length() > 10000) {
jjAlert("|WARNING: The amount of stored game sessions became dangerously high (over 10 thousand)!");
jjAlert("|Finishing the level will cause existing game sessions to be cleared out to avoid memory issues.");
jjAlert("|This may be a cause of new game sesions not being started and stored naturally");
jjAlert("|or you have been playing like a maniac.");
gameSessions = array<GameSession@>(0);
}
int randomId = jjRandom();
// There should be way less than 100k food available throughout the whole episode, so the real player food value should never conflict with the ID
while (randomId < 100000 || isIdExisting(randomId)) {
randomId = jjRandom();
}
return randomId;
}
void initializeGameSessions(bool shouldLevelCycleWriteNewGameSessionToFile) {
fioUtils::readGameSessionsFromFile();
@currentGameSession = fioUtils::getCurrentGameSessionByPlayerFood(play);
if (@currentGameSession is null) {
// Always ensure a non-null handle on currentGameSession so that we don't have to perform null-checks everywhere,
// but instead skip this one from the gameSessions that get written on file if shouldLevelCycleWriteNewGameSessionToFile equals to false
@currentGameSession = GameSession();
currentGameSession.id = getRandomUnusedGameId();
currentGameSession.startedTime = jjUnixTimeMs();
currentGameSession.charOrig = play.charOrig;
currentGameSession.difficulty = jjDifficultyOrig;
if (shouldLevelCycleWriteNewGameSessionToFile) {
gameSessions.insertLast(currentGameSession);
}
play.food = 0; // Just in case the game session re-initializes after failing to load game sessions from a save point or something
} else {
play.food = currentGameSession.food;
}
}
void initializeGlobals(
array<Checkpoint@> checkPointsFromLevel,
int questGemAmountFromLevel = 0,
int questRewardPointsFromLevel = 7000,
int questPerfectRewardPointsFromLevel = 3000,
bool shouldLevelCycleWriteNewGameSessionToFile = true) {
// Always initialize game session stuff, so that even cutscene levels can modify currentGameSession.cutscenesWatched
initializeGameSessions(shouldLevelCycleWriteNewGameSessionToFile);
@asPlay = Player(play);
staticPlayerRenderState = STATE_STATIC_PLAYER_RENDER_OFF;
playerFallingFrame = 0; // Don't remember why I need to reset this here
// Basically meant to be infinite by replenishing this value with +1 in every reloadGlobals call (via onLevelReload and handleLevelReload in common)
play.lives = 999;
checkpoints = checkPointsFromLevel;
questGemsInLevel = questGemAmountFromLevel;
totalGemsInLevel = calculateTotalGemAmountFromLevel();
questRewardPoints = questRewardPointsFromLevel;
questPerfectRewardPoints = questPerfectRewardPointsFromLevel;
if (isDebugModeOn) {
calculateTotalCoinAmountFromLevelForDebug();
}
centeredText.align = STRING::CENTER;
centeredText.pipe = STRING::SPECIALSIGN;
centeredText.at = STRING::SPECIALSIGN;
centeredText.newline = STRING::SPECIALSIGN;
centeredText.section = STRING::SPECIALSIGN;
jjUseLayer8Speeds = true;
jjObjectPresets[OBJECT::DESTRUCTSCENERY].points = 0;
jjObjectPresets[OBJECT::STOMPSCENERY].points = 0;
jjObjectPresets[OBJECT::TRIGGERCRATE].points = 0;
jjObjectPresets[OBJECT::SPRINGCRATE].points = 0;
jjObjectPresets[OBJECT::SPIKEBOLL].bulletHandling = HANDLING::DESTROYBULLET;
jjWeapons[WEAPON::SEEKER].comesFromGunCrates = false;
PurpleGem(jjObjectPresets[OBJECT::PURPLEGEM]);
RemovableAmmo15(jjObjectPresets[OBJECT::BOUNCERAMMO15]);
RemovableAmmo15(jjObjectPresets[OBJECT::ICEAMMO15]);
RemovableAmmo15(jjObjectPresets[OBJECT::RFAMMO15]);
RemovableAmmo15(jjObjectPresets[OBJECT::SEEKERAMMO15]);
RemovableAmmo15(jjObjectPresets[OBJECT::TOASTERAMMO15]);
RemovableCrate(jjObjectPresets[OBJECT::BOMBCRATE]);
RemovableCrate(jjObjectPresets[OBJECT::CARROTBARREL]);
RemovableCrate(jjObjectPresets[OBJECT::CARROTCRATE]);
RemovableCrate(jjObjectPresets[OBJECT::GEMBARREL]);
RemovableCrate(jjObjectPresets[OBJECT::GEMCRATE]);
RemovableCrate(jjObjectPresets[OBJECT::GUNCRATE]);
RemovableCrate(jjObjectPresets[OBJECT::GUNBARREL]);
RemovableCrate(jjObjectPresets[OBJECT::ONEUPBARREL]);
RemovableCrate(jjObjectPresets[OBJECT::ONEUPCRATE]);
RemovableCrate(jjObjectPresets[OBJECT::TRIGGERCRATE]);
RemovableEnemy(jjObjectPresets[OBJECT::BAT]);
RemovableEnemy(jjObjectPresets[OBJECT::DEMON]);
RemovableEnemy(jjObjectPresets[OBJECT::DOGGYDOGG]);
RemovableEnemy(jjObjectPresets[OBJECT::DRAGON]);
RemovableEnemy(jjObjectPresets[OBJECT::FATCHICK]);
RemovableEnemy(jjObjectPresets[OBJECT::FLOATLIZARD]);
RemovableEnemy(jjObjectPresets[OBJECT::HELMUT]);
RemovableEnemy(jjObjectPresets[OBJECT::LIZARD]);
RemovableEnemy(jjObjectPresets[OBJECT::RAPIER]);
RemovableEnemy(jjObjectPresets[OBJECT::RAVEN]);
RemovableEnemy(jjObjectPresets[OBJECT::SKELETON]);
RemovableEnemy(jjObjectPresets[OBJECT::SUCKER]);
RemovableEnemy(jjObjectPresets[OBJECT::TUFTURT]);
RemovableMonitor(jjObjectPresets[OBJECT::FIRESHIELD]);
RemovablePickup(jjObjectPresets[OBJECT::APPLE]);
RemovablePickup(jjObjectPresets[OBJECT::BOUNCERAMMO3]);
RemovablePickup(jjObjectPresets[OBJECT::BURGER]);
RemovablePickup(jjObjectPresets[OBJECT::CARROT]);
RemovablePickup(jjObjectPresets[OBJECT::CHEESE]);
RemovablePickup(jjObjectPresets[OBJECT::CHERRY]);
RemovablePickup(jjObjectPresets[OBJECT::CHICKENLEG]);
RemovablePickup(jjObjectPresets[OBJECT::CHOCBAR]);
RemovablePickup(jjObjectPresets[OBJECT::CUCUMB]);
RemovablePickup(jjObjectPresets[OBJECT::EGGPLANT]);
RemovablePickup(jjObjectPresets[OBJECT::FASTFIRE]);
RemovablePickup(jjObjectPresets[OBJECT::FRIES]);
RemovablePickup(jjObjectPresets[OBJECT::FULLENERGY]);
RemovablePickup(jjObjectPresets[OBJECT::GOLDCOIN]);
RemovablePickup(jjObjectPresets[OBJECT::GRAPES]);
RemovablePickup(jjObjectPresets[OBJECT::GUN8AMMO3]);
RemovablePickup(jjObjectPresets[OBJECT::HAM]);
RemovablePickup(jjObjectPresets[OBJECT::ICEAMMO3]);
RemovablePickup(jjObjectPresets[OBJECT::LEMON]);
RemovablePickup(jjObjectPresets[OBJECT::LETTUCE]);
RemovablePickup(jjObjectPresets[OBJECT::LIME]);
RemovablePickup(jjObjectPresets[OBJECT::ORANGE]);
RemovablePickup(jjObjectPresets[OBJECT::PEACH]);
RemovablePickup(jjObjectPresets[OBJECT::PEAR]);
RemovablePickup(jjObjectPresets[OBJECT::PEPSI]);
RemovablePickup(jjObjectPresets[OBJECT::PIZZA]);
RemovablePickup(jjObjectPresets[OBJECT::PRETZEL]);
RemovablePickup(jjObjectPresets[OBJECT::RFAMMO3]);
RemovablePickup(jjObjectPresets[OBJECT::SANDWICH]);
RemovablePickup(jjObjectPresets[OBJECT::SEEKERAMMO3]);
RemovablePickup(jjObjectPresets[OBJECT::SILVERCOIN]);
RemovablePickup(jjObjectPresets[OBJECT::SODAPOP]);
RemovablePickup(jjObjectPresets[OBJECT::TACO]);
RemovablePickup(jjObjectPresets[OBJECT::TOASTERAMMO3]);
RemovablePickup(jjObjectPresets[OBJECT::WATERMELON]);
RemovablePickup(jjObjectPresets[OBJECT::WEENIE]);
// Fireball Gun can be enabled e.g. in onLevelLoad() with jjChat("/fireball on"), but a console text will be displayed
// Not sure if that would bother me in the level beginning or not, but let's see, maybe I get used to it...
// EDIT: Fireball is definitely too OP against bosses, therefore disable it unless you can figure out a way to nerf it down a bit...
// jjChat("/fireball on");
}
bool isIdExisting(int id) {
for (uint i = 0; i < gameSessions.length(); ++i) {
if (gameSessions[i].id == id) {
return true;
}
}
return false;
}
void reloadGlobals() {
isPlayerInArmory = false;
if (play.lives < 999) {
play.lives = 999;
}
}
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.