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::GravityWell::Weapon()}); ///@MLLE-Generated
#include "MLLE-Include-1.7w.asc" ///@MLLE-Generated
#pragma require "HH24_Guardian-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "Cracco Castle 11.j2t" ///@MLLE-Generated
#pragma require "Legendary.j2t" ///@MLLE-Generated
#pragma require "xlmdamnice.j2t" ///@MLLE-Generated
#pragma require "coldcavern.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_Guardian.j2l" ///@MLLE-Generated
#include "WeaponVMega7.asc" ///@MLLE-Generated
#pragma require "WeaponVMega7.asc" ///@MLLE-Generated
#pragma require "SExmas.j2a"
#pragma require "HH18E1.j2a"
#pragma require "HH17_Roar.wav"
#include "hh17enemies_HH24.asc"
#include "SmokeBossUtilsV1.asc"
#pragma require "ShowgemsV.png"
#include "HH24.asc"
/*Custom enemies for Holiday Hare 18, by SmokeNC.
I didn't make any of the sprite animation myself and the credits go
to their rightul owners. They were mainly taken from the
Sprites Resource and Sprites-Inc*/
const uint8 ChromaKeyIndex = 32;
int LayerZ;
int elapsed = 0;
bool camera, halfmsg, showOnce = false;
uint labelFrameID;
void InitChroma()
{
auto layers = jjLayerOrderGet();
LayerZ = 4 - layers.findByRef(jjLayers[4]) + 1;
jjLAYER storm(1, 1);
storm.spriteMode = SPRITE::CHROMAKEY;
storm.spriteParam = ChromaKeyIndex;
storm.textureSurface = SURFACE::FULLSCREEN;
storm.xSpeed = storm.ySpeed = 1.1;
jjPIXELMAP stormImage(TEXTURE::DESOLATION);
array<uint8> recolor(256);
for (uint i = 0; i < 32; ++i)
recolor[176 + i] = 32 + uint8(i / 5.2);
stormImage.recolor(recolor).makeTexture(storm);
storm.textureStyle = TEXTURE::WAVE;
storm.tileWidth = storm.tileHeight = true;
layers.insertAt(0, storm);
jjLayerOrderSet(layers);
// mess with these numbers and see what looks good
storm.wave.amplitudeX = 0.125;
storm.wave.wavelengthX = 127;
storm.wave.amplitudeY = 0.25;
storm.wave.wavelengthY = 255;
storm.wave.waveSpeed = 3;
}
void onLevelLoad()
{
InitChroma();
jjAnimSets[ANIM::CUSTOM[17]].load(8, "HH18E1.j2a");
jjAnimSets[ANIM::CUSTOM[27]].load(12, "HH18E1.j2a");
// jjAnimSets[ANIM::CUSTOM[18]].load(3, "HH18E1.j2a");
jjAnimSets[ANIM::CUSTOM[21]].load(6, "HH18E1.j2a");
jjAnimSets[ANIM::DEVILDEVAN].load();
jjAnimSets[ANIM::ROCK].load();
jjAnimSets[ANIM::RAPIER].load();
jjObjectPresets[OBJECT::CARROT].behavior = SMOKE::StillPickup();
// jjObjectPresets[OBJECT::GOLDCOIN].behavior = SMOKE::NoDeactivateGenerator();
jjObjectPresets[OBJECT::GOLDCOIN].deactivates = false;
jjObjectPresets[OBJECT::GOLDCOIN].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::GOLDCOIN].scriptedCollisions = true;
jjObjectPresets[OBJECT::SILVERCOIN].behavior = SMOKE::GeneratorGenerator();
jjObjectPresets[OBJECT::SILVERCOIN].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::SILVERCOIN].scriptedCollisions = true;
jjObjectPresets[OBJECT::SILVERCOIN].deactivates = false;
jjObjectPresets[OBJECT::CARROT].deactivates = false;
SMOKE::ICEDRAGON(OBJECT::DRAGONFLY, OBJECT::CRAB);
HH17::setEnemy(OBJECT::LABRAT);
HH17::setEnemy(OBJECT::BAT);
HH17::setEnemy(OBJECT::DRAGON);
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;
}
if (jjAnimSets[ANIM::PLUS_BETA] == 0)
jjAnimSets[ANIM::PLUS_BETA].load();
uint setID;
jjANIMFRAME@ frame;
for (uint customAnimSetID = 0; customAnimSetID < 256; ++customAnimSetID)
if (jjAnimSets[setID = ANIM::CUSTOM[customAnimSetID]] == 0) {
@frame = jjAnimFrames[labelFrameID = jjAnimations[jjAnimSets[setID].load(jjPIXELMAP("ShowgemsV.png"), 85,42)]];
break;
}
frame.hotSpotX = -22;
frame.hotSpotY = -22;
@frame = jjAnimFrames[labelFrameID + 1];
frame.hotSpotX = -20;
frame.hotSpotY = -20;
HH24::levelLoad();
}
void onLevelReload() {
MLLE::SpawnOffgridsLocal();
HH17::processEnemyColors();
jjMusicLoad("jm-deepd3.it");
elapsed = 0;
camera = false;
halfmsg = false;
HH24::levelReload();
}
namespace SMOKE
{
const float PI = 3.14159265359;
float max(float x, float y) { return x > y ? x : y; }
int max(int x, int y) { return x > y ? x : y; }
int min(int x, int y) { return x < y ? x : y; }
int difficulty_dec()
{
return max(2 - jjDifficulty, 0);
}
int difficulty_inc()
{
return min(jjDifficulty, 2);
}
float abs(float x) { return x > 0 ? x : -x; }
int headId;
class StillPickup : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
if (obj.xPos > 100*32)
obj.direction = 1;
if (obj.xPos < 100*32)
obj.direction = -1;
if (obj.state == STATE::FLOATFALL) obj.state = STATE::FLOAT;
obj.behave(BEHAVIOR::PICKUP);
}
}
class GeneratorGenerator : jjBEHAVIORINTERFACE
{
void onBehave(jjOBJ@ obj)
{
int idx = jjAddObject(OBJECT::GOLDCOIN, obj.xPos, obj.yPos, obj.objectID, CREATOR::OBJECT);
jjObjects[idx].behavior = NoDeactivateGenerator();
obj.delete();
}
}
class NoDeactivateGenerator : jjBEHAVIORINTERFACE
{
int spawnTime = 70 * 20;
int _t = 0;
int sonID = -1;
void onBehave(jjOBJ@ obj)
{
if(sonID == -1)
{
sonID = jjAddObject(OBJECT::CARROT, obj.xPos, obj.yPos, obj.objectID, CREATOR::OBJECT);
}
else
{
if(jjObjects[sonID].isActive == false || jjObjects[sonID].eventID != OBJECT::CARROT)
{
_t++;
; if(_t == spawnTime)
{
sonID = -1;
_t = 0;
}
}
}
}
}
void ICEDRAGON(OBJECT::Object eventID1, OBJECT::Object eventID2)
{
jjAnimSets[ANIM::CUSTOM[22]].load(14, "HH18E1.j2a");
jjObjectPresets[eventID1].behavior = SMOKE::IceDrag();
jjObjectPresets[eventID2].behavior = SMOKE::IceDragBody();
jjObjectPresets[eventID2].determineCurAnim(ANIM::CUSTOM[22], 0);
jjObjectPresets[eventID2].energy = 127;
}
float g_maxR = 32;
int g_lastHeadIdx = 0;
float g_lastHpIDx = g_hpBossMax;
float g_hpBossMax = 300 + (difficulty_inc()*100);
float g_currBossHp = g_hpBossMax;
float g_hpBodyMax = 1;
int g_numCloseThresh = 32;
bool g_bossHalfHP = false;
const int NUM_BODY_PARTS = 250;
array<uint8> g_split_timestamps(int(g_hpBossMax*2/3), int(g_hpBossMax/3));
enum snakeHeadAttaks
{
STATE_FOLLOW_ACC,
STATE_FOLLOW,
STATE_FOLLOW_ARC,
STATE_FOLLOW_ACC_HALF_HP
};
class HandleFollowAcc : SyncTimer
{
bool shouldReset = false;
HandleFollowAcc(int dur, bool started = false, int off = 0, int outReady = -1)
{
super(dur, started, off, outReady);
}
void Perform(jjOBJ @obj, float cSpeed)
{
if (shouldReset)
{
obj.xSpeed = 0;
obj.ySpeed = 0;
shouldReset = false;
}
if (_t < 70)
{
cSpeed = -0.04;
}
// GoTo(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
// jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, cSpeed, 128);
float angle = GetAnglePlayer(obj);
if (abs(obj.xSpeed + sin(angle) * cSpeed) < 5)
obj.xSpeed += sin(angle) * cSpeed;
if (abs(obj.ySpeed + cos(angle) * cSpeed) < 5)
obj.ySpeed += cos(angle) * cSpeed;
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
}
void reset() override
{
SyncTimer::reset();
shouldReset = true;
}
}
class HandleFollowArc : SyncTimer
{
bool shouldReset = false;
HandleFollowArc(int dur, bool started = false, int off = 0, int outReady = -1)
{
super(dur, started, off, outReady);
}
void Perform(jjOBJ @obj, float cSpeed)
{
if (shouldReset)
{
obj.xSpeed = 0;
obj.ySpeed = 0;
shouldReset = false;
}
if(GetDistPlayer(obj) > 600 )
GoTo(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, 8, 0);
GoArc(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, 10, 0);
if(GetDistPlayer(obj) < 500 )
GoTo(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, -8, 0);
}
void reset() override
{
SyncTimer::reset();
shouldReset = true;
}
}
class FireSingleShot : SyncTimer
{
ChangeHeadAnim openMouth(1 * 70, true, 0);
ChangeHeadAnim closeMouth(2, false, 0.5 * 70);
FireSingleShot(int dur, bool started = false, int off = 0, int outReady = -1)
{
super(dur, started, off, outReady);
}
void FireShot(jjOBJ @obj)
{
}
void TelegraphShot(jjOBJ @obj)
{
}
void Perform(jjOBJ @obj)
{
if (openMouth.isReady(true))
{
openMouth.Perform(obj, 1);
TelegraphShot(obj);
}
else if (openMouth.JustFinished())
{
FireShot(obj);
closeMouth.reset();
}
else if (closeMouth.isReady(true))
{
closeMouth.Perform(obj, 0);
}
}
void reset() override
{
SyncTimer::reset();
openMouth.reset();
}
}
class FireStreamShots : SyncTimer
{
ChangeHeadAnim openMouthInitial(70, true, 0);
ChangeHeadAnim openMouth(0, false, 0);
ChangeHeadAnim closeMouth(2, false, 0);
int _numShots;
int _numShotsRemain;
FireStreamShots(int dur, bool started = false, int off = 0, int numShots = 1, int diffBetweenShots = 0, int outReady = -1)
{
super(dur, started, off, outReady);
_numShots = numShots;
_numShotsRemain = numShots;
openMouth._dur = (diffBetweenShots);
_dur = (diffBetweenShots)*numShots + 70 + 70;
}
void FireShot(jjOBJ @obj)
{
}
void TelegraphShot(jjOBJ @obj)
{
}
void Perform(jjOBJ @obj)
{
if (openMouthInitial.isReady(true))
{
openMouthInitial.Perform(obj, 1);
TelegraphShot(obj);
}
else if (openMouthInitial.JustFinished())
{
openMouth.reset();
}
else if (openMouth.isReady(true))
{
openMouth.Perform(obj, 1);
}
else if (openMouth.JustFinished())
{
FireShot(obj);
_numShotsRemain--;
if (_numShotsRemain > 0)
{
openMouth.reset();
}
if (_numShotsRemain == 0)
{
closeMouth.reset();
}
}
else if (closeMouth.isReady(true))
{
closeMouth.Perform(obj, 0);
}
}
void reset() override
{
SyncTimer::reset();
openMouth.reset();
openMouthInitial.reset();
_numShotsRemain = _numShots;
}
}
class Fire3Icicle : FireSingleShot
{
Fire3Icicle(int dur, bool started = false, int off = 0, int outReady = -1)
{
super(dur, started, off, outReady);
}
void FireShot(jjOBJ @obj)
{
for (int i = -1; i < 2; i++)
{
float xSpeed = 4 * obj.direction;
float ySpeed = i * 4;
jjOBJ @bullet = FireBullet(obj.xPos + obj.direction * 16,
obj.yPos, xSpeed > 0 ? 1 : -1, xSpeed, ySpeed, obj.objectID,
ANIM::CUSTOM[17], 3, OBJECT::BLASTERBULLET, true, 0.03 * xSpeed, 0 * ySpeed, true);
bullet. counterEnd = 57;
bullet.behavior = DullBullet();
if (g_bossHalfHP)
bullet.behavior = LaserGun();
bullet.playerHandling = HANDLING::SPECIAL;
bullet.scriptedCollisions = true;
}
}
void TelegraphShot(jjOBJ @obj)
{
obj.var[10] = 1;
if(jjGameTicks % 10 < 6)
{
for (int i = -1; i < 2; i++)
{
float xSpeed = 5 * obj.direction;
float ySpeed = i * 5;
float angle = atan2(xSpeed, ySpeed) - PI / 2;
jjDrawRotatedSprite(obj.xPos + 8*xSpeed, obj.yPos + ySpeed * 8, ANIM::CUSTOM[17], 3, 0, int((512 / PI) * angle), 1, 1 , SPRITE::TRANSLUCENT, 0, 2);
}
}
}
}
class FireChaosAtt : FireSingleShot
{
FireChaosAtt(int dur, bool started = false, int off = 0, int outReady = -1)
{
super(dur, started, off, outReady);
}
void FireShot(jjOBJ @obj)
{
int bulletID3 = jjAddObject(OBJECT::FIREBALLBULLET, obj.xPos + obj.direction * 16, obj.yPos + 8, obj.objectID, CREATOR::OBJECT);
jjOBJ @o = jjObjects[bulletID3];
o.direction = obj.direction;
float angle = GetAnglePlayer(obj);
o.xSpeed = 0;
o.ySpeed = 0;
o.animSpeed = 1;
o.determineCurAnim(ANIM::ROCK, 0);
ChaosBall chaosBall = ChaosBall();
chaosBall.halfHP = g_bossHalfHP;
o.behavior = chaosBall;
o.playerHandling = HANDLING::SPECIAL;
o.scriptedCollisions = true;
jjSamplePriority(SOUND::AMMO_LASER);
}
void TelegraphShot(jjOBJ @obj)
{
obj.var[10] = 2;
if(jjGameTicks % 10 < 6)
{
jjDrawSprite(obj.xPos + obj.direction * 16, obj.yPos + 8 , ANIM::ROCK, 0, 0, obj.direction, SPRITE::TRANSLUCENTCOLOR, ChromaKeyIndex, 2);
}
}
}
void
IceCloud(jjOBJ @obj)
{
obj.frameID = 0;
obj.determineCurFrame();
obj.behave(BEHAVIOR::BULLET, true);
int playerID = obj.findNearestPlayer(30000);
if (obj.state == STATE::FLY && obj.doesCollide(jjPlayers[playerID], true) && jjPlayers[playerID].blink == 0)
{
jjPlayers[playerID].freeze(true);
float angle = GetAnglePlayer(obj);
jjPlayers[playerID].xSpeed = -16 * sin(angle);
jjPlayers[playerID].ySpeed = -16 * cos(angle);
}
if(obj.counter > 200)
{
obj.delete();
}
}
float RandFloat(float min, float max)
{
float scale = jjRandom() / float(2147483647); /* [0, 1.0] */
return min + scale * (max - min); /* [min, max] */
}
class DullBullet : jjBEHAVIORINTERFACE
{
bool explodeOnContact = true;
void onBehave(jjOBJ @obj)
{
if (obj.state == STATE::START)
{
obj.state = STATE::FLY;
if (obj.creatorType == CREATOR::PLAYER)
obj.xSpeed += obj.var[7] / 65536.0; // xSpeed of the player when firing the bullet
}
else if (obj.state == STATE::DEACTIVATE)
{
obj.delete();
}
else if (obj.state == STATE::EXPLODE)
{
obj.behavior = BEHAVIOR::EXPLOSION2;
obj.frameID = 0; // display the full .killAnim animation
}
else
{
obj.xSpeed += obj.xAcc;
obj.ySpeed += obj.yAcc;
if ((--obj.counterEnd == 0) || (explodeOnContact && jjMaskedPixel(int(obj.xPos + obj.xSpeed), int(obj.yPos + obj.ySpeed))))
{
obj.state = STATE::EXPLODE;
}
else
{
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
obj.frameID = obj.objectID + jjGameTicks / 4;
obj.determineCurFrame();
Draw(obj);
// jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL, 0, 1);
}
}
}
void Draw(jjOBJ @obj)
{
float angle = atan2(obj.xSpeed, obj.ySpeed) - PI / 2;
jjDrawRotatedSpriteFromCurFrame(
obj.xPos,
obj.yPos, obj.curFrame,
int((512 / PI) * angle));
}
bool onObjectHit(
jjOBJ @obj, jjOBJ @bullet, jjPLAYER @player,
int force)
{
if (bullet is null && player.blink == 0)
{
player.buttstomp = 122;
player.hurt(1);
}
return true;
}
}
class ChaosBall : jjBEHAVIORINTERFACE
{
int counter = 160; //200
bool explodeOnContact = false;
bool halfHP = false;
void onBehave(jjOBJ @obj)
{
if (obj.state == STATE::START)
{
obj.state = STATE::FLY;
if (obj.creatorType == CREATOR::PLAYER)
obj.xSpeed += obj.var[7] / 65536.0; // xSpeed of the player when firing the bullet
}
else if (obj.state == STATE::DEACTIVATE)
{
obj.delete();
}
else if (obj.state == STATE::EXPLODE)
{
obj.behavior = BEHAVIOR::EXPLOSION2;
obj.frameID = 0; // display the full .killAnim animation
}
else
{
// obj.xSpeed += obj.xAcc;
// obj.ySpeed += obj.yAcc;
if ((--counter == 0) || (explodeOnContact && jjMaskedPixel(int(obj.xPos + obj.xSpeed), int(obj.yPos + obj.ySpeed))))
{
obj.state = STATE::EXPLODE;
}
else
{
if (halfHP && jjGameTicks % 20 == 0)
{
int bulletID3 = jjAddObject(OBJECT::ELECTROBULLETPU, obj.xPos - obj.direction * 8, obj.yPos + 8, obj.objectID, CREATOR::OBJECT);
jjOBJ @o = jjObjects[bulletID3];
o.direction = obj.direction;
float angle = GetAnglePlayer(obj);
o.xAcc = 0;
o.yAcc = 0;
o.xSpeed = sin(angle) * 4;
o.ySpeed = cos(angle) * 4;
o.animSpeed = 1;
o.counterEnd = 250;
o.playerHandling = HANDLING::ENEMYBULLET;
}
float cSpeed = 0.05;
// GoTo(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
// jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, cSpeed, 128);
float angle = GetAnglePlayer(obj);
if (abs(obj.xSpeed + sin(angle) * cSpeed) < 8)
obj.xSpeed += sin(angle) * cSpeed;
if (abs(obj.ySpeed + cos(angle) * cSpeed) < 8)
obj.ySpeed += cos(angle) * cSpeed;
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
obj.frameID = obj.objectID + jjGameTicks / 4;
obj.determineCurFrame();
Draw(obj);
// jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL, 0, 1);
}
}
}
void Draw(jjOBJ @obj)
{
// float angle = atan2(obj.xSpeed, obj.ySpeed) - PI/2;
// jjDrawRotatedSpriteFromCurFrame(
// obj.xPos,
// obj.yPos, obj.curFrame,
// int((512 / PI) * angle));
jjDrawSpriteFromCurFrame(obj.xPos,
obj.yPos, obj.curFrame, 0, SPRITE::SINGLECOLOR, ChromaKeyIndex);
}
bool onObjectHit(
jjOBJ @obj, jjOBJ @bullet, jjPLAYER @player,
int force)
{
if (bullet is null)
{
player.buttstomp = 122;
player.hurt(1);
}
return true;
}
}
class IceCloud2 : jjBEHAVIORINTERFACE
{
int counter = 160; //200
bool explodeOnContact = false;
bool halfHP = false;
void onBehave(jjOBJ @obj)
{
if (obj.state == STATE::START)
{
obj.state = STATE::FLY;
if (obj.creatorType == CREATOR::PLAYER)
obj.xSpeed += obj.var[7] / 65536.0; // xSpeed of the player when firing the bullet
}
else if (obj.state == STATE::DEACTIVATE)
{
obj.delete();
}
else if (obj.state == STATE::EXPLODE)
{
obj.behavior = BEHAVIOR::EXPLOSION2;
obj.frameID = 0; // display the full .killAnim animation
}
else
{
// obj.xSpeed += obj.xAcc;
// obj.ySpeed += obj.yAcc;
if ((--counter == 0) || (explodeOnContact && jjMaskedPixel(int(obj.xPos + obj.xSpeed), int(obj.yPos + obj.ySpeed))))
{
obj.state = STATE::EXPLODE;
}
else
{
// float cSpeed = 0.05;
// // GoTo(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
// // jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, cSpeed, 128);
// float angle = GetAnglePlayer(obj);
// if (abs(obj.xSpeed + sin(angle) * cSpeed) < 8)
// obj.xSpeed += sin(angle) * cSpeed;
// if (abs(obj.ySpeed + cos(angle) * cSpeed) < 8)
// obj.ySpeed += cos(angle) * cSpeed;
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
obj.frameID = obj.objectID + jjGameTicks / 4;
obj.determineCurFrame();
// obj.frameID = 0;
// obj.determineCurFrame();
Draw(obj);
// jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL, 0, 1);
}
}
}
void Draw(jjOBJ @obj)
{
float angle = atan2(obj.xSpeed, obj.ySpeed) - PI/2;
jjDrawRotatedSpriteFromCurFrame(
obj.xPos,
obj.yPos, obj.curFrame,
int((512 / PI) * angle), 1, 1, SPRITE::FROZEN);
// jjDrawSpriteFromCurFrame(obj.xPos,
// obj.yPos, obj.curFrame, 0, SPRITE::FROZEN);
}
bool onObjectHit(
jjOBJ @obj, jjOBJ @bullet, jjPLAYER @player,
int force)
{
if (bullet is null && player.blink == 0)
{
player.freeze(true);
float angle = GetAnglePlayer(obj);
player.xSpeed = -16 * sin(angle);
player.ySpeed = -16 * cos(angle);
}
return true;
}
}
class FireSnowBallStr : FireStreamShots
{
FireSnowBallStr(int dur, bool started = false, int off = 0, int numShots = 1, int diffBetweenShots = 0, int outReady = -1)
{
super(dur, started, off, numShots, diffBetweenShots, outReady);
}
void FireShot(jjOBJ @obj)
{
int bulletID3 = jjAddObject(OBJECT::BLASTERBULLET, obj.xPos + obj.direction * 16, obj.yPos + 8, obj.objectID, CREATOR::OBJECT);
jjOBJ @o = jjObjects[bulletID3];
o.direction = obj.direction;
float angle = GetAnglePlayer(obj);
o.xSpeed = sin(angle) * 5.5;
o.ySpeed = cos(angle) * 5.5;
o.animSpeed = 1;
o.determineCurAnim(ANIM::DEVILDEVAN, 17);
o.counterEnd = 80;
DullBullet dull = DullBullet();
dull.explodeOnContact = !g_bossHalfHP;
o.behavior = dull;
o.playerHandling = HANDLING::ENEMYBULLET;
o.playerHandling = HANDLING::SPECIAL;
o.scriptedCollisions = true;
jjSamplePriority(SOUND::AMMO_LASER);
}
void TelegraphShot(jjOBJ @obj)
{
obj.var[10] = 3;
if(jjGameTicks % 10 < 6)
{
float angle = GetAnglePlayer(obj);
float xSpeed = sin(angle) * 8;
float ySpeed = cos(angle) * 8;
angle = atan2(xSpeed, ySpeed) - PI / 2;
jjDrawRotatedSprite(obj.xPos + obj.direction * 64, obj.yPos + 8, ANIM::DEVILDEVAN, 17, 0, int((512 / PI) * angle), 1, 1 , SPRITE::TRANSLUCENT, 0, 2);
}
}
}
class FireIceCloudStr : FireStreamShots
{
float lastPlayerAngle;
FireIceCloudStr(int dur, bool started = false, int off = 0, int numShots = 1, int diffBetweenShots = 0, int outReady = -1)
{
super(dur, started, off, numShots, diffBetweenShots, outReady);
}
void FireShot(jjOBJ @obj)
{
int bulletID3 = jjAddObject(OBJECT::FIREBALLBULLET, obj.xPos + obj.direction * 16, obj.yPos + 8, obj.objectID, CREATOR::OBJECT);
jjOBJ @o = jjObjects[bulletID3];
// o.direction = obj.direction;
if(_numShotsRemain == _numShots)
{
lastPlayerAngle = GetAnglePlayer(obj);
jjSamplePriority(SOUND::AMMO_ICEPU2);
}
float angle = lastPlayerAngle;
o.behavior = IceCloud2();
o.xAcc = 0;
o.yAcc = 0;
o.xSpeed = sin(angle) * 4;
o.ySpeed = cos(angle) * 4;
o.animSpeed = 1;
o.determineCurAnim(ANIM::RAPIER, 0);
o.counterEnd = 240;
o.playerHandling = HANDLING::ENEMYBULLET;
o.playerHandling = HANDLING::SPECIAL;
o.scriptedCollisions = true;
// jjSamplePriority(SOUND::AMMO_LASER);
}
void TelegraphShot(jjOBJ @obj)
{
obj.var[10] = 4;
// if(jjGameTicks % 10 < 6)
// {
// float angle = GetAnglePlayer(obj);
// float xSpeed = sin(angle) * 8;
// float ySpeed = cos(angle) * 8;
// angle = atan2(xSpeed, ySpeed) - PI / 2;
// jjDrawRotatedSprite(obj.xPos + obj.direction * 64, obj.yPos + 8, ANIM::FLARE, 0, 0, int((512 / PI) * angle), 1, 1 , SPRITE::PALSHIFT, 16, 2);
// }
if(jjGameTicks % 10 < 6)
{
float angle = GetAnglePlayer(obj);
float xSpeed = sin(angle) * 8;
float ySpeed = cos(angle) * 8;
angle = atan2(xSpeed, ySpeed) - PI / 2;
jjDrawRotatedSprite(obj.xPos + obj.direction * 64, obj.yPos + 8, ANIM::RAPIER, 0, 0, int((512 / PI) * angle), 1, 1 , SPRITE::FROZEN, 0, 2);
}
}
}
class HandleFollow : SyncTimer
{
int state = 0;
Fire3Icicle fire3Icicle(1 * 70 + 0.5 * 70 + 70, true, 1 * 70);
FireSnowBallStr fireSnowball(0, true, 100, 10, 8);
FireIceCloudStr fireIceCloud(0, true, difficulty_dec() * 70, 1, 4);
FireChaosAtt fireChaos(1 * 70 + 0.5 * 70 + 70, true, 1 * 70);
bool shouldFireIceCloud = false;
HandleFollow(int dur, bool started = false, int off = 0, int outReady = -1)
{
super(dur, started, off, outReady);
}
void Perform(jjOBJ @obj, float t)
{
if(shouldFireIceCloud)
{
float offset = obj.var[IS_MASTER_HEAD] == 1 ? -200 : 200;
// if(GetDistPlayer(obj) > 300 )
GoTo(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
jjLocalPlayers[0].xPos+offset, jjLocalPlayers[0].yPos, 1, 0);
obj.yPos += 1 * sin(t+offset);
obj.xPos += 2 * sin(t+offset);
// GoArc(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
// jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, 1, 0);
// if(GetDistPlayer(obj) < 200 )
// GoTo(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
// jjLocalPlayers[0].xPos+offset, jjLocalPlayers[0].yPos, -1, 0);
}
else
{
GoTo(obj.xPos, obj.yPos, obj.xPos, obj.yPos, obj.xPos, obj.yPos,
jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, 1, 128);
obj.yPos += 1 * sin(t);
}
// obj.xPos += 1 * cos(t);
bool shouldFireSnowball = state == 0;
bool shouldFireIcicle = state == 1;
bool shouldFireChaos = state == 2;
if (fireIceCloud.isReady(shouldFireIceCloud))
{
fireIceCloud.Perform(obj);
}
else if (fireIceCloud.JustFinished())
{
fireIceCloud.reset();
}
if(shouldFireIceCloud)
{
return;
}
if (fireSnowball.isReady(shouldFireSnowball))
{
fireSnowball.Perform(obj);
}
else if (fireSnowball.JustFinished())
{
fireSnowball.reset();
}
if (fire3Icicle.isReady(shouldFireIcicle))
{
fire3Icicle.Perform(obj);
}
else if (fire3Icicle.JustFinished())
{
fire3Icicle.reset();
}
if (fireChaos.isReady(shouldFireChaos))
{
fireChaos.Perform(obj);
}
else if (fireChaos.JustFinished())
{
fireChaos.reset();
}
}
void reset() override
{
SyncTimer::reset();
fire3Icicle.reset();
fireSnowball.reset();
fireIceCloud.reset();
fireChaos.reset();
}
void SetState(int stateIn)
{
state = stateIn;
}
}
class ChangeHeadAnim : SyncTimer
{
ChangeHeadAnim(int dur, bool started = false, int off = 0, int outReady = -1)
{
super(dur, started, off, outReady);
}
void Perform(jjOBJ @obj, int frameIdx)
{
obj.frameID = frameIdx;
obj.determineCurFrame();
}
}
class GoNext : SyncTimer
{
GoNext(int dur, bool started = false, int off = 0, int outReady = -1)
{
super(dur, started, off, outReady);
}
void Perform()
{
HH24::endLevel();
}
}
float
GetAnglePlayer(jjOBJ @obj)
{
return atan2((jjLocalPlayers[0].xPos - obj.xPos),
(jjLocalPlayers[0].yPos - obj.yPos));
}
class LaserGun : jjBEHAVIORINTERFACE
{
void onBehave(jjOBJ @obj)
{
if (obj.state == STATE::START)
{
obj.state = STATE::FLY;
}
if (jjMaskedPixel(int(obj.xPos + obj.xSpeed + obj.var[7] / 65536.f), int(obj.yPos)))
{
obj.xSpeed = -obj.xSpeed;
obj.var[7] = -obj.var[7];
obj.xAcc = -obj.xAcc;
obj.ySpeed -= 1.5;
obj.counter -= 5;
// if (obj.state == STATE::FLY) randomSample(obj);
}
else if (jjMaskedPixel(int(obj.xPos), int(obj.yPos + obj.ySpeed)))
{
obj.ySpeed = -obj.ySpeed;
obj.yAcc = -obj.yAcc;
obj.xSpeed -= obj.direction == 1 ? -1.5 : 1.5;
obj.counter -= 5;
// if (obj.state == STATE::FLY) randomSample(obj);
}
obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
if (obj.state == STATE::FLY) // jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[0], 1, 0, obj.var[0], 0.9, 0.9, SPRITE::NORMAL);
jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.var[0]);
obj.behave(BEHAVIOR::BULLET, obj.state == STATE::FLY ? false : true);
}
bool onObjectHit(
jjOBJ @obj, jjOBJ @bullet, jjPLAYER @player,
int force)
{
if (bullet is null)
{
player.buttstomp = 122;
player.hurt(1);
}
return true;
}
}
// launch laser
jjOBJ @FireBullet(float x, float y, int direction, float xSpd, float ySpd, int objID, int set = -1, int anim = 0, int object = OBJECT::BLASTERBULLET, bool playsnd = true, float xAcc = 0, float yAcc = 0, bool longrange = false, int damage = 1)
{
jjOBJ @bullet = jjObjects[jjAddObject(object, x, y, objID)];
// bullet.behavior = BEHAVIOR::BULLET;
if (set != -1)
{
bullet.determineCurAnim(set, anim);
}
bullet.playerHandling = HANDLING::ENEMYBULLET;
bullet.xSpeed = xSpd;
bullet.ySpeed = ySpd;
bullet.xAcc = xAcc;
bullet.yAcc = yAcc;
bullet.direction = direction;
bullet.animSpeed = damage;
if (playsnd == true)
{
jjSamplePriority(SOUND::AMMO_LASER);
}
if (longrange == true)
{
bullet.counterEnd = 120;
}
return bullet;
}
snakeHeadAttaks s_state = STATE_FOLLOW_ACC;
float GetDistPlayer(jjOBJ @obj)
{
return sqrt((jjLocalPlayers[0].xPos - obj.xPos) * (jjLocalPlayers[0].xPos - obj.xPos) +
(jjLocalPlayers[0].yPos - obj.yPos) * (jjLocalPlayers[0].yPos - obj.yPos));
}
class IceDragBody : jjBEHAVIORINTERFACE
{
int bodyIdx = -1;
int test = 0;
HandleFollowAcc handleFollowAcc(70 * 20 / 4, true, 0, STATE_FOLLOW);
HandleFollowArc handleFollowArc(70 * 20 / 4, true, 0, STATE_FOLLOW);
HandleFollow handleFollow(/*70 * 13 + 70 * 5 +*/ 70 * 10, false, 0, STATE_FOLLOW_ACC);
GoNext goNext(1, true, 140*2);
int respawnTime = 70 * 12;
RandomArray randAttacks(3);
int invincCntMax = 70;
int invincCnt = 0;
int armor = 0;
bool shield = false;
bool displayHalfHpWarn = true;
bool DoSuperAttack()
{
return g_currBossHp < g_hpBossMax / 2;
}
// Calculate curr position of body part according to parent body part
void CalcNextPos(jjOBJ @cObj, jjOBJ @pObj)
{
float dx = cObj.xPos - pObj.xPos;
float dy = cObj.yPos - pObj.yPos;
float dist = sqrt(dx * dx + dy * dy);
if (dist < g_maxR)
{
return;
}
float ratio = g_maxR / dist;
cObj.xPos = pObj.xPos + ratio * dx;
cObj.yPos = pObj.yPos + ratio * dy;
}
float GetAngle(jjOBJ @cObj, jjOBJ @pObj)
{
float dx, dy;
if (IsHead(cObj))
{
dx = cObj.xSpeed;
dy = cObj.ySpeed;
}
else
{
dx = -cObj.xPos + pObj.xPos;
dy = -cObj.yPos + pObj.yPos;
}
return atan2(dx, dy);
}
void BehaveHead(jjOBJ @obj)
{
invincCnt = max(invincCnt - 1, 0);
// shield = invincCnt >= invincCntMax;
if (obj.justHit != 0) obj.special = 0;
else obj.special++;
if (obj.special > 0) {
if (obj.special < 35) {
if (obj.special % 5 == 0) {
armor--;
}
}
if (obj.special >= 70) {
if (obj.special % 3 == 0) {
armor--;
}
}
}
if (armor > 30) armor = 30;
if (armor < 0) armor = 0;
obj.isTarget = true;
if(obj.var[IS_MASTER_HEAD] == 1)
{
obj.energy = int(127 * g_currBossHp / g_hpBossMax);
ActivateBoss(obj);
}
obj.age++;
obj.direction = jjLocalPlayers[0].xPos - obj.xPos > 0 ? 1 : -1;
float cSpeed = 0.1; // originally 0.1
float t = float(obj.age) / 70;
if(IsHead(obj) && obj.var[BODYIDX] == int(NUM_BODY_PARTS * 1/3))
{
handleFollowArc.Perform(obj,0);
return;
}
if (handleFollow.isReady(true))
{
s_state = STATE_FOLLOW;
handleFollow.Perform(obj, t);
}
else if (handleFollow.JustFinished())
{
// g_bossHalfHP = obj.var[HP] < g_hpBossMax / 2;
jjSamplePriority(SOUND::DOG_AGRESSIV);
handleFollowAcc.reset();
obj.var[10] = 0;
}
if (handleFollowAcc.isReady(true))
{
obj.var[10] = 5;
s_state = STATE_FOLLOW_ACC;
handleFollowAcc.Perform(obj, cSpeed);
}
else if (handleFollowAcc.JustFinished())
{
// g_bossHalfHP = obj.var[HP] <= g_hpBossMax / 2;
if(g_currBossHp < g_hpBossMax / 3)
{
// for(int i = 0; i < 5; i++)
// jjAlert("The Ice Serpent's attacks gain special effects!");
// displayHalfHpWarn = false;
handleFollow.shouldFireIceCloud = true;
handleFollow._dur = 7000000;
}
jjSamplePriority(SOUND::DOG_AGRESSIV);
handleFollow.reset();
handleFollow.SetState(randAttacks.GetNumber());
}
}
void CollectNumClosePointsData(jjOBJ @obj)
{
// if(GetDistPlayer(obj) > )
}
void BehaveBody(jjOBJ @obj)
{
jjOBJ @pObj = GetParentNode(obj);
if (CheckIfParentAlive(obj, pObj) == false)
{
return;
}
CalcNextPos(obj, pObj);
if (IsUnActive(obj))
{
HandleUnActiveState(obj);
}
if (false)
{
if (jjRandom() % int(70 * 140 * 0.1 * 8) == 0)
{
float angle = GetAngle(obj, pObj);
float xSpeed = cos(angle);
float ySpeed = -sin(angle);
jjOBJ @bullet = FireBullet(obj.xPos,
obj.yPos, xSpeed > 0 ? 1 : -1, xSpeed, ySpeed, obj.objectID,
ANIM::CUSTOM[17], 3, OBJECT::BLASTERBULLET, true, 0.03 * xSpeed, 0 * ySpeed, true);
bullet. counterEnd = 57;
bullet.behavior = DullBullet();
bullet.playerHandling = HANDLING::SPECIAL;
bullet.scriptedCollisions = true;
}
}
}
void ActivateBoss(jjOBJ @obj)
{
// if(jjGameTicks % 70 ==1)
// jjAlert("id" + obj.objectID);
if (!camera && elapsed == 0) {
jjLocalPlayers[0].boss = obj.objectID;
jjLocalPlayers[0].bossActivated = true;
jjLocalPlayers[0].activateBoss();
jjMusicLoad("Whare_AllanZax.ogg");
jjLocalPlayers[0].cameraFreeze(obj.xPos, obj.yPos, true, false);
//jjAlert("||THE PRIMORDIAL ICE SERPENT", false, STRING::MEDIUM);
jjSamplePriority(SOUND::DOG_AGRESSIV);
camera = true;
}
}
jjOBJ @GetParentNode(jjOBJ @obj)
{
return jjObjects[obj.var[PARENT]];
}
bool CheckIfParentAlive(jjOBJ @obj, jjOBJ @pObj)
{
if (pObj.state == STATE::BOUNCE)
{
obj.state = STATE::BOUNCE;
obj.ySpeed = 3;
return false;
}
return true;
}
bool IsHead(jjOBJ @obj)
{
return -1 == obj.var[PARENT];
} // obj.var[PARENT] points to parent node, head has no parent
bool IsTail(jjOBJ @obj)
{
return 1 == obj.var[IS_TAIL];
} // condition used to specify tail
bool IsUnActive(jjOBJ @obj)
{
return obj.var[IS_INACTIVE] == 1;
}
void HandleUnActiveState(jjOBJ @obj)
{
obj.age++;
if (obj.age == respawnTime)
{
obj.age = 0;
obj.var[IS_INACTIVE] = 0;
}
}
void onBehave(jjOBJ @obj)
{
switch (obj.state)
{
case STATE::START:
obj.determineCurFrame();
obj.state = STATE::FLOAT;
break;
case STATE::FLOAT:
// test++;
// Split and turn to head
if(g_lastHeadIdx == obj.var[BODYIDX])
{
if(obj.var[PARENT] != -1)
{
jjOBJ @pObj = GetParentNode(obj);
pObj.var[IS_TAIL] = 1;
obj.var[IS_INACTIVE] = 0;
jjSamplePriority(SOUND::FAN_FAN);
jjSamplePriority(SOUND::FAN_FAN); //twice the volume
/*if (!halfmsg) jjAlert("||THE ICE SERPENT HAS SPLIT IN HALF!",false,STRING::MEDIUM);
halfmsg = true;*/
}
obj.var[PARENT] = -1;
}
if (IsHead(obj))
{
obj.determineCurAnim(ANIM::CUSTOM[27], 5);
obj.var[9] = 1;
}
else if (IsTail(obj))
{
obj.determineCurAnim(ANIM::CUSTOM[27], 6);
}
else
{
obj.determineCurAnim(ANIM::CUSTOM[17], 2);
}
obj.determineCurFrame();
if (IsHead(obj)) // dragon head
{
BehaveHead(obj);
}
else
{
BehaveBody(obj);
}
if (g_currBossHp <= 0)
{ // killed
if (IsHead(obj))
{
obj.state = STATE::BOUNCE;
obj.ySpeed = 3;
jjMusicStop();
if (!showOnce) {
jjLocalPlayers[0].showText("@@@@@@@@#||||~ICE SERPENT SLAIN", STRING::LARGE);
showOnce = true;
}
}
// obj.particlePixelExplosion(2);
}
break;
case STATE::FREEZE:
obj.unfreeze(0);
obj.state = obj.oldState;
// consider calling jjOBJ::unfreeze() here
break;
case STATE::DEACTIVATE: // can be left out if level is MP-only
obj.deactivate();
break;
case STATE::BOUNCE: // killed
obj.yPos += obj.ySpeed;
if (IsHead(obj) && jjGameTicks % 10 == 0)
{
jjLocalPlayers[0].bossActivated = false;
obj.unfreeze(0);
}
if (goNext.isReady(obj.var[IS_MASTER_HEAD] == 1))
{
goNext.Perform();
}
break;
case STATE::KILL: // can be left out if not using normal object energy
// handling
obj.state = STATE::BOUNCE;
// obj.delete();
break;
}
}
void onDraw(jjOBJ @obj)
{
// if (obj.state == STATE::BOUNCE)
// return;
int layer = IsHead(obj) ? 1 : 2;
auto sMode = obj.var[IS_INACTIVE] == 1 ? SPRITE::SHADOW
: (obj.justHit != 0 || shield == true) ? SPRITE::SINGLECOLOR
: SPRITE::NORMAL;
int param = shield == true ? 24 : obj.var[IS_INACTIVE] == 1? 50 : (jjLocalPlayers[0].blink == 0? 15:32);
if (IsTail(obj))
{
obj.frameID = 3;
obj.determineCurFrame();
jjOBJ @pObj = jjObjects[obj.var[PARENT]];
float angle = GetAngle(obj, pObj);
jjDrawRotatedSpriteFromCurFrame(
obj.xPos + (IsTail(obj) ? 26 * sin(angle) : 0),
obj.yPos + (IsTail(obj) ? 26 * cos(angle) : 0), obj.curFrame,
int((512 / PI) * angle), 1, 1, sMode, 15);
return;
}
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction,
sMode, param, layer);
}
bool onObjectHit(
jjOBJ @obj, jjOBJ @bullet, jjPLAYER @player,
int force)
{
if (obj.var[IS_INACTIVE] == 1 || obj.state == STATE::BOUNCE /* || bullet.creatorID == obj.objectID*/)
{
return true;
}
if (bullet !is null)
{
if ((bullet.var[6] & 16) == 0) // not a fireball
bullet.state = STATE::EXPLODE;
if (obj.freeze > 0 && force < 3)
force = 3;
if (true || invincCnt <= invincCntMax)
{
invincCnt += 30;
obj.var[HP] = int(obj.var[HP] - damageCalc(obj, bullet, armor, player));
// obj.energy = 127 * obj.var[HP] / g_hpBossMax;
float multiplier = ((bullet.var[3] == 4 || bullet.var[3] == 5)? 0.33f : bullet.var[3] == 9? 0.075f : 0.5f) * (player.blink == 0? 1:0.25);
if(IsHead(obj)) {
g_currBossHp -= (damageCalc(obj, bullet, armor, player) > (bullet.animSpeed * multiplier))? damageCalc(obj, bullet, armor, player) : (bullet.animSpeed * multiplier);
armor = armor + bullet.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,bullet,armor, player) > (bullet.animSpeed * multiplier)? damageCalc(obj,bullet,armor, player) : (bullet.animSpeed * multiplier));
}*/
}
// if(g_currBossHp <= g_lastHpIDx / 2 && (g_lastHpIDx / 2 > (g_hpBossMax / 2 - 10)))
// {
// g_lastHeadIdx = g_lastHeadIdx / 2;
// g_lastHpIDx = g_lastHpIDx / 2;
// }
if(g_currBossHp <= g_lastHpIDx *1/3)
{
g_lastHeadIdx = int( NUM_BODY_PARTS* 1/3);
}
else if(g_currBossHp <= g_lastHpIDx *2/3)
{
g_lastHeadIdx = int(NUM_BODY_PARTS * 2/3);
}
obj.justHit =
5; // flash white for 5 ticks--jjOBJ::justHit is automatically
// deincremented by the JJ2 engine, so individual behavior
// functions don't need to worry about doing that.
}
obj.freeze = 0;
}
else
{ // not attacking
player.hurt();
}
if (obj.var[HP] <= 0)
{ // killed
// if (IsHead(obj))
// {
// obj.state = STATE::BOUNCE;
// obj.ySpeed = 3;
// }
// else
if (!IsHead(obj))
{
obj.var[HP] = int(g_hpBodyMax);
obj.var[IS_INACTIVE] = 1;
}
// obj.particlePixelExplosion(2);
}
return true;
}
}
float damageCalc(jjOBJ@ obj, jjOBJ@ bullet, int armor, jjPLAYER@ play) {
if (armor <= bullet.animSpeed) {
return bullet.animSpeed * (play.blink == 0? 1:0.25);
} else {
return (bullet.animSpeed - ((0.0125f * (bullet.animSpeed*3)) * armor)) * (play.blink == 0? 1:0.25);
}
}
enum sharedVars
{
IS_TAIL,
HP,
IS_INACTIVE,
PARENT,
BODYIDX,
IS_MASTER_HEAD
};
class IceDrag : jjBEHAVIORINTERFACE
{
int numBody = NUM_BODY_PARTS;
int InitBodyPart(jjOBJ @obj, int idx, int parentObjIdx)
{
int dragBodyIdx =
jjAddObject(OBJECT::CRAB, obj.xPos + obj.direction * idx * g_maxR,
obj.yPos, obj.objectID, CREATOR::OBJECT);
jjOBJ @bodyObj = jjObjects[dragBodyIdx];
bool isHead = idx == 0;
bool isTail = idx == numBody - 1;
if (isHead)
{
bodyObj.determineCurAnim(ANIM::CUSTOM[27], 5);
bodyObj.var[9] = 1;
}
else if (isTail)
{
bodyObj.determineCurAnim(ANIM::CUSTOM[27], 6);
}
else
{
bodyObj.determineCurAnim(ANIM::CUSTOM[17], 2);
}
bodyObj.frameID = 0;
bodyObj.direction = -obj.direction;
bodyObj.deactivates = false;
bodyObj.counter = 0;
IceDragBody iceDragBody = IceDragBody();
iceDragBody.bodyIdx = idx;
bodyObj.behavior = iceDragBody;
// auto@ b = cast<IceDragBody>(cast<jjBEHAVIORINTERFACE>(bodyObj.behavior));
// b.bodyIdx++;
bodyObj.var[PARENT] =
isHead ? -1 : parentObjIdx; // obj.var[PARENT] points to parent node
bodyObj.var[IS_TAIL] = isTail ? 1 : 0;
bodyObj.var[HP] = isHead ? int(g_hpBossMax) : int(g_hpBodyMax);
bodyObj.var[IS_INACTIVE] = 0;
bodyObj.var[BODYIDX] = numBody - idx;
bodyObj.var[IS_MASTER_HEAD] = idx == 0 ? 1 : 0;
bodyObj.isTarget = isHead ? true : false;
bodyObj.age = 0;
bodyObj.playerHandling = HANDLING::SPECIAL;
bodyObj.scriptedCollisions = true;
bodyObj.bulletHandling = HANDLING::DETECTBULLET;
bodyObj.light = 8;
bodyObj.lightType = bodyObj.var[IS_INACTIVE] == 0? LIGHT::NORMAL : LIGHT::NONE;
return dragBodyIdx;
}
void onBehave(jjOBJ @obj)
{
if (obj.state == STATE::START)
{
g_currBossHp = g_hpBossMax;
g_lastHeadIdx = numBody;
g_lastHpIDx = g_hpBossMax;
// g_bossHalfHP = obj.var[HP] < g_hpBossMax / 2;
obj.direction =
jjPlayers[obj.findNearestPlayer(30000)].xPos - obj.xPos < 0 ? 1 : -1;
int parentPart = InitBodyPart(obj, 0, 0);
for (int i = 1; i < numBody; i++)
{
parentPart = InitBodyPart(obj, i, parentPart);
}
obj.state = STATE::KILL;
}
else
obj.delete();
}
} void applyGenericEnemySettingsToPreset(jjOBJ @preset)
{
preset.playerHandling = HANDLING::ENEMY;
preset.bulletHandling = HANDLING::HURTBYBULLET;
preset.causesRicochet = false;
preset.isBlastable = false;
preset.triggersTNT = true;
preset.isFreezable = true;
preset.isTarget = true;
preset.scriptedCollisions = false;
preset.direction = 1;
preset.freeze = 0;
}
} // namespace SMOKE
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
return MLLE::WeaponHook.drawAmmo(player, canvas);
}
void onPlayer(jjPLAYER@ play) {
if (camera) elapsed++;
if (elapsed == 120) {
play.cameraUnfreeze(false);
}
HH24::player(play);
if (play.yPos < 20*32 && play.xPos < 40*32) {
jjMusicLoad("slamjam.it");
} else {
if (play.yPos < 30*32) jjMusicLoad("jm-deepd3.it");
}
}
void onLevelBegin() {
MLLE::SpawnOffgrids();
jjSampleLoad(SOUND::DOG_AGRESSIV, "HH17_Roar.wav");
jjSampleLoad(SOUND::FAN_FAN, "HH17_Glass1.wav");
jjObjectPresets[OBJECT::ELECTROBULLET].animSpeed = 1;
//jjWeapons[WEAPON::GUN9].style = WEAPON::CAPPED;
jjLocalPlayers[0].showText(0,0,STRING::SMALL);
}
void onMain() {
HH17::handleEnemyProjectiles();
array<jjLAYER@> layers = jjLayerOrderGet();
layers[layers.length() - 3].hasTiles = jjColorDepth == 8? true:false;
layers[layers.length() - 2].hasTiles = layers[layers.length() - 1].hasTiles = jjColorDepth == 16? true:false;
HH24::main();
}
bool onDrawScore(jjPLAYER@ play, jjCANVAS@ canvas) {
if (play is null)
return false;
const auto rightBorder = jjSubscreenWidth + 50, bottomBorder = jjSubscreenHeight + 50;
for (int i = 1; i < jjObjectCount; ++i) {
const jjOBJ@ obj = jjObjects[i];
if (elapsed > 140 && obj.var[9] == 1 && obj.state != STATE::KILL && obj.state != STATE::BOUNCE) {
const int gemX = int(obj.xPos - 18 - play.cameraX) + jjBorderWidth, gemY = int(obj.yPos - 47 - play.cameraY) + jjBorderHeight;
int distance = int((abs(gemX) + abs(gemY))/32);
if (gemX >= -50 && gemY >= -50 && gemX < rightBorder && gemY < bottomBorder) { //onscreen
//do nothing lel
} else {
//https://stackoverflow.com/questions/32030488/get-the-coordinates-at-the-edge-of-the-screen-from-a-given-angle
const int centerX = jjSubscreenWidth / 2, centerY = jjSubscreenHeight / 2;
const int reducedWidth = centerX - 40, reducedHeight = centerY - 40;
const auto xDiff = obj.xPos - (centerX + jjBorderWidth + play.cameraX), yDiff = obj.yPos - (centerY + jjBorderHeight + play.cameraY);
const auto angle = atan2(yDiff, xDiff);
float xEnd = cos(angle) * 1000, yEnd = sin(angle) * 1000; //as measured from 0,0
float xFactor = reducedWidth / xEnd;
if (xEnd < -reducedWidth)
xFactor = -xFactor;
else if (xEnd <= reducedWidth)
xFactor = 1;
xEnd *= xFactor;
yEnd *= xFactor;
float yFactor = reducedHeight / yEnd;
if (yEnd < -reducedHeight)
yFactor = -yFactor;
else if (yEnd <= reducedHeight)
yFactor = 1;
xEnd *= yFactor;
yEnd *= yFactor;
xEnd += centerX;
yEnd += centerY;
canvas.drawRotatedSpriteFromCurFrame(int(xEnd),int(yEnd), labelFrameID + 1, int(angle * -512.0 * 0.318309886142228), 1,1, SPRITE::ALPHAMAP, distance > 60? 36 : distance > 50? 35 : distance > 40? 34 : 33);
canvas.drawResizedSprite(int(xEnd), int(yEnd), ANIM::CUSTOM[27], 5, 0, 0.35, 0.35, SPRITE::NORMAL);
if (obj.var[10] == 1) canvas.drawResizedSprite(int(xEnd + 12), int(yEnd + 12), ANIM::CUSTOM[17], 3, 0, 0.5, 0.5, SPRITE::NORMAL);
if (obj.var[10] == 2) canvas.drawResizedSprite(int(xEnd + 12), int(yEnd + 12), ANIM::ROCK, 0, 0, 0.15, 0.15, SPRITE::SINGLECOLOR, ChromaKeyIndex);
if (obj.var[10] == 3) canvas.drawResizedSprite(int(xEnd + 12), int(yEnd + 12), ANIM::DEVILDEVAN, 17, 0, 0.5, 0.5, SPRITE::NORMAL);
if (obj.var[10] == 4) canvas.drawResizedSprite(int(xEnd + 12), int(yEnd + 12), ANIM::RAPIER, 0, 0, 0.5, 0.5, SPRITE::FROZEN);
if (obj.var[10] == 5) canvas.drawResizedSprite(int(xEnd + 6), int(yEnd + 9), ANIM::FLAG, 0, 1, 0.5, 0.5);
}
}
}
HH24::score(play, canvas, false);
return false;
}
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.