Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Holiday Hare '18 | SmokeNC | Single player | 8.9 |
#include "MLLE-Include-1.4.asc"
const bool MLLESetupSuccessful = MLLE::Setup();
#pragma require "HH18_Guardian-MLLE-Data-2.j2l"
#pragma require "Castle2.j2t"
#pragma require "HH18_Guardian-MLLE-Data-1.j2l"
#pragma require "Damn1.j2t"
#pragma require "HH18_Guardian.j2l"
#pragma require "HH17_lowind.wav"
#pragma require "HH17_Glass1.wav"
#pragma require "HH18E1.j2a"
#include "HH18Smoke.asc"
#include "HH18savegems.asc"
enum EJStates {Intro, Run, Jump, Buttstomp, Uppercut, Hurt, EJ_Die, EJ_End};
uint currEJState = Intro;
enum DevanStates {Devan_Intro, Warp_Out, Warp_In, Device};
uint currDevanState = Devan_Intro;
enum DRAKStates {Hidden, Reveal, Freeze_Devan, Fade_Out, Awaiting, Begin, Fly, Icicles, Freeze_Cloud, Ice_Bomb, Summon_Enemies, Homing_Ice, Raining_Icicles, Icicle_Burst, Crystals, Complete};
uint currDRAKState = Hidden;
bool cutscene = false;
uint scene, elapsed, count = 0;
bool snowblind = false;
bool transition = false;
bool climax = false;
bool outro = false;
bool done = false;
int sample = 0;
int healthFactor = 4+jjDifficulty;
bool limitScrollBeforeDrak = false;
bool refresh = false;
const float PI = 3.1415927f;
bool facingPlayer(jjOBJ@ obj, jjPLAYER@ play) {
if (((obj.xPos < (play.xPos - 32) && obj.direction == 1) || (obj.xPos > (play.xPos + 32) && obj.direction == -1)) && obj.yPos <= int(play.yPos + 32) && obj.yPos >= int(play.yPos - 32)) return true;
return false;
}
void onLevelLoad() {
uint src = jjAnimSets[ANIM::CUSTOM[255]].load(0, "SExmas.j2a");
uint dest = jjAnimSets[ANIM::PICKUPS];
for (int i = 0; i < 95; i++) {
const jjANIMATION@ anim = jjAnimations[src + i];
if (anim.frameCount != 0)
jjAnimations[dest + i] = anim;
}
jjAnimSets[ANIM::BRIDGE].load(1, "SExmas.j2a");
jjAnimSets[ANIM::CUSTOM[0]].load(2, "SExmas.j2a");
jjAnimSets[ANIM::CUSTOM[10]].load(13, "HH18E1.j2a");
jjAnimSets[ANIM::CUSTOM[11]].load(0, "HH18E1.j2a");
jjAnimSets[ANIM::CUSTOM[12]].load(8, "HH18E1.j2a");
jjAnimSets[ANIM::CUSTOM[13]].load(12, "HH18E1.j2a");
jjUseLayer8Speeds = true;
jjObjectPresets[OBJECT::EGGPLANT].behavior = EvilJazz();
jjObjectPresets[OBJECT::EGGPLANT].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::EGGPLANT].bulletHandling = HANDLING::DETECTBULLET;
jjObjectPresets[OBJECT::EGGPLANT].scriptedCollisions = true;
jjObjectPresets[OBJECT::EGGPLANT].isTarget = true;
jjObjectPresets[OBJECT::EGGPLANT].isBlastable = false;
jjObjectPresets[OBJECT::EGGPLANT].energy = 12 + (3*jjDifficulty);
jjObjectPresets[OBJECT::EGGPLANT].lightType = LIGHT::PLAYER;
jjObjectPresets[OBJECT::EGGPLANT].light = 12;
jjObjectPresets[OBJECT::EGGPLANT].deactivates = false;
jjObjectPresets[OBJECT::EGGPLANT].points = 5000;
jjObjectPresets[OBJECT::WEENIE].determineCurAnim(ANIM::CUSTOM[10], 0);
jjObjectPresets[OBJECT::WEENIE].behavior = Drak();
jjObjectPresets[OBJECT::WEENIE].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::WEENIE].bulletHandling = HANDLING::DETECTBULLET;
jjObjectPresets[OBJECT::WEENIE].scriptedCollisions = true;
jjObjectPresets[OBJECT::WEENIE].isBlastable = false;
jjObjectPresets[OBJECT::WEENIE].energy = 100;
jjObjectPresets[OBJECT::WEENIE].age = 400 + (100*jjDifficulty);
jjObjectPresets[OBJECT::WEENIE].deactivates = false;
jjObjectPresets[OBJECT::WEENIE].points = 50000;
jjObjectPresets[OBJECT::THING].determineCurAnim(ANIM::DEVILDEVAN, 1);
jjObjectPresets[OBJECT::THING].behavior = Devan();
jjObjectPresets[OBJECT::THING].playerHandling = HANDLING::PARTICLE;
jjObjectPresets[OBJECT::THING].bulletHandling = HANDLING::IGNOREBULLET;
jjObjectPresets[OBJECT::THING].isBlastable = false;
jjObjectPresets[OBJECT::THING].deactivates = false;
jjObjectPresets[OBJECT::CAKE].behavior = Portal();
jjObjectPresets[OBJECT::CAKE].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::CAKE].bulletHandling = HANDLING::IGNOREBULLET;
jjObjectPresets[OBJECT::CAKE].scriptedCollisions = true;
jjObjectPresets[OBJECT::CAKE].isBlastable = false;
jjObjectPresets[OBJECT::CAKE].deactivates = false;
jjObjectPresets[OBJECT::STEADYLIGHT].behavior = SnowEffect();
jjAnimSets[ANIM::DEVAN].load();
jjAnimSets[ANIM::DEVILDEVAN].load();
jjAnimSets[ANIM::HATTER].load();
jjAnimSets[ANIM::ROBOT].load();
jjSampleLoad(SOUND::SCIENCE_PLOPKAOS, "HH17_lowind.wav");
jjSampleLoad(SOUND::FAN_FAN, "HH17_Glass1.wav");
SMOKE::FROZENSHADE(OBJECT::HATTER,2+jjDifficulty);
gem::restorePlayerGems();
}
void onLevelReload() {
MLLE::Palette.apply();
jjMusicLoad("cracking_ice.mod");
currEJState = Intro;
currDevanState = Devan_Intro;
cutscene = transition = climax = false;
refresh = true;
gem::restorePlayerGems();
}
void onLevelBegin() {
for (uint i = 0; i < 5; i++) {
jjANIMATION@ animDemon = jjAnimations[jjAnimSets[ANIM::CUSTOM[10]] + i];
for (uint j = 0; j < animDemon.frameCount; j++) {
jjANIMFRAME@ frame = jjAnimFrames[animDemon + j];
jjPIXELMAP sprite(frame);
for (uint x = 0; x < sprite.width; ++x) {
for (uint y = 0; y < sprite.height; ++y) {
if (sprite[x,y] >= 16 && sprite[x,y] <= 23) sprite[x,y] += 15;
if (sprite[x,y] >= 48 && sprite[x,y] <= 55) sprite[x,y] += 23;
if (i == 4) {
if (j == 0) {
if (x >= 29 || y >= 53) sprite[x,y] = 0;
}
if (j == 1) {
if (y >= 51) sprite[x,y] = 0;
}
if (j == 2) {
if (y >= 53) sprite[x,y] = 0;
}
}
}
}
sprite.save(frame);
}
}
for (uint i = 0; i < 5; i++) {
jjANIMATION@ animHatter = jjAnimations[jjAnimSets[ANIM::HATTER] + i];
for (uint j = 0; j < animHatter.frameCount; j++) {
jjANIMFRAME@ frame = jjAnimFrames[animHatter + j];
jjPIXELMAP sprite(frame);
for (uint x = 0; x < sprite.width; ++x) {
for (uint y = 0; y < sprite.height; ++y) {
sprite[x,y] = 254;
}
}
sprite.save(frame);
}
}
jjANIMATION@ animHatter = jjAnimations[jjAnimSets[ANIM::ROBOT] + 0];
for (uint j = 0; j < animHatter.frameCount; j++) {
jjANIMFRAME@ frame = jjAnimFrames[animHatter + j];
jjPIXELMAP sprite(frame);
for (uint x = 0; x < sprite.width; ++x) {
for (uint y = 0; y < sprite.height; ++y) {
if (sprite[x,y] >= 72 && sprite[x,y] <= 79) {
sprite[x,y] -= 40;
}
}
}
sprite.save(frame);
}
for (int i = 1; i < jjObjectCount; i++) {
if (jjObjects[i].eventID == OBJECT::HATTER) {
jjObjects[i].delete();
}
}
}
class EvilJazz : jjBEHAVIORINTERFACE {
int currAnim, fireRate, freezeTime, stompTime, hurtTime, blinkTime;
bool jump, blinking, dead;
void onBehave(jjOBJ@ obj) {
obj.determineCurAnim(ANIM::JAZZ, currAnim);
obj.determineCurFrame();
if (jjGameTicks % 7 == 0 && obj.freeze == 0 && currEJState != EJ_End) obj.frameID++;
if (!blinking) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, obj.freeze > 0? SPRITE::FROZEN : SPRITE::PLAYER, 31);
} else {
if (jjGameTicks % 10 / 5 == 0) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, obj.freeze > 0? SPRITE::FROZEN : SPRITE::PLAYER, 31);
}
for (int i = 0; i <= jjLocalPlayerCount; i++) {
jjLocalPlayers[i].boss = obj.objectID;
}
int playerID = obj.findNearestPlayer(500000);
jjPLAYER@ play = jjPlayers[playerID];
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
switch (jjDifficulty) {
case 0: fireRate = 28; break;
case 1: fireRate = 21; break;
case 2: fireRate = 14; break;
case 3: fireRate = 7;
}
if (obj.var[0] == 1) {
if (obj.energy > 0) {
if (!blinking) currEJState = Hurt;
} else {
currEJState = EJ_Die;
obj.frameID = 0;
obj.var[0] = 0;
jjMusicStop();
jjSample(play.xPos, play.yPos, SOUND::INTRO_BOEM1, 0, 32500);
}
if (blinking) {
obj.freeze = 0;
blinkTime++;
if (blinkTime == 140) {
obj.var[0] = 0;
blinkTime = 0;
blinking = false;
}
}
}
if (obj.freeze == 0) {
freezeTime = 0;
switch (currEJState) {
case Intro:
scene = 1;
obj.var[0] = 0;
currAnim = RABBIT::STAND;
blinking = false;
blinkTime = 0;
obj.direction = -1;
for (int i = 0; i < jjLocalPlayerCount; i++) {
jjLocalPlayers[i].limitXScroll(125,25);
}
break;
case Run:
if (obj.xSpeed == 0) {
currAnim = (play.keyDown && (!play.keyLeft || play.keyRight))? RABBIT::DIVEFIRERIGHT : RABBIT::FIRE;
} else {
if ((obj.direction == 1 && obj.xSpeed > 4) || (obj.direction == -1 && obj.xSpeed < -4)) {
currAnim = RABBIT::RUN2;
} else {
currAnim = RABBIT::RUN1;
}
}
obj.putOnGround(false);
if (obj.yPos > obj.yOrg) obj.yPos = obj.yOrg;
if (playerID >= -1) {
if (obj.freeze == 0) {
if ((play.xPos < obj.xPos - 256) || (play.xPos > obj.xPos + 256)) {
accelerate(obj, 6*obj.direction);
} else if (facingPlayer(obj, play)) {
decelerate(obj, 0);
}
shoot(obj, play);
if (play.xPos > obj.xPos + 48) obj.direction = 1;
else if (play.xPos < obj.xPos - 48) obj.direction = -1;
if (((play.yPos < int(obj.yPos - 96)) || ((play.charCurr == CHAR::SPAZ || play.charCurr == CHAR::LORI) && play.specialMove > 7)) && !jump) {
if ((obj.xPos < int(play.xPos + 64) && obj.xPos > int(play.xPos - 64))) {
currEJState = Uppercut;
stompTime = 0;
} else {
currEJState = Jump;
jump = true;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_JUMP);
}
}
if ((obj.xPos < int(play.xPos + 48) && obj.xPos > int(play.xPos - 48)) && play.ySpeed == 0) {
currEJState = Jump;
jump = true;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_JUMP);
}
}
}
break;
case Jump:
if (obj.ySpeed < -8.5) obj.ySpeed = -8.5;
if (playerID >= -1) {
if ((play.xPos < obj.xPos - 96) || (play.xPos > obj.xPos + 96)) accelerate(obj, 5.5*obj.direction);
else if (facingPlayer(obj, play)) decelerate(obj, 0);
if (play.xPos > obj.xPos + 48) obj.direction = 1;
else if (play.xPos < obj.xPos - 48) obj.direction = -1;
obj.ySpeed = obj.ySpeed + 0.225;
if (play.buttstomp == 41 && obj.yPos > play.yPos) obj.xSpeed = -2*obj.direction;
if (!jump && obj.ySpeed > 0 && obj.yPos > int(obj.yOrg - 10)) {
obj.putOnGround(false);
obj.ySpeed = 0;
obj.yPos = obj.yOrg;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_LANDPOP);
currEJState = Run;
}
if (!jump && !blinking && (obj.xPos < int(play.xPos + 8) && obj.xPos > int(play.xPos - 8)) && obj.yPos < play.yPos && obj.yPos > int(play.yPos - 224)) {
obj.xSpeed = obj.ySpeed = 0;
currEJState = Buttstomp;
stompTime = 0;
}
if ((play.xPos < obj.xPos - 224) || (play.xPos > obj.xPos + 224)) {
currAnim = obj.ySpeed < 0? RABBIT::ROLLING : RABBIT::RIGHTFALL;
} else {
shoot(obj, play);
currAnim = RABBIT::JUMPFIRERIGHT;
}
if (jump) {
obj.ySpeed = play.ySpeed == 0? -6 : (int(play.yPos-obj.yPos)/10);
jump = false;
}
}
break;
case Buttstomp:
stompTime++;
if (stompTime < 16 && obj.ySpeed == 0) {
currAnim = RABBIT::SPRING;
}
else {
currAnim = RABBIT::FALLBUTTSTOMP;
if (stompTime == 16) {
stompTime = 0;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_DOWN, 0, 32000);
}
obj.ySpeed = 8;
if (obj.yPos > int(obj.yOrg - 8)) {
obj.putOnGround(false);
obj.ySpeed = 0;
obj.yPos = obj.yOrg;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_LANDPOP);
currEJState = Run;
}
}
break;
case Uppercut:
stompTime++;
obj.xSpeed = 0;
if (stompTime < 14) {
currAnim = jjIsTSF? 64:79;
} else if (stompTime > 14 && stompTime < 40) {
currAnim = jjIsTSF? 62:77;
obj.ySpeed = -7;
} else if (stompTime > 40) {
currEJState = Jump;
stompTime = 0;
}
break;
case Hurt:
currAnim = RABBIT::HURT;
obj.freeze = 0;
if (!blinking) hurtTime++;
if (hurtTime == 1) {
randomHurtSounds(obj, jjRandom()%5);
}
if (hurtTime == 30) {
blinking = true;
hurtTime = 0;
currEJState = Jump;
} else if (hurtTime > 0) {
obj.xSpeed = -2 * obj.direction;
if (hurtTime < 14) obj.ySpeed = -2.5;
}
break;
case EJ_Die:
currAnim = RABBIT::DIE;
obj.xSpeed = 0;
obj.freeze = 0;
if (obj.yPos > int(obj.yOrg - 8)) {
obj.putOnGround(false);
obj.yPos = obj.yOrg;
obj.ySpeed = 0;
} else obj.ySpeed = 4;
if (obj.frameID >= 12) currEJState = EJ_End;
break;
case EJ_End:
obj.freeze = 0;
if (obj.frameID == 13 && jjGameTicks % 7 == 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_BENZIN1);
if (obj.frameID < 19) {
if (jjGameTicks % 7 == 0) obj.frameID++;
} else currAnim = RABBIT::CORPSE;
break;
}
} else {
obj.xSpeed = obj.ySpeed = 0;
freezeTime++;
if (freezeTime == 60) {
obj.unfreeze(1);
}
}
if (obj.xPos < 124*32) obj.xPos = 124*32;
if (obj.xPos > 151*32) obj.xPos = 151*32;
}
void shoot(jjOBJ@ obj, jjPLAYER@ play) {
if (jjGameTicks % fireRate == 0) {
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::BLASTERBULLET)];
bullet.killAnim = jjObjectPresets[OBJECT::BLASTERBULLET].killAnim;
bullet.playerHandling = HANDLING::ENEMYBULLET;
bullet.state = STATE::FLY;
bullet.animSpeed = 1;
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_GUNJAZZ);
}
}
void accelerate(jjOBJ@ obj, float speed) {
if (obj.direction == 1 && obj.xSpeed < speed) obj.xSpeed += 0.25;
else if (obj.direction == -1 && obj.xSpeed > speed) obj.xSpeed -= 0.25;
}
void decelerate(jjOBJ@ obj, float speed) {
if (obj.xSpeed > speed) obj.xSpeed -= 0.1;
else if (obj.xSpeed < speed) obj.xSpeed += 0.1;
if ((obj.direction == 1 && obj.xSpeed < 1) || (obj.direction == -1 && obj.xSpeed > -1)) {
obj.xSpeed = 0;
}
}
void randomHurtSounds(jjOBJ@ obj, int random) {
switch(random) {
case 0: jjSample(obj.xPos, obj.yPos, SOUND::JAZZSOUNDS_JAZZV1, 0, 10000); break;
case 1: jjSample(obj.xPos, obj.yPos, SOUND::JAZZSOUNDS_JAZZV2, 0, 10000); break;
case 2: jjSample(obj.xPos, obj.yPos, SOUND::JAZZSOUNDS_JAZZV3, 0, 10000); break;
case 3: jjSample(obj.xPos, obj.yPos, SOUND::JAZZSOUNDS_JAZZV4, 0, 10000); break;
case 4: jjSample(obj.xPos, obj.yPos, SOUND::JAZZSOUNDS_HEY4, 0, 10000); break;
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
if (bull !is null) {
if (bull.playerHandling == HANDLING::PLAYERBULLET && obj.var[0] == 0 && currEJState < EJ_Die && currEJState != Buttstomp && currEJState != Intro) {
obj.justHit = 5;
obj.energy -= (bull.var[6] == 8? 2:1);
obj.var[0] = 1;
if (obj.freeze > 0) {
obj.unfreeze(1);
}
}
if ((bull.var[6] & 16) == 0 && currEJState < EJ_Die) {
bull.state = STATE::EXPLODE;
}
} else if (play !is null) {
if (obj.var[0] == 0 && play.blink == 0 && force == 0 && currEJState < EJ_Die && (currEJState != Buttstomp || currEJState != Uppercut)) {
play.xAcc = 0;
play.xSpeed /= -2;
if (play.yPos < obj.yPos) play.ySpeed = -6;
}
if (currEJState == Buttstomp && obj.ySpeed > 0 && obj.yPos < play.yPos - 8 && !blinking) {
play.hurt(2, false);
obj.ySpeed = -8;
currEJState = Jump;
}
if (currEJState == Uppercut && play.yPos < obj.yPos && !blinking) {
play.hurt(2, play.buttstomp < 41 && play.blink == 0? true:false);
}
if (force != 0 && obj.var[0] == 0 && play.blink == 0 && currEJState < EJ_Die) {
obj.energy -= 2;
obj.var[0] = 1;
if (obj.freeze > 0) {
obj.unfreeze(1);
}
if (force > 0) {
play.buttstomp = 50;
play.ySpeed = play.ySpeed / -2 - 8;
play.yAcc = 0;
play.extendInvincibility(-70);
} else if (force == -101) {
play.xAcc = 0;
play.xSpeed /= -2;
play.ySpeed = -6;
play.extendInvincibility(-10);
}
}
}
return true;
}
}
class Devan : jjBEHAVIORINTERFACE {
ANIM::Set currSet;
int currAnim, warpTime;
bool draw;
void onBehave(jjOBJ@ devan) {
devan.determineCurAnim(currSet, currAnim);
devan.determineCurFrame();
if (jjGameTicks % 7 == 0 && devan.state != STATE::FREEZE) {
if (currAnim == 6 || currAnim == 10) devan.frameID--;
else devan.frameID++;
}
if (devan.var[0] == 1) devan.state = STATE::FREEZE;
switch (currDevanState) {
case Devan_Intro:
currSet = ANIM::DEVILDEVAN;
currAnim = 8;
devan.direction = -1;
if (cutscene && !outro) {
draw = true;
if (elapsed == 10) {
jjAlert("|||||||Devan: Atchoo! I have a cold, so I don't feel like fighting you right now.");
}
if (elapsed == 210) jjAlert("|||||||Devan: So I'll just have Evil Jazz do it for me. He's a bit bitey today...");
if (elapsed == 430) jjAlert("|||||||Devan: He'll keep you busy while I go get the device. Ho ho!");
if (elapsed == 630) {
cutscene = false;
devan.frameID = 7;
jjSamplePriority(SOUND::DEVILDEVAN_LAUGH);
jjSamplePriority(SOUND::COMMON_TELPORT1);
currDevanState = Warp_Out;
warpTime = 0;
currEJState = Run;
}
} else draw = false;
break;
case Warp_Out:
currAnim = 10;
warpTime++;
if (warpTime > 49 && currEJState < EJ_End) {
draw = false;
}
if (currEJState == EJ_End) {
if (warpTime == 210) {
for (int i = 0; i < jjObjectCount; i++) {
if (jjObjects[i].eventID == OBJECT::EGGPLANT) {
for (int j = 0; j < jjLocalPlayerCount; j++) {
givePlayerPointsForObject(jjLocalPlayers[j], jjObjects[i]);
jjLocalPlayers[j].bossActivated = false;
}
jjObjects[i].particlePixelExplosion(0);
jjObjects[i].delete();
}
}
}
if (warpTime == 350) {
draw = true;
cutscene = true;
jjSamplePriority(SOUND::COMMON_TELPORT2);
jjSamplePriority(SOUND::COMMON_BUBBLGN1);
currDevanState = Warp_In;
warpTime = 0;
devan.frameID = 6;
jjMusicLoad("Menur.mod");
}
} else if (currEJState == EJ_Die) {
warpTime = 0;
}
break;
case Warp_In:
currSet = ANIM::DEVAN;
currAnim = 6;
warpTime++;
for (int i = 0; i < jjLocalPlayerCount; i++) {
jjLocalPlayers[i].direction = devan.xPos > jjLocalPlayers[i].xPos? 1:-1;
devan.direction = devan.xPos > jjLocalPlayers[i].xPos? -1:1;
if (devan.xPos > int(jjLocalPlayers[i].xPos - 24) && devan.xPos < int(jjLocalPlayers[i].xPos + 24)) {
jjLocalPlayers[i].xSpeed = -10;
jjLocalPlayers[i].ySpeed = -3;
}
}
if (warpTime == 49) {
currDevanState = Device;
}
break;
case Device:
currAnim = 1;
if (cutscene && !outro) {
if (elapsed == 70) jjAlert("|||||||Devan: Your adventure ends here, Jackrabbit!");
if (elapsed == 280) jjAlert("|||||||Devan: Once I enable this device, my winter machine will activate at full power!");
if (elapsed == 490) jjAlert("|||||||Devan: At a push of a button, your pitiful world will be frozen for all eternity!");
if (elapsed == 700) jjAlert("|||||||Devan: Surrender now, or else Carrotus is finished!");
if (elapsed == 1190) jjAlert("|||||||Devan: Another silent protagonist, huh? Alright, prepare to freeze!");
if (elapsed == 1470) {
jjSamplePriority(SOUND::PINBALL_FLIP1);
jjMusicStop();
}
if (elapsed == 1680) {
jjSamplePriority(SOUND::PINBALL_FLIP1);
}
if (elapsed == 1750) {
jjSamplePriority(SOUND::PINBALL_FLIP1);
}
if (elapsed > 1750 && elapsed < 2100 && elapsed % 14 == 0) {
jjSamplePriority(SOUND::PINBALL_FLIP1);
}
if (elapsed == 1998) jjAlert("|||||||Devan: Stupid hunk of junk! Why won't you work?");
if (elapsed == 2170) {
jjSamplePriority(SOUND::PINBALL_FLIP1);
snowblind = true;
for (int i = 0; i < jjLocalPlayerCount; i++) {
jjLocalPlayers[i].lighting = 255;
}
}
if (elapsed == 2730) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~I HAVE BEEN @@@LIBERATED...", STRING::LARGE);
for (int i = 0; i < jjLocalPlayerCount; i++) {
jjLocalPlayers[i].lighting = 80;
jjSetDarknessColor(jjPALCOLOR(64,128,255));
jjLocalPlayers[i].xPos = (136*32)+(16*i);
jjLocalPlayers[i].direction = 1;
}
devan.direction = -1;
}
if (elapsed == 3080) {
scene = 2;
currDRAKState = Reveal;
jjLocalPlayers[0].showText("@@@@@@#Who are you?");
}
if (elapsed == 4310) {
jjAlert("|||||||Devan: My winter machine was using only a fraction of your full power.");
}
if (elapsed == 4520) {
jjAlert("|||||||Devan: I have used the machine to shatter your crystal and hence release you.");
}
if (elapsed == 4730) {
jjAlert("|||||||Devan: You may call me master. Now, freeze that rabbit, my servant!");
}
}
break;
}
if (draw) jjDrawSpriteFromCurFrame(devan.xPos, devan.yPos, devan.curFrame, devan.direction, devan.state == STATE::FREEZE? SPRITE::FROZEN : SPRITE::NORMAL);
devan.putOnGround(true);
}
}
class Drak : jjBEHAVIORINTERFACE {
int currAnim, currLeftHandAim, currRightHandAim;
int delayTime, attackTime, attackDelay, randcar, position;
bool draw, firstAttack;
void onBehave(jjOBJ@ obj) {
obj.determineCurAnim(ANIM::CUSTOM[10], currAnim);
obj.determineCurFrame();
obj.counter++;
if (jjGameTicks % 7 == 0 && currDRAKState < Complete) {
obj.frameID++;
}
obj.energy = int(obj.age / healthFactor);
if (obj.energy <= 0) {
currDRAKState = Complete;
}
attackDelay = obj.energy < 50? 95:175;
switch (jjDifficulty) {
case 0: randcar = 25; break;
case 1: randcar = 50; break;
case 2: randcar = 100; break;
default: randcar = 100; break;
}
int playerID = obj.findNearestPlayer(1000000);
jjPLAYER@ play = jjPlayers[playerID];
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
if (currDRAKState > Awaiting) {
for (int i = 0; i <= jjLocalPlayerCount; i++) {
jjLocalPlayers[i].boss = obj.objectID;
}
}
if (draw) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : SPRITE::NORMAL, 15);
if (obj.direction == 1) {
jjDrawSprite(obj.xPos + 9, obj.yPos - 9, ANIM::CUSTOM[10], 4, currLeftHandAim, obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : SPRITE::NORMAL, 15);
jjDrawSprite(obj.xPos - 24, obj.yPos - 11, ANIM::CUSTOM[10], 3, currRightHandAim, obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : SPRITE::NORMAL, 15);
} else if (obj.direction == -1) {
jjDrawSprite(obj.xPos - 9, obj.yPos - 9, ANIM::CUSTOM[10], 4, currLeftHandAim, obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : SPRITE::NORMAL, 15);
jjDrawSprite(obj.xPos + 24, obj.yPos - 11, ANIM::CUSTOM[10], 3, currRightHandAim, obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : SPRITE::NORMAL, 15);
}
obj.isTarget = true;
}
if (play.xPos > int(obj.xPos+32)) obj.direction = 1;
else if (play.xPos < int(obj.xPos-32)) obj.direction = -1;
int flightAngle = int(atan2(obj.xPos - int(play.xPos + (128*obj.direction)), obj.yPos - int(play.yPos - 160)) * (512 / PI));
int attackAngle = int(atan2(obj.xPos - play.xPos, obj.yPos - play.yPos) * (512 / PI));
obj.freeze = 0;
switch (currDRAKState) {
case Hidden:
draw = false;
currAnim = 0;
break;
case Reveal:
draw = true;
obj.direction = -1;
currLeftHandAim = 0;
currRightHandAim = 1;
position = jjResolutionHeight <= 480? 63*32 : 60*32;
if (obj.yPos < position) {
obj.ySpeed = 1;
} else {
obj.ySpeed = 0;
}
if (jjResolutionHeight <= 480) obj.xPos = obj.xOrg - 16;
obj.isTarget = true;
if (cutscene) {
if (elapsed == 3400) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~I AM AN@@@ICE DEMON.", STRING::LARGE);
}
if (elapsed == 3750) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~I HAVE BEEN FREED@@@FROM MY PRISON.", STRING::LARGE);
}
if (elapsed == 4100) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~WHO HAS@@@SUMMONED ME?", STRING::LARGE);
}
if (elapsed == 5080) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~YOU SHALL@@@PERISH, FOOL!", STRING::LARGE);
}
if (elapsed == 5220) {
currDRAKState = Freeze_Devan;
}
if (elapsed == 5480) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~SO YOU ARE THE@@@ONE WHO OPPOSES ME...", STRING::LARGE);
}
if (elapsed == 5830) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~VERY WELL.@@@YOU INTRIGUE ME.", STRING::LARGE);
}
if (elapsed == 6180) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~HOWEVER, I CANNOT@@@ALLOW YOU TO LIVE.", STRING::LARGE);
}
if (elapsed == 6530) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~I WANT TO START@@@A NEW ICE AGE.", STRING::LARGE);
}
if (elapsed == 6880) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~AN ICE AGE THAT@@@LASTS FOREVER.", STRING::LARGE);
}
if (elapsed == 7230) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~AS YOU DEFY ME,@@@I AM FORCED@@@TO DESTROY YOU.", STRING::LARGE);
}
if (elapsed == 7580) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~HOWEVER, I WILL@@@LET YOU FIGHT ME@@@AT FULL POWER.", STRING::LARGE);
}
if (elapsed == 7930) {
jjLocalPlayers[0].showText("@@@@@@@@@#|||||~STEP INTO THE PORTAL@@@AND CONFRONT YOUR FATE.", STRING::LARGE);
currDRAKState = Fade_Out;
}
}
break;
case Freeze_Devan:
if (elapsed < 5410 && elapsed % 8 == 0) {
currLeftHandAim = 2;
currRightHandAim = 0;
IceCloud temp;
for (int i = 0; i < 2; i++) {
jjOBJ@ freeze = jjObjects[jjAddObject(OBJECT::BULLET, i == 0? int(obj.xPos - 32): int(obj.xPos + 16), i == 0? int(obj.yPos - 9) : int(obj.yPos - 11), obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
freeze.state = STATE::FLY;
freeze.playerHandling = HANDLING::ENEMYBULLET;
freeze.counterEnd = 210;
freeze.xAcc = 0;
for (int j = 1; j < jjObjectCount; j++) {
jjOBJ@ devan = jjObjects[j];
if (devan.eventID == OBJECT::THING) {
int devanAngle = int(atan2(freeze.xPos - devan.xPos, freeze.yPos - devan.yPos) * (512 / PI));
freeze.xSpeed = -5 * jjSin(devanAngle);
freeze.ySpeed = -5 * jjCos(devanAngle);
jjSamplePriority(SOUND::AMMO_ICEGUN);
}
}
}
} else if (elapsed > 5410) {
currDRAKState = Reveal;
}
break;
case Fade_Out:
if (elapsed >= 7930 && elapsed <= 8335) {
draw = false;
if (elapsed % 8 == 0) {
jjOBJ@ glitter = jjObjects[jjAddObject(OBJECT::EXPLOSION, int(obj.xPos - 40) + jjRandom()%80, int(obj.yPos + 40) - jjRandom()%80)];
glitter.determineCurAnim(ANIM::PICKUPS, 86);
}
} else if (elapsed > 8335) {
cutscene = false;
obj.delete();
}
break;
case Awaiting:
draw = false;
delayTime = attackTime = 0;
transition = true;
firstAttack = false;
break;
case Begin:
currLeftHandAim = 0;
currRightHandAim = 1;
draw = true;
obj.direction = -1;
obj.yPos += jjCos(obj.counter*6);
delayTime++;
if (delayTime == 70) {
snowblind = transition = false;
climax = true;
currDRAKState = Fly;
jjMusicLoad("final_transition.xm");
delayTime = attackTime = 0;
}
break;
case Fly:
obj.yPos += jjCos(obj.counter*6);
moveIntoXPosition(obj, play, 3, flightAngle);
moveIntoYPosition(obj, play, 2, flightAngle);
attackTime++;
if (attackTime >= attackDelay) {
if (!firstAttack) {
firstAttack = true;
attackTime = 0;
currDRAKState = Icicles;
}
else {
randomAttack(obj);
}
}
currLeftHandAim = 0;
currRightHandAim = 1;
break;
case Icicles:
attackStance(obj);
if (attackTime >= 35 && attackTime < 280) {
obj.ySpeed = -0.25;
if (attackTime % 28 == 0) {
Icicle temp;
for (int i = 0; i < 2; i++) {
jjOBJ@ icicle = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, i == 0? int(obj.xPos - (32*obj.direction)): int(obj.xPos + (16*obj.direction)), i == 0? int(obj.yPos - 9) : int(obj.yPos - 11), obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
icicle.determineCurAnim(ANIM::CUSTOM[11], 2);
icicle.state = STATE::FLY;
icicle.playerHandling = HANDLING::ENEMYBULLET;
icicle.counterEnd = 210;
icicle.xSpeed = -6 * jjSin(attackAngle);
icicle.ySpeed = -6 * jjCos(attackAngle);
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_ICEPU2);
}
}
} else if (attackTime == 280) {
currDRAKState = Fly;
attackTime = 0;
}
break;
case Freeze_Cloud:
attackStance(obj);
if (attackTime >= 35 && attackTime < 280) {
obj.xSpeed = -2 * jjSin(attackAngle);
obj.ySpeed = -1.5 * jjCos(attackAngle);
if (attackTime % 10 == 0) {
IceCloud temp;
for (int i = 0; i < 2; i++) {
jjOBJ@ freeze = jjObjects[jjAddObject(OBJECT::BULLET, i == 0? int(obj.xPos - (32*obj.direction)): int(obj.xPos + (16*obj.direction)), i == 0? int(obj.yPos - 9) : int(obj.yPos - 11), obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
freeze.state = STATE::FLY;
freeze.playerHandling = HANDLING::SPECIAL;
freeze.scriptedCollisions = true;
freeze.counterEnd = 210;
freeze.xAcc = 0;
freeze.xSpeed = -4 * jjSin(attackAngle);
freeze.ySpeed = -4 * jjCos(attackAngle);
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_ICEGUN);
}
}
} else if (attackTime == 280) {
currDRAKState = Fly;
attackTime = 0;
}
break;
case Ice_Bomb:
attackStance(obj);
obj.yPos += jjCos(obj.counter*4);
moveIntoXPosition(obj, play, 1, flightAngle);
moveIntoYPosition(obj, play, 0.5, flightAngle);
if (attackTime >= 35 && attackTime < 350) {
if (attackTime % 65 == 0) {
IceBomb temp;
jjOBJ@ bomb = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, obj.xPos, int(obj.yPos - 10), obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
bomb.determineCurAnim(ANIM::CUSTOM[12], 2);
bomb.state = STATE::FLY;
bomb.playerHandling = HANDLING::ENEMYBULLET;
bomb.counterEnd = 60;
bomb.direction = obj.direction;
bomb.xSpeed = -4 * jjSin(attackAngle);
bomb.ySpeed = -3 - abs(jjRandom()%2);
bomb.xAcc = 0.05*obj.direction;
bomb.yAcc = 0.15;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_SWISH6);
}
} else if (attackTime == 350) {
currDRAKState = Fly;
attackTime = 0;
}
break;
case Summon_Enemies:
attackStance(obj);
if (attackTime == 35) {
jjOBJ@ spawn = jjObjects[jjAddObject(OBJECT::EXPLOSION, int(obj.xPos + (96 * obj.direction)), int(obj.yPos + 32), obj.objectID, CREATOR::OBJECT)];
spawn.determineCurAnim(ANIM::PICKUPS, 86);
spawn.frameID = 0;
jjSample(spawn.xPos, spawn.yPos, SOUND::COMMON_ITEMTRE);
jjOBJ@ enemy = jjObjects[jjAddObject(OBJECT::HATTER, int(obj.xPos + (96 * obj.direction)), int(obj.yPos + 38), obj.objectID, CREATOR::OBJECT)];
enemy.determineCurAnim(ANIM::CUSTOM[23], 0);
enemy.determineCurFrame();
enemy.energy = 2+jjDifficulty;
enemy.frameID = 1;
enemy.state = STATE::START;
currDRAKState = Fly;
attackTime = 0;
}
break;
case Homing_Ice:
attackStance(obj);
obj.yPos += jjCos(obj.counter*4);
if (attackTime == 70) {
HomingIce temp;
jjOBJ@ ice = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos + (64*obj.direction)), int(obj.yPos - 10), obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
ice.determineCurAnim(ANIM::ROBOT, 0);
ice.state = STATE::FLY;
ice.playerHandling = HANDLING::ENEMYBULLET;
ice.bulletHandling = HANDLING::DESTROYBULLET;
ice.counterEnd = 255;
ice.direction = obj.direction;
ice.xAcc = ice.yAcc = 0;
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_ICEPU4);
currDRAKState = Fly;
attackTime = 0;
}
break;
case Raining_Icicles:
attackStance(obj);
obj.yPos += jjCos(obj.counter*6);
if (attackTime == 104) jjSamplePriority(SOUND::FAN_FAN);
if (attackTime >= 105 && attackTime < 350) {
if (jjRandom()%randcar < (randcar - 1)) {
if (attackTime % 4 == 0) {
Icicle temp;
jjOBJ@ icicle = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, (3712 + jjRandom()%1056), 12*32, obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
icicle.xSpeed = 0;
icicle.ySpeed = 6;
icicle.direction = 1;
icicle.counterEnd = 200;
icicle.state = STATE::FLY;
icicle.playerHandling = HANDLING::ENEMYBULLET;
}
} else {
if (attackTime % 4 == 0) {
jjOBJ@ carrot = jjObjects[jjAddObject(OBJECT::CARROT, (3712 + jjRandom()%1056), 12*32, obj.objectID, CREATOR::OBJECT)];
carrot.state = STATE::FLOATFALL;
}
}
} else if (attackTime == 350) {
currDRAKState = Fly;
attackTime = 0;
}
break;
case Icicle_Burst:
attackStance(obj);
if (attackTime >= 55 && attackTime < 350) {
if (play.xPos > int(obj.xPos - 96) && play.xPos < int(obj.xPos + 96)) obj.xSpeed = -1 * obj.direction;
moveIntoYPosition(obj, play, 0.75, flightAngle);
if (obj.xPos > 150*32) obj.xPos = 150*32;
obj.yPos += jjCos(obj.counter*4);
if (attackTime % 70 == 0) {
Icicle temp;
for (int i = 0; i < 5; i++) {
jjOBJ@ icicle = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos + (32*obj.direction)), int(obj.yPos - 10), obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
icicle.determineCurAnim(ANIM::CUSTOM[11], 2);
icicle.state = STATE::FLY;
icicle.playerHandling = HANDLING::ENEMYBULLET;
icicle.counterEnd = 210;
icicle.xSpeed = 7 * obj.direction;
icicle.ySpeed = i == 0? -3 : i == 1? -1 : i == 2? 0 : i == 3? 1 : 3;
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_ICEPU2);
}
}
} else if (attackTime == 350) {
currDRAKState = Fly;
attackTime = 0;
}
break;
case Crystals:
attackStance(obj);
sample = jjSampleLooped(play.xPos, play.yPos, SOUND::COMMON_AIRBOARD, sample, 25000, 0);
if (attackTime > 70 && attackTime < 700) {
if (attackTime % 28 == 0) {
IceCrystal temp;
jjOBJ@ crystal = jjObjects[jjAddObject(OBJECT::BULLET, play.xPos, 35*32, obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
crystal.xSpeed = 0;
crystal.ySpeed = -2.75;
crystal.direction = 1;
crystal.state = STATE::FLY;
crystal.playerHandling = HANDLING::ENEMYBULLET;
crystal.bulletHandling = HANDLING::DESTROYBULLET;
}
} else if (attackTime == 700) {
currDRAKState = Fly;
attackTime = 0;
}
break;
case Complete:
obj.xSpeed = obj.ySpeed = 0;
outro = true;
if (elapsed == 10) {
for (int i = 0; i < jjLocalPlayerCount; i++) {
givePlayerPointsForObject(jjLocalPlayers[i], obj);
}
}
if (elapsed >= 350) {
jjDrawResizedSprite(int(obj.xPos - (16*obj.direction)), obj.yPos, ANIM::CUSTOM[13], 8, 0, 2, 2, SPRITE::TRANSLUCENT);
}
break;
}
}
void moveIntoXPosition(jjOBJ@ obj, jjPLAYER@ play, float speed, int angle) {
if (obj.xPos > int(play.xPos + 140) || obj.xPos < int(play.xPos - 140)) {
obj.xSpeed = -speed * jjSin(angle);
} else {
obj.xSpeed = 0;
}
}
void moveIntoYPosition(jjOBJ@ obj, jjPLAYER@ play, float speed, int angle) {
if (obj.yPos < int(play.yPos - 176) || obj.yPos > int(play.yPos - 64)) {
obj.ySpeed = -speed * jjCos(angle);
} else {
obj.ySpeed = 0;
}
}
void attackStance(jjOBJ@ obj) {
currLeftHandAim = 2;
currRightHandAim = 0;
attackTime++;
if (attackTime < 35) obj.xSpeed = obj.ySpeed = 0;
}
void randomAttack(jjOBJ@ obj) {
if (obj.energy >= 50) {
currDRAKState = getNextAttack(highHPQueue, highHPAttacks);
} else {
currDRAKState = getNextAttack(lowHPQueue, lowHPAttacks);
}
attackTime = 0;
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
if (bull !is null) {
if (bull.playerHandling == HANDLING::PLAYERBULLET && currDRAKState > Begin) {
obj.justHit = 5;
if (bull.var[3] == 5) {
obj.age -= 1;
} else {
obj.age -= bull.animSpeed;
}
}
if ((bull.var[6] & 16) == 0) {
bull.state = STATE::EXPLODE;
}
} else if (play !is null) {
play.hurt(1, false);
}
return true;
}
}
class IceCloud : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.counter++;
for (int i = 1; i < jjObjectCount; i++) {
if (jjObjects[i].eventID == OBJECT::THING && obj.doesCollide(jjObjects[i], true)) {
jjObjects[i].var[0] = 1;
obj.delete();
}
}
int playerID = obj.findNearestPlayer(1000);
if (playerID > -1) {
if (obj.doesCollide(jjPlayers[playerID], true)) jjPlayers[playerID].freeze(true);
}
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
if (obj.counter == int(obj.counterEnd)) obj.delete();
obj.determineCurAnim(ANIM::AMMO, 82);
obj.determineCurFrame();
if (jjGameTicks % 7 == 0) obj.frameID++;
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::FROZEN);
}
}
class Icicle : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::BULLET, false);
obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
if (obj.state == STATE::EXPLODE) {
obj.unfreeze(1);
obj.delete();
} else {
obj.xAcc = 0;
obj.yAcc = 0;
jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[11], 2, 0, obj.var[0], 1, 1, SPRITE::NORMAL);
}
}
}
class IceBomb : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::BULLET, true);
if (obj.state == STATE::EXPLODE && obj.isActive) {
obj.unfreeze(1);
for (int i = 0; i < 8; i++) {
int bulletID3 = jjAddObject(OBJECT::BLASTERBULLET, obj.xPos, obj.yPos, obj.objectID, CREATOR::OBJECT);
jjOBJ @ o = jjObjects[bulletID3];
o.determineCurAnim(ANIM::CUSTOM[12], 3);
o.direction = obj.direction;
o.ySpeed = 3*sin(i*PI/4);
o.yAcc=0;
o.xAcc=0;
o.xSpeed = 3 * cos(i*PI/4);
o.killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
o.playerHandling = HANDLING::ENEMYBULLET;
o.counterEnd = 90;
}
obj.delete();
}
}
}
class HomingIce : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::BULLET, false);
int playerID = obj.findNearestPlayer(1000000);
int angle = int(atan2(obj.xPos - jjPlayers[playerID].xPos, obj.yPos - jjPlayers[playerID].yPos) * (512 / PI));
obj.xSpeed = -4 * jjSin(angle);
obj.ySpeed = -4 * jjCos(angle);
if (obj.state == STATE::EXPLODE) {
obj.unfreeze(1);
obj.delete();
} else {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
}
}
}
class IceCrystal : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.determineCurAnim(ANIM::CUSTOM[13], 8);
obj.determineCurFrame();
obj.yPos = obj.yPos + obj.ySpeed;
if (obj.yPos < 10*32) obj.delete();
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TRANSLUCENT);
}
}
const array<DRAKStates> highHPAttacks = {Icicles, Freeze_Cloud, Ice_Bomb, Summon_Enemies, Homing_Ice};
const array<DRAKStates> lowHPAttacks = {Icicles, Freeze_Cloud, Ice_Bomb, Summon_Enemies, Homing_Ice, Raining_Icicles, Icicle_Burst, Crystals};
array<DRAKStates> highHPQueue;
array<DRAKStates> lowHPQueue;
void shuffle(array<DRAKStates>& input) {
for (int i = input.length(); i != 0;) {
int index = jjRandom() % i;
i--;
DRAKStates temp = input[index];
input[index] = input[i];
input[i] = temp;
}
}
DRAKStates pop(array<DRAKStates>& input) {
DRAKStates result = input[input.length() - 1];
input.removeLast();
return result;
}
DRAKStates getNextAttack(array<DRAKStates>& queue, const array<DRAKStates>& reserve) {
if (queue.isEmpty()) {
queue = reserve;
shuffle(queue);
}
return pop(queue);
}
class Portal : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.yPos = currDRAKState == Fade_Out? obj.yOrg:78*32;
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
if (play !is null) {
if (obj.var[0] == 0) {
play.warpToID(1, true);
play.cameraFreeze(78*32, jjResolutionHeight <= 480? 15*32 : 12*32, false, true);
play.limitXScroll(78, 75);
play.xSpeed = 0;
play.ySpeed = 4;
obj.var[0] = 1;
jjSamplePriority(SOUND::COMMON_TELPORT2);
}
}
return true;
}
}
bool givePlayerPointsForObject(jjPLAYER@ player, jjOBJ@ obj) { //This will probably be made a jjOBJ method as part of the real JJ2+ API eventually, because it shows up all the time in the native code, but that hasn't happened yet, so here you go. Increases the player's jjPLAYER::score to match the object's jjOBJ::points, and creates a string particle with that number which flies up to the top left corner of the screen.
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; //Ensures that JJ2 won't end up increasing the player's score TWICE, since a call to (the native version of) this function is actually part of the standard pickup-handling code and would otherwise therefore get you in trouble with the FiveUp onObjectHit code.
return true;
}
return false;
}
class SnowEffect : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::STEADYLIGHT);
obj.deactivates = false;
obj.light = snowblind? 127:-127;
obj.xPos = jjLocalPlayers[0].cameraX + 400;
obj.yPos = jjLocalPlayers[0].cameraY + 300;
}
}
void onPlayer(jjPLAYER@ play) {
gem::trackPlayerGems(play);
gem::upgradeHealth(play);
if (cutscene) {
play.keyLeft = play.keyRight = play.keyDown = play.keyJump = play.keyFire = play.keyRun = false;
play.keyUp = scene == 2? true:false;
play.idle = 0;
jjCharacters[play.charCurr].canRun = false;
elapsed++;
play.invincibility = -7;
} else {
elapsed = 0;
if (play.charCurr == CHAR::JAZZ || play.charCurr == CHAR::SPAZ || play.charCurr == CHAR::LORI) jjCharacters[play.charCurr].canRun = true;
}
gem::trackPlayerGems(play);
gem::upgradeHealth(play);
array<jjLAYER@> layers = jjLayerOrderGet();
if (snowblind) {
play.lightType = LIGHT::NONE;
sample = jjSampleLooped(play.xPos, play.yPos, SOUND::SCIENCE_PLOPKAOS, sample, 32, 0);
for (uint i = 0; i < layers.length(); i++) {
if (i != 15) layers[i].hasTiles = false;
else layers[i].hasTiles = true;
}
jjMusicStop();
} else {
play.lightType = LIGHT::PLAYER;
play.light = 12;
if (climax) {
for (uint i = 0; i < layers.length(); i++) {
if (i != 16) layers[i].hasTiles = false;
else layers[i].hasTiles = true;
}
} else {
for (uint i = 0; i < layers.length(); i++) {
if (i == 15) layers[i].hasTiles = false;
else layers[i].hasTiles = true;
}
}
jjSetDarknessColor(jjPALCOLOR(0,0,0));
}
if (transition && currDRAKState == Begin) {
play.cameraUnfreeze(false);
play.cameraFreeze(true, jjResolutionHeight <= 480? 15*32 : 12*32, false, true);
}
if (climax) play.lighting = 100;
if (outro) {
jjMusicStop();
cutscene = true;
scene = 0;
if (elapsed == 10) {
climax = false;
play.lighting = 255;
}
if (elapsed == 70) {
play.showText("@@@@@@@@@#|||||~ARGHHH!!!", STRING::LARGE);
}
if (elapsed == 350) {
done = true;
outro = false;
play.lighting = 60;
play.showText("@@@@@@#The Ice Demon has been re-sealed.@Carrotus is saved!");
}
if (elapsed == 700) {
jjNxt();
}
}
if (currDRAKState == Fade_Out && play.yPos < 30*32) {
currDRAKState = Awaiting;
play.cameraFreeze(true, false, false, true);
}
if (currDRAKState < Begin && jjGameTicks > 1) {
for (int i = 1; i < jjObjectCount; i++) {
if (jjObjects[i].eventID == OBJECT::HATTER) {
jjObjects[i].delete();
}
}
}
if (play.health == 0) {
limitScrollBeforeDrak = false;
if (currDRAKState > Awaiting) currDRAKState = Awaiting;
}
if (refresh) {
play.cameraFreeze(play.xOrg, play.yOrg, true, true);
play.xPos = 5*32;
play.yPos = 26*32;
count++;
if (count == 2) {
play.cameraUnfreeze();
play.xPos = play.xOrg;
play.yPos = play.yOrg;
refresh = false;
count = 0;
if (currDRAKState == Awaiting) {
play.cameraFreeze(true, jjResolutionHeight <= 480? 15*32 : 12*32, false, true);
if (!limitScrollBeforeDrak) {
snowblind = true;
jjSetDarknessColor(jjPALCOLOR(64,128,255));
play.cameraFreeze(78*32, jjResolutionHeight <= 480? 15*32 : 12*32, false, true);
play.cameraFreeze(true, jjResolutionHeight <= 480? 15*32 : 12*32, false, true);
play.limitXScroll(78, 75);
limitScrollBeforeDrak = true;
}
play.lighting = 80;
} else {
jjSetDarknessColor(jjPALCOLOR(0,0,0));
}
}
}
jjEnforceLighting = snowblind || transition || outro? LIGHT::COMPLETE : LIGHT::OPTIONAL;
}
bool onDrawLives(jjPLAYER@ play, jjCANVAS@ canvas) { return true; }
void onMain() {
jjPlayers[31].furSet(72, 40, 24, 32);
jjTexturedBGTexture = climax? TEXTURE::WTF : TEXTURE::LAYER8;
jjTexturedBGStyle = climax? TEXTURE::TUNNEL : TEXTURE::WARPHORIZON;
jjLayerXSpeed[8] = climax? 0:0.05f;
jjLayerXAutoSpeed[8] = climax? 0.2f:0;
jjLayerYAutoSpeed[8] = climax? 0.4f:0;
jjTexturedBGStars = climax? true:false;
gem::deleteCollectedGems();
gem::draw = false;
}
void onFunction0(jjPLAYER@ play) {
if (currEJState == Intro) {
cutscene = true;
jjMusicLoad("boss.s3m");
play.activateBoss(true);
play.cameraFreeze(137.5*32, jjResolutionHeight <= 480? 69.75*32 : 66.75*32, true, false);
}
}
void onFunction1(jjPLAYER@ play) {
if (currDRAKState == Awaiting) {
play.cameraFreeze(115*32, false, true, false);
play.limitXScroll(115, 50);
play.activateBoss(true);
play.lighting = 255;
currDRAKState = Begin;
}
}
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.