| Name | Author | Game Mode | Rating | |||||
|---|---|---|---|---|---|---|---|---|
| More Mori (Modded Lori,... |
jjturbo9 | Single player | 8 | |||||
const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
#include "MLLE-Include-1.8.asc" ///@MLLE-Generated
#pragma require "More Mori-MLLE-Data-2.j2l" ///@MLLE-Generated
#pragma require "More Mori-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "More Mori.j2l" ///@MLLE-Generated
///@SaveAndRunArgs -lori ///@MLLE-Generated
#pragma require "Hailstone.png"
#pragma require "SmallerTriggerCrate.png"
#pragma require "1tilePushable.png"
#pragma require "mirroredbigrock.png"
#pragma require "LoriPieceOfCake.wav"
#pragma require "artsun.it"
//Sugar rush counter
int layer5Timer = 0;
bool onDrawLives(jjPLAYER@ player, jjCANVAS@ screen) {
if (jjDifficulty == 0) {
screen.drawSprite(590, 45, ANIM::PICKUPS, 92, jjGameTicks>>2, -1, SPRITE::NORMAL);
screen.drawSprite(575, 45, ANIM::PICKUPS, 38, jjGameTicks>>2, -1, SPRITE::NORMAL);
screen.drawString(570, 15, "SUGAR RUSH", STRING::SMALL, STRING::PALSHIFT, 16);
screen.drawString(600, 35, formatInt(player.food%100, "1") + " / 100", STRING::MEDIUM, STRING::PALSHIFT, 16);}
return false;
}
//vvvvvvvvvv crate push logic vvvvvvvvvvvvv
bool isSolidObject(const jjOBJ@ obj) {
return obj.behavior == BEHAVIOR::AMMO15 || obj.behavior == BEHAVIOR::BIGOBJECT ||obj.behavior == BEHAVIOR::CRATE || obj.behavior == BEHAVIOR::MONITOR || obj.eventID == OBJECT::FIRESHIELD || obj.eventID == OBJECT::WATERSHIELD ||obj.eventID == OBJECT::CARROTCRATE || obj.eventID == OBJECT::BIGROCK || obj.eventID == OBJECT::BIGBOX || obj.eventID == OBJECT::LIGHTNINGSHIELD || obj.eventID == OBJECT::MORPH || obj.eventID == OBJECT::BIRDMORPH || obj.eventID == OBJECT::BLASTERPOWERUP || obj.eventID == OBJECT::BOUNCERPOWERUP || obj.eventID == OBJECT::ICEPOWERUP || obj.eventID == OBJECT::SEEKERPOWERUP || obj.eventID == OBJECT::RFPOWERUP || obj.eventID == OBJECT::TOASTERPOWERUP || obj.eventID == OBJECT::TNTPOWERUP || obj.eventID == OBJECT::GUN8POWERUP || obj.eventID == OBJECT::GUN9POWERUP;
}
array<bool> hasBeenRoasted(32, false);
bool checkGamemode = true;
bool solidsReset = false;
array<SolidWrapper> solids;
class SolidWrapper {
jjOBJ@ obj;
private int8 localID = jjLocalPlayers[0].playerID;
// These are only used by the server.
uint8 eventID;
uint8 eventIDActual; // If generator then this is different from eventID
float xOrg, yOrg;
float xPos, yPos;
array<int8> forces(32);
private SolidWrapper() {}
SolidWrapper(jjOBJ@ obj) {
@this.obj = obj;
eventIDActual = eventID = obj.eventID;
if (eventID == OBJECT::GENERATOR) {
eventIDActual = jjParameterGet(int(obj.xPos / 32),int(obj.yPos / 32), 0, 8);
}
xOrg = obj.xOrg;
yOrg = obj.yOrg;
updatePos();
}
void updatePos() {
jjOBJ@ actual = getSolid();
if (actual.isActive && isSolidObject(actual)) {
xPos = actual.xPos;
yPos = actual.yPos;
}
}
void updateForce(int8 solidness) {
jjOBJ@ obj = getSolid();
forces[localID] = solidness;
jjSTREAM update;
update.push(uint8(SOLID_FORCE));
update.push(localID);
update.push(xOrg);
update.push(yOrg);
update.push(solidness);
jjSendPacket(update);
}
void resetForcesForAllInactivePlayers() {
if ((jjGameTicks % 140) != 0) return; // ✅ Run every 2 seconds max
for (int i = 0; i < 32; i++) {
jjPLAYER@ player = jjPlayers[i];
if (!player.isActive && forces[i] != 0) {
forces[i] = 0;
jjSTREAM update;
update.push(uint8(SOLID_FORCE));
update.push(int8(i));
update.push(xOrg);
update.push(yOrg);
update.push(int8(0));
jjSendPacket(update);
}
}
}
int8 getForce() {
return forces[localID];
}
jjOBJ@ getSolid() {
// obj.var[0] will sometimes be -1 which will epically crash the client with an access violation if not taken care of
return obj.eventID == OBJECT::GENERATOR ? jjObjects[obj.var[0] < 0 ? 0 : obj.var[0]] : obj;
}
}
void initSolids() {
solids = array<SolidWrapper>();
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (
(jjEventGet(int(obj.xOrg / 32), int(obj.yOrg / 32)) == int(obj.eventID) && isSolidObject(obj)) ||
(obj.eventID == OBJECT::GENERATOR && obj.creatorType == CREATOR::LEVEL && isSolidObject(jjObjectPresets[jjParameterGet(int(obj.xPos / 32),int(obj.yPos / 32), 0, 8)]))) {
solids.insertLast(SolidWrapper(obj));
}
}
}
void moveSolid(jjOBJ@ obj, float dx, float dy) {
if (dx != 0 || dy != 0) {
obj.xPos += dx;
obj.yPos += dy;
jjSTREAM update;
update.push(uint8(SOLID_MOVE));
update.push(jjLocalPlayers[0].playerID);
update.push(obj.xOrg);
update.push(obj.yOrg);
update.push(obj.xPos);
update.push(obj.yPos);
jjSendPacket(update);
}
}
void ohServerPleaseGiveMeYourUpdatedSolidPositions() {
jjSTREAM request;
request.push(uint8(SOLID_JOINED));
request.push(jjLocalPlayers[0].playerID);
jjSendPacket(request);
}
//^^^^^^^^crate push logic ^^^^^^^^^^^^
void onLevelBegin() {
jjLayers[5].hasTiles = false;
// loadAssets("Hailstone.png", ANIM::CUSTOM, 28, 24, 0, 0, pal);
MLLE::SpawnOffgrids();
// loop over every player slot that might exist
for (uint i = 0; i < 32; ++i)
{
jjPLAYER@ p = jjPlayers[i];
if (!p.isActive) // skip empty slots
continue;
p.morphTo(CHAR::LORI, /*morphEffect=*/false);
p.charOrig = CHAR::LORI;
}
initSolids();
/* if (jjGameConnection != GAME::LOCAL) {
} */
if (jjDifficulty == 3 && jjMaxHealth != 1) {
// Turbo mode: set max health to 1 heart
jjChat("/maxhealth 1");
}
else if (jjDifficulty == 0 && jjMaxHealth != 3) {
// Easy mode: set max health to 3 hearts
jjChat("/maxhealth 3");
}
else if (jjIsServer || (/* jjGameConnection == GAME::LOCAL && */jjMaxHealth != 2)) {
// Normal mode: max health 2 hearts
jjChat("/maxhealth 2");
}
if (!jjIsServer) {
ohServerPleaseGiveMeYourAvailableTriggers();
ohServerPleaseGiveMeYourUpdatedSolidPositions();
}
}
void onLevelLoad(){
if (jjDifficulty == 2) {
jjEventSet(91, 0, 0);
jjEventSet(92, 0, 0);
}
if (jjDifficulty == 3) {
jjEventSet(91, 1, 0);
jjEventSet(92, 1, 0);
}
if (jjDifficulty == 2 || jjDifficulty == 3) {
jjEventSet(55, 17, 0);
jjEventSet(39, 25, 0);
jjEventSet(41, 25, 0);
jjEventSet(43, 25, 0);
}
if (jjDifficulty == 1) {
jjEventSet(45, 17, 0);
jjEventSet(47, 17, 0);
jjEventSet(49, 17, 0);
}
if (jjDifficulty == 0) {
jjEventSet(32, 9, 0);
}
// Hide layer 6 only in single player mode
if (jjGameMode == GAME::SP && jjGameConnection == GAME::LOCAL) {
jjLayers[6].hasTiles = false;
}
// Hide layer 2 only in online mp mode and have some tiles green instead of default blue and 2nd yellow button reachable
if (jjGameConnection != GAME::LOCAL) {
jjLayers[2].hasTiles = false;
if (!jjTriggers[24]) {jjTriggers[24] = true;}
if (!jjTriggers[7]) {jjTriggers[7] = true;}
}
if (jjDifficulty == 0) {
if (jjTriggers[24]) {jjTriggers[24] = false;}
}
jjObjectPresets[OBJECT::SNOW].behavior = BEHAVIOR::DESTRUCTSCENERY;
if (jjDifficulty == 1) { // normal mode
jjObjectPresets[OBJECT::RAVEN].energy = 1;
} else if (jjDifficulty == 2) { // hard mode
jjObjectPresets[OBJECT::RAVEN].energy = 2;
} else if (jjDifficulty == 3) { // turbo mode
jjObjectPresets[OBJECT::RAVEN].energy = 3;
}
jjObjectPresets[OBJECT::SUCKER].energy = 2;
jjLayers[5].hasTiles = false;
int CARROTCRATEPUSHABLEframeID = jjAnimations[jjAnimSets[ANIM::PICKUPS].firstAnim + 5].firstFrame;
jjAnimFrames[CARROTCRATEPUSHABLEframeID].hotSpotX = -16;
jjAnimFrames[CARROTCRATEPUSHABLEframeID].hotSpotY = -16;
jjAnimFrames[CARROTCRATEPUSHABLEframeID].coldSpotX = 32;
jjAnimFrames[CARROTCRATEPUSHABLEframeID].coldSpotY = 32;
revampNoFireHandling();
for (uint i = 0; i < 32; ++i) {
jjPLAYER@ p = jjPlayers[i];
if (!p.isActive) // skip empty slots
continue;
p.fastfire = 35;
p.currWeapon = WEAPON::BLASTER;
for (int w = 2; w <= 9; w++) {
p.ammo[w] = 0;
}
}
jjObjectPresets[OBJECT::WATERSHIELD].deactivates = false;
jjObjectPresets[OBJECT::TOASTERPOWERUP].deactivates = false;
jjObjectPresets[OBJECT::BIGROCK].deactivates = false;
jjObjectPresets[OBJECT::ICEPOWERUP].deactivates = false;
jjObjectPresets[OBJECT::BOMBCRATE].deactivates = false;
jjObjectPresets[OBJECT::CARROTBARREL].deactivates = false;
jjObjectPresets[OBJECT::CARROTCRATE].deactivates = false;
jjObjectPresets[OBJECT::TRIGGERCRATE].deactivates = false;
jjObjectPresets[OBJECT::SPRINGCRATE].deactivates = false;
jjObjectPresets[OBJECT::BLUESPRING].deactivates = false;
jjObjectPresets[OBJECT::GREENSPRING].deactivates = false;
jjObjectPresets[OBJECT::HORBLUESPRING].deactivates = false;
jjObjectPresets[OBJECT::SILVERCOIN].deactivates = false;
jjObjectPresets[OBJECT::GOLDCOIN].deactivates = false;
jjObjectPresets[OBJECT::TNT].deactivates = false;
jjObjectPresets[OBJECT::SPIKEBOLL].deactivates = false;
jjObjectPresets[OBJECT::SPIKEBOLL3D].deactivates = false;
jjObjectPresets[OBJECT::CUPCAKE].deactivates = false;
jjObjectPresets[OBJECT::GRAPES].deactivates = false;
jjObjectPresets[OBJECT::WATERMELON].deactivates = false;
jjObjectPresets[OBJECT::GUN9POWERUP].deactivates = false;
jjObjectPresets[OBJECT::GUN9AMMO3].deactivates = false;
jjObjectPresets[OBJECT::EXPLOSION].deactivates = false;
jjObjectPresets[OBJECT::ICEAMMO15].deactivates = false;
jjObjectPresets[OBJECT::EXTRALIFE].deactivates = false;
jjObjectPresets[OBJECT::CHECKPOINT].deactivates = false;
jjObjectPresets[OBJECT::EVA].deactivates = false;
jjObjectPresets[OBJECT::SUPERGEM].deactivates = false;
jjObjectPresets[OBJECT::QUEEN].deactivates = false;
jjObjectPresets[OBJECT::SPIKEBOLL].energy = 127;
jjObjectPresets[OBJECT::SPIKEBOLL3D].energy = 127;
jjAnimSets[ANIM::QUEEN].load();
jjPIXELMAP("Hailstone.png").save(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::QUEEN].firstAnim + 4].firstFrame]);
jjPIXELMAP("SmallerTriggerCrate.png").save(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PICKUPS].firstAnim + 52].firstFrame]);
/* if (obj.eventID == OBJECT::CARROTCRATE) { */
jjPIXELMAP("1tilePushable.png").save(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PICKUPS].firstAnim + 3].firstFrame]);
jjPIXELMAP("mirroredbigrock.png").save(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::BIGROCK].firstAnim].firstFrame]);
jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::PICKUPS].firstAnim + 65];
jjPIXELMAP sprite("1tilePushable.png");
for (int i = 0; i <= 9; i++)
sprite.save(jjAnimFrames[anim.firstFrame + i]);
jjAnimations[jjAnimSets[ANIM::LORI] + RABBIT::STATIONARYJUMP] = jjAnimations[jjAnimSets[ANIM::LORI] + RABBIT::STATIONARYJUMPEND] = jjAnimations[jjAnimSets[ANIM::LORI] + RABBIT::STATIONARYJUMPSTART];
jjObjectPresets[OBJECT::STOMPSCENERY].scriptedCollisions = true;
jjObjectPresets[OBJECT::FISH].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::FISH].behavior = function(obj) {
for (int i = 0; i < jjLocalPlayerCount; i++) {
jjPLAYER@ victim = jjLocalPlayers[i];
if (obj.doesCollide(victim)) {
int force = victim.getObjectHitForce(obj);
if (force != 0) {
victim.objectHit(obj, force, HANDLING::ENEMY);
} else {
victim.hurt(2);
}
}
}
obj.behave(BEHAVIOR::FISH);
};
if (jjGameConnection != GAME::LOCAL) {
jjObjectPresets[OBJECT::BIGROCK].behavior = BigObjectBehaviorBandaid;
}
modifyCarrotCrate();
jjAddObject(OBJECT::FISH, -10000, -10000);
}
bool QueenSpawned = false;
void onFunction1(jjPLAYER@ player) {
if (!QueenSpawned){
// Spawn Queen and get her object ID
int queenID = jjAddObject(OBJECT::QUEEN, 96 * 32 + 16, 8 * 32 + 16);
// Get the actual object reference
jjOBJ@ queen = jjObjects[queenID];
if (queen !is null) {
// Ensure animation is correct
queen.determineCurAnim(ANIM::QUEEN, 0);
queen.determineCurFrame(); // optional, for visual consistency
}
QueenSpawned = true;
}
if (!jjTriggers[12]) {
jjTriggers[12] = true;
}
}
void onLevelReload() {
jjLayers[5].hasTiles = false;
/* if (jjIsServer || jjGameConnection == GAME::LOCAL && jjMaxHealth != 1)
{
jjChat("/maxhealth 1"); // never allow more than 1 heart
} */
MLLE::SpawnOffgridsLocal();
initSolids();
/* if (jjGameConnection != GAME::LOCAL) {
} */
}
void onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int) {
if (obj.eventID == OBJECT::STOMPSCENERY && player.specialMove != 0){
obj.state = STATE::KILL;
}
}
const int X = 0x58;
const int Z = 0x5A; // legacy, not used
const int VK_TAB = 0x09; // for tab
bool zKeyHeld; // previous X key state
bool xKeyHeld; // current X key state
bool vk_tabKeyPrev; // previous Tab key state
bool vk_tabKeyCurr; // current Tab key state
bool helicopterMode = true; // start in Helicopter-Ears mode
int tabPressCount = 0; // counts Tab presses
// displayMode will be 0,1,2 for the three visible modes, or -1 for off
int displayMode = -1; // start off (no display)
void onPlayer(jjPLAYER@ p) {
if (p.health == 0 && !hasBeenRoasted[p.playerID]) {
hasBeenRoasted[p.playerID] = true;
}
if (p.health == 0){
if (jjGameMode == GAME::COOP){
removeQueenAtFixedTiles(95, 6, 99, 10);
jjTriggers[12] = false;
}
QueenSpawned = false;
if (jjGameConnection == GAME::LOCAL){ ItemsSpawned = false; }
jjLayers[2].hasTiles = false;
hasTriggeredCrate31 = false;
hasTriggeredZone6 = false;
hasTriggeredCrate23 = false;
hasTriggeredCrate5 = false;
hasTriggeredCrate11 = false;
if (jjGameConnection != GAME::LOCAL){jjLayers[3].hasTiles = true; jjLayers[2].hasTiles = true; jjLayers[1].hasTiles = true;}
text9 = false;
text9Timer = -1;
@text9Player = null;
text16 = false;
text16Timer = -1;
@text16Player = null;
text17 = false;
text17Timer = -1;
@text17Player = null;
text18 = false;
text18Timer = -1;
@text18Player = null;
text19 = false;
text19Timer = -1;
@text19Player = null;
text23 = false;
text23Timer = -1;
@text23Player = null;
text28 = false;
text28Timer = -1;
@text28Player = null;
text29 = false;
text29Timer = -1;
@text29Player = null;
text30 = false;
text30Timer = -1;
@text30Player = null;
}
// Define the area in tile units (converted to pixels)
int left = 60 * 32;
int right = 88 * 32;
int top = 13 * 32;
int bottom = 28 * 32;
int left2 = 63 * 32;
int right2 = 85 * 32;
int top2 = 27 * 32;
int bottom2 = 30 * 32;
// Check if the player is within the rectangle
bool inAntiGravZone =
p.xPos >= left && p.xPos <= right &&
p.yPos >= top && p.yPos <= bottom;
bool inAntiGravZone2 =
p.xPos >= left2 && p.xPos <= right2 &&
p.yPos >= top2 && p.yPos <= bottom2;
// Apply or remove anti-gravity accordingly
p.antiGrav = inAntiGravZone || inAntiGravZone2;
if (p.health == 0) {
jjLayers[3].hasTiles = true; // Show layer 3
}
if (p.powerup[WEAPON::TOASTER]) {
// Remove the power-up status
p.powerup[WEAPON::TOASTER] = false;
// Reset TOASTER ammo to zero, no matter what
p.ammo[WEAPON::TOASTER] = 0;
// If currently holding TOASTER weapon, switch back to default blaster
if (p.currWeapon == WEAPON::TOASTER) {
p.currWeapon = WEAPON::BLASTER;
}
}
noFireOnPlayer(p);
p.helicopter = 0;
jjCHARACTER@ char = jjCharacters[p.charCurr];
if (p.charCurr == CHAR::LORI) {
// Toggle jump mode with X key press
if (xKeyHeld && !zKeyHeld)
helicopterMode = !helicopterMode;
// Detect Tab press and update displayMode accordingly
if (vk_tabKeyCurr && !vk_tabKeyPrev) {
tabPressCount = (tabPressCount + 1) % 4; // cycle 0,1,2,3
if (tabPressCount == 0) {
displayMode = -1; // off mode (no display)
} else {
displayMode = tabPressCount - 1; // 1->0, 2->1, 3->2
}
}
// Apply jump behavior based on helicopterMode
if (helicopterMode) {
char.airJump = AIR::HELICOPTER;
char.groundJump = GROUND::LORI;
char.canHurt = true;
p.furSet(88, 33, 88, furColor); //40 for yellow, 88 for purple
} else {
char.airJump = AIR::DOUBLEJUMP;
char.doubleJumpXSpeed = 0;
char.doubleJumpYSpeed = -6.5;
char.doubleJumpCountMax = 2;
char.groundJump = GROUND::JAZZ;
p.furSet(77, 33, 88, furColor); //40 for yellow, 88 for purple
}
// Show below player text only if displayMode == 2
if (displayMode == 2) {
jjDrawString(
p.xPos - 78, //-78
p.yPos + 37, //+46
helicopterMode ? "Helicopter Ears" : " Double Jump",
STRING::SMALL,
STRING::PALSHIFT, // colored text below player
helicopterMode ? 24 : 16,
2 //layer for text
);
}
}
// Update key states
zKeyHeld = xKeyHeld;
xKeyHeld = jjKey[X];
vk_tabKeyPrev = vk_tabKeyCurr;
vk_tabKeyCurr = jjKey[VK_TAB];
}
bool onDrawScore(jjPLAYER@ p, jjCANVAS@ canvas) {
if (p.charCurr == CHAR::LORI) {
// Draw jump mode text in corner only if displayMode is 0 or 1
if (displayMode == 0 || displayMode == 1) {
canvas.drawString(
5, 50,
helicopterMode ? "Helicopter Ears" : "Double Jump",
STRING::SMALL,
displayMode == 1 ? STRING::PALSHIFT : STRING::NORMAL,
displayMode == 1 ? (helicopterMode ? 24 : 16) : 0
);
}
// If displayMode == -1 (off), or 2 (below player), no corner text is drawn here
}
return false;
}
bool hasTriggeredCrate31 = false;
bool hasTriggeredZone6 = false;
bool hasTriggeredCrate8 = false;
bool hasTriggeredCrate23 = false;
bool hasTriggeredCrate5 = false;
bool hasTriggeredCrate11 = false;
int stage = 0;
int switchTime = 0;
bool text9 = false;
int text9Timer = -1;
jjPLAYER@ text9Player;
bool text16 = false;
int text16Timer = -1;
jjPLAYER@ text16Player;
bool text17 = false;
int text17Timer = -1;
jjPLAYER@ text17Player;
bool text18 = false;
int text18Timer = -1;
jjPLAYER@ text18Player;
bool text19 = false;
int text19Timer = -1;
jjPLAYER@ text19Player;
bool text23 = false;
int text23Timer = -1;
jjPLAYER@ text23Player;
bool text28 = false;
int text28Timer = -1;
jjPLAYER@ text28Player;
bool text29 = false;
int text29Timer = -1;
jjPLAYER@ text29Player;
bool text30 = false;
int text30Timer = -1;
jjPLAYER@ text30Player;
bool ItemsSpawned = false;
bool fishReady = false;
int fishBase = 0;
bool startWarpTimer = false;
uint warpStartTime = 0;
bool warpTimerActive = false;
uint warpTimerStart = 0;
// global cooldown tracker
uint lastFunction31Tick = 0;
bool playerIsStillInTrigger31(jjPLAYER@ play) {
int px = int(play.xPos);
int py = int(play.yPos);
// Range: X = 34–43, Y = 57–62
return (px >= 33 * 32 && px < 44 * 32 && // 44 = one past 43
py >= 56 * 32 && py < 63 * 32); // 63 = one past 62
}
// Track whether the player has played another mode (SP, Battle, etc.)
bool PlayedOtherModeThanCoopInMp = false;
bool StopWarping26 = false;
uint stopWarpResetTimer = 0;
void onMain() {
// sets warping on every 2 seconds 140 ticks
if (jjGameConnection != GAME::LOCAL) {
if (jjGameTicks - stopWarpResetTimer >= 140) {
StopWarping26 = false;
stopWarpResetTimer = jjGameTicks;
}
}
jjSetWaterLevel(38 * 32, true);
jjPLAYER@ player = jjLocalPlayers[0];
// if we're in multiplayer and *not* in Coop, enable flag
if (jjGameConnection != GAME::LOCAL && jjGameMode != GAME::COOP && !PlayedOtherModeThanCoopInMp) {
PlayedOtherModeThanCoopInMp = true;
}
// only check warp zone logic if in Coop mode
if (jjGameConnection != GAME::LOCAL && jjGameMode == GAME::COOP) {
if (playerIsStillInTrigger31(player)) {
onFunction31(player);
}
}
if (jjGameConnection != GAME::LOCAL && jjGameMode != GAME::COOP) {
// get player position in tiles
int tileX = int(player.xPos / 32);
int tileY = int(player.yPos / 32);
// check if player is outside the rectangle
bool outsideWarpSafeZone = (tileX < 34 || tileX > 43 || tileY < 57 || tileY > 62);
// start timer if player leaves safe zone
if (!warpTimerActive && outsideWarpSafeZone) {
warpTimerActive = true;
warpTimerStart = jjGameTicks;
}
// cancel timer if player re-enters safe zone
if (!outsideWarpSafeZone) {
warpTimerActive = false;
}
// warp after 1 second (70 ticks) outside safe zone
if (warpTimerActive && outsideWarpSafeZone && jjGameTicks - warpTimerStart >= 50 && !StopWarping26) {
player.warpToID(26);
warpTimerActive = false;
StopWarping26 = true;
}
} else {
// reset if condition no longer holds
warpTimerActive = false;
}
if (jjGameConnection != GAME::LOCAL && !jjTriggers[25]) {jjTriggers[25] = true;}
if ((jjDifficulty == 2 || jjDifficulty == 3) && !jjTriggers[17]) {jjTriggers[17] = true;}
// Convert pixel position to tile coordinates
int tx = int(player.xPos / 32.0f);
int ty = int(player.yPos / 32.0f);
// Check if player is inside the rectangle (tiles 5–10, 8–13)
bool insideRect = (tx >= 5 && tx <= 10 && ty >= 8 && ty <= 13);
// Check if player is Jazz or Spaz
bool isJazzOrSpaz = (player.charCurr == CHAR::JAZZ || player.charCurr == CHAR::SPAZ);
// Start the timer only if player is Jazz/Spaz and outside the no-warp zone
if (isJazzOrSpaz && !insideRect) {
if (!startWarpTimer) {
startWarpTimer = true;
warpStartTime = jjGameTicks;
}
// Warp only if player is *still* Jazz/Spaz after 2 seconds
if (jjGameTicks - warpStartTime >= 140) {
if (player.charCurr == CHAR::JAZZ || player.charCurr == CHAR::SPAZ) {
player.warpToID(27);
}
startWarpTimer = false; // reset timer afterward
}
} else {
// Reset if not Jazz/Spaz, or inside rectangle
startWarpTimer = false;
}
if (WaitingForTriggerQ) {
if (jjGameTicks - startTick >= 30){
AwesomeCountingDownBellForFirstThree(player);
}
if (jjGameTicks - startTick >= 100){
AwesomeCountingDownBellForFirstThree(player);
}
if (jjGameTicks - startTick >= 170){
AwesomeCountingDownBellForFirstThree(player);
}
if (jjGameTicks - startTick >= 240){
jjLayers[2].hasTiles = true;
jjTriggers[14] = false;
AwesomeCountingDownBellForLastOne(player);
WaitingForTriggerQ = false; // stop checking
}
}
// Ensure fish anims exist
if (!fishReady && jjAnimSets[ANIM::FISH].firstAnim > 0) {
fishBase = jjAnimSets[ANIM::FISH].firstAnim;
fishReady = true;
}
for (int i = 0; i < jjObjectCount; i++) {
jjOBJ@ o = jjObjects[i];
if (o.eventID == OBJECT::CRAB) {
// --- Initialize once ---
if (fishReady && o.var[0] == 0) {
o.var[0] = 1;
o.curAnim = fishBase;
o.state = STATE::SLEEP;
o.var[1] = int(o.xPos); // base X
o.var[2] = int(o.yPos); // base Y (stay here!)
o.var[3] = 0; // dir: 0=left, 1=right
o.playerHandling = HANDLING::SPECIAL;
o.ySpeed = 0;
o.yAcc = 0;
o.bulletHandling = HANDLING::IGNOREBULLET;
}
if (o.var[0] == 0)
continue; // not ready yet
// --- Keep constant vertical position ---
float baseY = float(o.var[2]);
if (o.yPos != baseY) {
o.yPos = baseY;
o.ySpeed = 0;
o.yAcc = 0;
}
//move fish 64 to left 0 to right
const int leftDist = 52;
const int rightDist = -12;
const float moveSpeed = 0.5f;
if (o.var[3] == 0) { // moving left
o.xPos -= moveSpeed;
if (o.xPos <= float(o.var[1] - leftDist))
o.var[3] = 1; // switch to right
} else { // moving right
o.xPos += moveSpeed;
if (o.xPos >= float(o.var[1] + rightDist))
o.var[3] = 0; // switch to left
}
// --- Face direction + draw ---
o.direction = (o.var[3] == 0 ? -1 : 1);
o.determineCurFrame();
o.draw();
}
}
if (!ItemsSpawned && jjGameConnection == GAME::LOCAL) {
jjAddObject(OBJECT::SUPERGEM, 38*32 + 16, 44*32 + 16);
jjAddObject(OBJECT::GOLDCOIN, 84*32 + 16, 59*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 14*32 + 16, 1*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 19*32 + 16, 1*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 24*32 + 16, 1*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 29*32 + 16, 1*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 34*32 + 16, 1*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 39*32 + 16, 1*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 6*32 + 16, 26*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 18*32 + 16, 55*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 47*32 + 16, 61*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 55*32 + 16, 61*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 59*32 + 16, 61*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 94*32 + 16, 40*32 + 16);
if (jjDifficulty == 0 || jjDifficulty == 1){
jjAddObject(OBJECT::SILVERCOIN, 39*32 + 16, 25*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 41*32 + 16, 25*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 43*32 + 16, 25*32 + 16);
}
if (jjDifficulty == 2 || jjDifficulty == 3){
jjAddObject(OBJECT::SILVERCOIN, 45*32 + 16, 17*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 47*32 + 16, 17*32 + 16);
jjAddObject(OBJECT::SILVERCOIN, 49*32 + 16, 17*32 + 16);
}
ItemsSpawned = true;
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text9 && playerIsStillInTrigger9(play)) {
onFunction9(play);
}
}
}
if (text9) {
if (playerIsStillInTrigger9(text9Player)) {
if (text9Timer > 0) {
text9Timer--;
} else {
onFunction9(text9Player); // Re-show text
}
} else {
text9Player.showText(""); // Clear text
text9 = false;
text9Timer = -1;
@text9Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text16 && playerIsStillInTrigger16(play)) {
onFunction16(play);
}
}
}
if (text16) {
if (playerIsStillInTrigger16(text16Player)) {
if (text16Timer > 0) {
text16Timer--;
} else {
onFunction16(text16Player); // Re-show text
}
} else {
text16Player.showText(""); // Clear text
text16 = false;
text16Timer = -1;
@text16Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text17 && playerIsStillInTrigger17(play)) {
onFunction17(play);
}
}
}
if (text17) {
if (playerIsStillInTrigger17(text17Player)) {
if (text17Timer > 0) {
text17Timer--;
} else {
onFunction17(text17Player); // Re-show text
}
} else {
text17Player.showText(""); // Clear text
text17 = false;
text17Timer = -1;
@text17Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text18 && playerIsStillInTrigger18(play)) {
onFunction18(play);
}
}
}
if (text18) {
if (playerIsStillInTrigger18(text18Player)) {
if (text18Timer > 0) {
text18Timer--;
} else {
onFunction18(text18Player); // Re-show text
}
} else {
text18Player.showText(""); // Clear text
text18 = false;
text18Timer = -1;
@text18Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text19 && playerIsStillInTrigger19(play)) {
onFunction19(play);
}
}
}
if (text19) {
if (playerIsStillInTrigger19(text19Player)) {
if (text19Timer > 0) {
text19Timer--;
} else {
onFunction19(text19Player); // Re-show text
}
} else {
text19Player.showText(""); // Clear text
text19 = false;
text19Timer = -1;
@text19Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text23 && playerIsStillInTrigger23(play)) {
onFunction123(play);
}
}
}
if (text23) {
if (playerIsStillInTrigger23(text23Player)) {
if (text23Timer > 0) {
text23Timer--;
} else {
onFunction123(text23Player); // Re-show text
}
} else {
text23Player.showText(""); // Clear text
text23 = false;
text23Timer = -1;
@text23Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text28 && playerIsStillInTrigger28(play)) {
onFunction28(play);
}
}
}
if (text28) {
if (playerIsStillInTrigger28(text28Player)) {
if (text28Timer > 0) {
text28Timer--;
} else {
onFunction28(text28Player); // Re-show text
}
} else {
text28Player.showText(""); // Clear text
text28 = false;
text28Timer = -1;
@text28Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text29 && playerIsStillInTrigger29(play)) {
onFunction29(play);
}
}
}
if (text29) {
if (playerIsStillInTrigger29(text29Player)) {
if (text29Timer > 0) {
text29Timer--;
} else {
onFunction29(text29Player); // Re-show text
}
} else {
text29Player.showText(""); // Clear text
text29 = false;
text29Timer = -1;
@text29Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
if (!text30 && playerIsStillInTrigger30(play)) {
onFunction30(play);
}
}
}
if (text30) {
if (playerIsStillInTrigger30(text30Player)) {
if (text30Timer > 0) {
text30Timer--;
} else {
onFunction30(text30Player); // Re-show text
}
} else {
text30Player.showText(""); // Clear text
text30 = false;
text30Timer = -1;
@text30Player = null;
}
}
if (jjGameMode != GAME::COOP && jjGameConnection != GAME::LOCAL) {
for (int i = 0; i < jjLocalPlayerCount; ++i) {
jjPLAYER@ p = jjLocalPlayers[i];
int screenX = int(p.cameraX) + 57;
int screenY = int(p.cameraY);
// set vertical placement line 1
int line1Y = screenY + 200; // adjust this for the first line
jjDrawString(screenX, line1Y,
" !!!!Type T -> /coop to switch to COOP mode!!!!",
STRING::MEDIUM, STRING::NORMAL, 0, p.localPlayerID);
// set vertical placement line 2
int line2Y = screenY + 230; // adjust this for the second line
jjDrawString(screenX + 5, line2Y,
" Online, this level only works well in Coop mode.",
STRING::SMALL, STRING::NORMAL, 0, p.localPlayerID);
// set vertical placement line 3
int line3Y = screenY + 255; // adjust this for the third line
jjDrawString(screenX + 5, line3Y,
" If you switch to coop you'll be send back to the start...",
STRING::SMALL, STRING::NORMAL, 4, p.localPlayerID);
// set vertical placement line 4
int line4Y = screenY + 280; // adjust this for the third line
jjDrawString(screenX + 5, line4Y,
" Use cheats at your own 'risk', dying in sp breaks game mechanics.",
STRING::SMALL, STRING::NORMAL, 4, p.localPlayerID);
}
}
if (jjTriggers[5] && !hasTriggeredCrate5) {
jjTriggers[22] = true;
hasTriggeredCrate5 = true;
}
if (jjTriggers[6] && !hasTriggeredZone6) {
jjTriggers[27] = true;
hasTriggeredZone6 = true;
}
if (jjTriggers[31] && !hasTriggeredCrate31) {
jjTriggers[27] = false;
removeTntcratesAtFixedTiles(0, 0, 42, 62);
hasTriggeredCrate31 = true;
}
if (jjTriggers[23] && !hasTriggeredCrate23) {
hasTriggeredCrate23 = true; // prevents retriggering
jjLayers[1].hasTiles = false; // Hide layer 1
}
//Lori says piecofcake and music plays after
const int delayTicks = 140; // 2 seconds
if (jjTriggers[8] && !hasTriggeredCrate8) {
hasTriggeredCrate8 = true; // prevents retriggering
stage = 0;
// Hide layer 3
startTick = jjGameTicks;
WaitingForTriggerQ = true;
if (jjTriggers[23]){
jjLayers[3].hasTiles = false;
}
if (jjMusicLoad("LoriPieceOfCake.wav")) {
jjMusicPlay();
switchTime = jjGameTicks + delayTicks;
stage = 1;
}
}
if (stage == 1 && jjGameTicks >= switchTime) {
if (jjMusicLoad("artsun.it")) {
jjMusicPlay();
}
stage = 2;
}
//^^^ Lori says piecofcake and music plays after ^^^
if (jjTriggers[11] && (jjDifficulty == 1 || jjDifficulty == 2 || jjDifficulty == 3)) {
if (!hasTriggeredCrate11) {
removeTntcratesAtFixedTiles(0, 0, 42, 62);
hasTriggeredCrate11 = true;}
else if ((jjGameTicks % 140) == 0) {
removeTntcratesAtFixedTiles(0, 0, 42, 62);}
}
if (layer5Timer > 0) {
layer5Timer--;
if (layer5Timer == 0) {
jjLayers[5].hasTiles = false;
}
}
if (jjGameConnection != GAME::LOCAL) {
handleAllTriggers();
handleGoldCoinSync();
if (!solidsReset) {
for (int i=0; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (isSolidObject(obj)) {
obj.clearPlatform();
}
}
solidsReset = true;
}
//crate pushing code
if (jjGameMode == GAME::SP || jjGameMode == GAME::COOP) {
checkGamemode = true;
for (uint i = 0; i < solids.length(); i++) {
jjOBJ@ obj = solids[i].getSolid();
if (isSolidObject(obj)) {
if (!obj.isActive || obj.state == STATE::KILL) {
obj.clearPlatform();
} else {
jjANIMFRAME@ frame = @::jjAnimFrames[obj.curFrame];
int width = frame.width;
int height = frame.height;
//if (height > 30) height = 30;
int hotX = frame.hotSpotX;
int hotY = frame.hotSpotY;
int solidness = obj.beSolid();
if ((jjMaskedVLine(int(obj.xPos)+hotX, int(obj.yPos)+hotY, height-3) && obj.beSolid() == -1) || (jjMaskedVLine(int(obj.xPos)+hotX+width, int(obj.yPos)+hotY, height-3) && obj.beSolid() == 1)) {
//collision
} else {
bool col = false;
bool descend = true;
for (uint j = 0; j < solids.length(); j++) {
jjOBJ@ obj2 = solids[j].getSolid();
if (i != j && obj.objectID != obj2.objectID && isSolidObject(obj2) && obj2.state == STATE::SLEEP) { //state of crate
jjANIMFRAME@ frame2 = @::jjAnimFrames[obj2.curFrame];
int width2 = frame2.width;
int height2 = frame2.height;
int hotX2 = frame2.hotSpotX;
int hotY2 = frame2.hotSpotY;
if (abs(obj.yPos - obj2.yPos) <= 16 && abs(obj.xPos - obj2.xPos) <= 100) {
if ((abs((obj.xPos + hotX) - (obj2.xPos + hotX2 + width2)) <= 2 && obj.beSolid() == -1) || (abs((obj.xPos + hotX + width) - (obj2.xPos + hotX2)) <= 2 && obj.beSolid() == 1)) {
col = true;
break;
}
} else if (abs(obj.xPos - obj2.xPos) <= 32 && obj2.yPos - obj.yPos >= 0 && obj2.yPos - obj.yPos <= height/2 + height2/2) {
descend = false;
}
}
}
//can push object
if (!col && solids[i].getForce() != solidness) {
solids[i].updateForce(solidness);
} else if (col && solids[i].getForce() != 0) {
solids[i].updateForce(0);
}
if (!col) {
int netForce = 0;
for (uint n = 0; n < 32; n++) {
netForce += solids[i].forces[n];
}
obj.xPos += netForce * 0.75f;
}
if (!jjMaskedHLine(int(obj.xPos)+hotX+1, width-1, int(obj.yPos)+hotY+height) && descend && (obj.eventID == OBJECT::WATERSHIELD ||obj.eventID == OBJECT::CARROTCRATE || obj.eventID == OBJECT::SPRINGCRATE || obj.eventID == OBJECT::BOMBCRATE || obj.eventID == OBJECT::CARROTBARREL || obj.eventID == OBJECT::TRIGGERCRATE)) {
obj.yPos -= 3;
}
if (!jjMaskedHLine(int(obj.xPos)+hotX+1, width-1, int(obj.yPos)+hotY+height) && descend && (obj.eventID == OBJECT::TOASTERPOWERUP || obj.eventID == OBJECT::BIGROCK || obj.eventID == OBJECT::BIGBOX)) {
obj.yPos += 3;
}
else if (!jjMaskedHLine(int(obj.xPos)+hotX+1, width-1, int(obj.yPos)+hotY+height) && descend && (obj.eventID == OBJECT::WATERSHIELD ||obj.eventID == OBJECT::CARROTCRATE || obj.eventID == OBJECT::BIGROCK || obj.eventID == OBJECT::BIGBOX || obj.eventID == OBJECT::TOASTERPOWERUP || obj.eventID == OBJECT::TRIGGERCRATE || obj.eventID == OBJECT::SPRINGCRATE || obj.eventID == OBJECT::BOMBCRATE || obj.eventID == OBJECT::CARROTBARREL)) {
//moveSolid(obj, 0, 0);
}
else if (obj.state == STATE::FALL) {
if (obj.eventID == OBJECT::SPRINGCRATE || obj.eventID == OBJECT::BOMBCRATE || obj.eventID == OBJECT::CARROTBARREL || obj.eventID == OBJECT::WATERSHIELD ||obj.eventID == OBJECT::CARROTCRATE || obj.eventID == OBJECT::BIGROCK || obj.eventID == OBJECT::BIGBOX || obj.eventID == OBJECT::TOASTERPOWERUP || obj.eventID == OBJECT::TRIGGERCRATE) {
//moveSolid(obj, 0, 0); // drop the crate x pixels when falling
if (obj.yPos > jjWaterLevel){
//moveSolid(obj, 0, 0); // Raise the crate x pixels if underwater
}
}
obj.state = STATE::SLEEP;
}
}
}
if (jjIsServer) {
solids[i].resetForcesForAllInactivePlayers();
}
solids[i].updatePos();
}
}
}
else if (checkGamemode) {
for (int i = 0; i < 32; i++) {
if (jjPlayers[i].isInGame && jjPlayers[i].platform != 0) {
jjOBJ@ obj = jjObjects[jjPlayers[i].platform];
obj.clearPlatform();
if (obj.isActive && isSolidObject(obj)) {
jjPlayers[i].platform = 0;
}
}
}
checkGamemode = false;
solidsReset = false;
}
}
}
void removeTntcratesAtFixedTiles(int x1, int y1, int x2, int y2) {
for (int i = jjObjectCount - 1; i >= 0; i--) {
if (jjObjects[i].isActive) {
float x = jjObjects[i].xPos;
float y = jjObjects[i].yPos;
if (x >= x1 * 32 && x <= x2 * 32 && y >= y1 * 32 && y <= y2 * 32) {
if (jjObjects[i].eventID == OBJECT::BOMBCRATE) {
jjDeleteObject(i);
}
}
}
}
}
void removeQueenAtFixedTiles(int x1, int y1, int x2, int y2) {
for (int i = jjObjectCount - 1; i >= 0; i--) {
jjOBJ@ obj = jjObjects[i];
if (obj.isActive) {
float x = obj.xPos;
float y = obj.yPos;
// Check if the Queen is inside the given tile area
if (x >= x1 * 32 && x <= x2 * 32 && y >= y1 * 32 && y <= y2 * 32) {
if (jjObjects[i].eventID == OBJECT::QUEEN) {
jjDeleteObject(i);
}
}
}
}
}
void onFunction100(jjPLAYER@ player) {
if (!jjTriggers[0]) {
jjTriggers[0] = true;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
}
void onFunction101(jjPLAYER@ player) {
if (!jjTriggers[1]) {
jjTriggers[1] = true;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
}
void onFunction102(jjPLAYER@ player) {
if (!jjTriggers[2]) {
jjTriggers[2] = true;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
}
void onFunction103(jjPLAYER@ player) {
jjTriggers[3] = !jjTriggers[3];
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
void onFunction104(jjPLAYER@ player) {
if (!jjTriggers[4]) {
jjTriggers[4] = true;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
}
void onFunction106(jjPLAYER@ player) {
if (!jjTriggers[6]) {
jjTriggers[6] = true;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
if (!jjTriggers[21]) {
jjTriggers[21] = true;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
}
void onFunction107(jjPLAYER@ p)
{
if (jjDifficulty == 0 && jjMaxHealth == 3){ //easy mode
jjChat("/maxhealth 5");
p.health = 4;
}
else if (jjDifficulty == 1 && jjMaxHealth == 2){ //normal mode
jjChat("/maxhealth 3");
p.health = 2;
}
else if ((jjDifficulty == 3) && jjMaxHealth == 1) { //turbo mode
jjChat("/maxhealth 2");
p.health = 1;
}
}
void onFunction109(jjPLAYER@ player) {
jjTriggers[9] = !jjTriggers[9];
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
void onFunction110(jjPLAYER@ player) {
if (!jjTriggers[10]) {
jjTriggers[10] = true;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
}
void onFunction119(jjPLAYER@ player) {
if (!jjTriggers[19]) {
jjTriggers[19] = true;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
}
uint lastBellTick1 = 0;
uint lastBellTick2 = 0;
void AwesomeCountingDownBellForFirstThree(jjPLAYER@ player) {
if (jjGameTicks - lastBellTick1 >= 70) {
jjSample(player.xPos, player.yPos, SOUND::COMMON_BELL_FIRE2);
lastBellTick1 = jjGameTicks;
}
}
void AwesomeCountingDownBellForLastOne(jjPLAYER@ player) {
if (jjGameTicks - lastBellTick2 >= 70) {
jjSample(player.xPos, player.yPos, SOUND::COMMON_BELL_FIRE);
lastBellTick2 = jjGameTicks;
}
}
void onFunction255(jjPLAYER@ player) {
if (jjTriggers[29]) {
int activePlayers = 0;
for (int i = 0; i < 32; ++i) {
if (jjPlayers[i].isActive)
++activePlayers;
}
if (activePlayers == 1) {
jjTriggers[29] = false;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
else if (hasBeenRoasted[player.playerID]) {
jjTriggers[29] = false;
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
else {
jjSample(player.xPos, player.yPos, SOUND::COMMON_HORN1);
}
}
else {
jjSample(player.xPos, player.yPos, SOUND::COMMON_HORN1);
}
}
void onFunction129(jjPLAYER@ player) {
jjTriggers[29] = !jjTriggers[29];
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
}
// object respawn trigger
void onFunction111(jjPLAYER@ player) {
jjSample(player.xPos, player.yPos, SOUND::MENUSOUNDS_TYPEENTER);
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.isActive && isSolidObject(obj)) {
obj.particlePixelExplosion(1);
moveSolid(obj, obj.xOrg - obj.xPos, obj.yOrg - obj.yPos);
obj.putOnGround(false);
}
}
}
//Death on tile if you put a text-event (id100, AS enabled)
void onFunction112(jjPLAYER@ player) {
player.hurt(1); // -1 hp
}
//Death on tile if you put a text-event (id100, AS enabled)
void onFunction254(jjPLAYER@ player) {
player.hurt(5); // -5 hp
}
void onFunction113(jjPLAYER@ player) {
int oldFastfire = player.fastfire;
player.fastfire = 35;
bool tookAmmo = false;
for (int i = 2; i <= 9; i++) {
if (player.ammo[i] > 0) {
tookAmmo = true;
player.ammo[i] = 0;
}
}
player.currWeapon = WEAPON::BLASTER;
// Play sound if ammo was taken OR fire rate changed from less than 35 to 35
if (tookAmmo || (oldFastfire < 35 && player.fastfire == 35)) {
jjSample(player.xPos, player.yPos, SOUND::COMMON_HORN1);
}
}
// Texts stay on screen until you leave the text block. Use 2xx instead of xx (so 201 instead of 1, 215 instead of 15, etc.)
class HelpString {
int HelpID = -1;
int Timer = -200;
}
array<HelpString> HelpStrings(jjLocalPlayerCount);
jjTEXTAPPEARANCE spin(STRING::SPIN);
bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
HelpString@ helpString = HelpStrings[player.localPlayerID];
const uint xTile = uint(player.xPos) / 32, yTile = uint(player.yPos) / 32;
if (jjEventGet(xTile,yTile) == AREA::TEXT && jjParameterGet(xTile,yTile, 8,10) == 0) {
const int helpID = jjParameterGet(xTile,yTile, 0,8);
if (helpID >= 200 && helpID <= 215) {
if (helpID - 200 != helpString.HelpID) {
helpString.Timer = jjGameTicks + 70;
helpString.HelpID = helpID - 200;
} else if (helpString.Timer < jjGameTicks) {
helpString.Timer = jjGameTicks;
}
}
}
const int time = helpString.Timer - jjGameTicks;
if (time > -70) {
spin.xAmp = spin.yAmp = (time == 0) ? 0 : 1;
spin.spacing = (time == 0) ? 2 : 1;
canvas.drawString(
0x8000 + time * int(abs(time)) / 10,
10,
jjHelpStrings[helpString.HelpID],
STRING::SMALL,
spin,
uint(abs(time)) * 3
);
} else
helpString.HelpID = -1;
return false;
}
// in-game Texts "#|~" is light blueish grey, "#||~" is Turquoise, "#|||~" is Green, "#||||~" is Red, "#|||||~" is light blue,
// "#||||||~" is orange/yellow, "#|||||||~" is Pink, "#||||||||~" is white again, #for rainbow letters
void onFunction9 (jjPLAYER@ play) {
if (jjTriggers[29]) {
play.showText("@@@@@@#And for MP Coop mode;@sorry if there's a turquoise block blocking your way,@ask the players already in to open it, they closed it.@They don't want new players to mess up the puzzle.@@If you just got roasted, you are the only player@or you got warped for not playing coop;@go all the way to the left to unblock the way.");
text9 = true;
@text9Player = play;
text9Timer = 70 * 5; // reset timer here
}
}
bool playerIsStillInTrigger9(jjPLAYER@ play) {
int triggerX = 53 * 32;
int triggerY = 0 * 32;
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction16(jjPLAYER@ play) {
if (!BossActivated){
play.showText("#|||||||~@@@@@@@@@@@@ Thanks for playing!");
}
else if (BossActivated){
play.showText("#|||||||~@@@@@@@@@@@ Thanks for playing!");
}
text16 = true;
@text16Player = play;
text16Timer = 70 * 5; // reset timer here
}
bool playerIsStillInTrigger16(jjPLAYER@ play) {
int triggerX = 99 * 32;
int triggerY = 24 * 32;
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction17(jjPLAYER@ play) {
if (jjGameMode == GAME::SP && jjGameConnection == GAME::LOCAL){
play.showText("@@@@@ You might wanna reset the whole level... @@#||||~ IF the puzzle got too scrambled: @@ #-Type jjnext to start from the beginning.@@ -If Lori gets stuck in a wall use f10 @ and place her back.");
text17 = true;
@text17Player = play;
text17Timer = 70 * 5; // reset timer here
}
else if (jjGameConnection != GAME::LOCAL){
play.showText("@@@@@ You might wanna reset the whole level... @@#||||~ IF the puzzle got too scrambled: @@ #-To restart: Press t and type /c -> press enter.@@ -If Lori gets stuck in a wall press t -> /sp -> @ Lori will be warped out of the wall -> /coop.");
text17 = true;
@text17Player = play;
text17Timer = 70 * 5; // reset timer here
}
}
bool playerIsStillInTrigger17(jjPLAYER@ play) {
int triggerX = 13 * 32;
int triggerY = 36 * 32;
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction18(jjPLAYER@ play) {
play.showText("@@@@@@#|||||~Shoot the springs with freezer bullets.");
text18 = true;
@text18Player = play;
text18Timer = 70 * 5; // reset timer here
}
bool playerIsStillInTrigger18(jjPLAYER@ play) {
int triggerX = 38 * 32;
int triggerY = 40 * 32;
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction19(jjPLAYER@ play) {
play.showText("@@@@@@#||||~The fish insta-roast you. @ You'll need a watershield to get through there. @ Hint: don't open the shield with TNT.");
text19 = true;
@text19Player = play;
text19Timer = 70 * 5; // reset timer here
}
bool playerIsStillInTrigger19(jjPLAYER@ play) {
int triggerX = 16 * 32;
int triggerY = 49 * 32;
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction123(jjPLAYER@ play) {
play.showText("@@@@@@#||||~#Hint: You can reach the coin from the other side,@ but first you need to shoot it with the electro blaster.");
text23 = true;
@text23Player = play;
text23Timer = 70 * 5; // reset timer here
}
bool playerIsStillInTrigger23(jjPLAYER@ play) {
int triggerX = 95 * 32;
int triggerY = 60 * 32;
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction28(jjPLAYER@ play) {
play.showText("@@@@@#|||||||~#Hey Jazz or Spaz, sorry but this level is made for Lori.@ If you wanna play it you have to own The Secret Files (TSF).@ Or any version that has Lori as a playable character. @@@ If your game has Lori please come back with her; @ you can use jjnext for example. At the level start you will@ automatically be turned into Lori if she's available. @ You can also use jjmorph a few times to turn into Lori -> f10. @@@ There's no exit from here, so if you wanna exit@ press exit -> quit.");
text28 = true;
@text28Player = play;
text28Timer = 70 * 5; // reset timer here
}
bool playerIsStillInTrigger28(jjPLAYER@ play) {
int px = int(play.xPos);
int py = int(play.yPos);
// Range: X = 6–9, Y = 9–12
return (px >= 5 * 32 && px < 11 * 32 &&
py >= 8 * 32 && py < 14 * 32);
}
void onFunction29(jjPLAYER@ play) {
if (jjGameConnection != GAME::LOCAL) {
play.showText("@@@@@@#|||||||~#For Coop-mode: newly joined players might mess up your progress.@ With this button you can keep them out at first, @ so you can chat a bit before you potentially let them in.@ You can also use 't -> /maxplayers x' or a server password.");
text29 = true;
@text29Player = play;
text29Timer = 70 * 5; // reset timer here
}
}
bool playerIsStillInTrigger29(jjPLAYER@ play) {
int triggerX = 62 * 32;
int triggerY = 6 * 32;
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction30(jjPLAYER@ play) {
if (jjDifficulty == 0) {
play.showText("@@@@@@#On easy mode parts are blocked off.@ Open the trigger crate,@ then get the sugar rush and go through the fish maze!!!");
text30 = true;
@text30Player = play;
text30Timer = 70 * 5; // reset timer here
}
}
bool playerIsStillInTrigger30(jjPLAYER@ play) {
// Tile 1 coordinates
int triggerX1 = 12 * 32;
int triggerY1 = 20 * 32;
// Tile 2 coordinates
int triggerX2 = 37 * 32;
int triggerY2 = 37 * 32;
int px = int(play.xPos);
int py = int(play.yPos);
bool insideTile1 = (px >= triggerX1 && px < triggerX1 + 32 &&
py >= triggerY1 && py < triggerY1 + 32);
bool insideTile2 = (px >= triggerX2 && px < triggerX2 + 32 &&
py >= triggerY2 && py < triggerY2 + 32);
return insideTile1 || insideTile2;
}
void onFunction31(jjPLAYER@ play) {
if (PlayedOtherModeThanCoopInMp) {
hasBeenRoasted[play.playerID] = true;
StopWarping26 = false;
if (jjGameConnection != GAME::LOCAL && jjGameMode == GAME::COOP) {
play.warpToID(25);
}
// reset the flag so it won't warp again until another mode switch
PlayedOtherModeThanCoopInMp = false;
jjLayers[2].hasTiles = false;
jjLayers[3].hasTiles = true;
jjLayers[5].hasTiles = false;
}
}
//layer visibility
int startTick = 0;
bool WaitingForTriggerQ = false;
bool BossActivated = false;
void onFunction11(jjPLAYER@ play){
BossActivated = true;
}
void onFunction12(jjPLAYER@ play) {
if (jjGameConnection == GAME::LOCAL){
jjLayers[2].hasTiles = true;}
}
void onFunction214(jjPLAYER@ play) {
if (!jjTriggers[13]) {
jjTriggers[14] = true;
}
}
void onFunction215(jjPLAYER@ play) {
if (jjTriggers[13]){
jjLayers[2].hasTiles = true;
}
}
void onFunction216(jjPLAYER@ play) {
if (jjGameConnection != GAME::LOCAL){
jjLayers[2].hasTiles = false;}
}
void onFunction217(jjPLAYER@ play) {
if (jjGameConnection == GAME::LOCAL && !jjTriggers[22]) {jjTriggers[22] = true;}
}
void onFunction21(jjPLAYER@ play) {
jjLayers[3].hasTiles = true; // Show layer 3
}
void onFunction22(jjPLAYER@ play) {
jjLayers[3].hasTiles = false; // Hide layer 3
}
void onFunction32(jjPLAYER@ play) {
jjLayers[5].hasTiles = false;
}
void onFunction33(jjPLAYER@ player) {
jjLayers[5].hasTiles = true;
}
//sync triggers for all player in online coop (also newly joined players)
enum PACKET_TYPE {
TRIGGER_CHANGE,
TRIGGER_JOINED,
SOLID_MOVE,
SOLID_FORCE,
SOLID_JOINED,
GOLDCOIN_MOVE
}
// DISCLAIMER: some triggers may or may not necessarily need this.
array<bool> triggersSync(32);
void handleAllTriggers() {
if ((jjGameTicks % 140) == 0) { // ✅ Only run every 2 seconds
for (int i = 0; i < 32; i++) {
if (jjTriggers[i] != triggersSync[i]) {
triggersSync[i] = jjTriggers[i];
jjSTREAM request;
request.push(uint8(TRIGGER_CHANGE));
request.push(jjLocalPlayers[0].playerID);
request.push(i);
request.push(jjTriggers[i]);
jjSendPacket(request);
}
}
}
}
void handleGoldCoinSync() {
if (!jjIsServer) return;
// Run every 2 seconds
if ((jjGameTicks % 140) != 0) return;
for (int i = 0; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.isActive && obj.eventID == OBJECT::GOLDCOIN) {
jjSTREAM packet;
packet.push(uint8(GOLDCOIN_MOVE));
packet.push(int8(0)); // dummy player ID (server)
packet.push(obj.xOrg);
packet.push(obj.yOrg);
packet.push(obj.xPos);
packet.push(obj.yPos);
jjSendPacket(packet);
}
}
}
void ohServerPleaseGiveMeYourAvailableTriggers() {
jjSTREAM request;
request.push(uint8(TRIGGER_JOINED));
request.push(jjLocalPlayers[0].playerID);
jjSendPacket(request);
}
void onReceive(jjSTREAM &in packet, int clientID) {
jjSTREAM orig = packet;
uint8 type;
int8 playerID;
packet.pop(type);
packet.pop(playerID);
bool accept = (jjIsServer && jjPlayers[playerID].clientID == clientID) || !jjIsServer;
if (accept) {
switch (type) {
case TRIGGER_CHANGE: {
int triggerID;
packet.pop(triggerID);
bool status;
packet.pop(status);
// Skip syncing trigger 28
if (triggerID != 28) {
triggersSync[triggerID] = status;
jjTriggers[triggerID] = status;
if (jjIsServer) {
jjSendPacket(orig, -clientID);
}
}
} break;
case TRIGGER_JOINED: {
uint bitset = 0;
if (jjIsServer) {
for (int i = 0; i < 32; i++) {
if (i == 28) continue; // ← Skip trigger 28 here
if (triggersSync[i]) {
bitset |= 1 << i;
}
}
jjSTREAM update;
update.push(uint8(TRIGGER_JOINED));
update.push(int8(0));
update.push(bitset);
jjSendPacket(update, clientID);
} else {
packet.pop(bitset);
int id = 0;
while (bitset != 0 && id < 32) {
if (id != 28) { // ← Skip trigger 28 here too
jjTriggers[id] = triggersSync[id] = ((bitset & 1) != 0);
}
bitset >>>= 1;
id++;
}
}
} break;
case SOLID_FORCE: {
float xOrg, yOrg;
packet.pop(xOrg);
packet.pop(yOrg);
for (uint i = 0; i < solids.length(); i++) {
jjOBJ@ obj = solids[i].getSolid();
if (obj.xOrg == xOrg && obj.yOrg == yOrg) {
packet.pop(solids[i].forces[playerID]);
if (jjIsServer) {
jjSendPacket(orig, -clientID);
}
return;
}
}
} break;
case SOLID_MOVE: {
float xOrg, yOrg, newX, newY;
packet.pop(xOrg);
packet.pop(yOrg);
packet.pop(newX);
packet.pop(newY);
for (uint i = 0; i < solids.length(); i++) {
jjOBJ@ obj = solids[i].getSolid();
if (obj.xOrg == xOrg && obj.yOrg == yOrg) {
obj.xPos = newX;
obj.yPos = newY;
if (jjIsServer) {
jjSendPacket(orig, -clientID);
}
return;
}
}
} break;
case SOLID_JOINED: {
if (jjIsServer) {
const int limit = 10;
int objsLeft = 10;
jjSTREAM objects;
for (uint i = 0; i < solids.length(); i++) {
SolidWrapper@ sw = solids[i];
jjOBJ@ obj = sw.getSolid();
if (sw.eventID != OBJECT::GENERATOR || obj.isActive) {
objects.push(sw.eventIDActual);
objects.push(sw.xOrg);
objects.push(sw.yOrg);
objects.push(sw.xPos);
objects.push(sw.yPos);
objsLeft--;
}
if (objsLeft <= 0 || i == solids.length() - 1) {
jjSTREAM update;
update.push(uint8(SOLID_JOINED));
update.push(int8(0));
update.push(objects.getSize());
jjSTREAM objectsCompressed;
jjZlibCompress(objects, objectsCompressed);
update.push(objectsCompressed);
jjSendPacket(update, clientID);
objsLeft = limit;
objects = jjSTREAM();
}
}
} else {
uint uncompressedSize;
packet.pop(uncompressedSize);
jjSTREAM objects;
packet.pop(objects);
if (jjZlibUncompress(objects, objects, uncompressedSize)) {
while (objects.getSize() != 0) {
uint8 eventID;
float xOrg, yOrg;
float xPos, yPos;
objects.pop(eventID);
objects.pop(xOrg);
objects.pop(yOrg);
objects.pop(xPos);
objects.pop(yPos);
bool wasActive = false;
for (uint i = 0; i < solids.length(); i++) {
jjOBJ@ obj = solids[i].getSolid();
if (obj.isActive && obj.eventID == eventID && obj.xOrg == xOrg && obj.yOrg == yOrg) {
obj.xPos = xPos;
obj.yPos = yPos;
wasActive = true;
break;
}
}
if (wasActive) {
continue;
}
jjOBJ@ act = jjObjects[jjAddObject(eventID, xOrg, yOrg, 0, CREATOR::LEVEL)];
if (act.behavior == BEHAVIOR::CRATE || act.eventID == OBJECT::CARROTCRATE) {
act.state = STATE::ACTION;
act.xPos = xPos;
act.yPos = yPos;
act.putOnGround();
} else {
jjKillObject(act.objectID);
}
}
}
}
} break;
case GOLDCOIN_MOVE: {
float xOrg, yOrg, newX, newY;
packet.pop(xOrg);
packet.pop(yOrg);
packet.pop(newX);
packet.pop(newY);
for (int i = 0; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.isActive && obj.eventID == OBJECT::GOLDCOIN &&
obj.xOrg == xOrg && obj.yOrg == yOrg) {
obj.xPos = newX;
obj.yPos = newY;
return;
}
}
} break;
}
}
}
[SOLID]
void carrotCrateBehavior(jjOBJ@ obj) {
if (obj.state == STATE::START) {
obj.putOnGround();
obj.state = STATE::SLEEP;
} else if (obj.state == STATE::ACTION) {
//jjObjects[jjAddObject(OBJECT::SHARD, obj.xPos, obj.yPos, obj.objectID, CREATOR::OBJECT, BEHAVIOR::DEFAULT)].curAnim = obj.killAnim;
obj.particlePixelExplosion(2);
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_WOOD1);
obj.bulletHandling = HANDLING::IGNOREBULLET;
obj.playerHandling = HANDLING::EXPLOSION;
obj.state = STATE::KILL;
} else {
obj.behave(BEHAVIOR::CRATE);
}
}
void modifyCarrotCrate() {
uint firstAnim = jjAnimSets[ANIM::PICKUPS].firstAnim;
uint curFrame = jjAnimations[firstAnim + 5].firstFrame;
for (uint i = 0; i < 256; i++) {
jjANIMSET@ set = jjAnimSets[ANIM::CUSTOM[i]];
if (set.firstAnim == 0) {
set.load(jjPIXELMAP("1tilePushable.png"), 32, 32);
firstAnim = set.firstAnim;
curFrame = jjAnimations[firstAnim].firstFrame;
break;
}
}
jjObjectPresets[OBJECT::CARROTCRATE].killAnim = firstAnim;
jjObjectPresets[OBJECT::CARROTCRATE].curFrame = curFrame;
jjObjectPresets[OBJECT::CARROTCRATE].behavior = carrotCrateBehavior;
}
[SOLID]
void BigObjectBehaviorBandaid(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.putOnGround();
obj.state = STATE::SLEEP;
break;
case STATE::ACTION:
case STATE::SLEEP:
obj.draw();
break;
case STATE::FREEZE:
if ((jjGameTicks & 3) == 0 && obj.freeze > 0) {
obj.freeze--;
}
if (obj.freeze <= 0) {
obj.state = obj.oldState;
}
obj.draw();
break;
default:
obj.behave(BEHAVIOR::BIGOBJECT);
break;
}
}
// No Fire Zone Logic
array<bool> overrideNoFire(jjLocalPlayerCount);
class RevertNoFire : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::PICKUP);
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ player, int) {
overrideNoFire[player.localPlayerID] = true;
obj.behavior = BEHAVIOR::EXPLOSION2;
obj.scriptedCollisions = false;
obj.frameID = 0;
jjSample(player.xPos, player.yPos, SOUND::COMMON_PICKUPW1);
return true;
}
}
void revampNoFireHandling() {
jjObjectPresets[OBJECT::FASTFIRE].behavior = RevertNoFire();
jjObjectPresets[OBJECT::FASTFIRE].scriptedCollisions = true;
// now handling no fire zones in AngelScript don't place fire zones in MLLE
for (int y = 0; y < jjLayers[4].height; y++) {
for (int x = 0; x < jjLayers[4].width; x++) {
if (jjEventGet(x, y) == AREA::NOFIREZONE && jjParameterGet(x, y, 2, 2) == 0) { // Remove iff NO FIRE ZONE's var is No Fire
jjEventSet(x, y, 0);
}
}
}
}
void noFireOnPlayer(jjPLAYER@ player) {
int id = player.localPlayerID;
bool isInNoFireZone = player.yPos >= jjWaterTarget || (
player.xPos >= (61 * 32) && player.xPos <= (78 * 32) &&
player.yPos >= (31 * 32) && player.yPos <= (38 * 32)
);
player.noFire = !overrideNoFire[id] && isInNoFireZone;
// Reset if the player goes outside no fire zones;
if (overrideNoFire[id] && !isInNoFireZone) {
overrideNoFire[id] = false;
}
}
// Fur color change via chat, but only in a specific area
uint8 furColor = 40;
dictionary furCommands = {
{"!resetcolor", 40},
{"!lightgreen", 15},
{"!green", 16},
{"!darkgreen", 17},
{"!deeppurple", 89},
{"!red", 24},
{"!darkred", 25},
{"!darkerred", 25},
{"!blue", 32},
{"!darkblue", 33},
{"!pink", 48},
{"!white", 64},
{"!grey", 65},
{"!blue-gray", 72},
{"!blue-grey", 73},
{"!turquoise", 81},
{"!aqua", 80},
{"!orange", 41},
{"!purple", 88}
};
void onChat(int clientID, string &in text, CHAT::Type chatType) {
// Make text lowercase
for (uint i = 0; i < text.length(); i++) {
uint8 codepoint = text[i] | 32;
if (codepoint >= "a"[0] && codepoint <= "z"[0]) {
text[i] = codepoint;
}
}
if (furCommands.exists(text)) {
for (int i = 0; i < 32; i++) {
jjPLAYER@ player = jjPlayers[i];
if (player.clientID == clientID) {
bool inBooth = player.xPos >= 48 * 32 && player.xPos <= 53 * 32 &&
player.yPos >= 8 * 32 && player.yPos <= 12 * 32;
if (inBooth) {
if (jjLocalPlayers[0].clientID == clientID) {
furColor = uint8(furCommands[text]);
jjSample(player.xPos, player.yPos, SOUND::COMMON_SWISH4);
// ✅ Show layer 5 for 30 frames
jjLayers[5].hasTiles = true;
layer5Timer = 50;
}
if (jjIsServer) {
jjAlert(player.nameUnformatted + " turned " +
(text == "!resetcolor" ? "yellow" : text.substr(1)) + "!!!", true, STRING::SMALL);
}
} else {
if (jjLocalPlayers[0].clientID == clientID) {
jjSample(player.xPos, player.yPos, SOUND::COMMON_HORN1);
}
if (jjIsServer) {
jjAlert("Go back to the Hairspray Booth to change color...", true, STRING::SMALL);
}
}
break;
}
}
}
}
dictionary furCheats = {
{"jjcresetcolor", 40},
{"jjclightgreen", 15},
{"jjcgreen", 16},
{"jjcdarkgreen", 17},
{"jjcdeeppurple", 89},
{"jjcred", 24},
{"jjcdarkred", 25},
{"jjcdarkerred", 25},
{"jjcblue", 32},
{"jjcdarkblue", 33},
{"jjcpink", 48},
{"jjcwhite", 64},
{"jjcgrey", 65},
{"jjcblue-gray", 72},
{"jjcblue-grey", 73},
{"jjcturquoise", 81},
{"jjcaqua", 80},
{"jjcorange", 41},
{"jjcpurple", 88}
};
//!blue-gray !blue-grey
bool onCheat(string &in cheat) {
if (cheat == "jjcpu")
{
// Prevent the cheat from activating
return true;
}
if (furCheats.exists(cheat)) {
jjPLAYER@ play = jjLocalPlayers[0];
bool inBooth = play.xPos >= 48 * 32 && play.xPos <= 53 * 32 &&
play.yPos >= 8 * 32 && play.yPos <= 12 * 32;
if (inBooth) {
furColor = uint8(furCheats[cheat]);
jjSample(play.xPos, play.yPos, SOUND::COMMON_SWISH4);
// ✅ Show layer 5 for 30 frames
jjLayers[5].hasTiles = true;
layer5Timer = 50;
play.showText("@@@#" + play.nameUnformatted + " turned " + (cheat == "jjcresetcolor" ? "yellow" : cheat.substr(3)) + "!!!", STRING::SMALL);
}
else {
jjSample(play.xPos, play.yPos, SOUND::COMMON_HORN1);
play.showText("@@@#Go back to the Hairspray Booth to change color...", STRING::SMALL);
}
return true;
}
return false;
}
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.