Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Mighty Switch Test! | Violet CLM | Test | 8.4 |
#pragma offer "SUCCESSF.WAV"
#pragma offer "TC_TRANS.WAV"
array<int> SwitchesPerLevel;
array<CHAR::Char> CharactersPerLevel;
array<bool> TriggerColorsOld(3, true);
class Point {
float x; float y;
Point(){}
Point(int xx, int yy) {
x = xx * 32.f + 16;
y = yy * 32.f + 16;
}
}
array<Point> TargetsPerLevel;
void onLevelLoad() {
jjUseLayer8Speeds = true;
jjObjectPresets[OBJECT::ICEBULLET].behavior = Switch;
jjWeapons[WEAPON::ICE].defaultSample = false;
jjWeapons[WEAPON::BLASTER].infinite = false;
jjWeapons[WEAPON::BLASTER].replenishes = false;
jjObjectPresets[OBJECT::MORPH].behavior = InfiniteMonitor;
jjObjectPresets[OBJECT::SPARK].behavior = Electricity;
jjObjectPresets[OBJECT::SPARK].lightType = LIGHT::POINT2;
jjObjectPresets[OBJECT::SPARK].playerHandling = HANDLING::PARTICLE;
jjObjectPresets[OBJECT::BURGER].determineCurAnim(ANIM::TWEEDLE, 7);
jjObjectPresets[OBJECT::BURGER].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::BURGER].scriptedCollisions = true;
jjObjectPresets[OBJECT::BURGER].energy = 5;
jjObjectPresets[OBJECT::BURGER].behavior = Tweedle;
jjObjectPresets[OBJECT::BURGER].lightType = LIGHT::NORMAL;
jjObjectPresets[OBJECT::BURGER].light = 17;
jjObjectPresets[OBJECT::BURGER].direction = -1;
jjObjectPresets[OBJECT::APPLE].behavior = MovingTarget;
jjObjectPresets[OBJECT::APPLE].deactivates = false;
jjObjectPresets[OBJECT::APPLE].playerHandling = HANDLING::PARTICLE;
jjObjectPresets[OBJECT::APPLE].lightType = LIGHT::BRIGHT;
jjObjectPresets[OBJECT::APPLE].light = 14;
jjObjectPresets[OBJECT::FLICKERGEM].var[2] = 17;
jjObjectPresets[OBJECT::FLICKERGEM].playerHandling = HANDLING::DELAYEDPICKUP;
jjObjectPresets[OBJECT::FLICKERGEM].points = 0;
jjSampleLoad(SOUND::COMMON_HARP1, "SUCCESSF.WAV");
jjSampleLoad(SOUND::COMMON_NOCOIN, "TC_TRANS.WAV");
jjAnimSets[ANIM::FLARE].load();
jjGenerateSettableTileArea(5, 0, 0, jjLayerWidth[5], jjLayerHeight[5]);
for (int x = 0; jjEventGet(x, 0) != 0; ++x) {
const uint16 tileID = jjTileGet(4, x, 0);
if (tileID == 50) CharactersPerLevel.insertLast(CHAR::SPAZ);
else if (tileID == 60) CharactersPerLevel.insertLast(CHAR::JAZZ);
else if (tileID == 40) CharactersPerLevel.insertLast(jjIsTSF ? CHAR::LORI : CHAR::JAZZ); //I guess
else CharactersPerLevel.insertLast(CHAR::FROG); //placeholder
SwitchesPerLevel.insertLast(jjParameterGet(x, 0, 0, -8));
LaserBlockArraysPerLevel.insertLast(array<LaserBlock>());
}
TargetsPerLevel.resize(LaserBlockArraysPerLevel.length + 1);
for (int x = LayerWidth - 1; x >= 0; --x)
for (int y = LayerHeight - 1; y >= 0; --y) {
const uint8 eventID = jjEventGet(x, y);
if (eventID == OBJECT::APPLE) {
SwitchBlocks.insertLast(SwitchBlock(x, y));
jjEventSet(x, y, 0);
} else if (eventID == OBJECT::BANANA) {
LaserBlockArraysPerLevel[jjParameterGet(x, y, 0, 5)].insertLast(LaserBlock(x, y));
jjEventSet(x, y, 0);
} else if (eventID == AREA::WARPTARGET) {
const uint8 warpID = jjParameterGet(x, y, 0, 8);
if (warpID >= 200) {
TargetsPerLevel[warpID - 200] = Point(x, y);
}
}
if (jjTileGet(4, x, y) == 17 | TILE::ANIMATED) { //warp
jjTileSet(4, x, y, 0);
jjTileSet(5, x, y, 17 | TILE::ANIMATED);
}
}
int tileSourceID = 260;
jjANIMATION@ animation = jjAnimations[jjAnimSets[ANIM::CUSTOM[0]].allocate(array<uint> = {12})];
int curFrame = animation.firstFrame;
for (uint frameID = 0; frameID < animation.frameCount; ) {
jjPIXELMAP fullTile(tileSourceID); tileSourceID += 10;
jjPIXELMAP quarterTile(16, 16);
for (uint x = 0; x < quarterTile.width; ++x)
for (uint y = 0; y < quarterTile.height; ++y)
quarterTile[x,y] = fullTile[x,y];
for (int repetitionCount = (frameID == 0 || frameID == 6) ? 4 : 1; repetitionCount > 0; --repetitionCount) {
jjANIMFRAME@ frame = jjAnimFrames[curFrame++];
quarterTile.save(frame);
frame.hotSpotX = -8; frame.hotSpotY = -4;
++frameID;
}
}
jjAnimations[jjAnimSets[ANIM::AMMO] + 29] = animation; //ice
//jjAnimations[jjAnimSets[ANIM::PICKUPS] + 29] = animation; //jazz gun
//jjAnimations[jjAnimSets[ANIM::PICKUPS] + 30] = animation; //spaz gun
//if (jjIsTSF)
// jjAnimations[jjAnimSets[ANIM::PLUS_AMMO] + 5] = animation; //lori gun
/*if (jjObjectCount > 1) { //Load Game
WarpID = jjLocalPlayers[0].score - 1;
for (int i = jjObjectCount - 1; i >= 1; --i)
jjObjects[i].behavior = jjObjectPresets[jjObjects[i].eventID].behavior;
}*/
jjAddObject(OBJECT::APPLE, 0, 0); //target
}
void onLevelBegin() {
jjWeapons[WEAPON::ICE].allowed = true;
jjWeapons[WEAPON::ICE].allowedPowerup = true;
jjPLAYER@ play = jjLocalPlayers[0];
play.spriteMode = SPRITE::INVISIBLE;
play.lightType = LIGHT::NONE;
play.ammo[WEAPON::BLASTER] = 0;
onFunction0(play, 0); //play.score);
if (jjLocalPlayerCount > 1)
jjAlert("|This level is not designed for splitscreeners!");
}
int QuakeLayer4 = 0;
void onMain() {
for (int i = 0; i < 32; ++i) {
jjPLAYER@ play = jjPlayers[i];
if (play.isInGame && (play.blink == 0 || ((jjRenderFrame >> 2) & 1) == 1)) {
if (play.isLocal && crispyFried)
continue;
else if (play.isLocal && suckerOn) {
jjANIMATION@ BallAnim = jjAnimations[jjAnimSets[play.setID] + RABBIT::ROLLING];
jjDrawSpriteFromCurFrame(play.xOrg, play.yOrg, BallAnim.firstFrame + ((jjGameTicks >> 2) % BallAnim.frameCount), play.direction, SPRITE::PLAYER, i, 5);
} else if (play.isLocal && knockedIntoScreen != 0) {
jjANIMATION@ FallAnim = jjAnimations[jjAnimSets[play.setID] + RABBIT::FALL];
knockedIntoScreen += 0.2;
float scale = knockedIntoScreen;
if (scale > 3.0f)
scale = 3.0f;
//else
//play.cameraFreeze(play.cameraX + (jjRandom() % 7) - 3, play.cameraY + (jjRandom() % 7) - 3, false, true);
jjDrawResizedSprite(play.xOrg, play.yOrg, ANIM::AMMO, 5, int(scale * 2), scale, scale, SPRITE::SINGLEHUE, 210, 1);
jjDrawRotatedSpriteFromCurFrame(play.xOrg, play.yOrg + knockedIntoScreen * 2, FallAnim.firstFrame + ((jjGameTicks >> 3) % FallAnim.frameCount), int(knockedIntoScreen * 4 * play.direction), scale, scale, SPRITE::PLAYER, i, 1);
} else
jjDrawSpriteFromCurFrame(play.xPos, play.yPos, play.curFrame, play.direction, SPRITE::PLAYER, i, 5);
//jjDrawTile(int(play.xPos / 32) * 32, int(play.yPos / 32) * 32, 51);
//jjDrawPixel(play.xPos, play.yPos, 255);
//jjDrawString(play.xPos, play.yPos - 50, "" + (int(play.xPos) & 31));
//jjDrawString(play.xPos, play.yPos - 30, "" + (int(play.yPos) & 31));
}
}
if (QuakeLayer4 > 0) {
if (--QuakeLayer4 == 0) {
jjLayerXOffset[4] = 0;
jjLayerYOffset[4] = 0;
} else {
jjLayerXOffset[4] = (jjRandom() & 7) - 3.5;
jjLayerYOffset[4] = (jjRandom() & 7) - 3.5;
}
}
{ //update all special blocks
const bool atLeastOneColorChanged =
TriggerColorsOld[0] != jjTriggers[0] ||
TriggerColorsOld[1] != jjTriggers[1] ||
TriggerColorsOld[2] != jjTriggers[2];
const bool updateTubeDirections = (jjGameTicks % 70) == 69; //once a second
if (atLeastOneColorChanged)
for (int i = SwitchBlocks.length - 1; i >= 0; --i) {
SwitchBlocks[i].switchTile();
}
if (updateTubeDirections)
for (int i = SwitchBlocks.length - 1; i >= 0; --i) {
SwitchBlock@ block = SwitchBlocks[i];
if (updateTubeDirections && block.TubeBlock)
block.changeDirection();
}
for (int i = LaserBlockArraysPerLevel.length - 1; i >= 0; --i) {
array<LaserBlock>@ lasers = LaserBlockArraysPerLevel[i];
for (int j = lasers.length - 1; j >= 0; --j) {
if (i == WarpID) {
if (updateTubeDirections || atLeastOneColorChanged)
lasers[j].updatePath();
lasers[j].draw();
} else
lasers[j].dead();
}
}
if (atLeastOneColorChanged)
for (int i = 0; i < 3; ++i)
TriggerColorsOld[i] = jjTriggers[i];
}
}
bool suckerOn = false;
float suckerSpeedX = 0;
float suckerSpeedY = 0;
int lastSuckTile = 0;
void suckPlayer(jjPLAYER@ play, int xSpeed, int ySpeed) {
if (lastSuckTile != play.currTile) {
lastSuckTile = play.currTile;
suckerSpeedX = xSpeed * SuckerSpeedSpeed;
suckerSpeedY = ySpeed * SuckerSpeedSpeed;
play.xPos = play.xOrg = int(play.xPos / 32) * 32 + 15;
play.yPos = play.yOrg = int(play.yPos / 32) * 32 + 15;
suckerOn = true;
}
}
void unsuckPlayer(jjPLAYER@ play) {
suckerOn = false;
lastSuckTile = 0;
play.xSpeed = play.xAcc = play.ySpeed = play.yAcc = 0;
}
void onBelt(jjPLAYER@ play, int dir) {
if (play.keyJump) {
play.xSpeed = 0;
} else {
play.xSpeed = dir * 2;
play.direction = dir;
}
play.keyLeft = play.keyRight = false;
}
int getCurrTileValue(int x, int y) {
return (x >> 5) + (y >> 5 << 16);
}
bool maskedPixel(int x, int y) {
x += int(suckerSpeedX * 4) - 2;
y += int(suckerSpeedY * 4) + 4;
return getCurrTileValue(x, y) != lastSuckTile && jjEventGet(x / 32, y / 32) != AREA::ONEWAY && jjMaskedPixel(x, y);
}
int warpInsideTile(int xTile, int yTile) {
const uint16 tileID = jjTileGet(4, xTile, yTile);
if (tileID == 40 || tileID == 50 || tileID == 60) //special block came into solidity
return 1;
else if (tileID == 10 && jjEventGet(xTile, yTile) != AREA::ONEWAY) //wallclimbing
return 2;
else if (tileID == (4 | TILE::ANIMATED)) //tube block
return -1;
return 0;
}
int warpInsidePixel(float x, float y) {
return warpInsideTile(int(x) / 32, int(y) / 32);
}
const float SuckerSpeedSpeed = 4.f;
uint16 floorTileID = 0;
uint SpendFramesCheckingIfPlayerIsStuck = 0;
float knockedIntoScreen = 0.f;
bool crispyFried = false;
bool warping = false;
void onPlayer(jjPLAYER@ play) {
if (!jjLowDetail) {
uint rand = jjRandom();
if ((rand & 3) == 1) {
rand >>= 2;
const uint xOffset = rand % jjResolutionWidth;
rand >> 10;
const uint yOffset = rand % jjResolutionHeight;
rand >>= 9;
const uint xTile = uint(play.cameraX + xOffset) / 32;
const uint yTile = uint(play.cameraY + yOffset) / 32;
if (jjTileGet(4, xTile, yTile) == 10) { //normal wall
jjAddObject(OBJECT::SPARK, xTile * 32 + 16, yTile * 32 + 16);
}
}
}
if (!play.isInGame || play.xPos < 0 || play.yPos < 0) //spectating
return;
play.currWeapon = WEAPON::ICE;
floorTileID = 0;
if (attackingWildly) {
play.keyDown = play.ySpeed >= 0;
play.direction = -1;
play.keyUp = play.keyLeft = play.keyRight = play.keyFire = play.keySelect = play.keyJump = play.keyRun = false;
} else if (Endgame) {
play.keyFire = false;
const int timeTillFire = jjGameTicks % 210; //every third second
if (timeTillFire == 209)
play.fireBullet(WEAPON::ICE, false, false);
else if (timeTillFire == 209 - 35 || timeTillFire == 209 - 70 || timeTillFire == 209 - 105)
//jjSample(play.xPos, play.yPos, SOUND::COMMON_COIN);
jjSample(play.xPos, play.yPos, SOUND::COMMON_BELL_FIRE2);
if (timeTillFire >= 209 - 105) {
const int frameID = ((timeTillFire + 1) % 35) / 2;
if (frameID < 6)
jjDrawSprite(play.xPos, play.yPos, ANIM::FLARE, 0, frameID, 0, SPRITE::TRANSLUCENTCOLOR, 15, 1);
}
}
if (warping && play.warpID == 0) {
if (crispyFried || knockedIntoScreen != 0)
play.blink = 70;
knockedIntoScreen = 0;
crispyFried = false;
warping = false;
play.cameraUnfreeze();
jjTriggers[0] = jjTriggers[1] = jjTriggers[2] = false;
array<LaserBlock>@ lasers = LaserBlockArraysPerLevel[WarpID];
for (int i = lasers.length - 1; i >= 0; --i)
lasers[i].countdownToDestruction = 0; //restore to life
}
if (SpendFramesCheckingIfPlayerIsStuck > 0) {
--SpendFramesCheckingIfPlayerIsStuck;
if (!warping && warpInsidePixel(play.xPos, play.yPos) == 0) {
const int truncatedX = int(play.xPos) & 31;
if (truncatedX < 12) {
const int shouldWarp = warpInsidePixel(play.xPos - 32, play.yPos);
if (shouldWarp >= 1)
play.xPos += (12 - truncatedX); //extract
else if (shouldWarp == -1)
play.xPos -= 32; //squeeze into tube
} else if (truncatedX > 20) {
const int shouldWarp = warpInsidePixel(play.xPos + 32, play.yPos);
if (shouldWarp >= 1)
play.xPos -= (truncatedX - 20);
else if (shouldWarp == -1)
play.xPos += 32;
}
const int truncatedY = int(play.yPos) & 31;
if (truncatedY > 12) {
int shouldWarp = warpInsidePixel(play.xPos, play.yPos + 32);
if (shouldWarp == 0) {
if (truncatedX < 12)
shouldWarp = warpInsidePixel(play.xPos - 32, play.yPos + 32);
else if (truncatedX > 20)
shouldWarp = warpInsidePixel(play.xPos + 32, play.yPos + 32);
}
if (shouldWarp >= 1)
play.yPos -= (truncatedY - 12);
else if (shouldWarp == -1)
play.yPos += 32;
} /*else if (truncatedY < 4 || truncatedY ) {
if (jjMaskedPixel(int(play.xPos), int(play.yPos - 32)))
play.yPos += (4 - truncatedY);
} */
}
}
if (suckerOn) {
for (int i = 0; i < 2; ++i)
if (suckerSpeedX != 0) {
if (maskedPixel(int(play.xOrg), int(play.yOrg))) {
unsuckPlayer(play);
break;
} else {
play.xPos = play.xOrg += suckerSpeedX;
play.yPos = play.yOrg;
play.ySpeed = 0; //testing to make spectating look better
}
} else if (suckerSpeedY != 0) {
if (maskedPixel(int(play.xOrg), int(play.yOrg))) {
unsuckPlayer(play);
break;
} else {
play.xPos = play.xOrg;
play.yPos = play.yOrg += suckerSpeedY;
}
} else { //waiting for feedback
if (play.keyRight)
suckerSpeedX = SuckerSpeedSpeed;
else if (play.keyLeft)
suckerSpeedX = -SuckerSpeedSpeed;
else if (play.keyDown)
suckerSpeedY = SuckerSpeedSpeed;
else if (play.keyUp)
suckerSpeedY = -SuckerSpeedSpeed;
else {
play.xPos = play.xOrg;
play.yPos = play.yOrg;
play.xSpeed = play.xAcc = play.ySpeed = play.yAcc = 0;
suckerOn = false; lastSuckTile = 0;
}
const int distance = jjGameTicks & 15;
const int xTile = int(play.xPos / 32) * 32;
const int yTile = int(play.yPos / 32) * 32;
jjDrawTile(xTile + 32 + distance, yTile, BigArrowTileIDs[0] + 2);
jjDrawTile(xTile - 32 - distance, yTile, BigArrowTileIDs[2] + 2);
jjDrawTile(xTile, yTile + 32 + distance, BigArrowTileIDs[1] + 2);
jjDrawTile(xTile, yTile - 32 - distance, BigArrowTileIDs[3] + 2);
break;
}
} else {
if (play.warpID == 0 && (play.ySpeed == 0 || warpInsidePixel(play.xPos, play.yPos - 32) != 0)) { //probably standing
const int floorTileY = int(play.yPos / 32) + 1;
int floorTileX = int(play.xPos / 32);
bool onTileCorner = false;
if (!jjMaskedPixel(floorTileX * 32, floorTileY * 32)) {
const int playTruncatedX = int(play.xPos) & 31;
if (playTruncatedX < 12) --floorTileX;
else if (playTruncatedX > 18) ++floorTileX;
onTileCorner = true;
}
//jjDrawTile(floorTileX * 32, floorTileY * 32, 51);
floorTileID = jjTileGet(4, floorTileX, floorTileY);
if (floorTileID == 26 | TILE::ANIMATED) {
onBelt(play, -1);
} else if (floorTileID == 26 | TILE::ANIMATED | TILE::HFLIPPED) {
onBelt(play, 1);
} else if (floorTileID == 50 || floorTileID == 60) { //red, green specials
if (onTileCorner)
floorTileID = 0; //must be standing full on for one of these tiles
else
jjDrawTile(floorTileX * 32, floorTileY * 32, 231);
}
}
}
if (play.warpID == 0) {
play.xOrg = play.xPos;
play.yOrg = play.yPos;
const int xTile = int(play.xPos / 32);
const int yTile = int(play.yPos / 32);
array<LaserBlock>@ lasers = LaserBlockArraysPerLevel[WarpID];
for (int i = lasers.length - 1; i >= 0; --i) {
if (lasers[i].tileIsInPath(xTile, yTile)) {
crispyFried = true;
jjObjects[0].xPos = play.xPos;
jjObjects[0].yPos = play.yPos;
jjObjects[0].curFrame = play.curFrame;
jjObjects[0].direction = play.direction;
jjObjects[0].particlePixelExplosion(1);
play.cameraFreeze(play.cameraX, play.cameraY, false, true);
jjSample(play.xPos, play.yPos, SOUND::COMMON_BURN);
jjSample(play.xPos, play.yPos, SOUND::COMMON_ELECTRIC1);
onFunction1(play);
return;
}
}
const int shouldWarp = warpInsideTile(xTile, yTile);
if (shouldWarp >= 1) {
if (shouldWarp == 1 && play.fly == 0) {
jjSample(play.xPos, play.yPos, SOUND::COMMON_SMASH);
jjSample(play.xPos, play.yPos, SOUND::SPAZSOUNDS_AUTSCH1);
knockedIntoScreen = 1.0;
play.cameraFreeze(play.cameraX, play.cameraY, false, true);
}
onFunction1(play);
} else if (shouldWarp == -1) {
const uint16 backTileID = jjTileGet(5, xTile, yTile);
if (backTileID == BigArrowTileIDs[0]) {
suckPlayer(play, 1, 0);
} else if (backTileID == BigArrowTileIDs[1]) {
suckPlayer(play, 0, 1);
} else if (backTileID == BigArrowTileIDs[2]) {
suckPlayer(play, -1, 0);
} else if (backTileID == BigArrowTileIDs[3]) {
suckPlayer(play, 0, -1);
} else {
suckPlayer(play, 0, 0);
}
}
}
}
void Switch(jjOBJ@ obj) {
if (jjPlayers[obj.creator].isLocal) {
jjTriggers[0] = !jjTriggers[0];
if (floorTileID != 50) //red
jjTriggers[1] = !jjTriggers[1];
if (floorTileID != 60) //green
jjTriggers[2] = !jjTriggers[2];
QuakeLayer4 = 10;
jjSamplePriority(SOUND::COMMON_CANSPS);
}
obj.delete();
SpendFramesCheckingIfPlayerIsStuck = 2;
}
bool Successful = false;
int WarpID = -1;
void onFunction0(jjPLAYER@ play, int8 newWarpID) {
if (WarpID + 1 == newWarpID) {
Successful = true;
const bool wasEndgame = Endgame;
Endgame = SwitchesPerLevel[newWarpID] < 0;
jjTriggers[ENDGAMETRIGGERID] = Endgame;
WarpID = newWarpID;
play.score = newWarpID + 1; //in case if playing in SP
if (!Endgame) {
if (WarpID > 0)
jjSamplePriority(SOUND::COMMON_HARP1);
if (wasEndgame) {
jjLayerHasTiles[1] = true;
jjPalette.reset();
jjPalette.apply();
}
} else {
if (!wasEndgame) {
jjLayerHasTiles[1] = false;
jjSamplePriority(SOUND::COMMON_NOCOIN);
for (int i = 151; i <= 159; ++i)
jjPalette.color[i].swizzle(COLOR::BLUE, COLOR::BLUE, COLOR::RED);
for (int i = 176; i <= 208; ++i)
jjPalette.color[i].swizzle(COLOR::BLUE, COLOR::GREEN, COLOR::RED);
jjPalette.apply();
} else
jjSamplePriority(SOUND::COMMON_HARP1);
}
const int maxSwitches = int(abs(SwitchesPerLevel[newWarpID]));
jjWeapons[WEAPON::ICE].maximum = maxSwitches;
jjWeapons[WEAPON::ICE].infinite = maxSwitches >= 99;
play.ammo[WEAPON::ICE] = jjWeapons[WEAPON::ICE].maximum; //double sure?
if (!Endgame)
play.showText("@@@@@@@@@@@@@@#" + (CharactersPerLevel[newWarpID] == CHAR::JAZZ ? "|||" : (CharactersPerLevel[newWarpID] == CHAR::FROG ? "~" : "||||")) +"~ Incident " + formatInt(newWarpID + 1, "0", 2) + "!@@" + ((maxSwitches >= 99) ? "Unlimited" : formatInt(maxSwitches, "d")) + " switches", STRING::MEDIUM);
onFunction1(play);
}
}
void onFunction1(jjPLAYER@ play) {
attackingWildly = false;
//if (!warping) {
{
unsuckPlayer(play);
play.warpToID(WarpID);
warping = true;
play.ammo[WEAPON::ICE] = jjWeapons[WEAPON::ICE].maximum;
const CHAR::Char newChar = CharactersPerLevel[WarpID];
if (play.charCurr != newChar && newChar != CHAR::FROG)
play.morphTo(newChar, true);
}
}
bool Endgame = false;
const uint8 ENDGAMETRIGGERID = 21;
const array<uint16> BigArrowTileIDs = {0 | TILE::ANIMATED, 1 | TILE::ANIMATED, 0 | TILE::ANIMATED | TILE::HFLIPPED, 1 | TILE::ANIMATED | TILE::VFLIPPED};
class SwitchBlock {
bool TubeBlock = false;
bool EndgameOnly = false;
uint triggerID = 0;
bool triggerWasOn = false;
uint16 offTileID, onTileID;
uint currentDirection;
uint legalDirections;
uint xTile, yTile;
SwitchBlock(){}
SwitchBlock(uint x, uint y) {
xTile = x; yTile = y;
const uint16 animatedTileID = jjTileGet(4, xTile, yTile);
switch (animatedTileID ^ TILE::ANIMATED) {
case 18:
offTileID = 40;
onTileID = 41;
break;
case 19:
offTileID = 41;
onTileID = 40;
break;
case 20:
offTileID = 50;
onTileID = 51;
triggerID += 1;
break;
case 21:
offTileID = 51;
onTileID = 50;
triggerID += 1;
break;
case 22:
offTileID = 60;
onTileID = 61;
triggerID += 2;
break;
case 23:
offTileID = 61;
onTileID = 60;
triggerID += 2;
break;
case 24:
offTileID = (4 | TILE::ANIMATED);
onTileID = 81;
TubeBlock = true;
break;
case 25:
offTileID = 81;
onTileID = (4 | TILE::ANIMATED);
TubeBlock = true;
break;
case 26:
offTileID = 26 | TILE::ANIMATED;
onTileID = 26 | TILE::ANIMATED | TILE::HFLIPPED;
break;
case 26 | TILE::HFLIPPED:
offTileID = 26 | TILE::ANIMATED | TILE::HFLIPPED;
onTileID = 26 | TILE::ANIMATED;
break;
case 28:
case 29:
case 30: { //mirrors
const int color = ((animatedTileID & TILE::RAWRANGE) - 28);
triggerID += color;
offTileID = 271 + color * 10;
onTileID = offTileID | TILE::HFLIPPED;
break; }
case 28 | TILE::HFLIPPED:
case 29 | TILE::HFLIPPED:
case 30 | TILE::HFLIPPED: { //flipped mirrors
const int color = ((animatedTileID & TILE::RAWRANGE) - 28);
triggerID += color;
onTileID = 271 + color * 10;
offTileID = onTileID | TILE::HFLIPPED;
break; }
default:
jjDebug("invalid trigger tile!");
}
if (TubeBlock) {
legalDirections = jjParameterGet(xTile, yTile, 0, 4);
currentDirection = jjParameterGet(xTile, yTile, 4, 2);
}
EndgameOnly = jjParameterGet(xTile, yTile, 7, 1) == 1;
if (jjParameterGet(xTile, yTile, 6, 1) == 1) { //move downwards one tile, to allow placing blocks on top of warps
jjTileSet(4, xTile, yTile, 0);
yTile += 1;
}
}
void switchTile() {
const bool triggerOn = jjTriggers[triggerID];
if (triggerOn != TriggerColorsOld[triggerID])
jjTileSet(4, xTile,yTile, (!EndgameOnly || Endgame) ? (triggerOn ? onTileID : offTileID) : 0);
}
void changeDirection() {
if ((!EndgameOnly || Endgame) && legalDirections != 0) { //level-set direction, not player-set
int newDirection = currentDirection;
for (int i = 0; i < 3; ++i) {
newDirection = (newDirection + 1) & 3;
if ((legalDirections & (1 << newDirection)) != 0) {
currentDirection = newDirection;
break;
}
}
jjTileSet(5, xTile,yTile, BigArrowTileIDs[currentDirection]);
}
}
}
array<SwitchBlock> SwitchBlocks;
class TileCoordinates {
uint16 xTile, yTile;
float xPos, yPos;
uint16 tileID;
TileCoordinates(){}
TileCoordinates(uint x, uint y, uint oldDir, uint newDir) {
xPos = (xTile = x) * 32.f; yPos = (yTile = y) * 32.f;
if (oldDir == newDir) {
if ((oldDir & 1) == 0)
tileID = 32 | TILE::ANIMATED; //horizontal
else
tileID = 31 | TILE::ANIMATED; //vertical
} else switch(oldDir | (newDir << 2)) { //corner
case 12:
case 9:
tileID = 33 | TILE::ANIMATED;
break;
case 1:
case 14:
tileID = 33 | TILE::ANIMATED | TILE::HFLIPPED;
break;
case 4:
case 11:
tileID = 33 | TILE::ANIMATED | TILE::VFLIPPED;
break;
case 3:
case 6:
tileID = 33 | TILE::ANIMATED | TILE::HFLIPPED | TILE::VFLIPPED;
break;
}
}
}
const uint LayerWidth = jjLayerWidth[4];
const uint LayerHeight = jjLayerHeight[4];
class LaserBlock {
uint8 currentDirection;
uint8 countdownToDestruction = 0;
int xTile, yTile;
array<TileCoordinates> path(0);
LaserBlock(){}
LaserBlock(uint x, uint y) {
xTile = x; yTile = y;
const uint16 tileID = jjTileGet(4, xTile, yTile);
switch (tileID) {
case 301:
currentDirection = 3; //up
break;
case 301 | TILE::VFLIPPED:
currentDirection = 1; //down
break;
case 311:
currentDirection = 0; //right
break;
case 311 | TILE::HFLIPPED:
currentDirection = 2; //left
break;
default:
jjDebug("invalid laser tile!");
}
}
void updatePath() {
if (countdownToDestruction >= 2)
return;
path.resize(0);
uint pathDirection = currentDirection;
uint newDirection = pathDirection;
int newTileX = xTile;
int newTileY = yTile;
while (true) {
switch (pathDirection) {
case 0: //right
newTileX += 1;
break;
case 1: //down
newTileY += 1;
break;
case 2: //left
newTileX -= 1;
break;
case 3: //break
newTileY -= 1;
}
while (newTileX < 0) newTileX += LayerWidth;
while (newTileY < 0) newTileY += LayerHeight;
const uint16 tileID = jjTileGet(4, newTileX %= LayerWidth, newTileY %= LayerHeight);
//if (tileID != 0)
if (tileID == 40 || tileID == 50 || tileID == 60 || tileID == (26 | TILE::ANIMATED) || tileID == (26 | TILE::ANIMATED | TILE::HFLIPPED) || (tileID == 10 && jjEventGet(newTileX, newTileY) != AREA::ONEWAY)) //wall
break;
else if (tileID == 301 || tileID == (301 | TILE::VFLIPPED) || tileID == 311 || tileID == (311 | TILE::HFLIPPED)) { //hit another laser!
array<LaserBlock>@ lasers = LaserBlockArraysPerLevel[WarpID];
for (int i = lasers.length - 1; i >= 0; --i) {
LaserBlock@ laser = lasers[i];
if (laser.xTile == newTileX && laser.yTile == newTileY) { //found the right one
if (laser.countdownToDestruction == 0)
laser.countdownToDestruction = 1;
break;
}
} //if none are found, oh well, that's weird but keep going
break;
} else if (tileID == 4 | TILE::ANIMATED) { //sucker tube
const uint16 arrowTileID = jjTileGet(5, newTileX, newTileY);
for (int i = 0; i < 4; ++i)
if (arrowTileID == BigArrowTileIDs[i]) {
newDirection = i;
break;
}
if (((newDirection + 2) & 3) == pathDirection) //reversed
break;
} else if (tileID == 261 || tileID == 271 || tileID == 281 || tileID == 291) { //mirror
newDirection ^= 1;
} else if (tileID == (261 | TILE::HFLIPPED) || tileID == (271 | TILE::HFLIPPED) || tileID == (281 | TILE::HFLIPPED) || tileID == (291 | TILE::HFLIPPED)) { //mirror 2
newDirection = 3 - pathDirection;
}
path.insertLast(TileCoordinates(newTileX, newTileY, pathDirection, newDirection));
pathDirection = newDirection;
}
}
void dead() {
jjPARTICLE@ fire = jjAddParticle(((jjRandom() & 1) == 1) ? PARTICLE::SMOKE : PARTICLE::SPARK);
if (fire !is null) {
fire.xPos = xTile * 32 + (jjRandom() & 31);
fire.yPos = yTile * 32 + (jjRandom() & 31);
}
}
void draw() {
if (countdownToDestruction == 1) //just got hit, but stay alive
countdownToDestruction = 2;
else if (countdownToDestruction == 2) { //dead
dead();
} else {
const uint8 glowColor = 40 + (jjRenderFrame & 3);
const uint8 glowIntensity = uint8(abs(jjSin(jjRenderFrame << 4) * 64));
jjDrawRectangle(xTile * 32, yTile * 32, 32, 32, glowColor, SPRITE::BLEND_NORMAL, glowIntensity);
for (uint i = 0; i < path.length; ++i) {
jjDrawRectangle(path[i].xPos, path[i].yPos, 32, 32, glowColor, SPRITE::BLEND_NORMAL, glowIntensity);
jjDrawTile(path[i].xPos, path[i].yPos, path[i].tileID);
}
}
}
bool tileIsInPath(uint x, uint y) {
if (countdownToDestruction >= 2)
return false;
for (uint i = 0; i < path.length; ++i)
if (x == path[i].xTile && y == path[i].yTile)
return true;
return false;
}
}
array<array<LaserBlock>> LaserBlockArraysPerLevel;
bool onDrawLives(jjPLAYER@ play, jjCANVAS@ screen) {
return true;
}
bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ screen) {
return true;
}
bool onDrawAmmo(jjPLAYER@ play, jjCANVAS@ screen) {
//if (SwitchesPerLevel[WarpID] < 99)
// screen.drawString(20, jjResolutionHeight - 50, "" + SwitchesPerLevel[WarpID], STRING::LARGE);
const CHAR::Char intendedChar = CharactersPerLevel[WarpID];
const int frameID = jjRenderFrame >> 3;
if (jjIsTSF && (intendedChar == CHAR::LORI || intendedChar == CHAR::FROG))
screen.drawSprite(50, jjResolutionHeight, ANIM::FACES, 4, frameID, 0, SPRITE::PLAYER, play.playerID);
if (intendedChar == CHAR::SPAZ || intendedChar == CHAR::FROG)
screen.drawSprite(30, jjResolutionHeight, ANIM::FACES, (jjIsTSF) ? 5 : 4, frameID, 0, SPRITE::PLAYER, play.playerID);
if (intendedChar == CHAR::JAZZ || intendedChar == CHAR::FROG)
screen.drawSprite(10, jjResolutionHeight, ANIM::FACES, 3, frameID, 0, SPRITE::PLAYER, play.playerID);
return Endgame;
}
void InfiniteMonitor(jjOBJ@ obj) {
if (obj.state == STATE::KILL) { //destroyed
obj.state = STATE::START; //restart
obj.xPos = obj.xOrg;
obj.yPos = obj.yOrg;
obj.objType = jjObjectPresets[obj.eventID].objType;
obj.energy = jjObjectPresets[obj.eventID].energy;
}
obj.behave(BEHAVIOR::MONITOR);
}
void Electricity(jjOBJ@ obj) {
if (obj.state == STATE::DEACTIVATE || jjLowDetail) {
obj.delete(); //don't bother deactivating
return;
} else if (obj.state == STATE::START) {
obj.state = STATE::FLY;
switch (jjRandom() & 3) {
case 0:
obj.xSpeed = 2;
obj.ySpeed = 0;
break;
case 1:
obj.xSpeed = 0;
obj.ySpeed = 2;
break;
case 2:
obj.xSpeed = -2;
obj.ySpeed = 0;
break;
case 3:
obj.xSpeed = 0;
obj.ySpeed = -2;
break;
}
}
if (!jjMaskedPixel(int(obj.xPos+= obj.xSpeed), int(obj.yPos+= obj.ySpeed))) { //outside the wall
obj.delete();
uint rand = jjRandom();
for (int i = 0; i < 7; ++i) {
const int angle = (rand & 31) << 5;
rand >>= 5;
jjPARTICLE@ spark = jjAddParticle(PARTICLE::SPARK);
if (spark !is null) {
spark.xPos = obj.xPos;
spark.yPos = obj.yPos;
spark.xSpeed = jjSin(angle) * 2.f;
spark.ySpeed = jjCos(angle) * 2.f;
if (!Endgame) {
spark.spark.color -= 8; spark.spark.colorStop -= 8; //blue
}
}
}
} else if ((int(obj.xPos) & 31) == 16 && (int(obj.yPos) & 31) == 16) {
const uint rand = jjRandom();
if ((rand & 1) == 1) { //turn
if (obj.xSpeed != 0) {
obj.xSpeed = 0;
obj.ySpeed = ((rand & 2) == 2) ? 2 : -2;
} else {
obj.ySpeed = 0;
obj.xSpeed = ((rand & 2) == 2) ? 2 : -2;
}
}
}
//else
}
void Tweedle(jjOBJ@ obj) {
if (obj.state == STATE::DEACTIVATE)
obj.deactivate();
else if (obj.state == STATE::DONE) {
obj.state = STATE::FALL;
obj.determineCurAnim(ANIM::TWEEDLE, 5);
obj.ySpeed = -7;
obj.yAcc = 0.3;
} else if (obj.state == STATE::FALL) {
obj.xPos -= 2;
if ((obj.yPos += (obj.ySpeed += obj.yAcc)) > obj.yOrg + 5*32) {
obj.state = STATE::EXPLODE;
obj.counterEnd = 0;
}
} else if (obj.state == STATE::EXPLODE) {
if (obj.counterEnd++ < 240) {
QuakeLayer4 = 10;
obj.xPos += jjLayerXOffset[4];
obj.yPos += jjLayerYOffset[4];
if ((obj.counterEnd & 15) == 0) {
obj.justHit = 8;
obj.particlePixelExplosion(1);
jjSamplePriority(SOUND::COMMON_ELECTRIC1);
}
} else {
obj.state = STATE::START;
obj.xPos = obj.xOrg;
obj.yPos = obj.yOrg;
obj.energy = jjObjectPresets[obj.eventID].energy;
obj.curAnim = jjObjectPresets[obj.eventID].curAnim;
attackingWildly = false;
Endgame = false;
jjTriggers[ENDGAMETRIGGERID] = false;
WarpID = 0;
jjLocalPlayers[0].score = 1;
jjLocalPlayers[0].showText("#|||||~@@@CONGRATULATIONS!", STRING::LARGE);
jjLayerHasTiles[1] = true;
jjPalette.reset();
jjPalette.apply();
jjSamplePriority(SOUND::COMMON_EXPL_TNT);
}
} else if (WarpID == 0) {
return; //don't delete; wait until can be deactivated
}
obj.frameID = jjGameTicks >> 2;
obj.determineCurFrame();
obj.draw();
}
bool attackingWildly;
void onFunction2(jjPLAYER@ play) {
attackingWildly = true;
}
void onObjectHit(jjOBJ@ object, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet is null && force == 1 && WarpID != 0) {
player.buttstomp = 121;
player.ySpeed = -10;
player.yAcc = 0;
object.justHit = 6;
if (--object.energy <= 0) {
object.state = STATE::DONE;
}
}
}
void MovingTarget(jjOBJ@ obj) { //only an object because it has a lighttype
if (Successful) {
Successful = false;
for (int i = 0; i < 11; ++i) {
jjOBJ@ reward = jjObjects[jjAddObject(OBJECT::FLICKERGEM, obj.xPos, obj.yPos, obj.objectID)];
reward.xSpeed = ((jjRandom() & 15) - 7.5f) / 2.f;
reward.ySpeed = -1.1 - ((jjRandom() & 7) / 2.f);
reward.var[0] = (jjRandom() & 3) + 1;
}
}
uint targetID = WarpID + 1;
if (targetID >= TargetsPerLevel.length)
targetID = TargetsPerLevel.length - 1;
obj.xPos += (TargetsPerLevel[targetID].x - obj.xPos) / 128;
obj.yPos += (TargetsPerLevel[targetID].y - obj.yPos) / 128;
const float scale = 4.5f + jjSin(jjGameTicks << 4) * 2;
jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::PLUS_RETICLES, 1, 0, jjGameTicks << 2, scale, scale, SPRITE::SINGLECOLOR, 47 - (jjRenderFrame & 31), 1);
}
//go away you silly cheater
bool onLocalChat(string &in stringReceived, CHAT::Type chatType) {
array<string> regexResults;
if (jjRegexMatch(stringReceived, "SKIPTO (\\d+)", regexResults, false)) {
int newWarpID = parseInt(regexResults[1]) - 1;
if (newWarpID >= 0 && newWarpID < int(SwitchesPerLevel.length)) {
WarpID = newWarpID - 1; //force function to accept value
onFunction0(jjLocalPlayers[0], newWarpID);
} else
jjAlert("|Invalid Incident!");
return true;
}
return false;
}
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.