Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
![]() |
Holiday Hare 24![]() |
PurpleJazz | Single player | 10 | ![]() |
const bool MLLESetupSuccessful = MLLE::Setup(array<MLLEWeaponApply@> = {null, null, null, null, null, null, null, null, WeaponVMega::Voranj::Weapon()}); ///@MLLE-Generated
#include "MLLE-Include-1.7w.asc" ///@MLLE-Generated
#pragma require "HH24_level06-MLLE-Data-3.j2l" ///@MLLE-Generated
#pragma require "HH24_level06-MLLE-Data-2.j2l" ///@MLLE-Generated
#pragma require "HH24_level06-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "Waz18.j2t" ///@MLLE-Generated
#pragma require "Medivo.j2t" ///@MLLE-Generated
#pragma require "BioWinter.j2t" ///@MLLE-Generated
#pragma require "Aztec2.j2t" ///@MLLE-Generated
#pragma require "SSWorlds_SET7.j2t" ///@MLLE-Generated
#pragma require "HH24_level06.j2l" ///@MLLE-Generated
#include "WeaponVMega8.asc" ///@MLLE-Generated
#pragma require "WeaponVMega8.asc" ///@MLLE-Generated
#pragma require "hh17enemies_hh24.asc"
#include "HH18Smoke_HH24.asc"
#include "HH24IceGolem.asc"
#include "hh17enemies_HH24.asc"
#include "HH24.asc"
#include "Resize v11.asc"
#include "HH24_IceKnightEnemy.asc"
#pragma require "SExmas.j2a"
#pragma require "xmasTufTurt.j2a"
#pragma require "HH24.j2a"
#pragma require "HH24_Cold_Wind.ogg"
#pragma require "HH24_FallSound.ogg"
#pragma require "HH24_Sword.wav"
#pragma require "Sword.j2a"
#pragma require "HH17_Icicle.j2a"
#pragma require "SEhh24.j2a"
#pragma require "SEhh24dryfire.wav"
#pragma require "SEhh24launcher1.wav"
#pragma require "SEhh24launcher2.wav"
#pragma require "SEhh24launcherswitch.wav"
#pragma require "SEhh24secret.wav"
#pragma require "SEhh24spikeball1.wav"
#pragma require "SEhh24spikeball2.wav"
#pragma require "SEwaterbubble.png"
//#include "limitedoxygen.asc"
///@Event 7=H-Pole |+|Object|H-Pole | |Delay:7
///@Event 48=Present |+|Object |Xmas |Present |Event:8 |Number: 4 |Colour: 4
///@Event 107=Fire Dragon |-|Enemy |Fire |Dragon |Colour: 8
///@Event 118=Ice Knight |-|Enemy |Ice |Knight
///@Event 120=Polar Bear |-|Enemy |Polar |Bear
///@Event 123=Santa Ghost |-|Enemy |Santa |Ghost
///@Event 125=Ice Golem |-|Enemy |Ice |Golem
///@Event 142=Spike Orb |+|Object |Spike |Orb
///@Event 146=Fudge |+|Food |Fudge
///@Event 154=Swiss Roll |+|Food |Swiss |Roll
///@Event 160=Lollipop |+|Food |Lolli |-Pop
///@Event 161=Candy Bar |+|Food |Candy |Bar
///@Event 162=White Choc Bar |+|Food |White |Choc
///@Event 163=Eggnog |+|Food |Egg |Nog
///@Event 166=Apple Pie |+|Food |Apple |Pie
///@Event 170=Cookie |+|Food |Cookie
///@Event 175=Green Candy Cane |+|Food |Green |Cane
///@Event 176=Red Candy Cane |+|Food |Red |Cane
///@Event 177=Caramel |+|Food |Cara- |Mel
///@Event 178=Brownie |+|Food |Brownie
///@Event 191=Penguin |-|Enemy |Pen- |Guin
///@Event 232=Bubble Launcher |+|Object |Bubble |Launch |Angle 1:3 |Angle 2:3 |Switch: 4
///@Event 254=Delete This |-|Modifier |NOT |v
//bool showColdWaterMessage = false;
array<bool> SavedTriggers(32, false);
bool iceGolemFight, reachedCastle = false;
bool startQueenBattle = false;
bool splitscreen = jjLocalPlayerCount > 1;
const int GolemMaxHP = 60+(jjDifficulty*20);
int sample, sample2 = 0;
int swordSample = 0;
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
if (fallDownHole) {
canvas.drawRotatedSprite(int(jjSubscreenWidth/2), int(jjSubscreenHeight/2), fallAnimSet, 11, jjGameTicks >> 2, -fallAngle, fallScale, fallScale, SPRITE::PLAYER, player.playerID);
}
return MLLE::WeaponHook.drawAmmo(player, canvas);
}
bool fallDownHole, cycleOnce = false;
int fallAngle = 0;
float fallScale = 10;
ANIM::Set fallAnimSet;
int fallAnim;
namespace sound {
const SOUND::Sample spikeballHit = SOUND::INTRO_BOEM1;
const SOUND::Sample spikeballRecover = SOUND::INTRO_BOEM2;
const SOUND::Sample launcherSwitch = SOUND::INTRO_BRAKE;
const SOUND::Sample dryFire = SOUND::INTRO_END;
const SOUND::Sample secret = SOUND::INTRO_GRAB;
const SOUND::Sample launcherHit = SOUND::INTRO_GREN1;
const SOUND::Sample launcherFire = SOUND::INTRO_GREN2;
}
enum CustomAnim {
anim_bubble,
anim_rope,
anim_launcher,
anim_spike_ball,
}
enum BubbleVar {
bubble_size,
bubble_max_size
}
enum LauncherVar {
launcher_angle_1,
launcher_angle_2,
launcher_angle_target,
launcher_angle_alt,
launcher_angle_displayed,
launcher_angular_velocity,
launcher_state,
launcher_liquid_deficit
}
enum LauncherSwitchVar {
launcher_switch_rope_length,
launcher_switch_rope_frame
}
class Bubble : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) override {
obj.state = STATE::FLOAT;
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
float squish = 1.f + 0.075f * jjSin(obj.counter);
obj.counter += 9;
if (obj.var[bubble_size] < obj.var[bubble_max_size])
obj.var[bubble_size] = obj.var[bubble_size] + 1;
float scale = 1.f * obj.var[bubble_size] / obj.var[bubble_max_size];
if (isOnScreen(obj)) {
if (jjColorDepth != 8) {
TrueColor::DrawResizedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[anim_bubble], 0, 0, scale * squish, scale / squish, 3);
} else {
for (int i = 0; i < 3; i++) {
jjDrawResizedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[anim_bubble], 0, i, scale * squish, scale / squish, SPRITE::NEONGLOW, i, 3);
}
}
}
bool pop = false;
int width = jjLayerWidth[4] << 5;
int height = jjLayerHeight[4] << 5;
float outOfBounds = 2 * obj.var[1];
pop = obj.xPos < -outOfBounds || obj.yPos < -outOfBounds || obj.xPos > width + outOfBounds || obj.yPos > height + outOfBounds;
int radius = obj.var[0] * 3 / 4;
for (int i = 0; !pop && i < 1024; i += 32) {
int x = int(obj.xPos + jjSin(i) * radius);
int y = int(obj.yPos + jjCos(i) * radius);
pop = x >= 0 && y >= 0 && x < width && y < height && jjMaskedPixel(x, y) && jjEventAtLastMaskedPixel != AREA::STOPENEMY;
}
if (pop)
destroy(obj);
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet !is null) {
int radius = obj.var[bubble_size] * 3 / 4;
float x = bullet.xPos - obj.xPos;
float y = bullet.yPos - obj.yPos;
if (x * x + y * y < radius * radius)
destroy(obj);
}
return true;
}
void assign(jjOBJ@ obj) {
obj.behavior = this;
obj.counter = 0;
obj.var[bubble_size] = 2;
obj.var[bubble_max_size] = 64;
obj.playerHandling = HANDLING::SPECIAL;
obj.bulletHandling = HANDLING::DETECTBULLET;
obj.deactivates = false;
obj.scriptedCollisions = true;
obj.isBlastable = false;
obj.isFreezable = false;
obj.isTarget = false;
obj.causesRicochet = false;
obj.frameID = 0;
obj.determineCurAnim(ANIM::CUSTOM[anim_bubble], 0);
obj.determineCurFrame();
}
void destroy(jjOBJ@ obj) const {
int count = obj.var[bubble_size] * 4;
for (int i = 0; i < count; i++) {
auto@ part = jjAddParticle(PARTICLE::PIXEL);
if (part !is null) {
uint random = jjRandom();
int angle = random & 1023;
random >>= 10;
float z = (random & 63) / 64.f;
random >>= 6;
float r = sqrt(1.f - z * z) * float(obj.var[bubble_size]);
float xComp = jjSin(angle);
float yComp = jjCos(angle);
part.xPos = obj.xPos + xComp * r;
part.yPos = obj.yPos + yComp * r;
part.xSpeed = xComp + ((random & 15) - 7.5f) * 0.05f;
random >>= 4;
part.ySpeed = yComp + ((random & 15) - 7.5f) * 0.05f;
random >>= 4;
part.pixel.size = 1;
for (int j = 0; j < 4; j++) {
part.pixel.color[j] = (jjColorDepth != 8 ? 33 : 35) + (random & 3);
random >>= 2;
}
}
}
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PLOP2);
obj.delete();
}
bool isOnScreen(const jjOBJ@ obj) const {
float margin = obj.var[bubble_size] * 2;
float x = obj.xPos;
float y = obj.yPos;
for (int i = 0; i < jjLocalPlayerCount; i++) {
const auto@ player = jjLocalPlayers[i];
float left = player.cameraX;
float top = player.cameraY;
float right = left + jjSubscreenWidth;
float bottom = top + jjSubscreenHeight;
if (x + margin >= left && x - margin <= right && y + margin >= top && y - margin <= bottom)
return true;
}
return false;
}
}
class Launcher : jjBEHAVIORINTERFACE {
float bubbleSpeed = 2.f;
int fireTime = 64;
int rechargeTime = 120;
void onBehave(jjOBJ@ obj) override {
float xScale = 1.f;
float yScale = 1.f;
if (obj.state == STATE::START) {
obj.state = STATE::IDLE;
int x = int(obj.xPos) >> 5;
int y = int(obj.yPos) >> 5;
obj.var[launcher_angle_1] = jjParameterGet(x, y, 0, 3) << 7;
obj.var[launcher_angle_2] = jjParameterGet(x, y, 3, 3) << 7;
if (iabs(obj.var[launcher_angle_2] - obj.var[launcher_angle_1]) > 512) {
auto lesser = obj.var[launcher_angle_1] < obj.var[launcher_angle_2] ? launcher_angle_1 : launcher_angle_2;
obj.var[lesser] = obj.var[lesser] + 1024;
}
obj.var[launcher_angle_target] = obj.var[launcher_angle_1];
obj.var[launcher_angle_alt] = obj.var[launcher_angle_2];
obj.var[launcher_angle_displayed] = obj.var[launcher_angle_target];
int ropeLength = jjParameterGet(x, y, 6, 4) << 5;
if (ropeLength != 0) {
int id = jjAddObject(OBJECT::TRIGGERCRATE, obj.xPos, obj.yPos + ropeLength, obj.objectID, CREATOR::OBJECT, BEHAVIOR::INACTIVE);
if (id > 0) {
jjOBJ@ other = jjObjects[id];
launcherSwitch.assign(other);
other.var[launcher_switch_rope_length] = ropeLength;
}
}
}
if (obj.state == STATE::FIRE || obj.counter > 0) {
obj.counter++;
float squish = 1.f + 0.3f * jjSin(obj.counter * 1024 / fireTime);
xScale *= 1.f / squish;
yScale *= squish;
if (obj.var[launcher_liquid_deficit] < rechargeTime) {
obj.var[launcher_liquid_deficit] = obj.var[launcher_liquid_deficit] + obj.counter / 4;
if (obj.var[launcher_liquid_deficit] > rechargeTime)
obj.var[launcher_liquid_deficit] = rechargeTime;
}
if (obj.counter == fireTime * 3 / 4) {
float xOff = jjCos(obj.var[launcher_angle_target]);
float yOff = -jjSin(obj.var[launcher_angle_target]);
int id = jjAddObject(OBJECT::BUBBLE, obj.xPos + 40.f * xOff, obj.yPos + 40.f * yOff, obj.objectID, CREATOR::OBJECT, BEHAVIOR::INACTIVE);
if (id > 0) {
jjOBJ@ other = jjObjects[id];
bubble.assign(other);
other.xSpeed = xOff * bubbleSpeed;
other.ySpeed = yOff * bubbleSpeed;
jjSample(other.xPos, other.yPos, sound::launcherFire);
}
}
if (obj.counter >= fireTime) {
if (obj.state == STATE::FIRE)
obj.state = STATE::IDLE;
obj.counter = 0;
}
} else if (obj.var[launcher_liquid_deficit] > 0) {
obj.var[launcher_liquid_deficit] = obj.var[launcher_liquid_deficit] - 1;
}
if (obj.state == STATE::ROTATE) {
int direction = isign(obj.var[launcher_angle_target] - obj.var[launcher_angle_displayed]);
obj.var[launcher_angular_velocity] = obj.var[launcher_angular_velocity] + direction;
if (obj.var[launcher_angular_velocity] < -16)
obj.var[launcher_angular_velocity] = -16;
else if (obj.var[launcher_angular_velocity] > 16)
obj.var[launcher_angular_velocity] = 16;
obj.var[launcher_angle_displayed] = obj.var[launcher_angle_displayed] + obj.var[launcher_angular_velocity];
int newDirection = isign(obj.var[launcher_angle_target] - obj.var[launcher_angle_displayed]);
if (newDirection != direction) {
obj.var[launcher_angular_velocity] = -obj.var[launcher_angular_velocity] * 5 / 8;
obj.var[launcher_angle_displayed] = obj.var[launcher_angle_target] + obj.var[launcher_angular_velocity];
if (obj.var[launcher_angular_velocity] == 0)
obj.state = STATE::IDLE;
}
}
if (isOnScreen(obj)) {
SPRITE::Mode mode = obj.justHit > 0 ? SPRITE::SINGLECOLOR : SPRITE::MAPPING;
int liquidFrame = obj.var[launcher_liquid_deficit] * jjAnimations[obj.curAnim + 3].frameCount / (rechargeTime + 1);
jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, jjAnimations[obj.curAnim + 2], obj.var[launcher_angle_displayed], xScale, yScale, mode, 15);
if (mode != SPRITE::SINGLECOLOR)
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, jjAnimations[obj.curAnim + 3] + liquidFrame, 0, SPRITE::TRANSLUCENT);
jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, jjAnimations[obj.curAnim + 1], obj.var[launcher_angle_displayed], xScale, yScale, mode, obj.justHit > 0? 15:10);
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet !is null && obj.state == STATE::IDLE && obj.var[launcher_liquid_deficit] == 0) {
obj.justHit = 5;
bullet.state = STATE::EXPLODE;
obj.state = STATE::FIRE;
jjSample(obj.xPos, obj.yPos, sound::launcherHit);
}
return true;
}
void assign(jjOBJ@ obj) {
obj.behavior = this;
obj.playerHandling = HANDLING::SPECIAL;
obj.bulletHandling = HANDLING::DETECTBULLET;
obj.deactivates = false;
obj.scriptedCollisions = true;
obj.isBlastable = false;
obj.isFreezable = false;
obj.isTarget = false;
obj.causesRicochet = false;
obj.frameID = 0;
obj.determineCurAnim(ANIM::CUSTOM[anim_launcher], 0);
obj.determineCurFrame();
obj.killAnim = jjAnimations[obj.determineCurAnim(ANIM::CUSTOM[anim_launcher], 1, false)];
}
bool isOnScreen(const jjOBJ@ obj) const {
const float margin = 60.f;
float x = obj.xPos;
float y = obj.yPos;
for (int i = 0; i < jjLocalPlayerCount; i++) {
const auto@ player = jjLocalPlayers[i];
float left = player.cameraX;
float top = player.cameraY;
float right = left + jjSubscreenWidth;
float bottom = top + jjSubscreenHeight;
if (x + margin >= left && x - margin <= right && y + margin >= top && y - margin <= bottom)
return true;
}
return false;
}
}
class LauncherSwitch : jjBEHAVIORINTERFACE {
int revolutionCount = 2;
int spinTime = 110;
float angularVelocity = (2 * revolutionCount + 1) * 1024 / float(spinTime);
float angularAcceleration = angularVelocity / spinTime;
void onBehave(jjOBJ@ obj) override {
int angle = 0;
if (obj.state == STATE::START) {
obj.direction = 1;
obj.state = STATE::IDLE;
} else if (obj.state == STATE::ROTATE) {
int oldAngle = int(0.5f * angularAcceleration * (obj.counter * obj.counter)) & 1023;
obj.counter--;
if (obj.counter <= 0) {
obj.state = STATE::IDLE;
} else {
angle = int(0.5f * angularAcceleration * (obj.counter * obj.counter)) & 1023;
float product = jjSin(angle) * jjSin(oldAngle);
if (product < 0.f || product == 0.f && jjSin(angle) != 0.f)
jjSample(obj.xPos, obj.yPos, sound::launcherSwitch, 63, 20000 + 2000 * obj.counter);
}
}
int length = obj.var[launcher_switch_rope_length];
jjDrawSwingingVineSpriteFromCurFrame(obj.xPos, obj.yPos - length, obj.var[launcher_switch_rope_frame], length, 0, SPRITE::NORMAL, 0, 5);
SPRITE::Mode mode = obj.justHit > 0 ? SPRITE::SINGLECOLOR : SPRITE::NORMAL;
float xScale = abs(jjCos(angle));
jjDrawResizedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, xScale, 1.f, mode, 15);
const auto@ creator = jjObjects[obj.creatorID];
int frame = jjAnimations[obj.curAnim + 1] + (creator.var[angle < 256 || angle >= 768 ? launcher_angle_target : launcher_angle_alt] >> 7 & 7);
jjDrawResizedSpriteFromCurFrame(obj.xPos, obj.yPos, frame, xScale, 1.f, mode, 15);
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet !is null && obj.state == STATE::IDLE) {
obj.justHit = 5;
bullet.state = STATE::EXPLODE;
obj.state = STATE::ROTATE;
obj.counter = spinTime;
auto@ creator = jjObjects[obj.creatorID];
int prevAngle = creator.var[launcher_angle_target];
creator.var[launcher_angle_target] = creator.var[launcher_angle_alt];
creator.var[launcher_angle_alt] = prevAngle;
creator.state = STATE::ROTATE;
}
return true;
}
void assign(jjOBJ@ obj) {
obj.behavior = this;
obj.playerHandling = HANDLING::SPECIAL;
obj.bulletHandling = HANDLING::DETECTBULLET;
obj.deactivates = false;
obj.scriptedCollisions = true;
obj.isBlastable = false;
obj.isFreezable = false;
obj.isTarget = false;
obj.causesRicochet = false;
obj.frameID = 0;
obj.determineCurAnim(ANIM::CUSTOM[anim_launcher], 4);
obj.determineCurFrame();
obj.var[launcher_switch_rope_frame] = jjAnimations[jjAnimSets[ANIM::CUSTOM[anim_rope]]];
}
}
class SpikeBall : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) override {
if (obj.state == STATE::START) {
obj.state = STATE::ATTACK;
} else if (obj.state == STATE::HIDE) {
const int delay = 2;
if (obj.counter % delay == 0) {
if (obj.frameID > obj.counter / delay)
obj.frameID--;
else if (obj.frameID < int(jjAnimations[obj.curAnim].frameCount) - 1)
obj.frameID++;
}
obj.causesRicochet = true;
obj.counter--;
if (obj.counter <= 0) {
jjSample(obj.xPos, obj.yPos, sound::spikeballRecover);
obj.state = STATE::ATTACK;
obj.frameID = 0;
obj.causesRicochet = false;
}
}
obj.determineCurFrame();
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::MAPPING, 10);
if (obj.justHit == 0) {
const auto@ anim = jjAnimations[obj.curAnim + 1];
int frameCount = anim.frameCount;
int timerFrame = obj.counter * frameCount / (getMaxCounter() - 5);
if (timerFrame >= frameCount)
timerFrame = frameCount - 1;
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, anim + timerFrame, obj.direction, SPRITE::MAPPING, 10);
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet !is null) {
if (obj.state == STATE::ATTACK)
jjSample(obj.xPos, obj.yPos, sound::spikeballHit);
if (!obj.causesRicochet)
bullet.state = STATE::EXPLODE;
obj.justHit = 5;
obj.state = STATE::HIDE;
obj.counter = getMaxCounter();
} else if (obj.state == STATE::ATTACK) {
player.hurt();
}
return true;
}
int getMaxCounter() const {
return jjDifficulty < 3 ? 420 - jjDifficulty * 100 : 160;
}
void assign(jjOBJ@ obj) {
obj.behavior = this;
obj.playerHandling = HANDLING::SPECIAL;
obj.bulletHandling = HANDLING::DETECTBULLET;
obj.deactivates = false;
obj.scriptedCollisions = true;
obj.isBlastable = false;
obj.isFreezable = false;
obj.isTarget = false;
obj.causesRicochet = false;
obj.frameID = 0;
obj.determineCurAnim(ANIM::CUSTOM[anim_spike_ball], 0);
obj.determineCurFrame();
}
}
Bubble bubble;
Launcher launcher;
LauncherSwitch launcherSwitch;
SpikeBall spikeBall;
se::DefaultWeaponHook weaponHook;
array<int> gunJammedTimer(jjLocalPlayerCount);
jjPAL palette8;
jjPAL palette16;
int colorDepth = 16;
bool finished = false;
int iabs(int x) {
return x < 0 ? -x : x;
}
int isign(int x) {
return x < 0 ? -1 : x > 0 ? 1 : 0;
}
jjPIXELMAP@ makeRopeSprite() {
jjPIXELMAP tile(5);
jjPIXELMAP result(5, 8);
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 5; j++) {
result[j, i] = tile[i, 13 + j];
}
}
return result;
}
void loadResources() {
jjAnimSets[ANIM::CUSTOM[anim_bubble]].allocate(array<uint> = {TrueColor::NumberOfFramesPerImage});
jjAnimSets[ANIM::CUSTOM[anim_rope]].allocate(array<uint> = {1});
jjAnimSets[ANIM::CUSTOM[anim_launcher]].load(0, "SEhh24.j2a");
jjAnimSets[ANIM::CUSTOM[anim_spike_ball]].load(1, "SEhh24.j2a");
auto@ ropeAnimFrame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[anim_rope]]]];
makeRopeSprite().save(ropeAnimFrame);
ropeAnimFrame.hotSpotX = -ropeAnimFrame.width / 2;
TrueColor::Bitmap image("SEwaterbubble.png");
image.saveToAnimFrames(jjAnimations[jjAnimSets[ANIM::CUSTOM[anim_bubble]]], TrueColor::Coordinates(0, 0, image.width, image.height, -image.width / 2, -image.height / 2));
jjSampleLoad(sound::spikeballHit, "SEhh24spikeball1.wav");
jjSampleLoad(sound::spikeballRecover, "SEhh24spikeball2.wav");
jjSampleLoad(sound::launcherSwitch, "SEhh24launcherswitch.wav");
jjSampleLoad(sound::dryFire, "SEhh24dryfire.wav");
jjSampleLoad(sound::secret, "SEhh24secret.wav");
jjSampleLoad(sound::launcherHit, "SEhh24launcher1.wav");
jjSampleLoad(sound::launcherFire, "SEhh24launcher2.wav");
}
void setBehaviors() {
jjObjectPresets[OBJECT::BUBBLE].behavior = BEHAVIOR::INACTIVE;
launcher.assign(jjObjectPresets[OBJECT::BIGBOX]);
spikeBall.assign(jjObjectPresets[OBJECT::BANANA]);
int id = jjAddObject(0, 0.f, 0.f);
if (id > 0) {
auto@ obj = jjObjects[id];
obj.deactivates = false;
obj.behavior = function(obj) {
jjSetWaterLevel(jjLayerHeight[4] * 32 + 128.f, true);
};
}
}
jjOBJ@ getBubble(const jjPLAYER@ player) {
for (int i = 0; i < jjObjectCount; i++) {
const auto@ obj = jjObjects[i];
if (cast<const jjBEHAVIORINTERFACE>(obj.behavior) is bubble) {
float x = obj.xPos - player.xPos;
float y = obj.yPos - player.yPos;
if (x * x + y * y < obj.var[bubble_size] * obj.var[bubble_size])
return obj;
}
}
return null;
}
void onLevelLoad() {
jjUseLayer8Speeds = true;
HH24::levelLoad();
loadResources();
setBehaviors();
HH17::setEnemy(OBJECT::MONKEY);
HH17::setEnemy(OBJECT::RAVEN);
HH17::setEnemy(OBJECT::BAT);
HH17::setEnemy(OBJECT::LABRAT);
HH17::setEnemy(OBJECT::DRAGON);
HH24Enemies::MakeEventHH24Fencer(OBJECT::FENCER);
HH24Enemies::MakeEventHH24Hatter(OBJECT::HATTER);
SMOKE::MECHABEAR(OBJECT::DEMON,4);
SMOKE::PENGUINATOR(OBJECT::TUBETURTLE,2);
SMOKE::SANTAGHOST(OBJECT::DRAGONFLY,3);
SMOKE::CRYSTALKNIGHT(OBJECT::BUMBEE,3);
SMOKE::ICEKNIGHTENEMY(OBJECT::HELMUT,50);
SMOKE_HH24::ICEGOLEMEDIT(OBJECT::FATCHICK,GolemMaxHP);
SMOKE_ICEDRAG::ICEDRAGONFIXPAL(OBJECT::SPARK,OBJECT::CRAB);
jjAnimSets[ANIM::RAPIER].load();
jjAnimSets[ANIM::MENU].load();
//SMOKE::ICEKNIGHT(OBJECT::BEE,40);
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::SPIKEPLAT].load(4, "SExmas.j2a");
jjObjectPresets[OBJECT::SPIKEPLATFORM].deactivates = false;
jjObjectPresets[OBJECT::SPIKEPLATFORM].killAnim = jjObjectPresets[OBJECT::SPIKEPLATFORM].determineCurAnim(ANIM::SPIKEPLAT, 0) + 1;
jjObjectPresets[OBJECT::SPIKEBOLL].bulletHandling = HANDLING::DESTROYBULLET;
jjAnimSets[ANIM::SPIKEBOLL].load(3, "SExmas.j2a");
jjObjectPresets[OBJECT::SPIKEBOLL].killAnim = jjObjectPresets[OBJECT::SPIKEBOLL].determineCurAnim(ANIM::SPIKEBOLL, 0) + 1;
jjObjectPresets[OBJECT::ONEUPCRATE].behavior = GiftBox();
jjObjectPresets[OBJECT::ONEUPCRATE].determineCurAnim(ANIM::CUSTOM[0], 0);
jjWeapons[WEAPON::GUN8].comesFromGunCrates = true;
jjWeapons[WEAPON::GUN9].comesFromGunCrates = true;
jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckpointWrapper;
jjObjectPresets[OBJECT::SAVEPOST].deactivates = false;
jjObjectPresets[OBJECT::WATERMELON].behavior = GolemHP();
jjObjectPresets[OBJECT::WATERMELON].energy = GolemMaxHP;
jjObjectPresets[OBJECT::WATERMELON].playerHandling = HANDLING::PARTICLE;
jjObjectPresets[OBJECT::WATERMELON].isBlastable = false;
jjObjectPresets[OBJECT::TRIGGERCRATE].behavior = ColouredCrate();
src = jjAnimSets[ANIM::CUSTOM[254]].load(0, "HH24.j2a");
jjAnimations[dest + 18] = jjAnimations[src + 6]; // Chips -> Chocolate Chip Cookie
jjAnimations[dest + 23] = jjAnimations[src + 8]; // Cucumber -> White Chocolate
jjAnimations[dest + 48] = jjAnimations[src + 11]; // Lemon -> Swiss Roll
jjAnimations[dest + 49] = jjAnimations[src + 13]; // Lettuce -> Lollipop
jjAnimations[dest + 76] = jjAnimations[src + 12]; // Pie -> Apple Pie
jjAnimations[dest + 26] = jjAnimations[src + 9]; // Eggplant -> Candy Bar
jjAnimations[dest + 20] = jjAnimations[src + 7]; // Soft Drink -> Eggnog
jjAnimations[dest + 17] = jjAnimations[src + 5]; // Chicken Leg -> Caramel
jjAnimations[dest + 80] = jjAnimations[src + 16]; // Sandwich -> Brownie
jjAnimations[dest + 79] = jjAnimations[src + 15]; // Pretzel -> Fudge
// Christmas Tuf Turt
jjAnimSets[ANIM::TUFTUR].load(1, "HH24.j2a");
jjObjectPresets[OBJECT::TUFTURT].determineCurAnim(ANIM::TUFTUR, 0);
deleteUnwantedEvents();
jjObjectPresets[OBJECT::QUEEN].behavior = SwordQueen();
jjObjectPresets[OBJECT::QUEEN].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::QUEEN].bulletHandling = HANDLING::DETECTBULLET;
jjObjectPresets[OBJECT::QUEEN].scriptedCollisions = true;
jjObjectPresets[OBJECT::QUEEN].isBlastable = false;
jjObjectPresets[OBJECT::QUEEN].energy = 100;
//jjObjectPresets[OBJECT::QUEEN].age = 150 + (50*jjDifficulty);
jjObjectPresets[OBJECT::QUEEN].points = 20000;
jjObjectPresets[OBJECT::QUEEN].deactivates = false;
jjAnimSets[ANIM::CUSTOM[69]].load(0, "Sword.j2a");
jjAnimSets[ANIM::CUSTOM[70]].load(0, "HH17_Icicle.j2a");
Resize::Resize(
jjAnimations[jjAnimSets[ANIM::MENU] + 1],
3.5,
Resize::Method::Scale2x
);
jjAnimSets[ANIM::CUSTOM[anim_bubble]].allocate(array<uint> = {TrueColor::NumberOfFramesPerImage});
jjWaterLayer = 99;
jjObjectPresets[OBJECT::BUBBLE].behavior = BEHAVIOR::INACTIVE;
TrueColor::ProcessPalette();
jjAnimSets[ANIM::CUSTOM[anim_bubble]].allocate(array<uint> = {TrueColor::NumberOfFramesPerImage});
jjAnimSets[ANIM::CUSTOM[anim_launcher]].load(0, "SEhh24.j2a");
TrueColor::Bitmap image("SEwaterbubble.png");
image.saveToAnimFrames(jjAnimations[jjAnimSets[ANIM::CUSTOM[anim_bubble]]], TrueColor::Coordinates(0, 0, image.width, image.height, -image.width / 2, -image.height / 2));
launcher.assign(jjObjectPresets[232]);
jjWaterLighting = WATERLIGHT::GLOBAL;
}
void onLevelReload() {
MLLE::SpawnOffgridsLocal();
HH24::levelReload();
HH17::processEnemyColors();
jjWaterLighting = WATERLIGHT::GLOBAL;
for (uint i = 0; i < 32; ++i)
jjTriggers[i] = SavedTriggers[i];
jjMusicLoad(reachedCastle? "jj2_symphoni_dexentrique.mo3" : "falling_flakes.xm");
iceGolemFight = false;
if (!reachedCastle && SMOKE_HH24::golemDeathCount > 0) {
SMOKE_HH24::golemDeathCount = 0;
}
if (jjDifficulty > 0) {
currPhase = First;
}
if (currState < Exit) {
currState = Intro;
startQueenBattle = false;
}
}
void onLevelBegin() {
MLLE::SpawnOffgrids();
jjSampleLoad(SOUND::WIND_WIND2A, "HH24_Cold_Wind.ogg");
jjSampleLoad(SOUND::BONUS_BONUSBLUB, "HH17_Glass1.wav");
jjSampleLoad(SOUND::BONUS_BONUS1, "HH24_FallSound.ogg");
jjSampleLoad(SOUND::SCIENCE_PLOPKAOS, "HH24_Sword.wav");
jjObjectPresets[OBJECT::ELECTROBULLET].animSpeed = 1;
jjObjectPresets[OBJECT::ELECTROBULLETPU].animSpeed = 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);
}
}
}
enum QueenPhases {First, Second};
uint currPhase = First;
enum QueenStates {Intro, Run, Slash, Spin, Charge, Bunnyhop, SpinJump, Stomp, Downward, Bounce, Defeat, Transform, Icicles, PrepareThrow, Throw, MegaSlash, Ultimate, DefeatTwo, Exit};
uint currState = Intro;
const array<QueenStates> longRangeAttacks = {Charge, SpinJump, Stomp};
const array<QueenStates> shortRangeAttacks = {Slash, Bunnyhop, Stomp};
const array<QueenStates> longRangeAttacks_PhaseTwo = {Charge, SpinJump, Stomp, Icicles, PrepareThrow};
const array<QueenStates> shortRangeAttacks_PhaseTwo = {Slash, Bunnyhop, Stomp, Icicles, MegaSlash};
array<QueenStates> longRangeQueue;
array<QueenStates> shortRangeQueue;
array<QueenStates> longRangeQueue_PhaseTwo;
array<QueenStates> shortRangeQueue_PhaseTwo;
void shuffle(array<QueenStates>& input) {
for (int i = input.length(); i != 0;) {
int index = jjRandom() % i;
i--;
QueenStates temp = input[index];
input[index] = input[i];
input[i] = temp;
}
}
QueenStates pop(array<QueenStates>& input) {
QueenStates result = input[input.length() - 1];
input.removeLast();
return result;
}
QueenStates getNextAttack(array<QueenStates>& queue, const array<QueenStates>& reserve) {
if (queue.isEmpty()) {
queue = reserve;
shuffle(queue);
}
return pop(queue);
}
bool passive = false;
class SwordQueen : jjBEHAVIORINTERFACE {
int currAnim, sample, armor, armorCounter;
bool lowHP, jump, floating, immune, shield;
float jumpApex;
float bossHP = 150 + (25*jjDifficulty);
float bossHPMax = 150 + (25*jjDifficulty);
bool reverseAnim = false;
bool deflect = true;
int swordTime = (25*70) + ((5*70)*jjDifficulty);
void onBehave(jjOBJ@ obj) {
obj.determineCurAnim(ANIM::QUEEN, currAnim);
obj.determineCurFrame();
if (reverseAnim) {
if (jjGameTicks % 7 == 0) {
obj.frameID++;
}
} else {
if (jjGameTicks % 7 == 0) {
obj.frameID--;
}
}
if (startQueenBattle) obj.counter++;
if (obj.freeze > 0) {
obj.unfreeze(1);
}
if (obj.justHit != 0) armorCounter = 0;
else armorCounter++;
if (armorCounter > 0) {
if (armorCounter < 35) {
if (armorCounter % 7 == 0) {
armor--;
}
}
}
if (armorCounter >= 70) {
if (armorCounter % 4 == 0) {
armor--;
}
}
if (armor > 20) armor = 20;
if (armor < 0) armor = 0;
obj.energy = int(bossHP / (1.5+(0.25*jjDifficulty)));
if (obj.isActive) {
for (int i = 0; i <= jjLocalPlayerCount; i++) {
if (jjLocalPlayers[i].xPos >= 467*32) {
jjLocalPlayers[i].boss = obj.objectID;
startQueenBattle = true;
}
}
if (startQueenBattle && jjLocalPlayerCount == 1 && jjResolutionWidth >= 640 && currState < Exit) {
jjPLAYER@ play = jjLocalPlayers[0];
float left = 449*32;
float top = 135*32;
float right = jjResolutionWidth < 800? 466*32 : 462*32;
float bottom = jjResolutionHeight < 600? 138*32 : 135*32;
if (right - left <= 32.f)
left = right = (left + right) / 2.f;
if (bottom - top <= 32.f)
top = bottom = (top + bottom) / 2.f;
float xCam = play.xPos - (jjSubscreenWidth >> 1);
float yCam = play.yPos - (jjSubscreenHeight >> 1);
if (xCam < left)
xCam = left;
if (xCam > right)
xCam = right;
if (yCam < top)
yCam = top;
if (yCam > bottom)
yCam = bottom;
play.cameraFreeze(xCam, yCam, false, true);
}
}
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
/*if (jjMaskedHLine(int(obj.xPos*32), int(obj.xSpeed), int(obj.yPos*32))) {
obj.direction *= -1;
}*/
if (obj.xPos > 482*32) {
obj.direction *= -1;
obj.xSpeed = 0;
obj.xPos = 481.75*32;
}
if (obj.xPos < 453*32) {
obj.direction *= -1;
obj.xSpeed = 0;
obj.xPos = 453.25*32;
}
if (currPhase == Second && obj.energy <= 25 && !lowHP && currState == Run && jjDifficulty > 0) {
setNewState(obj, Ultimate);
lowHP = true;
}
int playerID = obj.findNearestPlayer(1000000);
jjPLAYER@ play = jjPlayers[playerID];
if (isFacingPlayer(obj, play.playerID) && play.xPos >= 467*32) startQueenBattle = true;
int attackAngle = int(atan2(obj.xPos - play.xPos - (play.xSpeed * 16), obj.yPos - play.yPos - (play.ySpeed * 16)) * (512 / PI));
if (currPhase == Second) {
if (obj.energy <= 0 && currState < DefeatTwo && currState > Intro) {
setNewState(obj, DefeatTwo);
jjSetModPosition(20,0,false);
}
} else {
if (obj.energy <= 0 && currState < Defeat && currState > Intro) {
setNewState(obj, Defeat);
bossHP = 0;
jjSetModPosition(22,0,false);
}
}
if (onGround(obj) && !jump && !floating && currState < Exit) {
obj.putOnGround(true);
}
if (jump) {
obj.ySpeed = -4;
}
if (obj.yPos <= jumpApex) {
if (obj.ySpeed < 4) obj.ySpeed += 0.35;
jump = false;
}
switch (currState) {
case Intro:
currAnim = 3;
reverseAnim = false;
deflect = true;
immune = true;
passive = true;
jump = false;
floating = false;
shield = false;
bossHP = bossHPMax;
lookAtPlayer(obj, play);
lowHP = false;
if (obj.counter == 1) {
jjMusicLoad("jj2_Claw6.xm");
jjLocalPlayers[0].showText("@@@@@@Your journey ends here.@@You will not uncover the secret of the castle!");
for (int i = 0; i <= jjLocalPlayerCount; i++) {
jjLocalPlayers[i].bossActivated = true;
}
int swrd = jjAddObject(OBJECT::SPRINGCORD, int(obj.xPos - (10 * obj.direction)), int(obj.yPos + 3));
jjOBJ@ sword = jjObjects[swrd];
sword.behavior = QueenSwordWeapon();
sword.playerHandling = HANDLING::ENEMYBULLET;
sword.bulletHandling = HANDLING::DESTROYBULLET;
sword.isBlastable = false;
sword.animSpeed = 1;
sword.energy = obj.energy;
}
if (obj.counter >= 210) {
jjLocalPlayers[0].showText("@@@@@@Well, not much of a secret now, is it...");
immune = passive = false;
setNewState(obj, Run);
}
break;
case Run:
currAnim = 7;
reverseAnim = true;
deflect = jump? false:true;
obj.xSpeed = 3 * obj.direction;
lookAtPlayer(obj, play);
if (obj.counter >= 55) {
randomAttack(obj, play);
}
break;
case Slash:
currAnim = 6;
lookAtPlayer(obj, play);
deflect = false;
obj.xSpeed = (jump? 3:2) * obj.direction;
if (play.yPos < (obj.yPos - 32) && !jump && onGround(obj)) {
obj.special++;
if (obj.special == 14) {
jump = true;
if (play.yPos < (obj.yPos - 96)) {
jumpApex = 146.5*32;
} else {
jumpApex = 147.5*32;
}
jjSample(play.xPos, play.yPos, SOUND::BUBBA_BUBBABOUNCE2);
obj.special = 0;
}
}
if (currPhase == Second) {
if (obj.counter % 6 == 0) {
jjOBJ@ iceball = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos + jjSin(obj.counter*20)), int(obj.yPos + jjCos(obj.counter*20)), obj.objectID, CREATOR::OBJECT)];
jjSample(iceball.xPos, iceball.yPos, SOUND::AMMO_ICEGUN, 0, 0);
iceball.behavior = IceCloud;
iceball.curAnim = iceball.determineCurAnim(ANIM::AMMO, 82, true);
iceball.frameID=5;
iceball.determineCurFrame();
iceball.counterEnd=20;
iceball.freeze = 210;
iceball.state = STATE::FLY;
iceball.playerHandling = HANDLING::PARTICLE;
iceball.lightType = LIGHT::NONE;
iceball.xSpeed = ((jjSin(obj.counter*20)*6) * obj.direction) + obj.xSpeed;
iceball.ySpeed = (jjCos(obj.counter*20)*6);
iceball.killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
}
}
if (obj.counter % 35 == 0) {
if (longRangeCriteria(obj, play) && onGround(obj)) {
randomAttack(obj, play);
} else {
setNewState(obj, Run);
}
jjSample(play.xPos, play.yPos, SOUND::COMMON_FOEW3, 63, 12000);
}
if (obj.counter >= 35 && jump) {
setNewState(obj, Spin);
}
if (obj.counter >= 105 && !jump) {
setNewState(obj, Run);
}
break;
case Spin:
currAnim = 5;
lookAtPlayer(obj, play);
deflect = false;
reverseAnim = false;
obj.xSpeed = 3 * obj.direction;
obj.yPos -= 0.1;
if (currPhase == Second) {
if (obj.counter % 6 == 0) {
jjOBJ@ iceball = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos + jjSin(obj.counter*20)), int(obj.yPos + jjCos(obj.counter*20)), obj.objectID, CREATOR::OBJECT)];
jjSample(iceball.xPos, iceball.yPos, SOUND::AMMO_ICEGUN, 0, 0);
iceball.behavior = IceCloud;
iceball.curAnim = iceball.determineCurAnim(ANIM::AMMO, 82, true);
iceball.frameID=5;
iceball.determineCurFrame();
iceball.counterEnd=20;
iceball.freeze = 210;
iceball.state = STATE::FLY;
iceball.playerHandling = HANDLING::PARTICLE;
iceball.lightType = LIGHT::NONE;
iceball.xSpeed = ((jjSin(obj.counter*20)*6) * obj.direction) + obj.xSpeed;
iceball.ySpeed = (jjCos(obj.counter*20)*6);
iceball.killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
}
}
if (onGround(obj)) {
setNewState(obj, Run);
}
break;
case Charge:
if (obj.counter == 1) jjSample(play.xPos, play.yPos, SOUND::INTRO_MONSTER2);
if (obj.counter < 35) {
obj.xSpeed = 0;
currAnim = 6;
reverseAnim = false;
lookAtPlayer(obj, play);
deflect = false;
} else {
if (obj.xPos >= 481.75*32 || obj.xPos <= 453.25*32) {
obj.xSpeed = 0;
obj.direction *= -1;
obj.var[0] = 0;
if (longRangeCriteria(obj, play) && onGround(obj)) {
randomAttack(obj, play);
} else {
setNewState(obj, Run);
}
} else {
currAnim = 7;
reverseAnim = true;
deflect = true;
obj.xSpeed = 7 * obj.direction;
if (currPhase == Second && obj.counter % 5 == 0) {
int bulletID3 = jjAddObject(OBJECT::TOASTERBULLET, obj.xPos - obj.direction * 32, obj.yPos, obj.objectID, CREATOR::OBJECT);
jjOBJ @ o = jjObjects[bulletID3];
o.behavior = IceCloud;
o.direction = obj.direction;
o.xSpeed = 0;
o.ySpeed = 0;
o.curAnim = o.determineCurAnim(ANIM::AMMO, 82, true);
o.frameID=5;
o.determineCurFrame();
o.counterEnd=20;
o.freeze = 210;
o.playerHandling = HANDLING::ENEMYBULLET;
}
if (play.yPos < (obj.yPos - 32) && !jump && onGround(obj) && obj.var[0] == 0 && obj.counter >= 35) {
obj.special++;
if (obj.special == 7) {
jump = true;
jumpApex = 147.5*32;
jjSample(play.xPos, play.yPos, SOUND::BUBBA_BUBBABOUNCE2);
obj.special = 0;
if (jjRandom()%2 == 0) {
if (play.yPos < (obj.yPos - 96)) {
jumpApex = 145.5*32;
} else {
jumpApex = 147.5*32;
}
setNewState(obj, Slash);
}
}
}
}
}
break;
case Bunnyhop:
if (obj.counter == 1) {
jump = true;
jumpApex = 148.25*32;
jjSample(play.xPos, play.yPos, SOUND::BUBBA_BUBBABOUNCE1);
} else {
if (onGround(obj) && !jump) {
if (shortRangeCriteria(obj, play)) {
setNewState(obj, Slash);
} else {
randomAttack(obj, play);
}
}
}
obj.xSpeed = 4 * obj.direction;
break;
case SpinJump:
currAnim = 5;
lookAtPlayer(obj, play);
deflect = false;
reverseAnim = false;
if (obj.counter < 28) obj.xSpeed = 0;
if (obj.counter == 28) {
jump = true;
jumpApex = play.yPos < 144*32? 143*32 : 145.5*32;
jjSample(play.xPos, play.yPos, SOUND::BUBBA_BUBBABOUNCE2);
jjSample(play.xPos, play.yPos, SOUND::COMMON_FOEW3, 63, 12000);
obj.xSpeed = -5.5 * jjSin(attackAngle);
} else {
if (obj.counter > 28) {
if (currPhase == Second) {
if (obj.counter % 9 == 0) {
jjOBJ@ iceball = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos + jjSin(obj.counter*20)), int(obj.yPos + jjCos(obj.counter*20)), obj.objectID, CREATOR::OBJECT)];
jjSample(iceball.xPos, iceball.yPos, SOUND::AMMO_ICEGUN, 0, 0);
iceball.behavior = IceCloud;
iceball.curAnim = iceball.determineCurAnim(ANIM::AMMO, 82, true);
iceball.frameID=5;
iceball.determineCurFrame();
iceball.counterEnd=20;
iceball.freeze = 210;
iceball.state = STATE::FLY;
iceball.playerHandling = HANDLING::PARTICLE;
iceball.lightType = LIGHT::NONE;
iceball.xSpeed = ((jjSin(obj.counter*20)*6) * obj.direction) + obj.xSpeed;
iceball.ySpeed = (jjCos(obj.counter*20)*6);
iceball.killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
}
}
if (!jump) {
if (directlyBelow(obj, play)) {
setNewState(obj, Downward);
}
obj.yPos -= 0.1;
if (onGround(obj)) {
obj.xSpeed = 0;
if (shortRangeCriteria(obj, play)) {
setNewState(obj, Slash);
} else {
setNewState(obj, Run);
}
}
}
}
}
break;
case Stomp:
currAnim = 6;
lookAtPlayer(obj, play);
deflect = false;
obj.xSpeed = 0;
if (currPhase == First) {
if (obj.counter % (32-(jjDifficulty*4)) == 0 && onGround(obj)) {
jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND2);
for (uint i = 0; i < (jjRandom()%3)+1; i++) {
float brickXPos = ((obj.xPos - 64) + (((jjRandom()%(abs(xDistance(obj,play))+6))*32)*obj.direction) <= 453*32)? 453*32 : ((obj.xPos - 64) + (((jjRandom()%(abs(xDistance(obj,play))+6))*32)*obj.direction) >= 482*32)? 482*32 : ((obj.xPos - 64) + (((jjRandom()%(abs(xDistance(obj,play))+6))*32)*obj.direction));
jjOBJ@ brick = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, brickXPos, 138.5*32 + jjRandom()%24, obj.objectID, CREATOR::OBJECT)];
brick.xSpeed = 0;
brick.xAcc = 0;
brick.behavior = QueenBricks();
brick.ySpeed = 2;
brick.yAcc = 0.1;
brick.direction = 1;
brick.counterEnd = 250;
brick.state = STATE::FLY;
brick.playerHandling = HANDLING::ENEMYBULLET;
brick.lightType = LIGHT::NONE;
}
}
} else {
if (obj.counter % 42 == 0) {
jjSample(play.xPos, play.yPos, SOUND::BONUS_BONUSBLUB);
}
if (obj.counter % (10-(jjDifficulty*2)) == 0 && onGround(obj)) {
jjOBJ@ icicles = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, ((452.5*32) + jjRandom()%944), 138.5*32, obj.objectID, CREATOR::OBJECT)];
icicles.xSpeed = 0;
icicles.xAcc = 0;
icicles.behavior = QueenIcicles();
icicles.ySpeed = 2;
icicles.yAcc = 0.1;
icicles.direction = 1;
icicles.counterEnd = 250;
icicles.state = STATE::FLY;
icicles.playerHandling = HANDLING::ENEMYBULLET;
icicles.lightType = LIGHT::NONE;
}
}
if (obj.counter >= 105) {
if (longRangeCriteria(obj, play) && onGround(obj)) {
randomAttack(obj, play);
} else {
setNewState(obj, Run);
}
}
break;
case Downward:
lookAtPlayer(obj, play);
deflect = false;
obj.xSpeed = 0;
if (obj.counter < 35) {
currAnim = 6;
obj.ySpeed = 0.1;
} else {
if (obj.counter == 35) jjSample(play.xPos, play.yPos, SOUND::COMMON_DOWN);
currAnim = 5;
obj.ySpeed = 8;
if (onGround(obj)) {
jjSample(play.xPos, play.yPos, SOUND::ROBOT_POLE);
jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1);
setNewState(obj, Bounce);
}
}
break;
case Bounce:
deflect = false;
obj.xSpeed = 0;
currAnim = 5;
if (currPhase == Second && obj.counter % 7 == 0 && obj.counter <= 21) {
for (int i = 0; i < 2; i++) {
jjOBJ@ blast = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos), int(obj.yPos+16), obj.objectID, CREATOR::OBJECT)];
blast.xSpeed = i == 0? -6:6;
blast.behavior = QueenBlast();
blast.ySpeed = 0;
blast.xAcc = blast.yAcc = 0;
blast.direction = -obj.direction;
blast.counterEnd = 90;
blast.state = STATE::FLY;
blast.playerHandling = HANDLING::ENEMYBULLET;
blast.lightType = LIGHT::NONE;
}
}
if (obj.counter <= 7) {
jump = true;
jumpApex = 149.5*32;
} else {
if (onGround(obj) && !jump) {
setNewState(obj, Run);
}
}
break;
case Defeat:
/*for (int i = 0; i <= jjLocalPlayerCount; i++) {
jjLocalPlayers[i].bossActivated = false;
}*/
obj.xSpeed = 0;
reverseAnim = false;
immune = true;
passive = true;
if (obj.counter == 1) {
//jjMusicStop();
currAnim = 1;
jjLocalPlayers[0].showText("@@@@@@@You're a lot tougher than I expected...");
/*if (jjDifficulty < 1) {
jjOBJ@ carrot = jjObjects[jjAddObject(OBJECT::FULLENERGY, 467*32, 144*32, 0, CREATOR::LEVEL)];
carrot.state = STATE::FLOATFALL;
}*/
}
if (obj.counter == 210) {
jjLocalPlayers[0].showText("@@@@@@@But you haven't won yet.");
currAnim = 3;
obj.var[1] = 1;
}
if (obj.counter == 350) {
setNewState(obj, Transform);
}
break;
case Transform:
currAnim = 5;
floating = true;
if (obj.yPos > 144*32) obj.ySpeed = -1;
else obj.ySpeed = 0;
if (bossHP < bossHPMax) {
if (obj.counter % (6-jjDifficulty) == 0) bossHP += 2;
immune = passive = true;
} else {
currPhase = Second;
floating = jump = false;
obj.ySpeed = 4;
if (onGround(obj)) {
jjLocalPlayers[0].showText("@@@@@@@We are one!");
immune = passive = false;
setNewState(obj, Run);
}
}
if (obj.counter == 14) {
jjMusicLoad("jj2_ckbattle.it");
jjLocalPlayers[0].showText("@@@@@@@Sister, lend me your power!");
jjSamplePriority(SOUND::ORANGE_SWEEP0R);
jjOBJ@ iceQueen = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos), int(obj.yPos-320), obj.objectID, CREATOR::OBJECT)];
iceQueen.xSpeed = 0;
iceQueen.ySpeed = 1;
iceQueen.behavior = IceQueenSpirit();
iceQueen.direction = obj.direction;
iceQueen.state = STATE::FLY;
iceQueen.playerHandling = HANDLING::PARTICLE;
iceQueen.lightType = LIGHT::LASER;
iceQueen.light = 8;
}
if (obj.counter > 14 && obj.counter % 14 == 0) {
jjOBJ@ cloud = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos+(128*jjSin(obj.counter*8))), int(obj.yPos+(128*jjCos(obj.counter*8))), obj.objectID, CREATOR::OBJECT)];
cloud.xSpeed = -3 * jjSin(objReturnAngle(obj, cloud));
cloud.ySpeed = -3 * jjCos(objReturnAngle(obj, cloud));
cloud.behavior = IceTrail();
cloud.xAcc = cloud.yAcc = 0;
cloud.var[1] = 1;
cloud.direction = -obj.direction;
cloud.counterEnd = 200;
cloud.state = STATE::FLY;
cloud.playerHandling = HANDLING::PARTICLE;
cloud.lightType = LIGHT::NONE;
}
break;
case Icicles:
currAnim = 0;
lookAtPlayer(obj, play);
deflect = false;
reverseAnim = false;
obj.xSpeed = 0;
if (obj.counter == 28) {
jjSample(play.xPos, play.yPos, SOUND::QUEEN_SCREAM);
jjSample(play.xPos, play.yPos, SOUND::QUEEN_SCREAM, 15, 0);
}
if (obj.counter % (18 - (jjDifficulty*3)) == 0 && obj.counter >= 28) {
jjOBJ@ icicle = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos), int(obj.yPos), obj.objectID, CREATOR::OBJECT)];
icicle.xSpeed = 7 * obj.direction;
icicle.xAcc = 0;
icicle.yAcc = 0;
icicle.behavior = QueenIcicles();
icicle.ySpeed = -(2.5 + jjRandom()%4);
icicle.direction = -obj.direction;
icicle.counterEnd = 250;
icicle.var[4] = 1;
icicle.state = STATE::FLY;
icicle.playerHandling = HANDLING::ENEMYBULLET;
icicle.lightType = LIGHT::NONE;
}
if (obj.counter >= 84) {
setNewState(obj, Run);
}
break;
case PrepareThrow:
currAnim = 0;
lookAtPlayer(obj, play);
deflect = false;
reverseAnim = false;
obj.xSpeed = 0;
obj.special = 0;
if (obj.counter >= 50) {
throwSword = true;
jjSample(play.xPos, play.yPos, SOUND::TUFBOSS_CATCH);
setNewState(obj, Throw);
}
break;
case Throw:
currAnim = 3;
lookAtPlayer(obj, play);
deflect = true;
reverseAnim = false;
obj.xSpeed = 0;
if (obj.special == 1) {
setNewState(obj, Run);
}
break;
case MegaSlash:
if (obj.counter == 49) {
deflect = false;
obj.xSpeed = 8 * obj.direction;
jjSample(play.xPos, play.yPos, SOUND::COMMON_FOEW3, 63, 12000);
currAnim = 6;
reverseAnim = true;
deflect = false;
} else if (obj.counter < 49) {
obj.xSpeed = 0;
currAnim = 3;
reverseAnim = false;
lookAtPlayer(obj, play);
deflect = true;
}
if (obj.counter > 49 && obj.counter % 5 == 0) {
jjOBJ@ iceball = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos + jjSin(obj.counter*20)), int(obj.yPos + jjCos(obj.counter*20)), obj.objectID, CREATOR::OBJECT)];
jjSample(iceball.xPos, iceball.yPos, SOUND::AMMO_ICEGUN, 0, 0);
iceball.behavior = IceCloud;
iceball.curAnim = iceball.determineCurAnim(ANIM::AMMO, 82, true);
iceball.frameID=5;
iceball.determineCurFrame();
iceball.counterEnd=20;
iceball.freeze = 210;
iceball.state = STATE::FLY;
iceball.playerHandling = HANDLING::PARTICLE;
iceball.lightType = LIGHT::NONE;
iceball.xSpeed = ((jjSin(obj.counter*20)*6) * obj.direction) + obj.xSpeed;
iceball.ySpeed = (jjCos(obj.counter*20)*6);
iceball.killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
}
if (obj.counter == 84 || (obj.xPos >= 481.75*32 || obj.xPos <= 453.25*32)) {
setNewState(obj, Run);
}
break;
case Ultimate:
currAnim = 5;
if (floating) {
immune = true;
if (obj.yPos > 144*32) {
obj.ySpeed = -2;
} else {
obj.ySpeed = 0;
}
if (obj.special == 0) {
if (obj.xPos < 466.5*32) {
obj.xSpeed = 4;
} else if (obj.xPos > 466.5*32) {
obj.xSpeed = -4;
}
}
if (obj.xPos > 465.5*32 && obj.xPos < 467.5*32 && obj.yPos <= 144*32 && obj.special == 0) {
obj.xSpeed = obj.ySpeed = 0;
obj.special = 1;
obj.counter = 0;
}
if (obj.special == 1 && obj.counter % (40 - (jjDifficulty*6)) == 0) {
if (jjRandom()%4 == 0) {
jjOBJ@ sword = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, obj.xPos-496, jjRandom()%2 == 0? play.yPos : (obj.yPos+jjRandom()%212*(jjRandom()%2 == 0? 1:-1)), obj.objectID, CREATOR::OBJECT)];
sword.behavior = SwordProjectiles();
sword.state = STATE::START;
sword.var[0] = 0;
sword.deactivates = false;
}
if (jjRandom()%4 == 1) {
jjOBJ@ sword = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, obj.xPos+536, jjRandom()%2 == 0? play.yPos : (obj.yPos+jjRandom()%212*(jjRandom()%2 == 0? 1:-1)), obj.objectID, CREATOR::OBJECT)];
sword.behavior = SwordProjectiles();
sword.state = STATE::START;
sword.var[0] = 1;
sword.deactivates = false;
}
if (jjRandom()%4 == 2) {
jjOBJ@ sword = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, jjRandom()%2 == 0? play.xPos : (obj.xPos+jjRandom()%460*(jjRandom()%2 == 0? 1:-1)), 138*32, obj.objectID, CREATOR::OBJECT)];
sword.behavior = SwordProjectiles();
sword.state = STATE::START;
sword.var[0] = 2;
sword.deactivates = false;
}
if (jjRandom()%4 == 3) {
jjOBJ@ sword = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, jjRandom()%2 == 0? play.xPos : (obj.xPos+jjRandom()%460*(jjRandom()%2 == 0? 1:-1)), 153*32, obj.objectID, CREATOR::OBJECT)];
sword.behavior = SwordProjectiles();
sword.state = STATE::START;
sword.var[0] = 3;
sword.deactivates = false;
}
}
}
if (obj.counter >= swordTime) {
floating = false;
immune = false;
shield = false;
obj.ySpeed = 4;
if (onGround(obj)) {
jjLocalPlayers[0].showText("@@@@@@Wait, you're still alive?!@@Maybe you are worthy after all...");
setNewState(obj, Run);
}
}
if (obj.counter == 1 && !floating) {
jjLocalPlayers[0].showText("@@@@@@You've fought well.@@But no way are you going to survive this...");
floating = true;
shield = true;
}
break;
case DefeatTwo:
for (int i = 0; i <= jjLocalPlayerCount; i++) {
jjLocalPlayers[i].bossActivated = false;
}
obj.xSpeed = 0;
reverseAnim = false;
immune = true;
passive = true;
shield = false;
//jjMusicStop();
if (obj.counter == 1) {
currAnim = 1;
jjLocalPlayers[0].showText("@@@@@@@Okay fine, you win for real this time!");
}
if (obj.counter == 210) {
jjLocalPlayers[0].showText("@@@@@@@I will let you pass.@@But you have no chance against@the dangers that lie ahead...");
currAnim = 3;
obj.var[1] = 1;
}
if (obj.counter == 500) {
setNewState(obj, Exit);
}
break;
case Exit:
currAnim = 5;
obj.ySpeed = -3;
jjMusicStop();
if (obj.counter == 1) {
jjLocalPlayers[0].showText("@@@@@@@Good luck.@@You will need it...");
play.cameraUnfreeze();
jjOBJ@ crate = jjObjects[jjAddObject(OBJECT::TRIGGERCRATE, 482*32, 149*32, 0, CREATOR::LEVEL)];
jjParameterSet(int(crate.xPos/32), int(crate.yPos/32), 0, 5, 6);
}
if (obj.xPos <= 457*32) {
obj.special++;
}
if (obj.yPos <= 130*32) {
obj.delete();
}
break;
}
}
void setNewState(jjOBJ@ obj, int state) {
obj.counter = 0;
obj.special = 0;
obj.frameID = 0;
currState = state;
}
void randomAttack(jjOBJ@ obj, jjPLAYER@ play) {
if (currPhase == First) {
if (shortRangeCriteria(obj, play)) {
setNewState(obj, getNextAttack(shortRangeQueue, shortRangeAttacks));
}
if (longRangeCriteria(obj, play)) {
setNewState(obj, getNextAttack(longRangeQueue, longRangeAttacks));
}
} else if (currPhase == Second) {
if (shortRangeCriteria(obj, play)) {
setNewState(obj, getNextAttack(shortRangeQueue_PhaseTwo, shortRangeAttacks_PhaseTwo));
}
if (longRangeCriteria(obj, play)) {
setNewState(obj, getNextAttack(longRangeQueue_PhaseTwo, longRangeAttacks_PhaseTwo));
}
}
}
bool shortRangeCriteria(jjOBJ@ obj, jjPLAYER@ play) {
return play.xPos > (obj.xPos - 160) && play.xPos < (obj.xPos + 160) && isFacingPlayer(obj, play.playerID) && onGround(obj);
}
bool longRangeCriteria(jjOBJ@ obj, jjPLAYER@ play) {
return (play.xPos > (obj.xPos + 160) || play.xPos < (obj.xPos - 160)) && isFacingPlayer(obj, play.playerID) && onGround(obj);
}
int xDistance(jjOBJ@ obj, jjPLAYER@ play) {
return int((obj.xPos - play.xPos) / 32);
}
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 - 88) || obj.yPos > int(play.yPos - 32)) {
obj.ySpeed = -speed * jjCos(angle);
} else {
obj.ySpeed = 0;
}
}
void onDraw(jjOBJ@ obj) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, -obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : SPRITE::NORMAL, jjLocalPlayers[0].blink == 0? 15:32);
if (shield) {
jjDrawSprite(obj.xPos, obj.yPos, ANIM::MENU, 1, 8, obj.direction, SPRITE::ALPHAMAP, 33);
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
if (bull !is null) {
float multiplier = (bull.var[3] == 4 || bull.var[3] == 5 || bull.var[3] == 6)? 0.4f : 0.6f;
if (bull.playerHandling == HANDLING::PLAYERBULLET && bull.creatorType == CREATOR::PLAYER) {
if (deflect && onGround(obj)) {
if (bull.xSpeed > 0) {
if (obj.direction == -1) {
bull.ricochet();
} else {
if (!immune) {
obj.justHit = 5;
if (bull.var[3] != 3) {
bossHP -= (damageCalc(obj, bull, armor, play) > (bull.animSpeed * multiplier))? damageCalc(obj, bull, armor, play) : (bull.animSpeed * multiplier);
armor += bull.animSpeed;
}
jjPARTICLE@ text = jjAddParticle(PARTICLE::STRING);
/*if (text !is null) {
text.xPos = obj.xPos;
text.yPos = obj.yPos;
text.ySpeed = -0.25;
text.string.text = "" + (damageCalc(obj,bull,armor,play) > (bull.animSpeed * multiplier)? damageCalc(obj,bull,armor, play) : (bull.animSpeed * multiplier));
}*/
}
if ((bull.var[6] & 16) == 0 && bull.creatorType == CREATOR::PLAYER) {
bull.state = STATE::EXPLODE;
}
}
} else if (bull.xSpeed < 0) {
if (obj.direction == 1) {
bull.ricochet();
} else {
if (!immune) {
obj.justHit = 5;
if (bull.var[3] != 3) {
bossHP -= (damageCalc(obj, bull, armor, play) > (bull.animSpeed * multiplier))? damageCalc(obj, bull, armor, play) : (bull.animSpeed * multiplier);
armor += bull.animSpeed;
}
jjPARTICLE@ text = jjAddParticle(PARTICLE::STRING);
/*if (text !is null) {
text.xPos = obj.xPos;
text.yPos = obj.yPos;
text.ySpeed = -0.25;
text.string.text = "" + (damageCalc(obj,bull,armor,play) > (bull.animSpeed * multiplier)? damageCalc(obj,bull,armor, play) : (bull.animSpeed * multiplier));
}*/
}
if ((bull.var[6] & 16) == 0 && bull.creatorType == CREATOR::PLAYER) {
bull.state = STATE::EXPLODE;
}
}
}
} else {
if (!immune) {
obj.justHit = 5;
if (bull.var[3] != 3) {
bossHP -= (damageCalc(obj, bull, armor, play) > (bull.animSpeed * multiplier))? damageCalc(obj, bull, armor, play) : (bull.animSpeed * multiplier);
armor += bull.animSpeed;
}
jjPARTICLE@ text = jjAddParticle(PARTICLE::STRING);
/*if (text !is null) {
text.xPos = obj.xPos;
text.yPos = obj.yPos;
text.ySpeed = -0.25;
text.string.text = "" + (damageCalc(obj,bull,armor,play) > (bull.animSpeed * multiplier)? damageCalc(obj,bull,armor, play) : (bull.animSpeed * multiplier));
}*/
}
if ((bull.var[6] & 16) == 0 && bull.creatorType == CREATOR::PLAYER) {
bull.state = STATE::EXPLODE;
}
}
}
} else if (play !is null && !passive) {
play.hurt(1, false);
}
return true;
}
}
float damageCalc(jjOBJ@ obj, jjOBJ@ bull, int armor, jjPLAYER@ play) {
if (armor <= bull.animSpeed) {
return bull.animSpeed * (play.blink == 0? 1:0.125);
} else {
return (bull.animSpeed - ((0.0125f * (bull.animSpeed*3)) * armor)) * (play.blink == 0? 1:0.125);
}
}
bool isFacingPlayer(jjOBJ@ obj, int playerID) {
if (((obj.xPos < (jjPlayers[playerID].xPos - 32) && obj.direction == 1) || (obj.xPos > (jjPlayers[playerID].xPos + 32) && obj.direction == -1)) && obj.yPos <= int(jjPlayers[playerID].yPos + 256) && obj.yPos >= int(jjPlayers[playerID].yPos - 256)) return true;
return false;
}
bool onGround(jjOBJ@ obj) {
return obj.yPos >= 149.25*32;
}
void lookAtPlayer(jjOBJ@ obj, jjPLAYER@ play) {
if (play.xPos > int(obj.xPos+96)) obj.direction = 1;
else if (play.xPos < int(obj.xPos-96)) obj.direction = -1;
}
bool directlyBelow(jjOBJ@ obj, jjPLAYER@ play) {
return ((play.xPos >= (obj.xPos - 16)) && (play.xPos <= (obj.xPos + 16))) && play.yPos > obj.yPos;
}
bool throwSword = false;
int halfScreenX() {
return int(jjSubscreenWidth / 2);
}
int halfScreenY() {
return int(jjSubscreenHeight / 2);
}
class QueenIcicles : 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 {
jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[70], 0, 0, obj.var[0], 1, 1, SPRITE::NORMAL);
if (obj.var[4] == 1) {
obj.ySpeed += 0.15;
}
if (obj.var[4] == 2) {
if (obj.ySpeed < 0) obj.ySpeed *= -1;
}
}
}
}
class QueenBricks : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::BULLET);
obj.determineCurAnim(ANIM::QUEEN, 4);
obj.determineCurFrame();
obj.xSpeed = obj.xAcc = 0;
if (obj.state == STATE::EXPLODE) {
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_COLLAPS);
obj.particlePixelExplosion(0);
obj.delete();
}
}
}
class QueenBlast : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.determineCurAnim(ANIM::CUSTOM[69], 2);
obj.determineCurFrame();
obj.xPos = obj.xPos + obj.xSpeed;
obj.counter++;
obj.yPos = 150*32;
if (obj.counter % 7 == 0) {
obj.frameID++;
}
if (uint(obj.counter) == obj.counterEnd || obj.xPos <= 451.5*32 || obj.xPos >= 482.5*32) {
obj.delete();
}
}
void onDraw(jjOBJ@ obj) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::SINGLEHUE, 32);
}
}
class IceQueenSpirit : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.determineCurAnim(ANIM::QUEEN, 5);
obj.determineCurFrame();
obj.yPos = obj.yPos + obj.ySpeed;
obj.counter++;
if (obj.counter % 7 == 0) {
obj.frameID++;
}
for (int i = 1; i < jjObjectCount; i++) {
if (jjObjects[i].eventID == OBJECT::QUEEN && obj.yPos <= jjObjects[i].yPos + 4 && obj.yPos >= jjObjects[i].yPos - 4) {
obj.delete();
}
}
}
void onDraw(jjOBJ@ obj) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, -obj.direction, SPRITE::TRANSLUCENTMAPPING, 0);
}
}
class IceTrail : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.counter++;
for (int i = 1; i < jjObjectCount; i++) {
if (jjObjects[i].eventID == OBJECT::QUEEN && obj.doesCollide(jjObjects[i], true) && obj.var[1] == 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 SwordProjectiles : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.determineCurAnim(ANIM::CUSTOM[69], 0);
obj.determineCurFrame();
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
obj.counter++;
if (obj.state == STATE::START) {
if (obj.counter == 1) jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ITEMTRE);
obj.xSpeed = obj.ySpeed = 0;
obj.playerHandling = HANDLING::PARTICLE;
if (obj.counter >= 70) {
obj.state = STATE::FLY;
jjSample(obj.xPos, obj.yPos, SOUND::SCIENCE_PLOPKAOS);
}
}
if (obj.state == STATE::FLY) {
obj.playerHandling = HANDLING::ENEMYBULLET;
switch (obj.var[0]) {
case 0: obj.xSpeed = 8; obj.ySpeed = 0; break;
case 1: obj.xSpeed = -8; obj.ySpeed = 0; break;
case 2: obj.xSpeed = 0; obj.ySpeed = 8; break;
case 3: obj.xSpeed = 0; obj.ySpeed = -8; break;
default: obj.xSpeed = 8; obj.ySpeed = 0; break;
}
if (obj.counter >= 420) obj.delete();
}
switch (obj.var[0]) {
case 0: obj.frameID = 14; break;
case 1: obj.frameID = 38; break;
case 2: obj.frameID = 26; break;
case 3: obj.frameID = 50; break;
default: obj.frameID = 14; break;
}
}
void onDraw(jjOBJ@ obj) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, obj.state == STATE::START? SPRITE::NEONGLOW : SPRITE::NORMAL, 5);
}
}
int objReturnAngle(jjOBJ@ obj, jjOBJ@ obj2) {
return int(atan2(obj2.xPos - obj.xPos, obj2.yPos - obj.yPos) * (512 / PI));
}
class QueenSwordWeapon : jjBEHAVIORINTERFACE {
int currAnim;
bool freezeAnim;
void onBehave(jjOBJ@ obj) {
obj.determineCurAnim(ANIM::CUSTOM[69], currAnim);
obj.determineCurFrame();
if (jjGameTicks % 2 == 0 && !freezeAnim) {
obj.frameID++;
}
int playerID = obj.findNearestPlayer(1000000);
jjPLAYER@ play = jjPlayers[playerID];
int attackAngle = int(atan2(obj.xPos - play.xPos - (play.xSpeed * 16), obj.yPos - play.yPos - (play.ySpeed * 16)) * (512 / PI));
if (passive) obj.playerHandling == HANDLING::PARTICLE;
else obj.playerHandling == HANDLING::ENEMYBULLET;
if (currState >= DefeatTwo) {
obj.particlePixelExplosion(31);
obj.delete();
}
switch (currState) {
case Intro:
currAnim = 0;
freezeAnim = true;
obj.frameID = 0;
break;
case Run:
currAnim = 1;
freezeAnim = true;
obj.frameID = 0;
break;
case Slash:
currAnim = 0;
freezeAnim = false;
if (obj.counter % 35 == 0) obj.frameID = 0;
break;
case Spin:
currAnim = 0;
freezeAnim = false;
break;
case Charge:
currAnim = 1;
freezeAnim = true;
obj.frameID = 0;
break;
case Bunnyhop:
currAnim = 1;
freezeAnim = true;
obj.frameID = 0;
break;
case SpinJump:
currAnim = 0;
freezeAnim = false;
break;
case Stomp:
currAnim = 0;
freezeAnim = true;
obj.frameID = 0;
break;
case Downward:
currAnim = 0;
freezeAnim = true;
obj.frameID = 26;
break;
case Bounce:
currAnim = 0;
freezeAnim = true;
obj.frameID = 26;
break;
case Defeat:
currAnim = 1;
freezeAnim = true;
obj.frameID = 0;
break;
case Transform:
currAnim = 0;
freezeAnim = true;
obj.frameID = 0;
break;
case Icicles:
currAnim = 0;
freezeAnim = true;
obj.frameID = 0;
break;
case PrepareThrow:
currAnim = 0;
freezeAnim = false;
break;
case Throw:
currAnim = 0;
freezeAnim = false;
if (obj.counter % 9 == 0) {
jjOBJ@ iceball = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, int(obj.xPos + jjSin(obj.counter*20)), int(obj.yPos + jjCos(obj.counter*20)), obj.objectID, CREATOR::OBJECT)];
jjSample(iceball.xPos, iceball.yPos, SOUND::AMMO_ICEGUN, 0, 0);
iceball.behavior = IceCloud;
iceball.curAnim = iceball.determineCurAnim(ANIM::AMMO, 82, true);
iceball.frameID=5;
iceball.determineCurFrame();
iceball.counterEnd=20;
iceball.freeze = 210;
iceball.state = STATE::FLY;
iceball.playerHandling = HANDLING::PARTICLE;
iceball.lightType = LIGHT::NONE;
iceball.xSpeed = ((jjSin(obj.counter*20)*6) * obj.direction) + obj.xSpeed;
iceball.ySpeed = (jjCos(obj.counter*20)*6);
iceball.killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
}
break;
case MegaSlash:
currAnim = 0;
if (obj.counter < 49) {
obj.frameID = 37;
freezeAnim = true;
} else {
freezeAnim = false;
}
break;
case Ultimate:
currAnim = 0;
freezeAnim = false;
break;
case DefeatTwo:
currAnim = 1;
freezeAnim = true;
obj.frameID = 0;
break;
case Exit:
currAnim = 1;
freezeAnim = true;
obj.frameID = 0;
break;
}
if (currState == SpinJump) {
if (obj.var[2] == 0) {
obj.frameID = 10;
obj.var[2] = 1;
}
} else {
obj.var[2] = 0;
}
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
if (!throwSword) obj.xSpeed = obj.ySpeed = 0;
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ o = jjObjects[i];
if (o.eventID == OBJECT::QUEEN) {
if (currState != Icicles && currState != PrepareThrow && currState != Throw) {
if (currState == Defeat || currState == DefeatTwo) {
if (obj.var[1] == 0) {
if (obj.yPos < 150*32) obj.yPos += 1;
else if (obj.var[0] == 0) {
jjSample(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::ROBOT_POLE);
obj.var[0] = 1;
}
}
} else {
obj.xPos = o.xPos - (10 * o.direction);
obj.yPos = o.yPos + 3;
}
} else {
if (throwSword) {
swordSample = jjSampleLooped(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::TUFBOSS_SWING, swordSample, 0, 0);
if (obj.var[1] == 0) {
obj.xSpeed = -7 * jjSin(attackAngle);
obj.ySpeed = -7 * jjCos(attackAngle);
obj.var[1] = 1;
}
if (obj.counter >= 90 && obj.var[1] == 1) {
obj.xSpeed = -7 * jjSin(objReturnAngle(o, obj));
obj.ySpeed = -7 * jjCos(objReturnAngle(o, obj));
if (obj.doesCollide(o, true)) {
throwSword = false;
obj.var[1] = 0;
o.special = 1;
}
}
} else {
obj.xPos = o.xPos - (27 * o.direction);
obj.yPos = o.yPos + 4;
}
}
obj.justHit = o.justHit;
obj.direction = o.direction;
obj.counter = o.counter;
obj.energy = o.energy;
obj.playerHandling = currState == Defeat? HANDLING::PARTICLE : HANDLING::ENEMYBULLET;
}
}
}
void onDraw(jjOBJ@ obj) {
jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 0, 1*obj.direction, 1, obj.justHit > 0? SPRITE::SINGLECOLOR : currPhase == Second? SPRITE::MAPPING : SPRITE::NORMAL, obj.justHit > 0? 15 : 1, 3, 3);
if (!freezeAnim) jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[69], currAnim, obj.frameID > 0? obj.frameID : obj.frameID-1, 0, 1*obj.direction, 1, currPhase == Second? SPRITE::TRANSLUCENTMAPPING : SPRITE::TRANSLUCENT, 1, 3, 3);
}
}
void onPlayer(jjPLAYER@ play) {
play.lightType = LIGHT::NONE;
HH24::player(play);
if (SMOKE_HH24::golemDeathCount == 2) {
jjAlert("||ARRRGH!",false,STRING::MEDIUM);
play.bossActivated = false;
jjTriggers[2] = false;
play.cameraUnfreeze(false);
SMOKE_HH24::golemDeathCount = -1;
jjMusicStop();
}
if (play.xPos > 231*32) reachedCastle = true;
if (reachedCastle && jjGameTicks == 7) jjMusicLoad("jj2_symphoni_dexentrique.mo3");
const int bounds = 24;
if (play.xPos > ((jjLayerWidth[4]*32) - bounds) || play.xPos < bounds) {
play.xPos = play.xPos < (bounds+1)? bounds : (jjLayerWidth[4]*32) - bounds;
play.xSpeed = 0;
play.specialMove = 0;
}
if (play.yPos < 8) play.yPos = 8;
if (play.yPos >= 164*32 && play.xPos >= 497*32) {
fallDownHole = true;
play.xPos = 394*32;
play.yPos = 164*32;
}
if (fallDownHole) {
play.keyLeft = play.keyRight = play.keyUp = play.keyDown = play.keyFire = play.keyJump = play.keyRun = play.keySelect = false;
play.idle = 0;
play.invisibility = true;
if (fallScale == 10) {
jjSample(play.xPos, play.yPos, SOUND::BONUS_BONUS1, 42, 0);
fallScale -= 0.05;
} else {
fallScale -= 0.05;
fallAngle += 5;
if (fallAngle > 1024) fallAngle = 0;
}
}
switch (play.charCurr) {
case CHAR::JAZZ: fallAnimSet = ANIM::JAZZ; break;
case CHAR::SPAZ: fallAnimSet = ANIM::SPAZ; break;
case CHAR::LORI: fallAnimSet = ANIM::LORI; break;
default: fallAnimSet = ANIM::JAZZ; break;
}
if (fallScale <= 0) {
fallScale = 0;
HH24::endLevel();
}
bool found = false;
for (int i = 0; i < jjObjectCount; i++) {
const auto@ obj = jjObjects[i];
if (cast<const jjBEHAVIORINTERFACE>(obj.behavior) is bubble) {
float x = obj.xPos - play.xPos;
float y = obj.yPos - play.yPos;
if (x * x + y * y < obj.var[0] * obj.var[0]) {
found = true;
break;
}
}
}
jjSetWaterLevel(getBubble(play) !is null ? 0.f : jjLayerHeight[4] * 32 + 128.f, true);
}
void onPlayerInput(jjPLAYER@ player) {
if (!player.keyFire) {
gunJammedTimer[player.localPlayerID] = 0;
return;
}
if (getBubble(player) !is null) {
if (gunJammedTimer[player.localPlayerID] == 0) {
jjSamplePriority(sound::dryFire);
const auto@ frame = jjAnimFrames[player.curFrame];
float x = player.xPos + (player.direction < 0 ? -1 : 1) * (frame.hotSpotX - frame.gunSpotX);
float y = player.yPos + frame.hotSpotY - frame.gunSpotY;
int id = jjAddObject(OBJECT::EXPLOSION, x, y);
if (id > 0)
jjObjects[id].determineCurAnim(ANIM::AMMO, 6);
}
player.keyFire = false;
gunJammedTimer[player.localPlayerID] = 30;
return;
}
if (gunJammedTimer[player.localPlayerID] > 0) {
player.keyFire = false;
gunJammedTimer[player.localPlayerID]--;
}
}
int triggerCount = 0;
void onMain() {
HH24::main();
HH17::handleEnemyProjectiles();
array<jjLAYER@> layers = jjLayerOrderGet();
layers[0].hasTiles = layers[1].hasTiles = layers[2].hasTiles = layers[3].hasTiles = jjColorDepth == 16 && !jjLowDetail? true:false;
jjLayers[8].spriteParam = fallDownHole? 3:9;
jjLayers[8].textureStyle = fallDownHole? TEXTURE::TUNNEL : TEXTURE::WARPHORIZON;
jjLayers[8].texture = fallDownHole? TEXTURE::CORRUPTEDSANCTUARY : TEXTURE::DIAMONDUSBETA;
jjLayers[8].xAutoSpeed = fallDownHole? 0 : 0.15;
if (fallDownHole) {
for (uint l = 0; l < layers.length() - 1; l++) {
layers[l].hasTiles = false;
}
}
sample = jjSampleLooped(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::WIND_WIND2A, sample, jjLocalPlayers[0].lighting < 100? 12:40, 0);
jjIsSnowing = !jjLowDetail && !fallDownHole;
if (jjIsSnowing) {
for (int i = 0; i < 1024; i++) {
jjPARTICLE@ particle = jjParticles[i];
if (particle.type == PARTICLE::FLOWER && particle !is null && !particle.isActive) {
if (jjTileGet(7, int(particle.xPos/32), int(particle.yPos/32)) != 0 && particle.flower.size >= 63) {
particle.type = PARTICLE::INACTIVE;
}
}
}
}
array<jjOBJ@> bubbles;
array<jjOBJ@> spikeBalls;
for (int i = 0; i < jjObjectCount; i++) {
auto@ obj = jjObjects[i];
if (obj.isActive) {
const auto@ behavior = cast<const jjBEHAVIORINTERFACE>(obj.behavior);
if (behavior is bubble)
bubbles.insertLast(@obj);
else if (behavior is spikeBall && obj.state == STATE::ATTACK)
spikeBalls.insertLast(@obj);
}
}
for (uint i = 0; i < bubbles.length(); i++) {
auto@ obj = bubbles[i];
int radius = obj.var[bubble_size];
for (uint j = 0; j < spikeBalls.length(); j++) {
auto@ ball = spikeBalls[j];
float x = ball.xPos - obj.xPos;
float y = ball.yPos - obj.yPos;
if (x * x + y * y < radius * radius)
bubble.destroy(obj);
}
}
}
void CheckpointWrapper(jjOBJ@ obj) {
if (obj.state == STATE::STOP) { //don't do anything anymore
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame);
} else if (obj.state == STATE::DEACTIVATE) { //due to death
obj.deactivate();
} else {
obj.behave(BEHAVIOR::CHECKPOINT);
if (obj.state == STATE::DONE) { //triggered by the player hitting it
obj.state = STATE::STOP;
//save the current state of some properties
for (uint i = 0; i < 32; ++i)
SavedTriggers[i] = jjTriggers[i];
//OPTIONAL: this loop makes checkpoints reusable, so only the most recent checkpoint you touched is ever active
for (int i = jjObjectCount; --i > 0;) {
jjOBJ@ obj2 = jjObjects[i];
if (obj2.eventID == OBJECT::CHECKPOINT && i != obj.objectID && obj2.isActive) {
obj2.state = STATE::SLEEP;
obj2.var[0] = 0;
}
}
}
}
}
class GolemHP : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::WALKINGENEMY, false);
for (int i = 0; i < jjLocalPlayerCount; i++) {
if (jjLocalPlayers[i].bossActivated) {
jjLocalPlayers[i].boss = obj.objectID;
}
}
obj.energy = int(SMOKE_HH24::golemDuoHP / 2);
}
}
class ColouredCrate : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::CRATE, false);
int colour = jjParameterGet(int(obj.xPos/32), int(obj.yPos/32), 0, 5);
int palShift = colour == 1? -32 : colour == 3? -48 : colour == 4? -24 : colour == 5? 24 : colour == 6? -40 : 0;
if (obj.state == STATE::KILL) {
for (uint i = 0; i < 32; ++i) {
SavedTriggers[i] = jjTriggers[i];
}
jjEventSet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0);
}
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, palShift);
}
}
class Imitation : jjBEHAVIORINTERFACE {
private uint8 eventID;
private jjBEHAVIOR behavior;
Imitation(uint8 realEventID, uint8 fakeEventID) {
jjOBJ@ obj = jjObjectPresets[realEventID];
eventID = obj.eventID;
behavior = obj.behavior;
obj.eventID = fakeEventID;
obj.behavior = this;
}
void onBehave(jjOBJ@ obj) override {
if (obj.state == STATE::DEACTIVATE)
obj.eventID = eventID;
obj.behave(behavior);
}
}
class GiftBox : jjBEHAVIORINTERFACE {
void destroy(jjOBJ@ obj) {
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_WOOD1);
{
int id = jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos);
if (id != 0) {
jjOBJ@ other = jjObjects[id];
other.determineCurAnim(ANIM::PICKUPS, 4);
}
}
for (int i = jjRandom() & 7 | 8; i-- != 0;) {
int id = jjAddObject(OBJECT::SHARD, obj.xPos, obj.yPos);
if (id != 0) {
jjOBJ@ other = jjObjects[id];
other.determineCurAnim(ANIM::PICKUPS, 93 + (jjRandom() & 1));
}
}
obj.yPos -= 8.f;
for (int i = obj.var[1]; i-- != 0;) {
int id = jjAddObject(obj.var[0], obj.xPos, obj.yPos);
if (id != 0) {
jjOBJ@ other = jjObjects[id];
if (other.playerHandling == HANDLING::PICKUP) {
int angle = (jjRandom() & 255) + 128;
other.xSpeed = jjCos(angle) * 5.f;
other.ySpeed = jjSin(angle) * -3.f;
} else if (other.playerHandling == HANDLING::SPECIAL) {
other.deactivates = false;
}
}
}
obj.clearPlatform();
obj.delete();
}
void onBehave(jjOBJ@ obj) override {
switch (obj.state) {
case STATE::START:
{
uint16 x = int(obj.xOrg) >>> 5;
uint16 y = int(obj.yOrg) >>> 5;
obj.var[0] = jjParameterGet(x, y, 0, 8);
obj.var[1] = jjParameterGet(x, y, 8, 4);
obj.curAnim += jjParameterGet(x, y, 12, 2);
obj.determineCurFrame();
obj.bulletHandling = HANDLING::DESTROYBULLET;
obj.scriptedCollisions = true;
}
break;
case STATE::FALL:
obj.var[2] = 1;
break;
case STATE::FREEZE:
case STATE::SLEEP:
if (obj.var[2] != 0) {
destroy(obj);
return;
}
}
obj.behave(BEHAVIOR::MONITOR);
}
bool onObjectHit(jjOBJ@, jjOBJ@, jjPLAYER@, int) {
return true;
}
bool onIsSolid(jjOBJ@) {
return true;
}
}
void onFunction0(jjPLAYER@ play, bool offset) {
if (SMOKE_HH24::golemDeathCount == 0 && !iceGolemFight) {
iceGolemFight = true;
jjMusicLoad("Battle05.s3m");
if (!splitscreen) play.cameraFreeze(198.5*32,85*32,true,false);
jjTriggers[2] = true;
play.bossActivated = true;
jjAlert("||WHO DARES ENTER OUR LAIR?", false, STRING::MEDIUM);
jjAlert("||YOU SHALL PERISH!", false, STRING::MEDIUM);
jjSamplePriority(SOUND::INTRO_MONSTER2);
jjSamplePriority(SOUND::INTRO_MONSTER2);
iceGolemFight = true;
}
}
void onFunction1(jjPLAYER@ play, bool offset) {
if (!reachedCastle) {
reachedCastle = true;
jjMusicLoad("jj2_symphoni_dexentrique.mo3");
}
}
namespace HH24Enemies {
namespace Private {
void applyGenericEnemySettingsToPreset(jjOBJ@ preset) {
preset.causesRicochet = false;
preset.isBlastable = false;
preset.triggersTNT = true;
preset.isFreezable = true;
preset.isTarget = true;
preset.direction = 1;
preset.freeze = 0;
preset.state = STATE::START;
}
}
jjOBJ@ MakeEventHH24Fencer(uint8 eventID) {
jjAnimSets[ANIM::FENCER].load();
for (uint i = 0; i < 2; i++) {
jjANIMATION@ animFencer = jjAnimations[jjAnimSets[ANIM::FENCER] + i];
for (uint j = 0; j < animFencer.frameCount; j++) {
jjANIMFRAME@ frame = jjAnimFrames[animFencer + j];
jjPIXELMAP sprite(frame);
for (uint x = 0; x < sprite.width; ++x) {
for (uint y = 0; y < sprite.height; ++y) {
if (sprite[x,y] == 24 || sprite[x,y] == 25) sprite[x,y] = 77;
if (sprite[x,y] == 26 || sprite[x,y] == 27) sprite[x,y] = 78;
if (sprite[x,y] == 28 || sprite[x,y] == 29) sprite[x,y] = 79;
if (sprite[x,y] == 30 || sprite[x,y] == 31) sprite[x,y] = 95;
if (sprite[x,y] >= 48 && sprite[x,y] <= 55) sprite[x,y] += 40;
if (sprite[x,y] >= 80 && sprite[x,y] <= 87) sprite[x,y] -= 24;
}
}
sprite.save(frame);
}
}
jjOBJ@ preset = jjObjectPresets[eventID];
HH24Enemies::Private::applyGenericEnemySettingsToPreset(preset);
[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.