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-1.j2l" ///@MLLE-Generated
#pragma require "More Mori.j2l" ///@MLLE-Generated
///@SaveAndRunArgs -hard ///@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::MONITOR ||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;
}
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;
// On the other hand, this is coordinated between all connections.
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() {
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));
}
}
// Probably will not be necessary any time soon
/* solids.sort(function(ob1, ob2) {
if (ob1.yOrg < ob1.yOrg) {
return true;
} else if (ob1.yOrg > ob2.yOrg) {
return false;
}
return ob1.xOrg < ob2.xOrg;
}); */
}
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;
// Switch the current character to Lori.
// • morphTo() is the safe, network-synced way to change chars
// • second argument = false => no white “morph” flash if you don’t want it
p.morphTo(CHAR::LORI, /*morphEffect=*/false);
// Make Lori count as their “original” character too,
// so Revert Morph monitors can’t send them back to Jazz/Spaz.
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 (jjIsServer || (/* jjGameConnection == GAME::LOCAL && */jjMaxHealth != 2)) {
// Normal mode: max health 2 hearts
jjChat("/maxhealth 2");
}
if (!jjIsServer) {
ohServerPleaseGiveMeYourAvailableTriggers();
ohServerPleaseGiveMeYourUpdatedSolidPositions();
}
}
void onLevelLoad(){
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;
/* int BIGROCKframeID = jjAnimations[jjAnimSets[ANIM::BIGROCK].firstAnim].firstFrame;
jjAnimFrames[BIGROCKframeID].hotSpotX = -30;
jjAnimFrames[BIGROCKframeID].hotSpotY = -40;
jjAnimFrames[BIGROCKframeID].coldSpotX = 60;
jjAnimFrames[BIGROCKframeID].coldSpotY = 48; */
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::ICEPOWERUP].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::QUEEN].deactivates = true;
jjObjectPresets[OBJECT::SPIKEBOLL].energy = 127;
jjObjectPresets[OBJECT::SPIKEBOLL3D].energy = 127;
//jjAnimSets[ANIM::QUEEN +4].load(); // just in case
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();
}
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;
}
/* if (obj.eventID == OBJECT::FISH) {
// Apply custom damage logic
if (player !is null && player.health > 0) {
player.hurt(2); // Deal 2 HP damage
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;
}
// 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 hasTriggeredCrate8 = false;
bool hasTriggeredCrate5 = false;
int stage = 0;
int switchTime = 0;
bool isMultiplayerMode = false;
void onFunction66(jjPLAYER@ player) {
isMultiplayerMode = true;
// Other event logic here...
}
bool text9 = false;
int text9Timer = -1;
jjPLAYER@ text9Player;
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 text24 = false;
int text24Timer = -1;
jjPLAYER@ text24Player;
bool text28 = false;
int text28Timer = -1;
jjPLAYER@ text28Player;
bool text29 = false;
int text29Timer = -1;
jjPLAYER@ text29Player;
bool text30 = false;
int text30Timer = -1;
jjPLAYER@ text30Player;
void onMain() {
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text9 && playerIsStillInTrigger9(play)) {
onFunction9(play);
}
}
}
if (text9) {
if (playerIsStillInTrigger9(text9Player)) {
if (text9Timer > 0) {
text9Timer--;
} else {
onFunction9(text9Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text9Player.showText(""); // Clear text
text9 = false;
text9Timer = -1;
@text9Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text17 && playerIsStillInTrigger17(play)) {
onFunction17(play);
}
}
}
if (text17) {
if (playerIsStillInTrigger17(text17Player)) {
if (text17Timer > 0) {
text17Timer--;
} else {
onFunction17(text17Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text17Player.showText(""); // Clear text
text17 = false;
text17Timer = -1;
@text17Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text18 && playerIsStillInTrigger18(play)) {
onFunction18(play);
}
}
}
if (text18) {
if (playerIsStillInTrigger18(text18Player)) {
if (text18Timer > 0) {
text18Timer--;
} else {
onFunction18(text18Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text18Player.showText(""); // Clear text
text18 = false;
text18Timer = -1;
@text18Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text19 && playerIsStillInTrigger19(play)) {
onFunction19(play);
}
}
}
if (text19) {
if (playerIsStillInTrigger19(text19Player)) {
if (text19Timer > 0) {
text19Timer--;
} else {
onFunction19(text19Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text19Player.showText(""); // Clear text
text19 = false;
text19Timer = -1;
@text19Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text23 && playerIsStillInTrigger23(play)) {
onFunction23(play);
}
}
}
if (text23) {
if (playerIsStillInTrigger23(text23Player)) {
if (text23Timer > 0) {
text23Timer--;
} else {
onFunction23(text23Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text23Player.showText(""); // Clear text
text23 = false;
text23Timer = -1;
@text23Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text24 && playerIsStillInTrigger24(play)) {
onFunction24(play);
}
}
}
if (text24) {
if (playerIsStillInTrigger24(text24Player)) {
if (text24Timer > 0) {
text24Timer--;
} else {
onFunction24(text24Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text24Player.showText(""); // Clear text
text24 = false;
text24Timer = -1;
@text24Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text28 && playerIsStillInTrigger28(play)) {
onFunction28(play);
}
}
}
if (text28) {
if (playerIsStillInTrigger28(text28Player)) {
if (text28Timer > 0) {
text28Timer--;
} else {
onFunction28(text28Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text28Player.showText(""); // Clear text
text28 = false;
text28Timer = -1;
@text28Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text29 && playerIsStillInTrigger29(play)) {
onFunction29(play);
}
}
}
if (text29) {
if (playerIsStillInTrigger29(text29Player)) {
if (text29Timer > 0) {
text29Timer--;
} else {
onFunction29(text29Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text29Player.showText(""); // Clear text
text29 = false;
text29Timer = -1;
@text29Player = null;
}
}
for (int i = 0; i < 32; i++) {
jjPLAYER@ play = jjPlayers[i];
if (play.isActive) {
// Only trigger if no one has triggered it yet
if (!text30 && playerIsStillInTrigger30(play)) {
onFunction30(play);
}
}
}
if (text30) {
if (playerIsStillInTrigger30(text30Player)) {
if (text30Timer > 0) {
text30Timer--;
} else {
onFunction30(text30Player); // Re-show text
}
} else {
// Player left the trigger: clear message
text30Player.showText(""); // Clear text
text30 = false;
text30Timer = -1;
@text30Player = null;
}
}
if (jjGameMode != GAME::COOP && isMultiplayerMode) {
for (int i = 0; i < 32; ++i) {
if (!jjPlayers[i].isActive) continue;
jjPLAYER@ p = jjPlayers[i];
int screenX = int(p.cameraX) + 90;
int screenY = int(p.cameraY) + 350;
jjDrawString(screenX, screenY, "!!!Type T -> /coop to switch to COOP mode!!!", STRING::MEDIUM, STRING::NORMAL, 0, i);
jjDrawString(screenX + 4, screenY + 30, " Online, this level only works well in Coop mode.", STRING::SMALL, STRING::NORMAL, 0, i);
}
}
if (jjTriggers[5] && !hasTriggeredCrate5) {
jjTriggers[22] = true;
hasTriggeredCrate5 = true;
}
//Lori says piecofcake and music plays after
const int delayTicks = 140; // 2 seconds
if (jjTriggers[8] && !hasTriggeredCrate8) {
hasTriggeredCrate8 = true; // prevents retriggering
stage = 0;
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 (jjDifficulty == 2) {
removeCarrotsInArea(89, 0, 94, 1);
}
if (jjDifficulty >= 3) {
removeCarrotsInArea(89, 1, 94, 3);
}
if (layer5Timer > 0) {
layer5Timer--;
if (layer5Timer == 0) {
jjLayers[5].hasTiles = false;
}
}
if (jjGameConnection != GAME::LOCAL) {
handleAllTriggers();
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 removeCarrotsInArea(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::CARROT) {
jjDeleteObject(i);
} else {
// Helpful debug line
// jjAlert("Not a carrot: ID=" + jjObjects[i].eventID);
}
}
}
}
}
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 == 2){ //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);
}
}
void onFunction27(jjPLAYER@ play) {
// Warp only if the player is Jazz or Spaz
if (play.charCurr == CHAR::JAZZ || play.charCurr == CHAR::SPAZ) {
play.warpToID(27); // Warp to warp target ID 27
}
if (jjGameConnection == GAME::LOCAL) {
jjTriggers[22]=true;
}
}
array<bool> hasBeenRoasted(32, false);
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);
}
/* void onFunction134(jjPLAYER@ player) {
jjMusicStop(); // Mutes/stops all music
} */
// 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
}
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 or you are the only player;@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; // X coordinate * 32 (check (X, y)
int triggerY = 0 * 32; // Y coordinate * 32 (check (x, Y)
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) {
play.showText("#|||||||~@@@@@@@@@ Thanks for playing!");
}
void onFunction17(jjPLAYER@ play) {
play.showText("@@@@@@You might wanna reset the whole level... @#||||~IF the puzzle got too scrambled: @#type jjnext (offline SP) or press t and type /c (online COOP).@@If Lori gets stuck in a wall use f10@or in coop: t -> /sp -> f10 -> 'move Lori from wall' @then put the gamemode back to /coop");
text17 = true;
@text17Player = play;
text17Timer = 70 * 5; // reset timer here
}
bool playerIsStillInTrigger17(jjPLAYER@ play) {
int triggerX = 13 * 32; // X coordinate * 32 (check (X, y)
int triggerY = 36 * 32; // Y coordinate * 32 (check (x, Y)
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; // X coordinate * 32 (check (X, y)
int triggerY = 40 * 32; // Y coordinate * 32 (check (x, Y)
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; // X coordinate * 32 (check (X, y)
int triggerY = 49 * 32; // Y coordinate * 32 (check (x, Y)
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction23(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; // X coordinate * 32 (check (X, y)
int triggerY = 60 * 32; // Y coordinate * 32 (check (x, Y)
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction24(jjPLAYER@ play) {
play.showText("@@@@@@#|||||||~#Jump over the purple line@or the gate to the Queen will close again.@You can always open it again with the purple button though.");
text24 = true;
@text24Player = play;
text24Timer = 70 * 5; // reset timer here
}
bool playerIsStillInTrigger24(jjPLAYER@ play) {
int triggerX = 9 * 32; // X coordinate * 32 (check (X, y)
int triggerY = 7 * 32; // Y coordinate * 32 (check (x, Y)
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.@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 triggerX = 7 * 32; // X coordinate * 32 (check (X, y)
int triggerY = 12 * 32; // Y coordinate * 32 (check (x, Y)
int px = int(play.xPos);
int py = int(play.yPos);
return (px >= triggerX && px < triggerX + 32 &&
py >= triggerY && py < triggerY + 32);
}
void onFunction29(jjPLAYER@ play) {
play.showText("@@@@@@#|||||||~#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; // X coordinate * 32 (check (X, y)
int triggerY = 6 * 32; // Y coordinate * 32 (check (x, Y)
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;
}
//layer visibility
void onFunction21(jjPLAYER@ play) {
jjLayers[3].hasTiles = true; // Show layer 3
}
void onFunction22(jjPLAYER@ play) {
jjLayers[3].hasTiles = false; // Hide layer 3
}
void onFunction31(jjPLAYER@ play) {
jjLayers[5].hasTiles = true; // show layer 5
}
void onFunction32(jjPLAYER@ play) {
jjLayers[5].hasTiles = false; // hide layer 5
}
//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
}
// DISCLAIMER: some triggers may or may not necessarily need this.
array<bool> triggersSync(32);
void handleAllTriggers() {
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 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);
// There's probably a better way to convey this?
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);
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 (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) {
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 a player gets "server denied you" it almost always means you're sending too large packets. Decrease the limit variable in that case.
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 { // This is the most inefficient shit ever but it should be okay because it only happens on join, right? :')
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;
}
}
}
[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;
// Preferably, you should remove no fire zones in your level editor instead since you're
// now handling no fire zones in AngelScript. The below is the totally unrecommended way of
// removing no fire zones originally added using your level editor though.
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 || jjred", 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.