Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Miscellaneous stuff | Violet CLM | Multiple | N/A |
const array<uint16> GroundTilesToRecolor = {292, 293, 395, 405}; //tiles used by the textured floor that need to fit into the proper palette space
const array<uint16> SewerTilesToRecolor = {247, 257, 267, 306, 307, 316, 317, 326, 327};
jjPIXELMAP@ SkyTexture, SurfaceGroundTexture, SewerTexture, SewerGroundTexture, TopTexture, BottomTexture;
jjPLAYER@ Play;
jjOBJ@ PlayObject;
const float FLOORBASICXOFFSET = 96; //middle of screen
const float PLAYERYSTART = 161*32; //where in the event map the player proxy object is created (presumably should be at/near the bottom of the level)
const float CAMERAMINDISTANCE = 18.f; //arbitrary constant, could even be 0
float CameraY = PLAYERYSTART + CAMERAMINDISTANCE;
uint ShadowFrame;
const array<int> HotSpotY3DCharAdjustments = {6, 7, 11, 12, 14, 6, 6};
uint TimeSpentInSewer = 0;
float SewerGroundScrollOffset = 0;
jjPAL DayPalette, NightPalette;
void onLevelLoad() {
jjChat("/3Ddepth 0");
jjChat("/3D TAB");
jjAnimSets[ANIM::JAZZ3D].load();
jjAnimSets[ANIM::SPAZ3D].load();
jjCharacters[CHAR::JAZZ].airJump = AIR::NONE; //This is just me feeling that the air jump moves don't add much to this 3D gameplay. They could be reinstated simply by removing these lines.
jjCharacters[CHAR::SPAZ].airJump = AIR::NONE;
if (jjIsTSF) jjCharacters[CHAR::LORI].airJump = AIR::NONE;
jjWeapons[WEAPON::BLASTER].defaultSample = false;
jjObjectPresets[OBJECT::BLASTERBULLET].behavior = BEHAVIOR::INACTIVE; //I'm replacing shooting with rolling. If you do want to include some sort of bullet mechanic, this should be replaced with a custom behavior that sets up a bullet in the 3D object space.
for (uint i = 0; i < HotSpotY3DCharAdjustments.length; ++i) {
jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::JAZZ3D] + i];
jjANIMATION@ anim2 = jjAnimations[jjAnimSets[ANIM::SPAZ3D] + i];
for (uint j = 0; j < anim.frameCount; ++j)
jjAnimFrames[anim + j].hotSpotY -= HotSpotY3DCharAdjustments[i]; //align 3D char frames with FEET, not whatever odd places the original team decided to put those hotspots
for (uint j = 0; j < anim2.frameCount; ++j)
jjAnimFrames[anim2 + j].hotSpotY -= HotSpotY3DCharAdjustments[i];
}
ShadowFrame = jjAnimations[jjAnimSets[ANIM::PINBALL].load()]; //it's a circle; what more can you ask for?
jjUseLayer8Speeds = true;
jjLayerXOffset[8] = FLOORBASICXOFFSET;
generateMode7SpritePositionLookupTable();
DayPalette = jjBackupPalette;
DayPalette.copyFrom(144, 16, 112, DayPalette); //get the wall gradient into the latter half of the palette space, which is what textured backgrounds can draw
DayPalette.apply();
NightPalette.load("Colon2.j2t");
NightPalette.copyFrom(144, 16, 112, NightPalette); //yes you too
NightPalette.copyFrom(96 | 128, 8, 96, NightPalette); //same for the sewer
NightPalette.gradient(113,247,148, 18,88,58, 176, 32);
for (uint i = 0; i < GroundTilesToRecolor.length; ++i) { //
jjPIXELMAP tile(GroundTilesToRecolor[i]);
for (uint x = 0; x < 32; ++x)
for (uint y = 0; y < 32; ++y) {
uint8 color = tile[x,y];
if (color >= 112 && color <= 127)
tile[x,y] = color + 32;
}
tile.save(GroundTilesToRecolor[i], true);
}
for (uint i = 0; i < SewerTilesToRecolor.length; ++i) {
jjPIXELMAP tile(SewerTilesToRecolor[i]);
for (uint x = 0; x < 32; ++x)
for (uint y = 0; y < 32; ++y)
tile[x,y] |= 128;
tile.save(SewerTilesToRecolor[i], true);
} { //recolor shingles to SurfaceGroundTexture
jjPIXELMAP tile(899);
for (uint x = 0; x < 32; ++x)
for (uint y = 0; y < 32; ++y)
tile[x,y] += 8;
tile.save(241 | TILE::VFLIPPED, true);
tile.save(899, true);
}
@TopTexture = @SkyTexture = jjPIXELMAP(TEXTURE::NORMAL);
@BottomTexture = @SurfaceGroundTexture = jjPIXELMAP(0, 512, 256, 256, 4);
@SewerTexture = jjPIXELMAP(0, 0, 256, 256, 8);
@SewerGroundTexture = jjPIXELMAP(0, 768, 256, 256, 4);
for (uint x = 0; x < 256; ++x)
for (uint y = 0; y < 256; ++y)
if (SewerGroundTexture[x,y] == 0)
SewerGroundTexture[x,y] = SkyTexture[x,y];
@Play = jjLocalPlayers[0];
@PlayObject = jjObjects[jjAddObject(OBJECT::STEADYLIGHT, 320, PLAYERYSTART, Play.playerID, CREATOR::PLAYER, BEHAVIOR::PlayerProxy)];
PlayObject.deactivates = false; //draw 3D
jjObjectPresets[OBJECT::REDGEM].behavior = MyPickup;
jjObjectPresets[OBJECT::REDGEM].scriptedCollisions = true; //won't use the standard onObjectHit, since nothing ever comes into contact with the actual player object, but this can be used for equivalent purposes
//jjObjectPresets[OBJECT::REDGEM].curAnim = jjObjectPresets[OBJECT::SUPERGEM].curAnim;
jjANIMATION@ gemAnim = jjAnimations[jjObjectPresets[OBJECT::REDGEM].curAnim];
for (uint i = 0; i < gemAnim.frameCount; ++i)
jjAnimFrames[gemAnim.firstFrame + i].hotSpotY = -1 - int(jjAnimFrames[gemAnim.firstFrame + i].height); //all 3D objects should have hotspots near the bottoms of their frames, specifically where they come into contact with the SurfaceGroundTexture if yPos==0
jjObjectPresets[OBJECT::BUBBLER].behavior = MyBubble;
--jjObjectPresets[OBJECT::BUBBLER].curAnim;
jjObjectPresets[OBJECT::BUBBLER].var[0] = 9;
//the following assignments deal specifically with Colon and would naturally need editing (or outright removing) for other tilesets
jjObjectPresets[OBJECT::STEADYLIGHT].behavior = UnmovingEyecandySprite;
jjObjectPresets[OBJECT::STEADYLIGHT].curAnim = jjAnimSets[ANIM::CUSTOM[0]].allocate(array<uint> = {5});
jjPIXELMAP streetlamp(8*32, 512, 32, 4*32, 4);
for (uint x = 0; x < 32; ++x)
for (uint y = 114; y < 128; ++y)
if (streetlamp[x,y] >= 136)
streetlamp[x,y] = 0;
jjANIMFRAME@ frame = jjAnimFrames[jjObjectPresets[OBJECT::STEADYLIGHT].determineCurFrame()];
streetlamp.save(frame);
frame.hotSpotX = -16;
frame.hotSpotY = -120;
jjObjectPresets[OBJECT::ONEUPBARREL].behavior = UnmovingEyecandySprite;
jjObjectPresets[OBJECT::ONEUPBARREL].curFrame = jjObjectPresets[OBJECT::STEADYLIGHT].curFrame + 1;
jjPIXELMAP barrel(9*32, 512, 32, 51, 4);
for (uint x = 0; x < 32; ++x)
for (uint y = 0; y < 51; ++y) {
uint8 color = barrel[x,y];
if (color<= 127)
barrel[x,y] = 0;
else if (color >= 136 && color <= 142 && (y > 42 || x < 2 || x > 43))
barrel[x,y] = 0;
}
@frame = jjAnimFrames[jjObjectPresets[OBJECT::ONEUPBARREL].determineCurFrame()];
barrel.save(frame);
frame.hotSpotX = -16;
frame.hotSpotY = -50;
jjObjectPresets[OBJECT::ONEUPCRATE].behavior = UnmovingEyecandySprite;
jjObjectPresets[OBJECT::ONEUPCRATE].curFrame = jjObjectPresets[OBJECT::STEADYLIGHT].curFrame + 2;
jjPIXELMAP bench(10*32, 512 + 21, 64, 38, 4);
@frame = jjAnimFrames[jjObjectPresets[OBJECT::ONEUPCRATE].determineCurFrame()];
bench.save(frame);
frame.hotSpotX = -32;
frame.hotSpotY = -37;
jjObjectPresets[OBJECT::BIGBOX].behavior = UnmovingEyecandySprite;
jjObjectPresets[OBJECT::BIGBOX].curFrame = jjObjectPresets[OBJECT::STEADYLIGHT].curFrame + 3;
jjPIXELMAP house(53*32,0, 21*32, 15*32, 4);
for (uint x = 0; x < house.width; ++x)
for (uint y = 14*32; y < 15*32; ++y)
if (house[x,y] >= 136) house[x,y] = 0; //remove floor
@frame = jjAnimFrames[jjObjectPresets[OBJECT::BIGBOX].determineCurFrame()];
house.save(frame);
frame.hotSpotX = -frame.width/2;
frame.hotSpotY = -frame.height;
jjObjectPresets[OBJECT::STOMPSCENERY].behavior = ManholeCover;
jjObjectPresets[OBJECT::STOMPSCENERY].curFrame = jjObjectPresets[OBJECT::STEADYLIGHT].curFrame + 4;
jjPIXELMAP manhole(10*32 + 2, 512 + 2*32 + 6, 61, 16, 4);
@frame = jjAnimFrames[jjObjectPresets[OBJECT::STOMPSCENERY].determineCurFrame()];
manhole.save(frame);
frame.hotSpotX = -30;
frame.hotSpotY = -8;
}
void onLevelBegin() {
for (int i = jjObjectCount - 1; i >= 1; --i) {
jjOBJ@ obj = jjObjects[i];
if (obj.isActive && obj.deactivates && obj.behavior != BEHAVIOR::TRIGGERSCENERY) //not 3D
obj.delete();
}
for (int y = jjLayerHeight[4] - 1; y > 22; --y) //22 is the bottom of the Jazz area
for (int x = 0; x < jjLayerWidth[4]; ++x) {
uint eventID = jjEventGet(x, y);
if (eventID != 0) {
jjOBJ@ obj = jjObjects[jjAddObject(eventID, x * 32, y * 32, 0, CREATOR::LEVEL)];
obj.deactivates = false; //draw 3D, and don't get erased by the repointed camera
}
}
Play.cameraFreeze(0, 0, false, true);
}
array<float> VerticalPositionsForLinesInTexturedGround();
void generateMode7SpritePositionLookupTable() {
for (float i = 0; i < 260; ++i)
VerticalPositionsForLinesInTexturedGround.insertLast((0x3C0000 / (i + 8) * i / 2048.f));
}
bool DrawingToLowerSubscreen(jjPLAYER@ play) { return play.subscreenY > 0; }
bool onDrawScore(jjPLAYER@ play, jjCANVAS@ screen) { return true || DrawingToLowerSubscreen(play); } //remove "true ||" if you want Score included
bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ screen) { return DrawingToLowerSubscreen(play); }
bool onDrawAmmo(jjPLAYER@ play, jjCANVAS@ screen) { return true || !DrawingToLowerSubscreen(play); } //and again
bool onDrawLives(jjPLAYER@ play, jjCANVAS@ screen) { return jjDifficulty <= 0 || !DrawingToLowerSubscreen(play); }
bool onCheat(string &in cheat) {
if (cheat == "jjmorph") { //don't allow characters without 3D sprites
if (Play.charCurr == CHAR::JAZZ)
Play.morphTo(CHAR::SPAZ);
else
Play.morphTo(CHAR::JAZZ);
return true;
}
return false;
}
float max(float a, float b) {
if (a > b) return a;
return b;
}
float min(float a, float b) {
if (a < b) return a;
return b;
}
void onPlayerInput(jjPLAYER@ play) {
play.keySelect = play.keyDown = false;
play.idle = 0;
if (PlayObject.special > 0) { //heading toward manhole
jjOBJ@ manhole = jjObjects[PlayObject.special];
if (play.xPos > manhole.xPos + 7.5) play.keyLeft = true;
else if (play.xPos < manhole.xPos - 7.5) play.keyRight = true;
else play.keyLeft = play.keyRight = false;
play.keyUp = (PlayObject.yOrg > manhole.yOrg + 4);
play.keyJump = false;
}
}
const float MAXDISTANCEFROMCOLLISIONOBJECT = 20;
void onMain() {
//if (PlayObject.special <= 0) { //not moved by another object
if (PlayObject.special <= 0 && Play.ballTime <= 0 && Play.keyFire && (Play.ySpeed == 0 || !Play.alreadyDoubleJumped)) { //roll!
Play.ballTime = 70;
Play.ySpeed = -5.5;
PlayObject.ySpeed = -6;
Play.alreadyDoubleJumped = true;
jjSamplePriority(SOUND::AMMO_BULFL2);
} else if (Play.ballTime <= 0 && Play.keyUp) {
if (Play.keyRun)
PlayObject.ySpeed = max(PlayObject.ySpeed - 0.07f, -3.f);
else
PlayObject.ySpeed = max(PlayObject.ySpeed - 0.045f, -2.f);
} else { //assumes, as with the camera code below, that the player will never move backwards; adjust as needed if that is not the case.
PlayObject.ySpeed = min(PlayObject.ySpeed + 0.08f, 0.f);
}
PlayObject.xPos = Play.xPos;
PlayObject.yOrg = PlayObject.yOrg + PlayObject.ySpeed;
if (TimeSpentInSewer != 1)
PlayObject.yPos = Play.yPos - Play.yOrg - 12.5f; //distance from SurfaceGroundTexture
else
PlayObject.yPos = jjObjects[PlayObject.special].yPos; //descending on manhole
//}
//CameraY = PlayObject.yOrg;
if (CameraY > PlayObject.yOrg + CAMERAMINDISTANCE)
CameraY -= (CameraY - (PlayObject.yOrg + CAMERAMINDISTANCE)) / ((Play.ballTime <= 20) ? 10 : 25);
for (int i = jjObjectCount - 1; i >= 1; --i) {
jjOBJ@ obj = jjObjects[i];
if (!obj.deactivates && obj.behavior != PlayerProxy && obj.behavior != ManholeCover)
obj.xPos = obj.xOrg - 4 * SewerGroundScrollOffset;
if (obj.isActive && obj.scriptedCollisions)
if ( //this is conceptually and computationally simple but I'm not 100% thrilled with it yet...
abs(obj.xPos - PlayObject.xPos) <= MAXDISTANCEFROMCOLLISIONOBJECT &&
abs(obj.yPos - PlayObject.yPos) <= MAXDISTANCEFROMCOLLISIONOBJECT &&
abs(obj.yOrg - PlayObject.yOrg) <= MAXDISTANCEFROMCOLLISIONOBJECT
) { //collide!
obj.state = STATE::ACTION;
}
}
if (TimeSpentInSewer > 1) {
TimeSpentInSewer += 1;
SewerGroundScrollOffset = jjSin(TimeSpentInSewer) * 256;
if (PlayObject.yOrg < MANHOLESTARTY - 7 || abs(PlayObject.xPos - MANHOLESTARTX) > 64) //not standing on the manhole cover
if (PlayObject.yPos >= -1 && int(PlayObject.xPos - 12 + 4 * SewerGroundScrollOffset) / 4 & 255 > 128) //standing in sludge
Play.hurt();
}
}
array<SPRITE::Mode> ObjectSpriteModes = {SPRITE::NORMAL, SPRITE::GEM, SPRITE::GEM, SPRITE::GEM, SPRITE::GEM, SPRITE::NORMAL, SPRITE::NORMAL, SPRITE::NORMAL, SPRITE::NORMAL, SPRITE::TRANSLUCENTPALSHIFT};
bool drawObject(jjOBJ@ obj, jjCANVAS@ screen, float yPos) {
//xPos: xPos
//yPos: yPos
//yOrg: zPos
//yAcc: offset of yPos (used by pickups)
if (!obj.isActive || obj.deactivates || obj.curFrame == 0 || obj.yOrg < MANHOLESTARTY - 32 && TimeSpentInSewer == 0) return false;
float distanceFromFade = VerticalPositionsForLinesInTexturedGround[jjSubscreenHeight] - (CameraY - obj.yOrg);
if (distanceFromFade <= 1300)
return false;
if (distanceFromFade > 1900) {
obj.delete();
return false;
}
for (uint j = 0; true; ++j) {
if (j >= VerticalPositionsForLinesInTexturedGround.length) return false;
if (VerticalPositionsForLinesInTexturedGround[j] > distanceFromFade) {
distanceFromFade = float(j) / float(jjSubscreenHeight);
break;
}
}
float distanceFromCamera = 1.f - distanceFromFade;
float xPos = jjSubscreenWidth/2 - distanceFromFade * (jjSubscreenWidth/2 - (obj.xPos - 964)) * 1.0714285714f; //this doesn't work quite right at other resolutions yet
//if (obj.behavior != PlayerProxy && obj.behavior != ManholeCover) //those two don't scroll back and forth with the rest of the sewer
// xPos -= distanceFromFade * 4 * SewerGroundScrollOffset;
yPos -= distanceFromCamera * float(jjSubscreenHeight);
if (obj.behavior != UnmovingEyecandySprite && obj.behavior != MyBubble) //always stuck to the SurfaceGroundTexture, no shadow needed
screen.drawResizedSpriteFromCurFrame(int(xPos), int(yPos), ShadowFrame, distanceFromFade, distanceFromFade / 2, SPRITE::SHADOW);
screen.drawResizedSpriteFromCurFrame(int(xPos), int(yPos + (obj.yPos + obj.yAcc) * distanceFromFade), obj.curFrame, distanceFromFade * 2, distanceFromFade * 2, ObjectSpriteModes[obj.var[0]], obj.var[0] - 1);
//screen.drawPixel(xPos, yPos, 15); //debug: show exact position
return true;
}
void onDrawLayer8(jjPLAYER@ play, jjCANVAS@ screen) {
if (DrawingToLowerSubscreen(play)) {
jjTexturedBGFadePositionY = 1; //sky
jjLayerYOffset[8] = -CameraY;
if (TimeSpentInSewer > 0) {
//jjTexturedBGStyle = TEXTURE::TUNNEL;
//jjLayerXOffset[8] = 0;
} else jjLayerXAutoSpeed[8] = 0.75;
TopTexture.makeTexture();
jjLayerYOffset[2] = Layer2YOffset;
} else {
jjTexturedBGFadePositionY = 0; //SurfaceGroundTexture
jjLayerYOffset[8] = CameraY;
if (TimeSpentInSewer > 0) {
//jjTexturedBGStyle = TEXTURE::WARPHORIZON;
jjLayerXOffset[8] = FLOORBASICXOFFSET + SewerGroundScrollOffset;
} else jjLayerXAutoSpeed[8] = 0;
BottomTexture.makeTexture();
jjLayerYOffset[2] = Layer2YOffset + jjSubscreenHeight;
}
}
void onDrawLayer4(jjPLAYER@ play, jjCANVAS@ screen) {
for (int i = jjObjectCount - 1; i >= 1; --i) {
drawObject(jjObjects[i], screen, jjResolutionHeight - play.subscreenY);
}
}
//0: idle
//1: jump
//2: roll
//3: run
//4: skid
//5: spin
//6: stand
void PlayerProxy(jjOBJ@ obj) {
int16 oldCurAnim = obj.curAnim;
ANIM::Set animSet = (Play.charCurr == CHAR::SPAZ) ? ANIM::SPAZ3D : ANIM::JAZZ3D; //sorry, Lori
if (PlayObject.special > 0 && jjObjects[PlayObject.special].state == STATE::TURN) //standing on the manhole cover
obj.determineCurAnim(animSet, 5); //spin
else if (Play.ballTime > 20)
obj.determineCurAnim(animSet, 2); //roll
else if (Play.ySpeed != 0)
obj.determineCurAnim(animSet, 1); //jump
else if ((Play.xSpeed != 0 || obj.ySpeed != 0) && (Play.keyLeft || Play.keyRight || Play.keyUp))
obj.determineCurAnim(animSet, 3); //run
else if (Play.xSpeed != 0 || obj.ySpeed != 0)
obj.determineCurAnim(animSet, 4); //skid
else
obj.determineCurAnim(animSet, 0); //idle
if (obj.curAnim != oldCurAnim) {
obj.frameID = 0;
obj.determineCurFrame();
} else if (jjGameTicks & 3 == 3) {
++obj.frameID;
obj.determineCurFrame();
}
if (Play.blink != 0 && jjGameTicks & 15 < 8)
obj.curFrame = 0; //don't draw
}
void MyPickup(jjOBJ@ obj) {
if (obj.state == STATE::START) {
obj.yPos = -32 * jjParameterGet(uint(obj.xOrg / 32), uint(obj.yOrg / 32), 0, 3);
obj.state = STATE::IDLE;
} if (obj.state == STATE::ACTION) {
obj.state = STATE::KILL;
obj.curAnim = obj.killAnim;
obj.frameID = 0;
obj.determineCurFrame();
obj.scriptedCollisions = false;
jjSamplePriority(SOUND::COMMON_PICKUP1);
//potentially do pickup-related stuff here: score, ammo, whatever.
}
obj.yAcc = 4 * jjSin(int((jjGameTicks + obj.objectID * 8 + obj.xOrg + obj.yPos * 256) * 16)); //standard formula for determining a pickup's yPos offset
if (jjGameTicks & 3 == 3) {
if (uint(++obj.frameID) >= jjAnimations[obj.curAnim].frameCount) {
if (obj.state == STATE::KILL) {
obj.delete();
} else
obj.frameID = 0;
}
obj.determineCurFrame();
}
}
void UnmovingEyecandySprite(jjOBJ@ obj) {
if (obj.state == STATE::START) {
obj.yPos = 0;
obj.yAcc = 0;
obj.state = STATE::IDLE;
}
}
float MANHOLESTARTX;
float MANHOLESTARTY;
float Layer2YOffset = 0;
void ManholeCover(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.yPos = 0;
obj.yAcc = 0;
obj.state = STATE::DELAYEDSTART;
MANHOLESTARTX = obj.xOrg;
MANHOLESTARTY = obj.yOrg;
case STATE::DELAYEDSTART:
if (PlayObject.special == 0) {
if (PlayObject.yOrg - MANHOLESTARTY < 256) {
PlayObject.special = obj.objectID; //approach manhole
obj.counter = 0;
}
} else if (PlayObject.special == obj.objectID) {
if (PlayObject.yOrg < obj.yOrg) {
PlayObject.yOrg = obj.yOrg; //don't overshoot
PlayObject.ySpeed = 0;
}
if (Play.xSpeed == 0 && PlayObject.ySpeed == 0 && ++obj.counter == 70) {
obj.state = STATE::TURN;
obj.counter = 0;
}
}
break;
case STATE::TURN:
obj.counter += 2;
if (obj.yPos < 0) obj.yPos = obj.yPos + 2;
Layer2YOffset = obj.counter * 2;
if (obj.counter == 220)
@BottomTexture = @SewerGroundTexture;
else if (obj.counter > 220 && obj.counter <= 320 && (obj.counter & 3) == 2) {
jjPalette = DayPalette;
jjPalette.copyFrom(1, 254, 1, NightPalette, (obj.counter - 220) / 100.f);
jjPalette.apply();
if (obj.counter == 270)
obj.yPos = -760;
} else if (obj.counter == 350) {
@TopTexture = @SewerTexture;
TimeSpentInSewer = 1;
jjLayerXAutoSpeed[8] = 0;
jjTriggers[0] = true;
jjSetFadeColors(0);
MANHOLESTARTY -= 64;
CameraY -= 64;
obj.yOrg = MANHOLESTARTY;
PlayObject.yOrg = MANHOLESTARTY;
} else if (obj.counter == 1140) {
jjSamplePriority(SOUND::JAZZSOUNDS_PFOE);
PlayObject.special = 0;
TimeSpentInSewer = 2;
obj.state = STATE::DONE;
}
break;
}
}
void MyBubble(jjOBJ@ obj) {
if (obj.state == STATE::START) {
obj.yPos = 0;
obj.yAcc = 0;
obj.state = STATE::DELAYEDSTART;
}
if (TimeSpentInSewer == 0) return;
uint rand = jjRandom();
if (obj.state == STATE::DELAYEDSTART) {
obj.curFrame = 0;
if (rand & 15 == 0) obj.state = STATE::FLY;
} else if (obj.state == STATE::FLY) {
if (jjGameTicks & 3 == 0)
++obj.frameID;
obj.determineCurFrame();
if (rand & 511 == 0) {
obj.state = STATE::DELAYEDSTART;
obj.yPos = 0;
obj.xPos = obj.xOrg;
jjSamplePriority(SOUND::COMMON_BLUB1);
} else {
rand >>= 9;
obj.xPos = obj.xPos + (rand & 15) - 7.5;
rand >>= 4;
if (rand & 1 == 1)
obj.yPos = obj.yPos - 1;
}
}
}
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.