| Name | Author | Game Mode | Rating | |||||
|---|---|---|---|---|---|---|---|---|
| Castle + Damn Ultimate | DoubleGJ | Tileset conversion | N/A | |||||
const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
#include "MLLE-Include-1.8.asc" ///@MLLE-Generated
#pragma require "CastUltEx1-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "Tube.j2t" ///@MLLE-Generated
#pragma require "CastUltEx1.j2l" ///@MLLE-Generated
#pragma require "CastUlt-BDay.j2t"
#pragma require "DD-Anims.j2a"
#include "DD-Order.asc"
#include "DD-Rank.asc"
#pragma offer "Castle_OCRemix.mp3"
#pragma offer "DD-CastleFast.it"
#pragma offer "TM-RAGE.S3M"
#pragma require "noahbangs-earthquake.ogg"
#pragma offer "castleboss.it"
#pragma require "crazy-laugh.wav"
#pragma require "chain-rattle1.wav"
#pragma require "chain-rattle2.wav"
#pragma require "window-break.wav"
#pragma require "rockfall.wav"
#pragma require "Bat_takeoff.ogg"
#pragma require "Bat_loop.ogg"
#pragma require "Bat_idle1.ogg"
#pragma require "Bat_idle2.ogg"
#pragma require "Bat_idle3.ogg"
#pragma require "Bat_idle4.ogg"
#pragma require "empty.wav"
#pragma require "common-gemsmsh1.wav"
#pragma require "blink.wav"
#pragma require "dl-funk.wav"
#pragma require "squidward-walkL.wav"
#pragma require "squidward-walkR.wav"
const int bestTime = 480;
const float maxScore = 130000;
const int easyTimer = 0;
const int normalTimer = 0;
const int hardTimer = 84000;
const int turboTimer = 63000;
array<uint> fastFeetMode(4, 0);
bool noFast = true;
string storeMusic = "Castle_OCRemix.mp3";
int farPlayerID, nearPlayerID = 0;
bool NPCnear, guardKey, Earthquake, isFalling, startQuake, breakBG = false;
uint8 inmates = 5;
array<bool> inmateCheck(inmates, false);
int8 drawBubba = 2;
int bubbaxOffset = 0;
uint elapsed = 0;
int crumbleChannel, batChannel = 0;
array<int> addBrickX = {153, 212, 86, 62};
array<int> addBrickY = {26, 54, 41, 42};
//array<uint16> brickTileX, brickTileY, brickTileID;
jjPAL myPal;
const uint8 CornerEventID = OBJECT::GUN8AMMO3; //whatever
enum PlayerState { Normal, Loop, Ropeway };
enum LoopAngle { Floor, WallOnLeft, Ceiling, WallOnRight };
class PlayerX {
PlayerX(){}
PlayerState State = PlayerState::Normal;
LoopAngle CurrentAngle = LoopAngle::Floor, NextAngle;
float CornerXOrg, CornerYOrg, CornerSpeed, CornerElapsed = -1,/* CornerLength,*/ LastX, LastY;
//int LastLoopEventParameter = -1;
int LastTile;
int CornerAngleChange, CornerRepositioningMultiplier, CornerAngleMultiplier;
int LastHealth = -1;
}
array<PlayerX> PlayerXs(jjLocalPlayerCount);
class CornerEventResult {
LoopAngle Angle;
int XOrg, YOrg;
CornerEventResult(LoopAngle a, int x, int y) { Angle = a; XOrg = x; YOrg = y; }
}
const array<const CornerEventResult@> NextLoopAngles = {
null, CornerEventResult(WallOnRight, 0, -1), //floor, turn left, loop
null, CornerEventResult(Floor, 2, 0), //wallonleft, turn left, loop
null, CornerEventResult(WallOnLeft, 1, 2), //ceiling, turn left, loop
null, CornerEventResult(Ceiling, -1, 1), //wallonright, turn left, loop
CornerEventResult(WallOnLeft, 1, -1), null, //floor, turn right, loop
CornerEventResult(Ceiling, 2, 1), null, //wallonleft, turn right, loop
CornerEventResult(WallOnRight, 0, 2), null, //ceiling, turn right, loop
CornerEventResult(Floor, -1, 0), null, //wallonright, turn right, loop
CornerEventResult(WallOnRight, 1, 3), null, //floor, turn left, corner
CornerEventResult(Floor, -2, 1), null, //wallonleft, turn left, corner
CornerEventResult(WallOnLeft, 0, -2), null, //ceiling, turn left, corner
CornerEventResult(Ceiling, 3, 0), null, //wallonright, turn left, corner
null, CornerEventResult(WallOnLeft, 0, 3), //floor, turn right, corner
null, CornerEventResult(Ceiling, -2, 0), //wallonleft, turn right, corner
null, CornerEventResult(WallOnRight, 1, -2), //ceiling, turn right, corner
null, CornerEventResult(Floor, 3, 1) //wallonright, turn right, corner
};
void onLevelLoad() {
initiateDialogue();
initiateRanking();
initFade();
jjAnimSets[ANIM::CUSTOM[37]].load(37, "DD-Anims.j2a");
jjAnimSets[ANIM::ROBOT].load();
jjAnimSets[ANIM::BUBBA].load();
jjAnimSets[ANIM::CUSTOM[14]].load(14, "DD-Anims.j2a");
jjSampleLoad(SOUND::INTRO_MONSTER, "crazy-laugh.wav");
jjAnimSets[ANIM::ROCK].load(4, "DD-Anims.j2a");
///@Event 39=Corner Object |+|Modifier |Corner
jjObjectPresets[CornerEventID].behavior = BEHAVIOR::INACTIVE; //zonify
///@Event 40=Loop Object |+|Modifier |Loop
jjObjectPresets[OBJECT::GUN9AMMO3].behavior = LoopObject;
jjObjectPresets[OBJECT::GUN9AMMO3].playerHandling = HANDLING::PARTICLE;
AddCornerEvents();
///@Event 38=Little Bat |+|Scenery |Batty |Batt
jjAnimSets[ANIM::CUSTOM[12]].load(12, "DD-Anims.j2a");
jjObjectPresets[OBJECT::TNTAMMO3].behavior = littleBat();
jjObjectPresets[OBJECT::TNTAMMO3].playerHandling = HANDLING::PARTICLE;
jjSampleLoad(SOUND::P2_THROW, "Bat_takeoff.ogg");
jjSampleLoad(SOUND::P2_FART, "Bat_loop.ogg");
jjSampleLoad(SOUND::P2_FOEW1, "Bat_idle1.ogg");
jjSampleLoad(SOUND::P2_FOEW4, "Bat_idle2.ogg");
jjSampleLoad(SOUND::P2_FOEW5, "Bat_idle3.ogg");
jjSampleLoad(SOUND::P2_POEP, "Bat_idle4.ogg");
jjAnimSets[ANIM::CUSTOM[3]].load(3, "DD-Anims.j2a"); // DD pickups
///@Event 159=Cookie|+|Food |Cookie
jjObjectPresets[OBJECT::GRAPES].determineCurAnim(ANIM::CUSTOM[3], 0);
///@Event 160=Bread (white)|+|Food |Bread
jjObjectPresets[OBJECT::LETTUCE].determineCurAnim(ANIM::CUSTOM[3], 68);
///@Event 156=Bread (wholegrain)|+|Food |Whole |Bread
jjObjectPresets[OBJECT::THING].determineCurAnim(ANIM::CUSTOM[3], 69);
///@Event 157=Bun |+|Food |Bun
jjObjectPresets[OBJECT::WATERMELON].determineCurAnim(ANIM::CUSTOM[3], 70);
///@Event 161=Kaiser Roll |+|Food |Kaiser
jjObjectPresets[OBJECT::EGGPLANT].determineCurAnim(ANIM::CUSTOM[3], 71);
///@Event 162=Croissant |+|Food |Croi- |ssant
jjObjectPresets[OBJECT::CUCUMBER].determineCurAnim(ANIM::CUSTOM[3], 72);
///@Event 158=Swiss Roll |+|Food |Swiss |Roll
jjObjectPresets[OBJECT::PEACH].determineCurAnim(ANIM::CUSTOM[3], 51);
///@Event 142=Canned Beans |+|Food |Can |Bean
jjObjectPresets[OBJECT::BANANA].determineCurAnim(ANIM::CUSTOM[3], 74);
///@Event 143=Eclair |+|Food |Eclair
jjObjectPresets[OBJECT::CHERRY].determineCurAnim(ANIM::CUSTOM[3], 76);
///@Event 144=Apple Pie |+|Food |Apple |Pie
jjObjectPresets[OBJECT::ORANGE].determineCurAnim(ANIM::CUSTOM[3], 52);
///@Event 147=Toast |+|Food |Toast
jjObjectPresets[OBJECT::STRAWBERRY].determineCurAnim(ANIM::CUSTOM[3], 75);
///@Event 155=Muffin |+|Food |Muffin
jjObjectPresets[OBJECT::LIME].determineCurAnim(ANIM::CUSTOM[3], 77);
///@Event 170=White Chocolate |+|Food |White |Choc
jjObjectPresets[OBJECT::CHIPS].determineCurAnim(ANIM::CUSTOM[3], 48);
///@Event 67=Super Item |+|Goodies |Super |Item|Type:{Red Gem,Green Gem,Blue Gem,Purple Gem,Watermelon,Jackfruit,Cheese Wheel}3
jjObjectPresets[OBJECT::SUPERGEM].behavior = SuperGemWrapper;
jjObjectPresets[OBJECT::FLICKERGEM].behavior = flickerWrapper;
jjSampleLoad(SOUND::COMMON_GEMSMSH1, "empty.wav");
jjSampleLoad(SOUND::INTRO_GRAB, "common-gemsmsh1.wav");
jjObjectPresets[OBJECT::FASTFEET].points = 100;
jjObjectPresets[OBJECT::FASTFEET].scriptedCollisions = true;
jjObjectPresets[OBJECT::FASTFEET].behavior = FastFeet();
///@Event 41=Bomb Demon (armed) |+|Object |Demon |Bomb
///@Event 117=Bomb Demon |-|Enemy |Bomb |Demon |Pinkie:c1
jjSampleLoad(SOUND::TURTLE_SPK1TURT, "squidward-walkL.wav");
jjSampleLoad(SOUND::TURTLE_SPK2TURT, "squidward-walkR.wav");
jjSampleLoad(SOUND::TURTLE_IDLE1, "blink.wav");
jjAnimSets[ANIM::TURTLE].load(29, "DD-Anims.j2a");
jjObjectPresets[OBJECT::NORMTURTLE].behavior = bombDemon();
jjObjectPresets[OBJECT::NORMTURTLE].scriptedCollisions = true;
jjObjectPresets[OBJECT::NORMTURTLE].playerHandling = HANDLING::SPECIAL;
// jjObjectPresets[OBJECT::NORMTURTLE].bulletHandling = HANDLING::SPECIAL;
demonBomb(jjObjectPresets[OBJECT::TURTLESHELL]);
jjPlayers[31].furSet(88, 48, 32, 72);
jjPlayers[30].furSet(64, 64, 64, 64); // lol fuck it
///@Event 125=Tuff Demon |-|Enemy |Tuff |Demon
jjAnimSets[ANIM::CUSTOM[27]].load(27, "DD-Anims.j2a");
jjObjectPresets[OBJECT::FATCHICK].energy = 5;
jjObjectPresets[OBJECT::FATCHICK].points = 500;
jjObjectPresets[OBJECT::FATCHICK].behavior = tuffDemon();
jjSampleLoad(SOUND::SCIENCE_PLOPKAOS, "dl-funk.wav");
/* if (jjAnimSets[ANIM::DESTSCEN] == 0) jjAnimSets[ANIM::DESTSCEN].load();
jjOBJ@ myDestructSceneryVariant1 = jjObjectPresets[OBJECT::DESTRUCTSCENERYBOMB];
myDestructSceneryVariant1.playerHandling = HANDLING::SELFCOLLISION;
myDestructSceneryVariant1.behavior = bombDestructScenery;*/
jjObjectPresets[OBJECT::BOMB].behavior = bombEdit;
///@Event 107=Imp |-|Enemy|Imp| |X Range:3|Y Range:3
jjAnimSets[ANIM::CUSTOM[26]].load(26, "DD-Anims.j2a");
jjObjectPresets[OBJECT::SPARK].behavior = imp();
jjObjectPresets[OBJECT::SPARK].determineCurAnim(ANIM::CUSTOM[26], 0);
jjObjectPresets[OBJECT::SPARK].points = 50;
///@Event 217=NPC |+|Object |NPC | |Type:{Jill,Yoman,Chief,Savage (male),Savage (female),Guard}3 |Order ID:5
jjAnimSets[ANIM::CUSTOM[16]].load(16, "DD-Anims.j2a"); // NPC sprites
jjAnimSets[ANIM::CUSTOM[5]].load(5, "DD-Anims.j2a"); // dialogue prompt sprite
jjObjectPresets[OBJECT::EVA].behavior = NPC();
///@Event 96=Decorative Vine|+|Scenery|Decor|Vine|Length:3|Foreground:c1|XOffset:3|YOffset:3
jjSampleLoad(SOUND::INTRO_UP1, "chain-rattle1.wav");
jjSampleLoad(SOUND::INTRO_UP2, "chain-rattle2.wav");
const uint vineFrameID = jjAnimations[jjAnimSets[ANIM::CUSTOM[13]].allocate(array<uint> = {8})];
for (uint i = 0; i < 8; ++i) {
jjANIMFRAME@ vineFrame = jjAnimFrames[vineFrameID + i];
jjPIXELMAP vineImage(i * 32 + 8, 5*32, 16, 52 + i*16, 4);
for (uint x = 0; x < vineImage.width; ++x)
for (uint y = 0; y < vineImage.height; ++y)
if (vineImage[x,y] == 0)
vineImage[x,y] = 128;
vineImage.save(vineFrame);
vineFrame.hotSpotX = -8;
}
jjOBJ@ preset = jjObjectPresets[96];
preset.behavior = DecorativeVine;
preset.curFrame = vineFrameID;
preset.playerHandling = HANDLING::PARTICLE;
jjSampleLoad(SOUND::SKELETON_BONE1, "window-break.wav");
jjSampleLoad(SOUND::DRAGFLY_BEELOOP, "rockfall.wav");
///@Event 111=Destructible Pillar |+|Trigger |Dest |Pilr
jjObjectPresets[OBJECT::CHESHIRE1].behavior = quakeScen;
///@Event 112=Lori Block |+|Trigger |Lori |Scen
jjOBJ@ myDestructSceneryVariant2 = jjObjectPresets[OBJECT::CHESHIRE2];
myDestructSceneryVariant2.points = 50;
myDestructSceneryVariant2.playerHandling = HANDLING::SELFCOLLISION;
myDestructSceneryVariant2.behavior = loriBlock;
jjAnimSets[ANIM::BIGTREE].load();
jjPIXELMAP cell(16, 0, 32, 32, 4);
jjANIMFRAME@ bigtree = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::BIGTREE].firstAnim].firstFrame];
cell.save(bigtree);
bigtree.hotSpotX = -16;
bigtree.hotSpotY = -16;
jjPIXELMAP pillar(0, 32, 32, 128, 4);
jjANIMFRAME@ smalltree = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::SMALTREE].firstAnim].firstFrame];
pillar.save(smalltree);
bigtree.hotSpotX = -16;
bigtree.hotSpotY = -16;
jjObjectPresets[OBJECT::SMALLTREE].behavior = fallPillar;
///@Event 218=Dripper |+|Scenery |Drip | |Speed:2 |Lava:c1
jjAnimSets[ANIM::CUSTOM[28]].load(28, "DD-Anims.j2a");
jjObjectPresets[OBJECT::BUBBLER].behavior = dripper();
// jjObjectPresets[OBJECT::BUBBLER].determineCurAnim(ANIM::CUSTOM[28], 0);
///@Event 244=Falling Brick |+|Object |Brick |Trap |BGWall:c1
jjObjectPresets[OBJECT::CTFBASE].behavior = brickTrap();
jjAnimSets[ANIM::QUEEN].load();
///@Event 129=Takeoff |+|Modifier |Take |Off |Left:c1
jjObjectPresets[OBJECT::STEAM].behavior = BEHAVIOR::INACTIVE; //zonify
///@Event 254=Delet This |-|Modifier |NOT |v
deleteUnwantedEvents();
jjLAYER@ spriteLayer = jjLayers[4];
spriteLayer.generateSettableTileArea(223, 61, 13, 11);
spriteLayer.generateSettableTileArea(208, 67, 7, 5);
spriteLayer.generateSettableTileArea(156, 34, 18, 7);
spriteLayer.generateSettableTileArea(134, 26, 13, 14);
spriteLayer.generateSettableTileArea(84, 40, 14, 10);
jjLAYER@ sky = jjLayers[8];
sky.xSpeed = 0.02;
sky.ySpeed = 0.02;
sky.xAutoSpeed = -0.2;
sky.yAutoSpeed = -0.01;
sky.xSpeedModel = LAYERSPEEDMODEL::NORMAL;
sky.ySpeedModel = LAYERSPEEDMODEL::NORMAL;
myPal = jjBackupPalette;
jjSetFadeColors(myPal.color[207]);
}
void onLevelBegin() {
MLLE::SpawnOffgrids();
setTimer();
/* int i = 0;
for (int xTile = jjLayerWidth[4]; --xTile >= 0;)
for (int yTile = jjLayerHeight[4]; --yTile >= 0;)
if (jjTileGet(4, xTile, yTile) == 4060 || jjTileGet(4, xTile, yTile) == 4061) {
brickTileX.resize(i+1);
brickTileY.resize(i+1);
brickTileID.resize(i+1);
brickTileX[i] = xTile;
brickTileY[i] = yTile;
brickTileID[i] = jjTileGet(4, xTile, yTile);
i++;
}*/
}
void onLevelReload() {
MLLE::SpawnOffgridsLocal();
scoreSeconds = storeScoreSeconds;
if (isFalling) {
jjLayers[6].xAutoSpeed = 0;
jjLayers[6].yAutoSpeed = 0;
isFalling = false;
}
if (Earthquake)
{
jjTriggers[2] = true;
jjMusicLoad("castleboss.it");
storeMusic = "castleboss.it";
jjLAYER@ hideDest = MLLE::GetLayer("Hide Dest");
hideDest.hasTiles = false;
for (uint i = 0; i < addBrickX.length; i++) jjAddObject(OBJECT::CTFBASE, (addBrickX[i] * 32) + 16, (addBrickY[i] * 32) + 16);
}
if (breakBG) jjLayers[7].hasTiles = false;
if (guardKey) jjTriggers[1] = true;
// for (uint16 i = 0; i < brickTileID.length; i++) jjTileSet(4, brickTileX[i], brickTileY[i], brickTileID[i]);
}
void onMain() {
elapsed++;
if (jjDifficulty == 0) oneUpsIntoBlueGems();
if ((jjGameTicks % 70) == 0 && !levelOver && CurrentPopup is null) scoreSeconds++;
rankingSetup();
if (levelOver) updateFade();
for (int i = 0; i <= jjLocalPlayerCount; i++) {
if (fastFeetMode[i] != 0) {
noFast = false;
break;
} else noFast = true;
}
if (noFast && !levelOver) jjMusicLoad(storeMusic);
/* for (int xTile = jjLayerWidth[4]; --xTile >= 0;)
for (int yTile = jjLayerHeight[4]; --yTile >= 0;)
if (jjEventGet(xTile,yTile) == CornerEventID)
jjDrawString(xTile*32 + 10, yTile*32 + 10, "" + jjParameterGet(xTile,yTile, 0,4)); */
}
bool onCheat(string &in cheat) { // made for testing but do go off
if (cheat == "jjfast") {
fastFeetMode[0] = 1400; // can only cheat in SP mode anyway
if (jjMusicFileName != "DD-CastleFast.it") {
storeMusic = jjMusicFileName;
jjMusicLoad("DD-CastleFast.it");
}
jjAlert("jjfast", size: STRING::MEDIUM);
return true;
}
if (levelOver || CurrentPopup !is null) return true;
else return false;
}
void onPlayer(jjPLAYER@ play) {
dialogueInterface(play);
rankingInterface(play);
if (!levelOver) {
if (fastFeetMode[play.playerID] > 0) {
// if (playX.State != PlayerState::Loop) {
if (play.xAcc > 0) {
play.xAcc = play.xAcc + 1;
play.xSpeed = play.xSpeed + 2;
}
if (play.xAcc < 0) {
play.xAcc = play.xAcc - 1;
play.xSpeed = play.xSpeed - 2;
}
play.keyRun = true;
// }
fastFeetMode[play.playerID]--;
}
PlayerX@ playX = PlayerXs[play.localPlayerID];
if (playX.LastHealth != play.health) {
if (playX.LastHealth > play.health) { //injured
playX.State = PlayerState::Normal;
playX.CurrentAngle = LoopAngle::Floor;
playX.LastTile = -1;
play.invisibility = false;
play.cameraUnfreeze();
}
playX.LastHealth = play.health;
return;
}
if (playX.State == PlayerState::Ropeway) {
play.keyLeft = play.keyRight = play.keyRun = play.keyDown = false;
play.xSpeed = play.ySpeed = 0;
//play.invincibility = -2;
return;
}
const uint xTile = uint(play.xPos) >> 5, yTile = uint(play.yPos) >> 5;
const uint8 currEvent = jjEventGet(xTile,yTile);
if (currEvent == CornerEventID) {
const int loopEventParameter = jjParameterGet(xTile, yTile, 0, 4);
if ((loopEventParameter >> 2) == playX.CurrentAngle) {
const auto curAnim = play.curAnim - jjAnimSets[play.setID];
if (play.currTile != playX.LastTile && (playX.State == PlayerState::Loop || (curAnim >= RABBIT::RUN1 && curAnim <= RABBIT::RUN3))) { //running
//if (loopEventParameter != playX.LastLoopEventParameter) {
const int circumstance = ((loopEventParameter & 3) << 3) | (playX.CurrentAngle << 1) | (play.direction == 1 ? 1 : 0);
const CornerEventResult@ result = NextLoopAngles[circumstance];
if (result !is null) {
playX.LastTile = play.currTile;
//playX.LastLoopEventParameter = loopEventParameter;
//jjAlert(result.Angle + "," + result.XOrg + "," + result.YOrg);
playX.NextAngle = result.Angle;
playX.CornerXOrg = (int(xTile) + result.XOrg) * 32;
playX.CornerYOrg = (int(yTile) + result.YOrg) * 32;
const bool isLoop = loopEventParameter & 2 == 0;
//playX.CornerLength = isLoop ? 256 : 256; //todo
playX.CornerElapsed = 0; //todo?
playX.CornerRepositioningMultiplier = isLoop ? 50 : 84; //40:120
playX.CornerAngleMultiplier = isLoop ? 1 : -1;
playX.CornerSpeed = isLoop ? 30 : 18;
playX.CornerAngleChange = int(playX.NextAngle - playX.CurrentAngle);
if (abs(playX.CornerAngleChange) == 3)
playX.CornerAngleChange /= -3;
playX.State = PlayerState::Loop;
play.invisibility = true;
//play.noclipMode = true;
play.xSpeed = play.ySpeed = 0;
if (jjParameterGet(xTile, yTile, -2, 1) == 1)
play.cameraFreeze(play.cameraX, play.cameraY, false, true);
}
//}
}
}
} else if (currEvent == AREA::FLYOFF) { //this feels like the sensible choice
if (playX.State == PlayerState::Loop) play.keyJump = true;
play.ballTime = 0;
} else if (currEvent == AREA::PATH) { // not renaming the event and its parameters cuz the original functionality is retained, this is just an add-on; refer to param values below
float entryX;
if (abs(play.xSpeed) >= 16.0) entryX = play.xSpeed;
else entryX = play.xSpeed * 1.05;
if (entryX != 0) {
if (play.currTile != playX.LastTile && jjParameterGet(xTile, yTile, 9, 1) == 0) { // visited tile for the first time AND is entry/exit point?
play.ballTime = 120;
play.xPos = xTile * 32 + 16;
play.yPos = yTile * 32 + 16;
playX.LastTile = play.currTile;
play.xSpeed = entryX;
switch (jjParameterGet(xTile, yTile, 6, 3)) { // entry/exit type
case 0: play.ySpeed = entryX / 2.1; break; // left edge spiral top
case 1: play.ySpeed = entryX / -2.1; break; // right edge spiral top
case 2: if (entryX < 0) play.ySpeed = entryX / 2; else play.ySpeed = 0; break; // left edge spiral bottom
case 3: if (entryX > 0) play.ySpeed = entryX / -2; else play.ySpeed = 0; break; // right edge spiral bottom
case 4: if (entryX < 0) play.ySpeed = entryX / 2; else play.ySpeed = entryX; break; // left edge spiral bottom into slope
case 5: if (entryX > 0) play.ySpeed = entryX / -2; else play.ySpeed = entryX * -1; break; // right edge spiral bottom into slope
}
play.noclipMode = !play.noclipMode; // if already noclip then not noclip, else do noclip.
} else if (play.currTile != playX.LastTile && jjParameterGet(xTile, yTile, 9, 1) == 1 && play.noclipMode) { // inner spiral node, must be noclip
play.xPos = xTile * 32 + 16;
play.yPos = yTile * 32 + 16;
playX.LastTile = play.currTile;
play.xSpeed = entryX * -1; // bounce to the other side
play.ySpeed = play.ySpeed * 1.05; // adjust for increasing xSpeed
}
}
} else if (currEvent == OBJECT::STEAM) {
if (play.currTile != playX.LastTile && ((play.xSpeed >= 4.0 && jjParameterGet(xTile, yTile, 0, 1) == 0) || (play.xSpeed <= -4.0 && jjParameterGet(xTile, yTile, 0, 1) == 1))) {
play.spring(0, abs(play.xSpeed) * -1, true, false);
playX.LastTile = play.currTile;
}
} else playX.LastTile = -1;
int angle = playX.CurrentAngle << 8;
const int speedDivider = play.keyRun ? 1 : 4;
if (playX.CornerElapsed >= 0) {
playX.CornerElapsed += playX.CornerSpeed / speedDivider;
if (playX.CornerElapsed > 256)
playX.CornerElapsed = 256;
angle += int(playX.CornerElapsed * playX.CornerAngleChange);
//play.xPos = playX.LastX = playX.CornerXOrg + jjSin(angle) * playX.CornerRepositioningMultiplier * -playX.CornerAngleMultiplier;
//play.yPos = playX.LastY = playX.CornerYOrg - jjCos(angle) * playX.CornerRepositioningMultiplier * -playX.CornerAngleMultiplier;
playX.LastX = playX.CornerXOrg + jjSin(angle) * playX.CornerRepositioningMultiplier * -playX.CornerAngleMultiplier;
playX.LastY = playX.CornerYOrg - jjCos(angle) * playX.CornerRepositioningMultiplier * -playX.CornerAngleMultiplier;
play.xSpeed = playX.LastX - play.xPos;
play.ySpeed = playX.LastY - play.yPos;
if (playX.CornerElapsed == 256) {
playX.CornerElapsed = -1;
playX.CurrentAngle = playX.NextAngle;
}
} else if (playX.State == PlayerState::Loop) {
const int speedMultiplier = 16 / speedDivider * play.direction;
//play.xPos = playX.LastX += jjCos(angle) * speedMultiplier;
//play.yPos = playX.LastY += jjSin(angle) * speedMultiplier;
playX.LastX += jjCos(angle) * speedMultiplier;
playX.LastY += jjSin(angle) * speedMultiplier;
play.xSpeed = playX.LastX - play.xPos;
play.ySpeed = playX.LastY - play.yPos;
}
if (playX.State == PlayerState::Loop) {
if (playX.CornerAngleMultiplier == 1 && playX.CornerElapsed >= 0) //quarter pipe
play.keyJump = false;
if ((playX.CornerElapsed < 0 && playX.CurrentAngle == LoopAngle::Floor) || play.keyJump || (play.direction == 1 && !play.keyRight) || (play.direction == -1 && !play.keyLeft) || jjMaskedPixel(int(playX.LastX), int(playX.LastY))) {
playX.State = PlayerState::Normal;
playX.CurrentAngle = LoopAngle::Floor;
playX.CornerElapsed = -1;
play.invisibility = false;
//play.noclipMode = false;
play.xSpeed = play.direction * jjCos(angle) * 16 / speedDivider;
play.ySpeed = play.direction * jjSin(angle) * 16 / speedDivider;
play.cameraUnfreeze();
} else {
play.keyLeft = play.keyRight = play.keyFire = false;
jjDrawRotatedSprite(play.xPos, play.yPos, play.setID, play.keyRun ? RABBIT::RUN3 : RABBIT::RUN1, jjGameTicks >> 2, -angle, play.direction,1, SPRITE::PLAYER, play.playerID);
}
}
if (isFalling) {
makeBrick(play);
crumbleChannel = jjSampleLooped(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::DRAGFLY_BEELOOP, crumbleChannel);
if ((jjGameTicks % 3) == 0) {
if ((jjGameTicks % 6) == 0) {
jjLayers[6].setXSpeed(3, true);
} else
jjLayers[6].setXSpeed(-3, true);
}
}
if ((jjGameTicks % 3) == 0 && startQuake == true) {
if ((jjGameTicks % 6) == 0) play.cameraFreeze((play.xPos - (jjSubscreenWidth / 2)), play.yPos, true, false);
else play.cameraFreeze((play.xPos + (jjSubscreenWidth / 2)), play.yPos, true, false);
}
if (elapsed == 240 && startQuake == true) {
continueTalk(play);
}
if ((play.currTile == 2490435 || play.currTile == 2555971) && jjMaskedPixel(int(play.xPos), int(play.yPos))) play.xPos = play.xPos - 16;
}
}
bool onDrawScore(jjPLAYER@ player, jjCANVAS@ canvas) {
if (fastFeetMode[player.playerID] > 210 || (fastFeetMode[player.playerID] > 0 && jjGameTicks % 2 == 0)) canvas.drawString((jjSubscreenWidth / 2) - (jjGetStringWidth("GOTTA GO FAST 00", STRING::MEDIUM, STRING::BOUNCE) / 2), 50, "GOTTA GO FAST " + uint(fastFeetMode[player.playerID] / 70), STRING::MEDIUM, STRING::BOUNCE, 16);
if (CurrentPopup !is null) {
CurrentPopup.Draw(canvas);
return !CurrentPopup.DrawHUD();
}
if (levelOver) {
drawFade(player, canvas);
if (rankLine < 6) rankingDisplay(player, canvas);
}
if (NPCnear) {
canvas.drawSprite((jjSubscreenWidth / 2) - 16, jjSubscreenHeight - (jjSubscreenHeight / 32), ANIM::BIGTREE, 0, 0);
canvas.drawString(jjSubscreenWidth / 2, jjSubscreenHeight - (jjSubscreenHeight / 32), "x" + inmates, smallScreen ? STRING::SMALL : STRING::MEDIUM);
}
if (rankLine >= 6) return true;
else return false;
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
if (jjColorDepth < 16) {
canvas.drawRectangle(0, 0, jjSubscreenWidth, jjSubscreenHeight, 79);
canvas.drawString((jjSubscreenWidth / 16), (jjSubscreenHeight / 2) - (jjSubscreenHeight / 8), "This level does not", smallScreen ? STRING::SMALL : STRING::MEDIUM);
canvas.drawString((jjSubscreenWidth / 16), (jjSubscreenHeight / 2) - (jjSubscreenHeight / 16), "support 8-bit color depth.", smallScreen ? STRING::SMALL : STRING::MEDIUM);
canvas.drawString((jjSubscreenWidth / 16), (jjSubscreenHeight / 2) + (jjSubscreenHeight / 16), "Please go to options menu", smallScreen ? STRING::SMALL : STRING::MEDIUM);
canvas.drawString((jjSubscreenWidth / 16), (jjSubscreenHeight / 2) + (jjSubscreenHeight / 8), "and switch to 16-bit color.", smallScreen ? STRING::SMALL : STRING::MEDIUM);
}
if (rankLine >= 6) return true;
else return CurrentPopup !is null && !CurrentPopup.DrawHUD();
}
bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
if (rankLine >= 6) return true;
else return CurrentPopup !is null && !CurrentPopup.DrawHUD();
}
bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {
if (jjDifficulty == 0 && CurrentPopup is null) {
return true;
}
if (rankLine >= 6) return true;
else return CurrentPopup !is null && !CurrentPopup.DrawHUD();
}
bool onDrawPlayerTimer(jjPLAYER@ play, jjCANVAS@ canvas) { return CurrentPopup !is null && !CurrentPopup.DrawHUD(); }
void onDrawLayer4(jjPLAYER@ player, jjCANVAS@ canvas) {
if (drawBubba == 1) {
canvas.drawSprite((270 * 32) - bubbaxOffset, (74 * 32) + 20, ANIM::BUBBA, 6, (jjGameTicks/10) & 11, -1);
if (bubbaxOffset < 640) bubbaxOffset += 4;
else drawBubba = 0;
}
if (drawBubba == 2) canvas.drawSprite((270 * 32), (74 * 32) + 20, ANIM::BUBBA, 5, 0, 1);
canvas.drawSprite((296 * 32) + 16, (36 * 32) + 16, ANIM::CUSTOM[27], 1, (jjGameTicks/9) & 3 + 8, 1);
canvas.drawSprite((299 * 32) + 16, (36 * 32) + 16, ANIM::CUSTOM[27], 1, (jjGameTicks/9) & 3 + 8, -1);
}
void LoopObject(jjOBJ@ obj) {
if (obj.state == STATE::START) {
obj.state = STATE::ROTATE;
obj.var[0] = jjParameterGet(uint(obj.xOrg) >> 5,uint(obj.yOrg) >> 5, 0,1);
obj.var[1] = jjParameterGet(uint(obj.xOrg) >> 5,uint(obj.yOrg) >> 5, 1,4);
obj.doesHurt = 0; //starts at 0, so (LoopsUnmaskedOnLeft != leftSideUnmasked) below will always be true the first time
} if (obj.state == STATE::DEACTIVATE)
obj.deactivate();
else {
const auto nearestPlayerID = obj.findNearestPlayer(960*960);
if (nearestPlayerID >= 0) {
const jjPLAYER@ play = jjPlayers[nearestPlayerID];
const uint spacing = obj.var[1];
uint8 leftSideUnmasked;
if (play.xPos < obj.xOrg - 32)
leftSideUnmasked = 2;
else if (play.xPos > obj.xOrg + spacing*32 + 128)
leftSideUnmasked = 1;
else if (PlayerXs[play.localPlayerID].CurrentAngle == LoopAngle::Ceiling)
leftSideUnmasked = play.direction == -1 ? 2 : 1;
else
return;
if (obj.doesHurt != leftSideUnmasked) {
obj.doesHurt = leftSideUnmasked;
const uint xTile = uint(obj.xOrg) >> 5, yTile = uint(obj.yOrg) >> 5;
const bool grassOnRight = obj.var[0] == 1;
if (leftSideUnmasked == 2) {
jjTileSet(4, xTile + 0, yTile, 0);
jjTileSet(4, xTile + 1, yTile, 0);
jjTileSet(4, xTile + spacing + 2, yTile, grassOnRight ? 323 : 306);
jjTileSet(4, xTile + spacing + 3, yTile, grassOnRight ? 324 : 307);
jjTileSet(4, xTile + 0, yTile - 1, 0);
jjTileSet(4, xTile + spacing + 3, yTile - 1, 297);
jjEventSet(xTile + 1, yTile, 0);
jjEventSet(xTile + spacing + 2, yTile, CornerEventID);
} else {
jjTileSet(4, xTile + 0, yTile, grassOnRight ? 304 : 325);
jjTileSet(4, xTile + 1, yTile, grassOnRight ? 305 : 326);
jjTileSet(4, xTile + spacing + 2, yTile, 0);
jjTileSet(4, xTile + spacing + 3, yTile, 0);
jjTileSet(4, xTile + 0, yTile - 1, 294);
jjTileSet(4, xTile + spacing + 3, yTile - 1, 0);
jjEventSet(xTile + 1, yTile, CornerEventID);
jjEventSet(xTile + spacing + 2, yTile, 0);
}
}
}
}
}
void AddCornerEvents() {
for (int xTile = jjLayerWidth[4]; --xTile >= 0;)
for (int yTile = jjLayerHeight[4]; --yTile >= 0;) {
LoopAngle inputAngle = LoopAngle::Floor;
bool turnRight = false;
bool isLoop = false;
int xOffset = 0, yOffset = 0;
switch (jjTileGet(4, xTile, yTile)) {
case 677:
case 855:
case 2284:
case 2352:
yOffset = -1;
break;
case 686:
case 864:
case 2293:
case 2361:
xOffset = -1;
turnRight = true;
inputAngle = LoopAngle::WallOnRight;
break;
case 685:
case 819:
case 2262:
case 2362:
xOffset = 1;
turnRight = true;
inputAngle = LoopAngle::WallOnLeft;
break;
case 694:
case 828:
case 2271:
case 2371:
yOffset = 1;
inputAngle = LoopAngle::Ceiling;
break;
case 670:
case 852:
case 2280:
case 2353:
yOffset = -1;
turnRight = true;
break;
case 681:
case 863:
case 2291:
case 2364:
xOffset = 1;
inputAngle = LoopAngle::WallOnLeft;
break;
case 682:
case 838:
case 2292:
case 2363:
xOffset = -1;
inputAngle = LoopAngle::WallOnRight;
break;
case 693:
case 849:
case 2303:
case 2374:
yOffset = 1;
turnRight = true;
inputAngle = LoopAngle::Ceiling;
break;
case 956:
yOffset = 1;
isLoop = true;
turnRight = true;
inputAngle = LoopAngle::Ceiling;
break;
case 696:
case 866:
case 2369:
xOffset = -1;
isLoop = true;
turnRight = true;
inputAngle = LoopAngle::WallOnRight;
break;
case 714:
case 874:
case 2387:
yOffset = -1;
isLoop = true;
break;
case 713:
case 873:
case 2377:
yOffset = -1;
isLoop = true;
turnRight = true;
break;
case 691:
case 861:
case 2355:
xOffset = 1;
isLoop = true;
inputAngle = LoopAngle::WallOnLeft;
break;
case 953:
yOffset = 1;
isLoop = true;
inputAngle = LoopAngle::Ceiling;
break;
default:
continue;
}
if (jjEventGet(xTile + xOffset, yTile + yOffset) == 0) {
jjEventSet(xTile + xOffset, yTile + yOffset, CornerEventID);
jjParameterSet(xTile + xOffset, yTile + yOffset, 0,4, (turnRight ? 1 : 0) | (isLoop ? 0 : 2) | (inputAngle << 2));
}
}
}
void deleteUnwantedEvents() {
int width = jjLayerWidth[4];
int height = jjLayerHeight[4] - 1;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int event = jjEventGet(j, i);
if (event == 254)
jjParameterSet(j, i + 1, -12, 32, 0);
}
}
}
void loriBlock(jjOBJ@ obj) {
obj.behave(BEHAVIOR::DESTRUCTSCENERY);
int nearPlayerID = obj.findNearestPlayer(256);
if (obj.state != STATE::KILL && obj.state != STATE::DONE) {
if (jjLocalPlayers[nearPlayerID].charCurr == CHAR::LORI && jjLocalPlayers[obj.findNearestPlayer(256)].doesCollide(obj) && jjLocalPlayers[nearPlayerID].specialMove > 0) {
givePlayerPointsForObject(jjLocalPlayers[nearPlayerID], obj);
obj.state = STATE::KILL;
}
}
}
bool givePlayerPointsForObject(jjPLAYER@ player, jjOBJ@ obj) {
if (player is null)
return false;
if (obj.points != 0 && (jjGameMode == GAME::SP || jjGameMode == GAME::COOP)) {
player.score += obj.points;
jjPARTICLE@ particle = jjAddParticle(PARTICLE::STRING);
if (particle !is null) {
particle.xPos = obj.xPos;
particle.yPos = obj.yPos;
particle.xSpeed = (-32768 - int(jjRandom() & 0x3FFF)) / 65536.f;
particle.ySpeed = (-65536 - int(jjRandom() & 0x7FFF)) / 65536.f;
particle.string.text = formatInt(obj.points);
}
obj.points = 0;
return true;
}
return false;
}
class littleBat : jjBEHAVIORINTERFACE {
float offset_x,offset_y,ceiling_y;
int distance_counter,rolled_angle;
void onBehave(jjOBJ@ obj) {
// jjANIMFRAME@ frame = jjAnimFrames[obj.curFrame];
//jjDebug("obj.xPos = " + formatFloat(obj.xPos, '', 8, 2));
//jjDebug("obj.yPos = " + formatFloat(obj.yPos, '', 8, 2));
switch (obj.state) {
case STATE::START:
while (!jjMaskedPixel(int(obj.xPos), int(obj.yPos) - 9)) obj.yPos = obj.yPos - 1; // fall up
ceiling_y = obj.yPos;
obj.counter = jjRandom() % 1023;
//rolled_angle = jjRandom() % 512 - 256;
//if (rolled_angle < 0) rolled_angle = 1023 - rolled_angle;
//obj.counter = rolled_angle;
obj.state = STATE::IDLE;
break;
case STATE::IDLE:
obj.counter++;
obj.determineCurAnim(ANIM::CUSTOM[12], 2);
obj.frameID = 0;
if (obj.findNearestPlayer(128 * 128) >= 0)
{
jjSample(obj.xPos, obj.yPos, SOUND::P2_THROW, 23);
obj.state = STATE::FLY;
distance_counter = 64;
offset_x = (obj.xOrg + jjSin((obj.counter + distance_counter)*4)*32 - obj.xPos)/64;
//offset_y = (obj.yOrg + jjCos((obj.counter + distance_counter)*2)*24 - obj.yPos)/64;
offset_y = (ceiling_y + jjCos((obj.counter + distance_counter)*2)*24 - obj.yPos)/64;
}
break;
case STATE::FLY:
if (offset_x < 0)
obj.direction = -1;
else
obj.direction = 1;
obj.xPos += offset_x;
obj.yPos += offset_y;
distance_counter -= 1;
if (distance_counter <= 0) obj.state = STATE::FLOAT;
obj.counter++;
obj.determineCurAnim(ANIM::CUSTOM[12], 1);
obj.frameID = obj.counter / 6 % jjAnimations[obj.curAnim].frameCount;
break;
case STATE::FLOAT:
obj.counter += 1 + (jjRandom() % 3);
obj.xPos = obj.xOrg + jjSin(obj.counter * 4) * 32 + (int(jjRandom() % 7) - 3);
batChannel = jjSampleLooped(obj.xPos, obj.yPos, SOUND::P2_FART, batChannel, 23);
if ((obj.counter % 255) < 128) {
if (obj.direction > 0)
playRandomBat(obj.xPos, obj.yPos, 42000 + jjRandom()%4000);
obj.direction = -1;
} else {
if (obj.direction < 0)
playRandomBat(obj.xPos, obj.yPos, 42000 + jjRandom()%4000);
obj.direction = +1;
}
obj.yPos = obj.yOrg + jjCos(obj.counter * 2) * 24 + (int(jjRandom() % 7) - 3);
obj.determineCurAnim(ANIM::CUSTOM[12], 1);
obj.frameID = obj.counter / 6 % jjAnimations[obj.curAnim].frameCount;
if (obj.findNearestPlayer(256 * 256) == -1)
{
obj.state = STATE::STOP;
obj.counter = 0;
distance_counter = 64;
offset_x = (obj.xOrg - obj.xPos) / 64;
offset_y = (ceiling_y - obj.yPos) / 64;
}
break;
case STATE::STOP:
if (offset_x < 0)
obj.direction = -1;
else
obj.direction = 1;
obj.xPos += offset_x;
obj.yPos += offset_y;
distance_counter -= 1;
if (distance_counter <= 0) {
obj.state = STATE::IDLE;
}
obj.counter++;
obj.determineCurAnim(ANIM::CUSTOM[12], 1);
obj.frameID = obj.counter / 6 % jjAnimations[obj.curAnim].frameCount;
break;
case STATE::FREEZE:
if (obj.freeze > 0) obj.freeze--;
if (obj.freeze == 0) {
obj.state = STATE::STOP;
obj.counter = 0;
distance_counter = 64;
offset_x = (obj.xOrg - obj.xPos) / 64;
offset_y = (ceiling_y - obj.yPos) / 64;
}
if (obj.freeze < 30) {
obj.yPos += 4 * (jjRandom() % 16383);
obj.state = obj.oldState;
}
break;
case STATE::KILL:
obj.delete();
break;
case STATE::DEACTIVATE:
//jjDebug("obj.xPos = " + formatFloat(obj.xPos, '', 8, 2));
//jjDebug("obj.yPos = " + formatFloat(obj.yPos, '', 8, 2));
obj.deactivate();
break;
}
}
void onDraw(jjOBJ@ obj) {
if (obj.isActive && obj.state != STATE::KILL) {
obj.determineCurFrame();
obj.draw();
}
}
void playRandomBat(float xPos, float yPos, int freq) {
switch(jjRandom()%4) {
case 0: jjSample(xPos, yPos, SOUND::P2_FOEW1, 47, freq); break;
case 1: jjSample(xPos, yPos, SOUND::P2_FOEW4, 47, freq); break;
case 2: jjSample(xPos, yPos, SOUND::P2_FOEW5, 47, freq); break;
case 3: jjSample(xPos, yPos, SOUND::P2_POEP, 47, freq); break;
}
}
}
void bombEdit(jjOBJ@ obj) {
float dx, dy, distance;
obj.behave(BEHAVIOR::BOMB);
if (obj.state == STATE::EXPLODE) {
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ other = jjObjects[i];
dx = abs(obj.xPos - other.xPos);
dy = abs(obj.yPos - other.yPos);
distance = sqrt(pow(dx, 2) + pow(dy, 2)); // A Perfect Circle...
if (other.eventID == OBJECT::DESTRUCTSCENERYBOMB && other.state == STATE::SLEEP && distance <= 64) {
other.state = STATE::KILL;
// givePlayerPointsForObject(jjLocalPlayers[obj.var[6]], other); -- don't know how to determine this yet :(
}
}
}
}
class FastFeet : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::PICKUP);
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ player, int) {
fastFeetMode[player.playerID] = 1400;
if (noFast) storeMusic = jjMusicFileName;
jjMusicLoad("DD-CastleFast.it");
obj.behavior = BEHAVIOR::EXPLOSION2; //this is _essential_. just like enemies die by getting their states set to STATE::KILL, and bullets die by getting their states set to STATE::EXPLODE, pickups die by getting their behavior set to BEHAVIOR::EXPLOSION2. yes, sometimes a little consistency is in fact too much to ask for.
obj.scriptedCollisions = false; //or obj.playerHandling = HANDLING::EXPLOSION; or something like that
obj.frameID = 0;
//you should probably play a sound here too, using jjSample. pick one! it'll be an adventure!
return true; //for details, see discussion in the onObjectHit item in the method list below, but basically you should almost always return "true" here
}
}
void flickerWrapper(jjOBJ@ obj) {
if (jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3) > 3) {
obj.eventID = 157;
obj.behave(BEHAVIOR::FLICKERGEM);
obj.points = 50;
obj.var[0] = 0;
if (jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3) == 4) obj.determineCurAnim(ANIM::PICKUPS, 92);
if (jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3) == 5) obj.determineCurAnim(ANIM::CUSTOM[3], 43);
if (jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3) == 6) obj.determineCurAnim(ANIM::PICKUPS, 15);
}
else {
obj.behave(BEHAVIOR::FLICKERGEM);
obj.var[0] = jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3) + 1;
int points = jjObjectPresets[OBJECT::REDGEM + obj.var[0] - 1].points;
obj.points = points;
obj.determineCurAnim(ANIM::PICKUPS, 22);
}
}
void SuperGemWrapper(jjOBJ@ obj) {
if (obj.state == STATE::START) {
if (jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,3) < 4)
obj.var[0] = jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,3) + 1; //color
else {
obj.var[0] = 0;
obj.age = jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,3) - 3;
}
}
obj.behave(BEHAVIOR::SUPERGEM);
if (obj.state == STATE::SLEEP && obj.var[0] == 0) {
if (obj.age == 1) obj.determineCurAnim(ANIM::CUSTOM[3], 6);
if (obj.age == 2) obj.determineCurAnim(ANIM::CUSTOM[3], 42);
if (obj.age == 3) obj.determineCurAnim(ANIM::CUSTOM[3], 73);
}
if (obj.state == STATE::ACTION) {
if (jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,3) < 4) jjSample(obj.xOrg, obj.yOrg, SOUND::INTRO_GRAB);
else jjSample(obj.xOrg, obj.yOrg, SOUND::MONKEY_SPLUT);
}
}
class imp : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.scriptedCollisions = true;
obj.playerHandling = HANDLING::SPECIAL;
if (obj.isActive && obj.freeze == 0) {
obj.var[6] = jjParameterGet(uint16(obj.xOrg / 32), uint16(obj.yOrg / 32), 0, 3);
obj.var[7] = jjParameterGet(uint16(obj.xOrg / 32), uint16(obj.yOrg / 32), 3, 3);
obj.counter += 1 + (jjRandom() & 3);
int arg = obj.counter / ((obj.var[6] + 1) + (obj.var[7] + 1) / 2);
obj.xPos = obj.xOrg + jjSin(arg * 4) * 32 * (obj.var[6] + 1);
obj.yPos = obj.yOrg + jjCos(arg * 2) * 24 * (obj.var[7] + 1);
if (arg & 255 < 128 ^^ obj.direction < 0) {
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_FOEW1, 10, (obj.direction < 0 ? 30300 : 30000) + (jjRandom() & 0x7FF));
obj.direction = obj.direction < 0 ? 1 : -1;
}
obj.frameID = obj.counter / 6 % jjAnimations[obj.curAnim].frameCount;
obj.determineCurFrame();
obj.draw();
}
if (obj.state == STATE::KILL) obj.delete();
if (obj.freeze > 0) {
if (obj.freeze == 0) {
obj.unfreeze(0);
obj.state = obj.oldState;
}
else obj.freeze--;
obj.determineCurFrame();
obj.draw();
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet !is null) {
if (bullet.var[3] == 1 && bullet.animSpeed == 1) obj.grantPickup(player, 5);
else obj.grantPickup(player, 10);
obj.justHit = 20;
obj.state = STATE::KILL; // surprisingly, freezing still works
player.score += 50;
jjPARTICLE@ particle = jjAddParticle(PARTICLE::STRING);
particle.string.text = "50";
particle.xPos = obj.xPos;
particle.yPos = obj.yPos;
particle.xSpeed = -0.7;
particle.ySpeed = -1.5;
bullet.state = STATE::EXPLODE;
if (bullet.var[6] == 2) {
obj.particlePixelExplosion(1);
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BURN);
}
else { obj.particlePixelExplosion(0); }
}
else if (player !is null && force != 0) {
obj.state = STATE::KILL;
player.score += 50;
jjPARTICLE@ particle = jjAddParticle(PARTICLE::STRING);
particle.string.text = "50";
particle.xPos = obj.xPos;
particle.yPos = obj.yPos;
particle.xSpeed = -0.7;
particle.ySpeed = -1.5;
if (obj.freeze != 0) {
obj.unfreeze(0);
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ICECRUSH);
} else {
obj.particlePixelExplosion(2);
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_SMASH);
}
}
return true;
}
}
class tuffDemon : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::FATCHICK, false);
if (obj.state == STATE::WALK) {
obj.determineCurAnim(ANIM::CUSTOM[27], 0);
obj.frameID = ((obj.counter / 6) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurFrame();
}
if (obj.state == STATE::ACTION) obj.determineCurAnim(ANIM::CUSTOM[27], 1);
}
void onDraw(jjOBJ@ obj) {
obj.draw();
}
}
class bombDemon : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
jjANIMFRAME@ frame = jjAnimFrames[obj.curFrame];
int nearPlayerID = obj.findNearestPlayer(0x2000);
bool onPlatform = false;
float platformOffset = 0.f;
float py = frame.hotSpotY - frame.coldSpotY;
if (obj.isActive && obj.state != STATE::KILL) {
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ other = jjObjects[i];
if ((other.eventID == 231 || other.eventID == 232) && other.yPos > obj.yPos + 50 && other.yPos < obj.yPos + 58 && abs(other.xPos - obj.xPos) <= 30) {
onPlatform = true;
platformOffset = abs(other.xPos - obj.xPos);
obj.ySpeed = 0;
obj.xPos = obj.xPos + other.xSpeed;
break;
}
if ((other.eventID == 231 || other.eventID == 232) && obj.state == STATE::WALK && abs(other.yPos - obj.yPos) <= 24 && int(other.xPos) == int(obj.xPos + (36 * obj.direction))) {
obj.counter = 0;
obj.frameID = 0;
obj.determineCurAnim(ANIM::TURTLE, 2);
jjSample(obj.xPos, obj.yPos, SOUND::FROG_FROG5);
obj.xPos -= 2 * obj.direction;
obj.state = STATE::TURN;
}
if ((other.eventID == 85 || other.eventID == 86 || other.eventID == 87) && obj.doesCollide(other)) {
obj.counter = 0;
obj.ySpeed = 81.0 - float(other.eventID);
obj.determineCurAnim(ANIM::TURTLE, 5);
obj.state = STATE::FALL;
other.state = STATE::SPRING;
jjSample(other.xPos, other.yPos, SOUND::SPRING_SPRING1);
}
}
}
//jjPrint(""+obj.state + " " + onPlatform + " " + platformOffset + " " + obj.direction);
switch (obj.state) {
case STATE::START:
if (obj.creatorType == CREATOR::LEVEL) obj.var[7] = jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,1);
obj.var[5] = 0; // idle counter
obj.counter = 0;
obj.counterEnd = 64;
obj.putOnGround();
if (jjRandom()%2 == 0) obj.direction = -1;
else obj.direction = 1;
obj.xSpeed = obj.direction * 1;
obj.ySpeed = 0;
obj.yAcc = 0.1;
obj.determineCurAnim(ANIM::TURTLE, 7);
obj.state = STATE::WALK;
case STATE::WALK:
obj.frameID = ((obj.counter / 5) % jjAnimations[obj.curAnim].frameCount);
obj.counter++;
obj.var[5] = obj.var[5] + 1;
if (obj.counterEnd > 0) obj.counterEnd--;
obj.determineCurFrame();
if (obj.counter % 5 == 0) {
if (obj.frameID == 5) jjSample(obj.xPos, obj.yPos, SOUND::TURTLE_SPK1TURT);
else if (obj.frameID == 10) jjSample(obj.xPos, obj.yPos, SOUND::TURTLE_SPK2TURT);
}
if (nearPlayerID > -1 && obj.counter > 20 - (jjDifficulty * 5)) { // attack trigger
if (((obj.direction < 0 && jjLocalPlayers[nearPlayerID].xPos < obj.xPos) ||
(obj.direction > 0 && jjLocalPlayers[nearPlayerID].xPos > obj.xPos)) &&
abs(jjLocalPlayers[nearPlayerID].yPos - obj.yPos) < 64 &&
obj.counterEnd == 0) {
obj.determineCurAnim(ANIM::TURTLE, 0);
obj.counter = 0;
obj.frameID = 0;
jjSample(obj.xPos, obj.yPos, SOUND::TURTLE_NECK);
obj.state = STATE::ATTACK;
}
}
if (obj.var[5] > 224 + (jjDifficulty * 16) + (jjRandom() % 32)) { // idle trigger
obj.counter = 0;
obj.frameID = 0;
obj.determineCurAnim(ANIM::TURTLE, 1);
obj.state = STATE::IDLE;
}
if (((!jjMaskedPixel(int(obj.xPos + obj.xSpeed), int(obj.yPos + py)) && obj.var[7] == 1) || !jjMaskedPixel(int(obj.xPos - (obj.direction * 2)), int(obj.yPos + py))) && !onPlatform) { // fall or turn trigger
obj.counter = 0;
obj.determineCurAnim(ANIM::TURTLE, 5);
obj.state = STATE::FALL;
}
else if (
(!onPlatform && (!jjMaskedVLine(int(obj.xPos + obj.xSpeed), int(obj.yPos + py), 2)
||
jjMaskedVLine(int(obj.xPos + (obj.xSpeed * (frame.width / 2))), int(obj.yPos - (frame.height / 4)), int(frame.height / 2))
||
jjEventGet(uint16(obj.xPos / 32), uint16(obj.yPos / 32)) == 14))
^^
(onPlatform && obj.var[7] == 0 && platformOffset >= 27.f) // don't go *too* close to 30.f
) {
obj.counter = 0;
obj.frameID = 0;
obj.determineCurAnim(ANIM::TURTLE, 2);
jjSample(obj.xPos, obj.yPos, SOUND::FROG_FROG5);
obj.xPos -= 2 * obj.direction;
obj.state = STATE::TURN;
}
else obj.xPos = obj.xPos + obj.xSpeed;
break;
case STATE::IDLE:
if (!onPlatform && !jjMaskedVLine(int(obj.xPos), int(obj.yPos + py), 2)) { // GROUND NOT IS THERE (GONE) (STOLEM)
obj.counter = 0;
obj.determineCurAnim(ANIM::TURTLE, 5);
obj.state = STATE::FALL;
}
if (nearPlayerID > -1 && obj.counter > 20 - (jjDifficulty * 5)) { // unlike turtle can still bite...
if (((obj.direction < 0 && jjLocalPlayers[nearPlayerID].xPos < obj.xPos) ||
(obj.direction > 0 && jjLocalPlayers[nearPlayerID].xPos > obj.xPos)) &&
abs(jjLocalPlayers[nearPlayerID].yPos - obj.yPos) < 64 &&
obj.counterEnd == 0) {
obj.determineCurAnim(ANIM::TURTLE, 0);
obj.counter = 0;
obj.frameID = 0;
obj.state = STATE::ATTACK;
}
}
if (obj.counterEnd > 0) obj.counterEnd--;
obj.counter++;
if (obj.counter % 7 == 0) {
obj.frameID++;
if (obj.frameID == 6) jjSample(obj.xPos, obj.yPos, SOUND::TURTLE_IDLE1, 63, jjRandom()%2 == 0 ? 48000 : 38097);
if (uint(obj.frameID) > jjAnimations[obj.curAnim].frameCount) {
obj.frameID = 0;
obj.counter = 0;
obj.var[5] = 0;
obj.determineCurAnim(ANIM::TURTLE, 7);
obj.state = STATE::WALK;
}
}
obj.determineCurFrame();
break;
case STATE::TURN:
if (!jjMaskedPixel(int(obj.xPos + obj.xSpeed), int(obj.yPos + py)) && obj.var[7] == 1) obj.state == STATE::FALL;
obj.counter++;
if (obj.counter % 7 == 0) obj.frameID++;
if (uint(obj.frameID) == jjAnimations[obj.curAnim].frameCount) {
obj.frameID = 0;
obj.counter = 0;
if (uint(obj.curAnim) == jjAnimSets[ANIM::TURTLE].firstAnim + 2) {
obj.determineCurAnim(ANIM::TURTLE, 3);
obj.direction = obj.direction * -1;
obj.xSpeed = obj.xSpeed * -1;
jjSample(obj.xPos, obj.yPos, SOUND::FROG_FROG2);
} else {
obj.determineCurAnim(ANIM::TURTLE, 7);
obj.state = STATE::WALK;
}
}
obj.determineCurFrame();
break;
case STATE::ATTACK:
obj.counter++;
if (obj.counter % 4 == 0) {
obj.frameID++;
obj.determineCurFrame();
if (obj.frameID == 3) jjSample(obj.xPos, obj.yPos, SOUND::TURTLE_BITE3);
if (uint(obj.frameID) > jjAnimations[obj.curAnim].frameCount) {
obj.determineCurAnim(ANIM::TURTLE, 7);
obj.counter = 0;
obj.counterEnd = 96;
obj.frameID = 0;
obj.state = STATE::WALK;
}
}
break;
case STATE::FALL:
obj.frameID = (jjGameTicks/7) & 1;
obj.determineCurFrame();
obj.special++; // fall timer
if (jjMaskedPixel(int(obj.xPos), int(obj.yPos - py)) && obj.ySpeed < 0) {
obj.ySpeed = 1;
}
else if (obj.ySpeed < 5) {
obj.ySpeed += obj.yAcc;
}
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
if (obj.ySpeed > 0 && (jjMaskedPixel(int(obj.xPos + obj.xSpeed), int(obj.yPos + obj.ySpeed + py)) || onPlatform)) { // landing detection
if (obj.special > 10) jjSample(obj.xPos, obj.yPos, SOUND::FROG_FROG2); // don't play sound if the drop is tiny
obj.determineCurAnim(ANIM::TURTLE, 7);
obj.ySpeed = 0;
obj.special = 0;
obj.state = STATE::WALK;
}
break;
case STATE::FREEZE:
if (obj.freeze == 0) obj.state = obj.oldState;
else obj.freeze--;
break;
case STATE::DEACTIVATE:
obj.deactivate();
break;
case STATE::KILL:
if (obj.creatorType == CREATOR::OBJECT) obj.var[8] = 0; // if I'm thinking right this should be exactly one tick
obj.delete();
break;
}
}
void onDraw(jjOBJ@ obj) {
if (obj.var[7] == 1) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PLAYER, jjPlayers[31].playerID);
else obj.draw();
// jjDrawString(obj.xPos, obj.yPos - 64, formatInt(obj.counter) + " " + formatInt(obj.frameID), STRING::MEDIUM);
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
int lastHitID = player.playerID;
if (bullet !is null) {
if ((bullet.var[6] & 16) == 0) { //not a fireball
bullet.state = STATE::EXPLODE;
}
if (obj.freeze != 0) {
if (force < 3)
force = 3;
obj.unfreeze(0);
}
if (bullet.objectID == 0) obj.energy = 0;
else {
obj.energy -= bullet.animSpeed;
obj.justHit = 5;
}
if (obj.energy <= 0) {
if (obj.var[9] == 0) {
jjOBJ@ bomb = jjObjects[jjAddObject(OBJECT::TURTLESHELL, obj.xPos, obj.yPos)];
obj.var[9] = 1;
bomb.ySpeed = 4;
bomb.xSpeed = bullet.xSpeed * (0.1 + (jjRandom() % 10 * 0.1));
bomb.determineCurAnim(ANIM::TURTLE, 4);
bomb.var[6] = lastHitID;
bomb.var[7] = obj.var[7];
playRandomSwish(obj.xPos, obj.yPos);
}
if (obj.creatorType == CREATOR::OBJECT) obj.var[8] = 1;
obj.state = STATE::KILL;
obj.grantPickup(jjLocalPlayers[lastHitID], bullet.animSpeed == 0 ? 10 : bullet.animSpeed * 5);
givePlayerPointsForObject(jjLocalPlayers[lastHitID], obj);
}
}
else if (force == 0) { //collision
if (obj.state != STATE::FREEZE)
player.hurt();
} else {
if (force > 0) { //buttstomp or sugar rush
player.buttstomp = 50; //landing
player.ySpeed = player.ySpeed / -2 - 8;
player.yAcc = 0;
player.extendInvincibility(-70);
} else if (force == -101) { //frozen+running
player.xAcc = 0;
player.xSpeed /= -2;
player.ySpeed = -6;
player.extendInvincibility(-10);
}
if (obj.var[9] == 0) {
jjOBJ@ bomb = jjObjects[jjAddObject(OBJECT::TURTLESHELL, obj.xPos, obj.yPos)];
obj.var[9] = 1;
bomb.ySpeed = 4;
bomb.xSpeed = (jjRandom() % 4 - 2) * 0.1;
bomb.determineCurAnim(ANIM::TURTLE, 4);
bomb.var[6] = lastHitID;
bomb.var[7] = obj.var[7];
playRandomSwish(obj.xPos, obj.yPos);
}
obj.state = STATE::KILL;
givePlayerPointsForObject(jjLocalPlayers[lastHitID], obj);
}
return true;
}
}
class demonBomb : jjBEHAVIORINTERFACE {
demonBomb(jjOBJ@ objectPreset) {
objectPreset.behavior = this;
objectPreset.playerHandling = HANDLING::SPECIAL;
objectPreset.bulletHandling = HANDLING::DETECTBULLET;
objectPreset.scriptedCollisions = true;
objectPreset.isTarget = false;
objectPreset.isFreezable = false;
objectPreset.triggersTNT = false;
objectPreset.deactivates = true;
}
void onBehave(jjOBJ@ obj) {
float dx, dy, distance;
if (obj.state == STATE::START) {
obj.counterEnd = 250;
obj.state = STATE::FLY;
}
if (obj.state == STATE::FLY) {
int calc = 0;
int calc2 = 0;
obj.counterEnd--;
if (obj.counterEnd < 125 && jjGameTicks % 10 == 0) {
obj.justHit = 5;
}
obj.xSpeed *= 0.984375;
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
bool noUp = jjMaskedPixel(int(obj.xPos), int(obj.yPos) - 4);
bool noDown = jjMaskedPixel(int(obj.xPos), int(obj.yPos) + 16);
bool noLeft = jjMaskedPixel(int(obj.xPos) - 8, int(obj.yPos));
bool noRight = jjMaskedPixel(int(obj.xPos) + 8, int(obj.yPos));
if (abs(obj.xSpeed) > 0.015625) {
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ other = jjObjects[i];
if (other.isActive && (other.playerHandling == HANDLING::ENEMY || other.playerHandling == HANDLING::SPECIAL) && obj.doesCollide(other) && other.eventID != 237) {
if (jjPlayers[obj.var[6]].objectHit(other, -1, other.playerHandling)) {
if (obj.xSpeed > 0) noRight = true;
else if (obj.xSpeed < 0) noLeft = true;
}
}
}
}
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ other = jjObjects[i];
if (other.eventID == OBJECT::DESTRUCTSCENERY && other.state == STATE::SLEEP && other.var[6] == 0 && obj.doesCollide(other)) {
other.state = STATE::KILL;
givePlayerPointsForObject(jjLocalPlayers[obj.var[6]], other);
}
}
if (obj.xSpeed > 0 && noRight) {
calc = 1 + int(obj.xSpeed);
calc2 = jjMaskedTopVLine(int(obj.xPos + 8), int(obj.yPos) + 16 - calc, calc);
if (calc2 <= 1 || calc2 >= calc) {
obj.xPos -= obj.xSpeed;
obj.xSpeed *= -1;
} else {
obj.yPos -= calc;
obj.xSpeed *= 0.9375;
}
} else if (obj.xSpeed < 0 && noLeft) {
calc = 1 - int(obj.xSpeed);
calc2 = jjMaskedTopVLine(int(obj.xPos - 8), int(obj.yPos) + 16 - calc, calc);
if (calc2 <= 1 || calc2 >= calc) {
obj.xPos -= obj.xSpeed;
obj.xSpeed *= -1;
} else {
obj.yPos -= calc;
obj.xSpeed *= 0.9375;
}
}
if (obj.ySpeed < 0 && noUp) {
obj.yPos -= obj.ySpeed;
obj.ySpeed = -obj.ySpeed * 0.5;
} else if (noDown) {
calc = jjMaskedTopVLine(int(obj.xPos), int(obj.yPos), 16);
if (calc != 0) {
obj.yPos -= 16 - calc;
} else {
obj.yPos -= obj.ySpeed;
}
if (obj.ySpeed > 1) {
playRandomImpact(obj.xPos, obj.yPos);
}
obj.ySpeed = -obj.ySpeed * 0.5;
} else {
if (obj.yPos > jjWaterTarget) {
obj.ySpeed += 0.03125;
if (obj.ySpeed > 4) obj.ySpeed = 4;
else if (obj.ySpeed < -4) obj.ySpeed -4;
} else {
obj.ySpeed += 0.125;
if (obj.ySpeed > 8) obj.ySpeed = 8;
}
}
}
if (obj.counterEnd <= 0 && obj.isActive) {
jjOBJ@ boom = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos)];
boom.determineCurAnim(ANIM::AMMO, 81);
// boom.playerHandling = HANDLING::ENEMYBULLET;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_EXPL_TNT);
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ other = jjObjects[i];
dx = abs(obj.xPos - other.xPos);
dy = abs(obj.yPos - other.yPos);
distance = sqrt(pow(dx, 2) + pow(dy, 2)); // A Perfect Circle...
if (other.eventID == OBJECT::DESTRUCTSCENERYBOMB && other.state == STATE::SLEEP && distance <= 64) {
other.state = STATE::KILL;
givePlayerPointsForObject(jjLocalPlayers[obj.var[6]], other);
} else if (other.bulletHandling == HANDLING::HURTBYBULLET && distance <= 64) {
other.energy -= 1;
if (other.energy <= 0) givePlayerPointsForObject(jjLocalPlayers[obj.var[6]], other);
}
if (obj.findNearestPlayer(64 * 64) > -1) jjLocalPlayers[obj.findNearestPlayer(64 * 64)].hurt(1);
}
obj.delete();
}
if (obj.state == STATE::DEACTIVATE) obj.delete();
}
void onDraw(jjOBJ@ obj) {
obj.lightType = LIGHT::POINT2;
obj.light = 100;
obj.frameID = ((jjGameTicks/5) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurFrame();
if (obj.justHit > 0) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, SPRITE::SINGLECOLOR, 15, 1);
else if (obj.var[7] == 1) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PLAYER, jjPlayers[31].playerID);
else obj.draw();
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet !is null) {
obj.xSpeed = obj.xSpeed + bullet.xSpeed;
if (obj.causesRicochet) {
if ((bullet.var[6] & 6) == 0) //not fire-based, not a laser beam
bullet.ricochet();
else if ((bullet.var[6] & 4) == 0) //not a laser beam
bullet.delete();
} else if ((bullet.var[6] & 16) == 0) //not a fireball
bullet.state = STATE::EXPLODE;
jjSample(obj.xPos, obj.yPos, SOUND::TURTLE_HITSHELL);
}
obj.var[6] = player.playerID;
return true;
}
}
void playRandomImpact(float xPos, float yPos) {
switch(jjRandom()%8) {
case 0: jjSample(xPos, yPos, SOUND::COMMON_IMPACT1); break;
case 1: jjSample(xPos, yPos, SOUND::COMMON_IMPACT2); break;
case 2: jjSample(xPos, yPos, SOUND::COMMON_IMPACT3); break;
case 3: jjSample(xPos, yPos, SOUND::COMMON_IMPACT4); break;
case 4: jjSample(xPos, yPos, SOUND::COMMON_IMPACT5); break;
case 5: jjSample(xPos, yPos, SOUND::COMMON_IMPACT6); break;
case 6: jjSample(xPos, yPos, SOUND::COMMON_IMPACT7); break;
case 7: jjSample(xPos, yPos, SOUND::COMMON_IMPACT8); break;
}
}
void playRandomSwish(float xPos, float yPos) {
switch(jjRandom()%8) {
case 0: jjSample(xPos, yPos, SOUND::COMMON_SWISH1); break;
case 1: jjSample(xPos, yPos, SOUND::COMMON_SWISH2); break;
case 2: jjSample(xPos, yPos, SOUND::COMMON_SWISH3); break;
case 3: jjSample(xPos, yPos, SOUND::COMMON_SWISH4); break;
case 4: jjSample(xPos, yPos, SOUND::COMMON_SWISH5); break;
case 5: jjSample(xPos, yPos, SOUND::COMMON_SWISH6); break;
case 6: jjSample(xPos, yPos, SOUND::COMMON_SWISH7); break;
case 7: jjSample(xPos, yPos, SOUND::COMMON_SWISH8); break;
}
}
void quakeScen(jjOBJ@ scen) {
scen.deactivates = false;
if (scen.state != STATE::DONE) {
if (Earthquake) {
scen.state = STATE::KILL;
}
}
scen.behave(BEHAVIOR::DESTRUCTSCENERY);
}
void fallPillar(jjOBJ@ scen) {
scen.deactivates = false;
if (scen.state == STATE::SLEEP) {
if (startQuake) {
scen.var[3] = -8;
scen.state = STATE::HIT;
}
}
scen.behave(BEHAVIOR::POLE);
}
void makeBrick(jjPLAYER@ play) {
if (elapsed & 20 == 0) {
jjOBJ@ brick = jjObjects[jjAddObject(OBJECT::BOUNCEONCE, (play.cameraX + 4) + (jjRandom()%96 * 4), 89 * 32)];
brick.determineCurAnim(ANIM::QUEEN, 4);
brick.xAcc = 0;
brick.xSpeed = 0;
}
if (play.xPos <= p.cameraX + 4 && jjLayers[4].maskedVLine(int(play.xPos), int(play.yPos) - 32, 32)) {
play.kill();
isFalling = false;
}
}
class brickTrap : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
const uint xTile = uint(obj.xOrg)>>5, yTile = uint(obj.yOrg)>>5;
int animTileID = jjParameterGet(xTile, yTile, 0, 1) == 0 ? 127 : 128;
if (obj.state == STATE::START) {
jjTileSet(4, xTile, yTile, animTileID | TILE::ANIMATED);
const auto@ tile = jjAnimatedTiles[animTileID];
const auto@ Frames = tile.getFrames();
obj.var[1] = Frames[Frames.length-1];
obj.yPos = obj.yOrg + 16;
obj.state = STATE::SLEEP;
}
if (obj.state == STATE::SLEEP) {
jjPARTICLE@ particle = jjAddParticle(PARTICLE::ICETRAIL);
if (particle !is null) {
particle.xPos = obj.xPos + (jjRandom()%28 - 15);
particle.yPos = obj.yPos;
particle.icetrail.color = 72;
particle.icetrail.colorStop = 80;
}
if (jjLocalPlayers[obj.findNearestPlayer(128 * 128)].yPos >= obj.yPos && abs(jjLocalPlayers[obj.findNearestPlayer(128 * 128)].xPos - obj.xPos) <= 64.f) {
jjOBJ@ brick = jjObjects[jjAddObject(OBJECT::BOUNCEONCE, obj.xPos, obj.yPos + 6)];
brick.determineCurAnim(ANIM::QUEEN, 4);
brick.xAcc = 0;
brick.xSpeed = 0;
brick.playerHandling = HANDLING::ENEMYBULLET;
brick.animSpeed = 1;
jjSample(obj.xPos, obj.yPos + 6, SOUND::COMMON_HOLYFLUT);
jjTileSet(4, xTile, yTile, obj.var[1]);
jjPrint(formatUInt(jjTileGet(4, xTile, yTile)));
obj.delete();
}
}
}
void onDraw (jjOBJ@ obj) {
// don't draw
}
}
class dripper : jjBEHAVIORINTERFACE {
//bool canDrip = true; // prevent spamming
void onBehave(jjOBJ@ obj) {
jjANIMFRAME@ frame = jjAnimFrames[obj.curFrame];
switch (obj.state) {
case STATE::START:
while (!jjMaskedPixel(int(obj.xPos), int(obj.yPos) + frame.hotSpotY - 6)) obj.yPos = obj.yPos - 1; // fall up
//canDrip = true;
obj.animSpeed = jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,2);
obj.counter = 0;
obj.var[0] = jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 2,1);
obj.determineCurAnim(ANIM::CUSTOM[28], obj.var[0] == 1 ? 2 : 0);
obj.state = STATE::SLEEP;
case STATE::SLEEP:
obj.counter++;
//if (obj.frameID == 1 && canDrip) {
if ((obj.animSpeed == 0 && obj.counter == 20) || (obj.animSpeed != 0 && obj.counter == int(18 / obj.animSpeed))) {
jjOBJ@ droplet = jjObjects[jjAddObject(OBJECT::BOUNCEONCE, obj.xPos, obj.yPos)];
droplet.xAcc = 0;
droplet.xSpeed = 0;
droplet.ySpeed = 1;
droplet.yAcc = 0.1;
droplet.behavior = dripperDrop();
droplet.var[0] = obj.var[0];
//canDrip = false;
}
//if (obj.frameID == 8) canDrip = true; // frameID chosen arbitrarily
if (obj.var[0] == 1) {
obj.playerHandling = HANDLING::ENEMYBULLET;
obj.lightType = LIGHT::POINT;
}
break;
case STATE::DEACTIVATE:
obj.deactivate();
break;
}
if ((obj.animSpeed == 0 && uint(obj.counter) > 20 * jjAnimations[obj.curAnim].frameCount)
||
(obj.animSpeed != 0 && uint(obj.counter) > uint(18 / obj.animSpeed) * jjAnimations[obj.curAnim].frameCount))
obj.counter = 0;
}
void onDraw(jjOBJ@ obj) {
if (obj.isActive) {
if (obj.animSpeed == 0) obj.frameID = ((obj.counter / 20) % jjAnimations[obj.curAnim].frameCount);
else obj.frameID = ((obj.counter / int(18 / obj.animSpeed)) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurFrame();
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, SPRITE::TRANSLUCENT, 0);
}
}
}
class dripperDrop : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::BOUNCEONCE);
if (obj.var[0] == 1) {
obj.playerHandling = HANDLING::ENEMYBULLET;
obj.lightType = LIGHT::POINT;
obj.animSpeed = 2;
if (jjEventGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5) == OBJECT::FENCER) {
if (abs(obj.yPos - ((uint(obj.yPos) >> 5) * 32 + jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,8))) <= 32.f) { // round yPos up to top of tile, then add Adjust Y to that
jjOBJ@ plop = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, (uint(obj.yPos) >> 5) * 32 + jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,8))];
plop.determineCurAnim(ANIM::CUSTOM[9], 1);
playRandomSquish(obj.xPos, obj.yPos, 31);
obj.delete();
}
}
}
else if (jjEventGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5) == AREA::WATERBLOCK) {
if (abs(obj.yPos - ((uint(obj.yPos) >> 5) * 32 + jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,8))) <= 32.f) { // round yPos up to top of tile, then add Adjust Y to that
jjOBJ@ plop = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, (uint(obj.yPos) >> 5) * 32 + jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,8))];
plop.determineCurAnim(ANIM::COMMON, 3);
plop.lightType = LIGHT::NONE;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_WATER, 31, 18000 + jjRandom()%7000);
obj.delete();
}
}
if ((obj.var[0] == 0 && jjEventGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5) == OBJECT::FENCER)
||
(obj.var[0] == 1 && jjEventGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5) == AREA::WATERBLOCK)) {
if (abs(obj.yPos - ((uint(obj.yPos) >> 5) * 32 + jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,8))) <= 32.f) { // round yPos up to top of tile, then add Adjust Y to that
for (uint i = 0; i < 6; ++i) {
jjPARTICLE@ particle = jjAddParticle(PARTICLE::SMOKE);
particle.xPos = obj.xPos;
particle.yPos = (uint(obj.yPos) >> 5) * 32 + jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,8);
}
jjSample(obj.xPos, obj.yPos, SOUND::STEAM_STEAM, 31, 30000 + jjRandom()%7000);
obj.delete();
}
}
if (obj.state == STATE::FLY) {
jjOBJ@ plop = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos)];
if (obj.var[0] == 1) {
plop.determineCurAnim(ANIM::CUSTOM[28], 4);
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PLOP3, 31, 36000 + jjRandom()%7000);
} else {
plop.determineCurAnim(ANIM::CUSTOM[28], 1);
plop.lightType = LIGHT::NONE;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PLOOP1, 31, 68000 + jjRandom()%7000);
}
obj.delete();
}
}
void onDraw(jjOBJ@ obj) {
if (obj.state == STATE::START || obj.state == STATE::ACTION) {
if (obj.var[0] == 1) obj.determineCurAnim(ANIM::CUSTOM[28], 3);
else obj.determineCurAnim(ANIM::COMMON, 0);
obj.frameID = ((jjGameTicks/9) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurFrame();
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, SPRITE::TRANSLUCENT, 0);
}
}
}
void playRandomSquish(float xPos, float yPos, int volume = 63) {
switch(jjRandom()%5) {
case 0: jjSample(xPos, yPos, SOUND::FROG_FROG1, volume); break;
case 1: jjSample(xPos, yPos, SOUND::FROG_FROG2, volume); break;
case 2: jjSample(xPos, yPos, SOUND::FROG_FROG3, volume); break;
case 3: jjSample(xPos, yPos, SOUND::FROG_FROG4, volume); break;
case 4: jjSample(xPos, yPos, SOUND::FROG_FROG5, volume); break;
}
}
void DecorativeVine(jjOBJ@ obj) {
if (obj.state == STATE::START) {
const uint eventX = uint(obj.xPos) >> 5, eventY = uint(obj.yPos) >> 5;
const int length = jjParameterGet(eventX,eventY, 0, 3);
obj.age = jjRandom();
obj.curFrame += length;
obj.var[0] = 50 + length*16;
obj.var[1] = 4 - jjParameterGet(eventX,eventY, 3, 1);
obj.xPos += jjParameterGet(eventX,eventY, 4, 3) << 2;
obj.yPos += jjParameterGet(eventX,eventY, 8, 3) << 2;
obj.state = STATE::IDLE;
} else if (obj.state == STATE::DEACTIVATE) {
obj.deactivate();
} else {
if (!jjLowDetail)
jjDrawSwingingVineSpriteFromCurFrame(int(obj.xPos), int(obj.yPos), obj.curFrame, obj.var[0], int(jjSin(obj.age += 6) * 0x4000), SPRITE::NORMAL,0, obj.var[1]);
if ((jjSin(obj.age) * 0x4000) % 12000 == 0) playRandomRattle(obj.xPos, obj.yPos);
}
}
void playRandomRattle(float xPos, float yPos) {
switch(jjRandom()%2) {
case 0: jjSample(xPos, yPos, SOUND::INTRO_UP1, 63, 42000 + jjRandom()%4000); break;
case 1: jjSample(xPos, yPos, SOUND::INTRO_UP2, 63, 42000 + jjRandom()%4000); break;
}
}
class NPC : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.putOnGround();
farPlayerID = obj.findNearestPlayer(256 * 32);
nearPlayerID = obj.findNearestPlayer(256 * 16);
if (obj.state == STATE::START) obj.state = STATE::STILL;
if (farPlayerID > -1 && obj.state == STATE::DEACTIVATE) obj.state = STATE::STILL;
if (nearPlayerID > -1 && obj.state == STATE::STILL && jjLocalPlayers[nearPlayerID].charCurr != CHAR::FROG) {
obj.state = STATE::WAKE;
NPCnear = true;
}
if (obj.state == STATE::WAKE) {
if (farPlayerID > -1) {
if (obj.xPos > jjLocalPlayers[farPlayerID].xPos) obj.direction = -1;
else obj.direction = 1;
}
if (jjKey[0x54] && CurrentPopup is null) NPCspeaks(jjLocalPlayers[nearPlayerID], jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 3, 5));
else if (nearPlayerID < 0) {
obj.state = STATE::STILL;
NPCnear = false;
}
}
}
void onDraw(jjOBJ@ obj) {
obj.frameID = ((jjGameTicks/10) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurAnim(ANIM::CUSTOM[16], jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 0, 3));
obj.determineCurFrame();
if (jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 0, 3) > 0) obj.draw();
if (obj.state == STATE::WAKE) jjDrawSprite(obj.xPos - 40, obj.yPos - 48, ANIM::CUSTOM[5], 0, (jjGameTicks/12) & 1);
}
}
void NPCspeaks(const jjPLAYER@ play, int8 orderID) {
switch (orderID) {
case 0:
if (!guardKey) {
if (inmates == 0) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("have you managed to check on all the prisoners?", right: Guard, direction: 1, color: 72)),
Screen(
top: Line("yeah, i think so. a pretty chill one, a feisty one, a talkative one, an oblivious one...", left: Jazz, right: Guard, direction: -1),
bottom: Line("...and a TERRIBLE singer.", right: Lori, direction: 1, color: 40)
),
Screen(
top: Line("yes, that checks out. so i take it they are safe from the invaders?", left: Jazz, right: Guard, direction: 1, color: 72),
bottom: Line("they seem to be doing fine, all things considered.", right: Lori)
),
Screen(
top: Line("thank you for your assistance. in return, take this BLUE KEY.", left: Jazz, right: Guard, direction: 1, color: 72),
bottom: Line("cool, what's it for?", left: Spaz, right: Lori, direction: -1, color: 24)
),
Screen(
top: Line("it opens the GUARD POST ahead. you may take whatever you need from there. good hunting!", right: Guard, direction: 1, color: 72),
finish: function(bool skipping) {
guardKey = true;
jjTriggers[1] = true;
}
),
});
} else {
if (jjTriggers[5]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(
top: Line("have you managed to check on all the prisoners?", right: Guard, direction: 1, color: 72),
bottom: Line("not yet, but we're working on it. we'll get back to you once we have.", left: Lori, direction: -1, color: 40)
)
});
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("so, uh... you're the head of dungeon guard, right? not the nicest place to hang out at, huh?", left: Jazz, direction: -1)),
Screen(
top: Line(left: Jazz),
bottom: Line("sir, this is my JOB. i am not stationed here for fun.", right: Guard, direction: 1, color: 72)
),
Screen(
top: Line("well, YEAH, i get that. i dunno, i guess it's easier to stay detached on the OTHER side of the bars.", left: Jazz, direction: -1),
bottom: Line(right: Guard)
),
Screen(
top: Line(left: Jazz),
bottom: Line("ah, i SEE. yes, i do recall YOU were a prisoner here, once.", right: Guard, direction: 1, color: 72)
),
Screen(
top: Line("so i was. and i'd probably STILL be, if not for my wife and brother. assuming we'd all still be part of history with nobody to stop DEVAN SHELL.", left: Jazz, direction: -1),
bottom: Line(right: Guard)
),
Screen(
top: Line(left: Jazz),
bottom: Line("well sir, i DO see your point. while i cannot CONDONE a prison breakout against the LAW, i understand it was for the greater good.", right: Guard, direction: 1, color: 72)
),
Screen(
top: Line(left: Jazz),
bottom: Line("all things considered, i am GLAD you were able to clear your name. it means that in the end, the system DOES work.", right: Guard, direction: 1, color: 72)
),
Screen(
top: Line("yeah, but don't you ever wonder - what if someone ELSE is in the same situation here as I once was? like, right NOW?", left: Jazz, direction: -1),
bottom: Line(right: Guard)
),
Screen(
top: Line(left: Jazz),
bottom: Line("sir, it is not my PLACE to ponder such things. it would make my job a lot more DIFFICULT, and it is already not so simple to BEGIN with.", right: Guard, direction: 1, color: 72)
),
Screen(
top: Line(left: Jazz),
bottom: Line("besides, i recall PRINCESS EVA specifically introduced a resolution that would make it impossible for ANY monarch to simply incarcerate a citizen on a WHIM, like you were.", right: Guard, direction: 1, color: 72)
),
Screen(
top: Line("right, i forgot. i tend to get mixed up in all this law and administration MUMBO-JUMBO.", left: Jazz, direction: -1),
bottom: Line(right: Guard)
),
Screen(
top: Line(left: Jazz),
bottom: Line("well sir, if i may... for the sake of the STATE, i believe it would be wise to get ACCUSTOMED with these things. you ARE our prince now, after all.", right: Guard, direction: 1, color: 72)
),
Screen(
top: Line("true... alright, let's start from the small things, then. we'll let you know when we've checked up on all the INMATES.", left: Jazz, direction: -1),
bottom: Line(right: Guard),
finish: function(bool skipping) { jjTriggers[5] = true; }
),
});
}
}
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("good luck. i wish i could join you, but i cannot abandon my SQUAD.", right: Guard, direction: 1, color: 72)),
});
}
break;
case 1:
if (jjTriggers[2]) {
if (jjTriggers[6]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("it's okay, i'm okay. my four walls are in one piece. just gotta breathe slow...", direction: -1, color: 64)),
});
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("holy CRAP that was scary!! my insides feel like JELLO!", direction: -1, color: 64)),
Screen(
top: Line("hang in there, inmate! i'm sure the guards are coming to get you outta here!", right: Jazz, direction: 1),
bottom: Line("heck, WE'D get you out, but we don't have any cell keys!", right: Spaz, direction: 1, color: 24)
),
Screen(
top: Line(right: Jazz),
bottom: Line("it's okay, i'm okay. my four walls are in one piece. just gotta breathe slow...", right: Spaz, direction: -1, color: 64),
finish: function(bool skipping) { jjTriggers[6] = true; }
)
});
}
} else {
if (!inmateCheck[0]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("roll call! you good in there?", right: Jazz, direction: 1)),
Screen(
top: Line(right: Jazz),
bottom: Line("aside from being LOCKED UP? yeah, it's cool. these demons make less of a ruckus than the LAST GUY next cell.", direction: -1, color: 64),
finish: function(bool skipping) {
inmates -= 1;
inmateCheck[0] = true;
}
),
});
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("you wouldn't happen to have a sports magazine on ya, eh? gets awfully boring in here... even with the demons running around.", direction: -1, color: 64)),
});
}
}
break;
case 2:
if (!inmateCheck[1]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("WHERE MY PRISON DOGS AT?!", left: Spaz, direction: -1, color: 24)),
Screen(
top: Line(left: Spaz),
bottom: Line("oh good heavens, FINALLY someone came by. this solitary confinement is driving me INSANE!", direction: 1, color: 64)
),
Screen(top: Line("what you in for that they locked you up in a spot like THIS, anyway?", left: Spaz, right: Jazz, direction: 1)),
Screen(
top: Line(left: Spaz, right: Jazz),
bottom: Line("oh, nothing grand, you know. just a little TAX FRAUD. once, maybe twice.", direction: 1, color: 64)
),
Screen(
top: Line(left: Spaz, right: Jazz),
bottom: Line("okay, okay, maybe, like, 20 YEARS in a row. but i'm a good conversation buddy, you know? won't you stay a while longer? PLEASE?", direction: 1, color: 64),
finish: function(bool skipping) {
inmates -= 1;
inmateCheck[1] = true;
}
),
});
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("thanks for sticking around! maybe we could play CARDS? got any? i PROMISE, i won't cheat. well, maybe A LITTLE. depends if it's CANASTA.", direction: 1, color: 64)),
Screen(bottom: Line("cheating at canasta just comes too NATURALLY, you know. you know, don't you?", direction: 1, color: 64)),
});
}
break;
case 3:
if (jjTriggers[2]) {
if (jjTriggers[7]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("governments CRACK and systems FALL, 'cause unity is POWERFUL!", direction: -1, color: 64))
});
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("lights go out!! walls come TUMBLING DOWN!!!", direction: -1, color: 64)),
Screen(
top: Line("dude, lay LOW! the guards are coming to get you out, just stay safe from the DEMONS!", right: Lori, direction: 1, color: 40),
bottom: Line("YES, THEY DO!!!", direction: -1, color: 64),
finish: function(bool skipping) { jjTriggers[7] = true; }
)
});
}
} else {
if (!inmateCheck[2]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("HEY! we're checking up on prisoners, are you alright in there?", right: Lori, direction: 1, color: 40)),
Screen(
top: Line(right: Lori),
bottom: Line("...BUT MY LEEEE-E-E-EANIN' POST IS DONE LEFT AND GONE~~", direction: -1, color: 64)
),
Screen(
top: Line("...yeah, he's fine.", right: Lori, direction: 1, color: 40),
bottom: Line("SHE'S LOOO-ooo-OOO-oo-ONG GOOO-ooo-OOO-oo-ONE and NOOO-oo-OOOOOO-oW IIIIII'M LoooOOOOoonesome bluu-UUUUEEEE~~~", direction: -1, color: 64),
finish: function(bool skipping) {
inmates -= 1;
inmateCheck[2] = true;
}
),
});
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("...she told me on sunday she was checkin' me OUT...~", direction: -1, color: 64)),
});
}
}
break;
case 4:
if (!inmateCheck[3]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("hey, anyone THERE? we're checking if prisoners are safe from demons!", left: Jazz, direction: -1)),
Screen(
top: Line(left: Jazz),
bottom: Line("ey boss, all good in he- waitaminute did you say DEMONS?!?", direction: 1, color: 64)
),
Screen(
top: Line("yeah, we got a bit of a situation here, but rest assured, citizen! everything is under control!", left: Jazz, direction: -1),
bottom: Line("good lord, i thought these guys're just REALLY SUNBURNED!", direction: 1, color: 64),
finish: function(bool skipping) {
inmates -= 1;
inmateCheck[3] = true;
}
),
});
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("c'mon, at least hand me a stick or somethin' to 'FEND MYSELF!", direction: -1, color: 64)),
});
}
break;
case 5:
if (!inmateCheck[4]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("you there, inmate? are the demons giving you any TROUBLE?", right: Lori, direction: 1, color: 40)),
Screen(
top: Line(right: Lori),
bottom: Line("nahh, they my mans, sista! i's call'd 'em in, ye i did! dis da sound of da REVOLUTION, hear what i'm sayin'!", direction: -1, color: 64)
),
Screen(
top: Line("dude, you best be JOKING. if this invasion is all your doing, i will personally go in there and BREAK YOUR LEGS.", left: Jazz, right: Lori, direction: -1),
bottom: Line("ah ya dam' UPPA CLASS FOLK can't take no banter! ye i ain't know VOODOO, brudda! 's jus' WISHFUL THINKIN'!", direction: -1, color: 64),
finish: function(bool skipping) {
inmates -= 1;
inmateCheck[4] = true;
}
),
});
} else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(
top: Line("bro, between you and me, i got here from the BOTTOM. you can do better.", right: Jazz, direction: 1),
bottom: Line("ye, gimme dat gun a' yerz 'n i'll carve me path to GLORY, too!", direction: -1, color: 64)
),
});
}
break;
default:
jjAlert("Scene missing!");
break;
}
}
void onFunction0(jjPLAYER@ player) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("welcome to the castle ultimate tileset example! this is a demo excerpt from DEMON DASH EPISODE 1: STRAIGHT OUTTA RETIREMENT.", color: 40)),
Screen(
top: Line("this demo starts near the very beginning of the adventure - BUBBA has just stormed carrotus castle and kidnapped jazz and eva's CHILDREN!", color: 40),
bottom: Line("the demons have blocked all the usual castle exits, so our heroes must follow the supernatural fiend through the DUNGEON!", color: 40)
),
Screen(top: Line("(note: you can SKIP dialogue scenes such as this one by pressing BACKSPACE.)", color: 40)),
Screen(bottom: Line("the CASTLE DUNGEON... let's move on fast - i have some BAD MEMORIES about this place.", left: Jazz, direction: -1)),
Screen(
top: Line("mister jazz, can you hear me?", right: Addie, direction: 1, color: 64),
bottom: Line("loud and clear, which is weird. how are you DOING this?", left: Jazz, direction: -1)
),
Screen(
top: Line("well, duh. it's MAGIC!! a magic crystal ball, to be exact.", right: Addie, direction: 1, color: 64),
bottom: Line("ohhh. so COOL.", left: Jazz, right: Spaz, direction: 1, color: 24)
),
Screen(
top: Line(right: Addie),
bottom: Line("alright, i'll roll with that explanation for now. so, how do we go OUTSIDE from here?", left: Jazz, right: Spaz, direction: -1)
),
Screen(
top: Line("well, umm, there's supposed to be a tunnel STRAIGHT AHEAD! you might have to unlock the gate first, though...", right: Addie, direction: 1, color: 64),
bottom: Line(left: Jazz, right: Spaz)
),
Screen(
top: Line("and please be CAREFUL out there! i think i can sense bubba's minions gathering DARK POWER for something!", right: Addie, direction: 1, color: 64),
bottom: Line(left: Jazz, right: Spaz)
),
Screen(
top: Line("there's still LOTS of them in the castle... it seems they're trying to assemble into some RITUAL FORMATION.", right: Addie, direction: 1, color: 64),
bottom: Line("not in MY castle they aren't!", left: Jazz, right: Spaz, direction: -1)
),
Screen(
top: Line("well, let's get going then. WHATEVER they're planning, we can HANDLE it.", left: Lori, right: Addie, direction: -1, color: 40),
bottom: Line(left: Jazz, right: Spaz)
)
});
}
void onFunction1(jjPLAYER@ player) {
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.eventID == OBJECT::EVA) obj.direction = -1;
}
@CurrentPopup = Conversation(array<Screen@> = {
Screen(
top: Line("CAPTAIN SPARKY reporting for duty, sir!", right: Guard, direction: 1, color: 72),
bottom: Line("AT EASE, my dude. we're just passing through.", left: Jazz, direction: -1)
),
Screen(
top: Line("your highness, if i may ADVISE. it is NOT SAFE to advance. the entire dungeon is SWARMING with opposing forces.", right: Guard, direction: 1, color: 72),
bottom: Line("yeah, i FIGURE. that's EXACTLY why we're going. i NEED to get my kids back, and i'm NOT stopping 'til they're ALL home.", left: Jazz, direction: -1)
),
Screen(
top: Line("hey, why don't you JOIN US? you can FIGHT, right? the more, the merrier!", left: Spaz, right: Guard, direction: -1, color: 24),
bottom: Line(left: Jazz)
),
Screen(
top: Line("apologies, sir, but us royal guards have PROTOCOLS. i must hold this post until my SQUAD arrives.", left: Spaz, right: Guard, direction: 1, color: 72),
bottom: Line("heh, it's a good thing these protocols don't apply to US, ain't it?", left: Jazz, direction: -1)
),
Screen(
top: Line("CERTAINLY, sir. actually, regarding that matter... may i ask a FAVOR of you?", left: Spaz, right: Guard, direction: 1, color: 72),
bottom: Line("well, i dunno... we gotta hurry after the BIG BAD and all.", left: Jazz, direction: -1)
),
Screen(
top: Line("it should be ON YOUR PATH, anyway. there are not many ways through the dungeon. i just need someone to CHECK IF OUR INMATES ARE FINE.", left: Spaz, right: Guard, direction: 1, color: 72),
bottom: Line(left: Jazz)
),
Screen(
top: Line("they may be criminals, but they're still PEOPLE. with the enemy running loose, they're left DEFENSELESS in their cells.", left: Spaz, right: Guard, direction: 1, color: 72),
bottom: Line(left: Jazz)
),
Screen(
top: Line("there are currently 5 PRISONERS in our dungeon. just WALK UP to their CELLGATES and see if anything's HAPPENED to them.", left: Spaz, right: Guard, direction: 1, color: 72),
bottom: Line("okay, we'll see what we can do.", left: Jazz, direction: -1)
),
Screen(
top: Line("thank you, sir. if you manage to find the time for this TASK, i shall make it WORTH YOUR WHILE.", left: Spaz, right: Guard, direction: 1, color: 72),
bottom: Line("cool. and HOW exactly are we supposed to TALK with them?", left: Jazz, direction: -1)
),
Screen(
top: Line("my sources inform me that YOU CAN TALK WITH VARIOUS CHARACTERS USING THE ''T'' key.", left: Spaz, right: Guard, direction: 1, color: 72),
bottom: Line(left: Jazz)
),
Screen(
top: Line("...what sources?", left: Spaz, right: Guard, direction: -1, color: 24),
bottom: Line(left: Jazz)
),
Screen(
top: Line("that is CLASSIFIED.", left: Spaz, right: Guard, direction: 1, color: 72),
bottom: Line("of COURSE it is.", left: Jazz, direction: -1)
),
});
}
void onFunction2(jjPLAYER@ play) {
if (!isFalling) {
isFalling = true;
elapsed = 0;
play.limitXScroll(276, uint16(jjSubscreenWidth / 32));
jjLayers[6].setYSpeed(-0.5, true);
}
}
void onFunction3(jjPLAYER@ p) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("hey, ADMAEL! you can HEAR me, right? that little freak is making us run in CIRCLES! we need more DIRECTIONS!", right: Jazz, direction: 1)),
Screen(top: Line("umm, yes, i can hear you, mister jazz! uhh, let me ask princess eva... where are you now? i can barely SENSE you through all the DARK MAGIC!", left: Addie, right: Jazz, direction: -1, color: 64)),
Screen(
top: Line(left: Addie, right: Jazz),
bottom: Line("we're pretty much right below where we entered the dungeon. the emergency exit is OUT, just like the main gate!", right: Lori, direction: 1, color: 40)
),
Screen(
top: Line("yeah, you could say the bad guys really did their HOMEWORK! is there any OTHER way outta here?", left: Addie, right: Jazz, direction: 1),
bottom: Line(right: Lori)
),
Screen(
top: Line("uh-huh... uh-huh... actually, YES! the princess says there's an emergency-EMERGENCY exit, and you're on the right path!", left: Addie, right: Jazz, direction: -1, color: 64),
bottom: Line(right: Lori)
),
Screen(
top: Line("you'll have to NAVIGATE a twisted MAZE inside the tunnel you're probably in. after that, there's a deadly drop - HUG THE RIGHT WALL AS YOU FALL!", left: Addie, right: Jazz, direction: -1, color: 64),
bottom: Line("cool, THANKS!", left: Spaz, right: Lori, direction: -1, color: 24)
),
Screen(
top: Line("i second that, thanks a ton! and how are YOU TWO holding up? the whole castle seems to be COMING APART!", left: Addie, right: Jazz, direction: 1),
bottom: Line(left: Spaz, right: Lori)
),
Screen(
top: Line("well, umm, it's not pretty... but we'll manage! princess eva took me to what she says is a BUNKER ROOM!", left: Addie, right: Jazz, direction: -1, color: 64),
bottom: Line(left: Spaz, right: Lori)
),
Screen(
top: Line("ATTA GIRL! alright, we'll be on our way then. stay safe!", left: Addie, right: Jazz, direction: 1),
bottom: Line("LET'S GO!!!", left: Spaz, right: Lori, direction: -1, color: 24)
)
});
}
void onFunction4(jjPLAYER@ play) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(
top: Line("great, another DEAD END. what NOW?", right: Jazz, direction: 1),
bottom: Line("hey, someone's behind that PILLAR!", right: Lori, direction: 1, color: 40),
finish: function() {
jjMusicLoad("TM-RAGE.S3M");
storeMusic = "TM-RAGE.S3M";
jjLocalPlayers[0].cameraFreeze(270 * 32, 74 * 32, true, false);
}
),
Screen(
top: Line("why hello there, MORTALS! how are you enjoying my REVENGE?", left: Bubba, right: Jazz, direction: -1, color: 24),
bottom: Line("revenge for WHAT? who ARE you?!", right: Lori, direction: 1, color: 40)
),
Screen(
top: Line("hahaha, your brothers didn't tell you ANYTHING, did they? i am BUBBA, one of the MIGHTIEST masters of hell!! a man of wealth and taste.", left: Bubba, right: Jazz, direction: -1, color: 24),
bottom: Line(right: Lori)
),
Screen(
top: Line("that's 'cause there ain't much to talk ABOUT. we found the time period devan was hiding in, got ambushed by this guy deep underground, kicked his butt.", left: Bubba, right: Jazz, direction: 1),
bottom: Line("yeah, and then we fought him AGAIN on the way back! how is he still ALIVE and kicking after all that, anyway?!", left: Spaz, right: Lori, direction: -1, color: 24)
),
Screen(
top: Line("FOOL! i am a DEMON LORD! i am BEYOND DEATH!!!", left: Bubba, right: Jazz, direction: -1, color: 24),
bottom: Line(left: Spaz, right: Lori)
),
Screen(
top: Line("yeah, right. demons, hell, end of days, like i'm gonna believe that. you're just another sicko cruisin' for a bruisin'.", left: Bubba, right: Jazz, direction: 1),
bottom: Line("wait, you've been to LITERAL HELL and you still don't believe in magic NOR demons?", left: Spaz, right: Lori, direction: 1, color: 40)
),
Screen(
top: Line("sis, the way i see it, i've been to a really big cavern full of bones where i nailed a bunch of guys with horns on their heads. my blaster mowed them down just the same.", left: Bubba, right: Jazz, direction: 1),
bottom: Line(left: Spaz, right: Lori)
),
Screen(
top: Line("ah, you're one of THOSE. alright then. i suppose a DISPLAY OF POWER is in order!", left: Bubba, right: Jazz, direction: -1, color: 24),
bottom: Line(left: Spaz, right: Lori)
),
Screen(
top: Line("sure, come over here and we'll see what you're MADE OF.", left: Bubba, right: Jazz, direction: 1),
bottom: Line(left: Spaz, right: Lori)
),
Screen(
top: Line("ohoho! i do like your ATTITUDE, infidel! but i have a better idea.", left: Bubba, right: Jazz, direction: -1, color: 24),
bottom: Line(left: Spaz, right: Lori)
),
Screen(
top: Line("you see, i sense that my DENIZENS have just finished casting a RUNE onto the castle foundation. that means it's my CUE!", left: Bubba, right: Jazz, direction: -1, color: 24),
bottom: Line("...for WHAT?", left: Spaz, right: Lori, direction: 1, color: 40),
finish: function(bool skipping) {
jjSamplePriority(SOUND::INTRO_MONSTER);
drawBubba = 1;
jjMusicLoad("noahbangs-earthquake.ogg");
storeMusic = "noahbangs-earthquake.ogg";
startQuake = true;
elapsed = 0;
uint16 tileCache;
for (uint x = 0; x < 13; x++) {
for (uint y = 0; y < 11; y++) {
tileCache = jjLayers[4].tileGet(283 + x, y);
jjLayers[4].tileSet(223 + x, 61 + y, tileCache);
}
}
for (uint x = 0; x < 7; x++) {
for (uint y = 0; y < 5; y++) {
tileCache = jjLayers[4].tileGet(289 + x, 11 + y);
jjLayers[4].tileSet(208 + x, 67 + y, tileCache);
}
}
for (uint x = 0; x < 18; x++) {
for (uint y = 0; y < 7; y++) {
tileCache = jjLayers[4].tileGet(278 + x, 16 + y);
jjLayers[4].tileSet(156 + x, 34 + y, tileCache);
}
}
for (uint x = 0; x < 13; x++) {
for (uint y = 0; y < 14; y++) {
tileCache = jjLayers[4].tileGet(270 + x, y);
jjLayers[4].tileSet(134 + x, 26 + y, tileCache);
}
}
for (uint x = 0; x < 14; x++) {
for (uint y = 0; y < 10; y++) {
tileCache = jjLayers[4].tileGet(290 + x, 69 + y);
jjLayers[4].tileSet(84 + x, 40 + y, tileCache);
}
}
}
)
});
}
void continueTalk(jjPLAYER@ play) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(
top: Line("watch out!! the dark magic spell is--", left: Addie, right: Jazz, direction: -1, color: 64),
bottom: Line("AN EARTHQUAKE!!!", left: Spaz, right: Lori, direction: 1, color: 40),
finish: function(bool skipping) {
jjMusicLoad("castleboss.it");
storeMusic = "castleboss.it";
jjTriggers[2] = true;
startQuake = false;
Earthquake = true;
jjLocalPlayers[0].cameraUnfreeze(false);
jjLAYER@ hideDest = MLLE::GetLayer("Hide Dest");
hideDest.hasTiles = false;
for (uint i = 0; i < addBrickX.length; i++) jjAddObject(OBJECT::CTFBASE, (addBrickX[i] * 32) + 16, (addBrickY[i] * 32) + 16);
}
)
});
}
void onFunction5(jjPLAYER@ play) {
jjTriggerRock(1);
jjLayers[7].hasTiles = false;
jjSamplePriority(SOUND::SKELETON_BONE1);
breakBG = true;
for (int n = 0; n < 50; n++) {
jjOBJ@ glassShard = jjObjects[jjAddObject(OBJECT::SHARD, (132 * 32) + (jjRandom()%112 * 4), (48 * 32) + (jjRandom()%24 * 4), 50, CREATOR::OBJECT)];
glassShard.behavior = BEHAVIOR::SHARD;
glassShard.determineCurAnim(ANIM::PICKUPS, (jjRandom()%3 + 44));
}
}
void onFunction6(jjPLAYER@ play) {
endLevel(play);
}
void onFunction7(jjPLAYER@ play) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("...huh.", right: Jazz, direction: 1)),
Screen(
top: Line("okay, update: looks like we WON'T be solving any mazes today after all.", right: Jazz, direction: 1),
bottom: Line("oh, WOE is me.", right: Lori, direction: 1, color: 40)
)
});
}
void onFunction8(jjPLAYER@ play) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("HEY! how did YOU get in here? this place is supposed to be TOP SECRET!", left: Jazz, direction: -1)),
Screen(top: Line("why, a good merchant sets up shop EXACTLY where they're needed! and let me tell you, do i consider myself a GOOD SALESMAN!", left: Jazz, direction: 1, color: 80)),
Screen(
top: Line(left: Jazz),
bottom: Line("mister, haven't you NOTICED? the castle foundations are being TORN APART and you're SELLING THINGS?", left: Lori, direction: -1, color: 40)
),
Screen(
top: Line("of course! you DO need any stuff you can get your hands on, RIGHT? i can see it in your EYES that you do!", left: Jazz, direction: 1, color: 80),
bottom: Line(left: Lori)
),
Screen(
top: Line("okay, so maybe we do. but cut us some SLACK, man! it's me, JAZZ JACKRABBIT! i've saved the world a few times, and i'm doing it again RIGHT NOW!", left: Jazz, direction: -1),
bottom: Line("ahem, WE'RE doing it right now, THANK you.", left: Lori, direction: -1, color: 40)
),
Screen(
top: Line("ahh, of course i know who you are! but you are TEARING ME APART! i have to think of feeding my FAMILY! i have a wife and TWELVE children, you know!!", left: Jazz, direction: 1, color: 80),
bottom: Line(left: Lori)
),
Screen(
top: Line("SURE you do. come on, at least drop the price! we were in such a hurry, we only got a bunch of COINS we picked up on the way.", left: Jazz, direction: -1),
bottom: Line("like we always do, LOL!", left: Lori, right: Spaz, direction: 1, color: 24)
),
Screen(
top: Line("weeeeeeeeell since i DO like you guys, then tell you what! if you give me 20 coins a SECOND time, i'll give you a TOTALLY different set of items!", left: Jazz, direction: 1, color: 80),
bottom: Line(left: Lori, right: Spaz)
),
Screen(
top: Line("buddy, that's-- that's the EXACT same price. you're asking us to pay even MORE, actually. just to get more stuff.", left: Jazz, direction: -1),
bottom: Line(left: Lori, right: Spaz)
),
Screen(
top: Line("i know, MORE STUFF! it's a BARGAIN, isn't it?! come on, think of my fourteen children!!", left: Jazz, direction: 1, color: 80),
bottom: Line("didn't you say TWELVE children just a moment ago...?", left: Lori, right: Spaz, direction: -1, color: 40)
),
Screen(
top: Line("okay, either we get down to BUSINESS or not, but one way or another you do NOT make fun of my bad MEMORY.", left: Jazz, direction: 1, color: 80),
bottom: Line(left: Lori, right: Spaz)
)
});
}
void onFunction9(jjPLAYER@ play) {
if (isFalling) {
isFalling = false;
elapsed = 0;
}
}
void onFunction10(jjPLAYER@ play, bool pauseSound) {
if (pauseSound) jjMusicPause();
else jjMusicResume();
}
void onFunction11(jjPLAYER@ play) {
PlayerX@ playX = PlayerXs[play.localPlayerID];
if (playX.CurrentAngle == LoopAngle::Ceiling) {
playX.State = PlayerState::Normal;
playX.CurrentAngle = LoopAngle::Floor;
playX.CornerElapsed = -1;
play.invisibility = false;
play.noclipMode = true;
play.xSpeed = play.ySpeed = 15;
}
}
void onHelp(array<jjHELPPAGE@>& pages) {
@pages[3] = (jjHELPPAGE(
{
jjHELPPARAGRAPH(
"Blocks",
textSize: STRING::MEDIUM,
textAlignment: STRING::CENTER
),
jjHELPPARAGRAPH(
"Blocks frequently stand between you and a bountiful harvest of goodies and secrets. Fortunately, blocks are made to be smashed. The symbol (or lack thereof) on the face of each block indicates what you need to do to destroy it, and proceed to the riches beyond.",
textSize: STRING::SMALL,
textAlignment: STRING::LEFT
),
jjHELPPARAGRAPH(
"|Blast'em Blocks",
textSize: STRING::MEDIUM,
textAlignment: STRING::LEFT
),
jjHELPPARAGRAPH(
"Use your weapons to blast these blocks to smithereens. Different weapons are better suited to destroying blocks in certain locations, so if you can't seem to blast a block away with one weapon, try using another in your arsenal.",
textSize: STRING::SMALL,
textAlignment: STRING::LEFT,
image: jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim + 4].firstFrame + 10],
position: HELP::FLOATLEFT
),
jjHELPPARAGRAPH(
"|Countdown Blocks",
textSize: STRING::MEDIUM,
textAlignment: STRING::LEFT
),
jjHELPPARAGRAPH(
"Countdown Blocks are Blast'em Blocks with an attitude. These toughies don't crack easily. You will need to blast them several times (just how many times is indicated by the numeral on each block) to make them fall to pieces.",
textSize: STRING::SMALL,
textAlignment: STRING::LEFT,
image: jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim + 4].firstFrame + 9],
position: HELP::FLOATLEFT
),
jjHELPPARAGRAPH(
"|Weapon Blocks",
textSize: STRING::MEDIUM,
textAlignment: STRING::LEFT
),
jjHELPPARAGRAPH(
"These blocks can only be destroyed with the particular weapon pictured on their sides. For this reason, it is a good idea to try to save at least a few rounds of ammo in each of the weapons you possess in case you need them to blast through a Weapon Block and access the treasure behind.",
textSize: STRING::SMALL,
textAlignment: STRING::LEFT,
image: jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim + 4].firstFrame + 15],
position: HELP::FLOATLEFT
),
jjHELPPARAGRAPH(
"|Speed Blocks",
textSize: STRING::MEDIUM,
textAlignment: STRING::LEFT
),
jjHELPPARAGRAPH(
"These plain-faced blocks can only be smashed by running through them at full speed, or by using special physical attacks.",
textSize: STRING::SMALL,
textAlignment: STRING::LEFT,
image: jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim + 4].firstFrame],
position: HELP::FLOATLEFT
),
jjHELPPARAGRAPH(
"(Note that in Demon Dash, Speed Blocks always have this unique embossing instead. Blocks without any symbol are reserved for special cases, not destructible by any of your conventionally available means.)",
textSize: STRING::SMALL,
textAlignment: STRING::LEFT,
image: jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim + 4].firstFrame + 3],
position: HELP::FLOATLEFT
),
jjHELPPARAGRAPH(
[preview ends here]
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.