Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
![]() |
Jungle Ultimate![]() |
DoubleGJ | Tileset conversion | 10 | ![]() |
const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
#include "MLLE-Include-1.7.asc" ///@MLLE-Generated
#pragma require "JungUltEx1-MLLE-Data-2.j2l" ///@MLLE-Generated
#pragma require "JungUltEx1-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "JungUltEx1.j2l" ///@MLLE-Generated
#pragma require "DD-Anims.j2a"
#include "DD-Order.asc"
#include "DD-Rank.asc"
#pragma require "JungUlt-Canopy1.png"
#pragma require "JungUlt-Canopy2.png"
#pragma require "JungUlt-Foreground1.png"
#pragma require "JungUlt-Foreground2.png"
#pragma require "JungUlt-Foreground3.png"
#pragma require "JungUlt-Foreground4.png"
#pragma require "JungUlt-Foreground5.png"
#pragma require "waterfall-loop.wav"
#pragma require "big-bite.wav"
#pragma require "common-gemsmsh1.wav"
#pragma require "acid2.wav"
#pragma require "aslime2.wav"
#pragma require "empty.wav" // endall solution for unwanted sounds
#pragma require "kookaburra-loop1.wav"
#pragma require "kookaburra-loop2.wav"
#pragma require "gibbon-calls.wav"
#pragma require "frog-croak.wav"
#pragma require "jungle-calls.wav"
#pragma require "jungle-ambience.wav"
#pragma require "jungle-birdloop.wav"
#pragma require "frog-loop.wav"
#pragma require "jungle-birds.wav"
#pragma require "bird-takeoff1.wav"
#pragma require "bird-takeoff2.wav"
#pragma require "bird-takeoff3.wav"
#pragma require "nakotak1.wav"
#pragma require "nakotak2.wav"
#pragma require "bonk.wav"
#pragma require "vonyipp-death1.wav"
#pragma require "laser.wav" // I dream of the day when I can input SOUND::sound instead of a file in jjSampleLoad...
#pragma require "phaser2.wav"
#pragma require "rustle.wav"
#pragma require "bigcopter.wav"
#pragma offer "DD-Jungle.it"
#pragma offer "CD-Normal.mod"
#pragma offer "firage_-_buns_and_guns_2.it"
#pragma offer "damn-akane.mp3"
#pragma offer "bloopin.mp3"
const int bestTime = 570;
const float maxScore = 120000;
const int easyTimer = 0;
const int normalTimer = 0;
const int hardTimer = 84000;
const int turboTimer = 63000;
// Devan stuff:
array<jjLAYER@>@ backupLayerOrder = array<jjLAYER@> = {jjLayers[1], jjLayers[2], jjLayers[3], jjLayers[4], jjLayers[5], jjLayers[6], jjLayers[7], jjLayers[8]};
array<jjLAYER@>@ bossLayerOrder = array<jjLAYER@> = {jjLayers[1], jjLayers[2], jjLayers[3], jjLayers[4], jjLayers[5], jjLayers[6], jjLayers[7], jjLayers[8]};
bool boemFloor;
bool devanWait = false;
bool foughtDevan = false;
bool endFlag = false;
bool layer2Destroyed = false;
CustomBossHealthBar customBossHealthBar;
int bossHealth, bossPhase;
// Jill stuff:
bool jillNear = false;
bool drawBirdie = true;
bool jillMet = false;
bool beenBirdie = false;
int farPlayerID = 0;
int nearPlayerID = 0;
bool copterSpawned, copterDefeated = false;
int startHP = 5;
int copterChannel, emoteFade = 0;
const uint NumberOfBitsDevotedToSyncParameter = 3; //this should correspond to the number of bits you assign to the Sync parameter in your JCS.ini (or MLLE equivalent) entry for Swinging Vine. So for example if you give it a parameter Sync:2, this variable should ALSO equal 2.
void onLevelLoad() {
initiateDialogue();
initiateRanking();
initFade();
jjAnimSets[ANIM::HELMUT].load(); // for pixelmaps
jjAnimSets[ANIM::BUBBA].load(); // for ambient sfx
jjAnimSets[ANIM::CHUCK].load(); // birdie friend!
jjSampleLoad(SOUND::SCIENCE_PLOPKAOS, "waterfall-loop.wav");
jjSampleLoad(SOUND::BUBBA_BUBBABOUNCE1, "kookaburra-loop1.wav");
jjSampleLoad(SOUND::BUBBA_BUBBABOUNCE2, "kookaburra-loop2.wav");
jjSampleLoad(SOUND::BUBBA_BUBBAEXPLO, "gibbon-calls.wav");
jjSampleLoad(SOUND::BUBBA_FROG2, "frog-croak.wav");
jjSampleLoad(SOUND::BUBBA_FROG3, "jungle-calls.wav");
jjSampleLoad(SOUND::BUBBA_FROG4, "jungle-ambience.wav");
jjSampleLoad(SOUND::BUBBA_FROG5, "jungle-birdloop.wav");
jjSampleLoad(SOUND::BUBBA_SNEEZE2, "frog-loop.wav");
jjSampleLoad(SOUND::BUBBA_TORNADOATTACK2, "jungle-birds.wav");
jjAnimSets[ANIM::DEVILDEVAN].load();
jjAnimSets[ANIM::DEVAN].load();
jjAnimSets[ANIM::BILSBOSS].load();
jjAnimSets[ANIM::CUSTOM[19]].load(19, "DD-Anims.j2a"); // Devan extra sprites
tweakDevanRemoteAnim();
DDevan(jjObjectPresets[OBJECT::DEVILDEVAN]);
jjSampleLoad(SOUND::INTRO_MONSTER2, "vonyipp-death1.wav");
jjAnimSets[ANIM::CUSTOM[7]].load(3, "DD-Anims.j2a"); // custom pickups sprites
///@Event 67=Super Item |+|Goodies |Super |Item |Type:{Red Gem,Green Gem,Blue Gem,Purple Gem,Watermelon,Jackfruit}3
jjObjectPresets[OBJECT::SUPERGEM].behavior = SuperGemWrapper;
jjObjectPresets[OBJECT::FLICKERGEM].behavior = flickerWrapper;
jjSampleLoad(SOUND::COMMON_GEMSMSH1, "empty.wav");
jjAnimSets[ANIM::DOG].load();
jjSampleLoad(SOUND::DOG_SNIF1, "common-gemsmsh1.wav");
///@Event 174=Kiwi |+|Food |Kiwi
jjObjectPresets[OBJECT::BURGER].determineCurAnim(ANIM::CUSTOM[7], 1);
///@Event 175=Pineapple |+|Food | Pine |Apple
jjObjectPresets[OBJECT::PIZZA].determineCurAnim(ANIM::CUSTOM[7], 13);
///@Event 168=Mango |+|Food |Mango
jjObjectPresets[OBJECT::DONUT].determineCurAnim(ANIM::CUSTOM[7], 14);
// not renaming Purple Gem since it's just a reskin
jjObjectPresets[OBJECT::PURPLEGEM].behavior = specialApple();
jjObjectPresets[OBJECT::PURPLEGEM].determineCurAnim(ANIM::CUSTOM[7], 16);
///@Event 176=Melon |+|Food |Melon
jjObjectPresets[OBJECT::FRIES].determineCurAnim(ANIM::CUSTOM[7], 15);
///@Event 180=Avocado |+|Food |Avo |Cado
jjObjectPresets[OBJECT::WEENIE].determineCurAnim(ANIM::CUSTOM[7], 17);
///@Event 181=Dragon Fruit |+|Food |Dragon |Fruit
jjObjectPresets[OBJECT::HAM].determineCurAnim(ANIM::CUSTOM[7], 18);
///@Event 182=Pomegranate |+|Food |Pome |Granat
jjObjectPresets[OBJECT::CHEESE].determineCurAnim(ANIM::CUSTOM[7], 19);
///@Event 167=Lychee |+|Food |Lychee
jjObjectPresets[OBJECT::CAKE].determineCurAnim(ANIM::CUSTOM[7], 20);
///@Event 169=Star Fruit |+|Food |Star |Fruit
jjObjectPresets[OBJECT::CUPCAKE].determineCurAnim(ANIM::CUSTOM[7], 21);
///@Event 170=White Grapes |+|Food |White |Grapes
jjObjectPresets[OBJECT::CHIPS].determineCurAnim(ANIM::CUSTOM[7], 11);
///@Event 141=Persimmon |+|Food |Persi-|mmon
jjObjectPresets[OBJECT::APPLE].determineCurAnim(ANIM::CUSTOM[7], 35);
///@Event 177=Nectarine |+|Food |Necta-|rine
jjObjectPresets[OBJECT::CHICKENLEG].determineCurAnim(ANIM::CUSTOM[7], 36);
///@Event 172=Ginger Root|+|Food |Ginger
jjObjectPresets[OBJECT::CHOCBAR].determineCurAnim(ANIM::CUSTOM[7], 65);
///@Event 162=Lulo |+|Food |Lulo
jjObjectPresets[OBJECT::CUCUMBER].determineCurAnim(ANIM::CUSTOM[7], 26);
///@Event 146=Papaya |+|Food |Papaya
jjObjectPresets[OBJECT::PRETZEL].determineCurAnim(ANIM::CUSTOM[7], 29);
///@Event 161=Guanabana |+|Food |Guana-|bana
jjObjectPresets[OBJECT::EGGPLANT].determineCurAnim(ANIM::CUSTOM[7], 53);
///@Event 178=Cherimoya |+|Food |Che-|rimoya
jjObjectPresets[OBJECT::SANDWICH].determineCurAnim(ANIM::CUSTOM[7], 46);
///@Event 166=Parsley Max Energy +1 |+|Goodies |Parsley
jjObjectPresets[OBJECT::PIE].determineCurAnim(ANIM::CUSTOM[7], 2);
jjObjectPresets[OBJECT::PIE].scriptedCollisions = true;
jjObjectPresets[OBJECT::PIE].behavior = parsley();
startHP = jjMaxHealth;
///@Event 111=Lori Block |+|Trigger |Lori |Scen
jjOBJ@ myDestructSceneryVariant = jjObjectPresets[OBJECT::CHESHIRE1];
myDestructSceneryVariant.points = 50;
myDestructSceneryVariant.playerHandling = HANDLING::SELFCOLLISION;
myDestructSceneryVariant.behavior = loriBlock;
///@Event 179=Big Grub |+|Object |Big |Grub
jjAnimSets[ANIM::CUSTOM[2]].load(15, "DD-Anims.j2a"); // dragonfly extra sprites
jjSampleLoad(SOUND::DOG_AGRESSIV, "big-bite.wav");
CustomDragonfly(jjObjectPresets[OBJECT::DRAGONFLY]);
jjObjectPresets[OBJECT::TACO].determineCurAnim(ANIM::CUSTOM[2], 0);
jjObjectPresets[OBJECT::TACO].behavior = bigGrub();
///@Event 196=Grub |-|Enemy |Grub | |Stick to:{Ground,Right wall,Ceiling,Left wall}2
jjOBJ@ grubPreset = jjObjectPresets[OBJECT::CRAB];
grubPreset.behavior = grub();
grubPreset.determineCurAnim(ANIM::CUSTOM[2], 2);
grubPreset.energy = 1;
grubPreset.points = 100;
///@Event 42=Swinging Vine |+|Object |Swing |Vine |Sync:3 |Length:4
jjObjectPresets[OBJECT::SWINGINGVINE].behavior = SyncedVine;
///@Event 217=Jill of the Jungle |+|Morph |Jill
jjAnimSets[ANIM::CUSTOM[3]].load(16, "DD-Anims.j2a"); // Jill sprite
jjAnimSets[ANIM::CUSTOM[4]].load(5, "DD-Anims.j2a"); // dialogue prompt sprite
jjObjectPresets[OBJECT::EVA].behavior = jill();
jjObjectPresets[OBJECT::EVA].determineCurAnim(ANIM::CUSTOM[3], 0);
jjObjectPresets[OBJECT::EVA].bulletHandling = HANDLING::IGNOREBULLET;
jjSampleLoad(SOUND::COMMON_SHIELD_ELEC, "empty.wav"); // yeah you won't hear the plasma shield if you use a cheat in this level, sorry
///@Event 100=Shaman Demon |-|Enemy |Shaman |Demon
jjAnimSets[ANIM::CUSTOM[9]].load(20, "DD-Anims.j2a"); // shaman demon sprites
jjSampleLoad(SOUND::INTRO_INHALE, "nakotak1.wav");
jjSampleLoad(SOUND::INTRO_UHTURT, "nakotak2.wav");
jjSampleLoad(SOUND::INTRO_HITTURT, "bonk.wav");
jjOBJ@ shamanDemonPreset = jjObjectPresets[OBJECT::TUFTURT];
shamanDemonPreset.behavior = shamanDemon();
shamanDemonPreset.energy = 15 + (jjDifficulty * 5) ;
shamanDemonPreset.points = 1000;
shamanDemonPreset.scriptedCollisions = true;
shamanDemonPreset.playerHandling = HANDLING::SPECIAL;
///@Event 106=
jjOBJ@ stormCloudPreset = jjObjectPresets[OBJECT::RAPIER];
stormCloudPreset.behavior = stormCloud();
stormCloudPreset.determineCurAnim(ANIM::CUSTOM[9], 4);
stormCloudPreset.playerHandling = HANDLING::PARTICLE;
stormCloudPreset.bulletHandling = HANDLING::IGNOREBULLET;
stormCloudPreset.isTarget = false;
stormCloudPreset.isFreezable = false;
stormCloudPreset.triggersTNT = false;
stormCloudPreset.deactivates = true;
///@Event 237=Lizard (Heavy Copter) |-|Enemy |Heavy |Fl.Liz
jjSampleLoad(SOUND::ORANGE_BUBBELSR, "bigcopter.wav");
jjAnimSets[ANIM::CUSTOM[23]].load(23, "DD-Anims.j2a");
jjOBJ@ preset = jjObjectPresets[OBJECT::BEEBOY];
preset.determineCurAnim(ANIM::CUSTOM[23], 3);
preset.frameID = 0;
preset.behavior = HeavyCopter();
preset.energy = 35;
preset.points = 1000;
preset.xSpeed = preset.ySpeed = 0;
preset.scriptedCollisions = true;
preset.playerHandling = HANDLING::SPECIAL;
preset.bulletHandling = HANDLING::DETECTBULLET;
preset.causesRicochet = false;
preset.isTarget = true;
preset.isBlastable = true;
preset.isFreezable = true;
preset.triggersTNT = true;
preset.state = STATE::START;
preset.lightType = LIGHT::NONE;
preset.direction = 1;
preset.var[hcNextPhysicalInjury] = 0;
preset.var[hcNextNearbyPlayerCheck] = 0;
preset.var[hcTargetPlayerID] = -1;
preset.var[hcCurrentAngle] = 0; //0 = forwards, 17 = down, 1-16 = turning (each frame is 4 ticks)
jjAnimSets[ANIM::CUSTOM[5]].load(17, "DD-Anims.j2a"); // emote
jjSampleLoad(SOUND::DOG_WAF1, "acid2.wav");
///@Event 204=Falling Log (single) |+|Object |Log
jjAnimSets[ANIM::CUSTOM[6]].load(10, "DD-Anims.j2a"); // log sprite
Log(jjObjectPresets[OBJECT::PSYCHPOLE]);
///@Event 79=Log Spawner |+|Object |Log |Start|Amount:4 |Delay:4
LogSpawner(jjObjectPresets[OBJECT::FASTFEET]);
///@Event 8=Log Resetter |+|Object |Log |Reset
///@Event 126=Lava Block |+|Scenery |Lava |Block | Adjust Y:5
jjAnimSets[ANIM::CUSTOM[12]].load(9, "DD-Anims.j2a"); // visual fx
jjSampleLoad(SOUND::WIND_WIND2A, "aslime2.wav");
jjOBJ@ lavaBlockPreset = jjObjectPresets[OBJECT::FENCER]; // not expecting anyone to put fencers in a level with lava in it
lavaBlockPreset.behavior = lavaBlock();
lavaBlockPreset.determineCurAnim(ANIM::DESTSCEN, 4);
lavaBlockPreset.playerHandling = HANDLING::SPECIAL;
lavaBlockPreset.scriptedCollisions = true;
lavaBlockPreset.bulletHandling = HANDLING::IGNOREBULLET;
lavaBlockPreset.isTarget = false;
lavaBlockPreset.isFreezable = false;
lavaBlockPreset.triggersTNT = false;
lavaBlockPreset.deactivates = true;
lavaBlockPreset.energy = 50; // just in case
///@Event 205=Fireball Turret |+|Object |Fire |Turret |Adjust Y:5|Adjust X:-6 |Direction:{Left,Right}1 |Speed:3
jjObjectPresets[OBJECT::DIAMONDUSPOLE].behavior = totemTurret();
jjObjectPresets[OBJECT::DIAMONDUSPOLE].determineCurAnim(ANIM::CUSTOM[12], 0);
///@Event 127=Sparkle |+|Scenery |Sparkle
jjObjectPresets[OBJECT::FISH].behavior = sparkle;
jjObjectPresets[OBJECT::FISH].determineCurAnim(ANIM::CUSTOM[12], 0);
jjObjectPresets[OBJECT::FISH].playerHandling = HANDLING::PARTICLE;
jjObjectPresets[OBJECT::FISH].bulletHandling = HANDLING::IGNOREBULLET;
jjObjectPresets[OBJECT::FISH].triggersTNT = false;
jjObjectPresets[OBJECT::FISH].isTarget = false;
jjObjectPresets[OBJECT::FISH].isFreezable = false;
///@Event 191=Little Bird |+|Scenery |Bird |Bird
jjSampleLoad(SOUND::INTRO_UP1, "bird-takeoff1.wav");
jjSampleLoad(SOUND::INTRO_UP2, "bird-takeoff2.wav");
jjSampleLoad(SOUND::INTRO_SKI, "bird-takeoff3.wav");
jjAnimSets[ANIM::CUSTOM[25]].load(25, "DD-Anims.j2a");
jjOBJ@ presetObject = jjObjectPresets[OBJECT::TUBETURTLE];
presetObject.behavior = littleBird();
presetObject.playerHandling = HANDLING::PARTICLE;
presetObject.bulletHandling = HANDLING::IGNOREBULLET;
presetObject.isTarget = false;
presetObject.isFreezable = false;
presetObject.triggersTNT = false;
presetObject.deactivates = true;
///@Event 113=Guerrilla Doofus |-|Enemy |Guerla |Doofus
jjAnimSets[ANIM::PLUS_SCENERY].load();
jjAnimSets[ANIM::CUSTOM[22]].load(22, "DD-Anims.j2a");
jjSampleLoad(SOUND::INTRO_GREN2, "rustle.wav");
jjOBJ@ guerrillaPreset = jjObjectPresets[OBJECT::HATTER];
guerrillaPreset.behavior = guerrillaDoofus();
guerrillaPreset.energy = 5;
guerrillaPreset.points = 500;
///@Event 254=Delet This |-|Modifier |NOT |v
deleteUnwantedEvents();
// custom platform pixelmap:
jjPIXELMAP platform((17 * 32), 0, 96, 32, 4);
jjANIMFRAME@ grassplat = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::GRASSPLAT].firstAnim].firstFrame];
platform.save(grassplat);
grassplat.hotSpotX = -grassplat.width/2;
grassplat.hotSpotY = -grassplat.height/2 + 10;
// flip Carrotus Pole to match other trees
jjPIXELMAP carrpoleimg(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CARROTPOLE].firstAnim].firstFrame]);
jjANIMFRAME@ carrpole = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CARROTPOLE].firstAnim].firstFrame];
carrpoleimg.flip(SPRITE::FLIPH);
carrpoleimg.save(carrpole);
carrpole.hotSpotX = -10;
// canopy layer 1 pixelmap:
jjPIXELMAP tree1("JungUlt-Canopy1.png");
tree1.crop(0, 64, 960, 352); // shaving off top two tiles (you might not need to)
jjANIMFRAME@ helmut1 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim].firstFrame];
tree1.save(helmut1);
helmut1.hotSpotX = 0;
helmut1.hotSpotY = 0;
// canopy layer 2 pixelmap:
jjPIXELMAP tree2("JungUlt-Canopy2.png");
jjANIMFRAME@ helmut2 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim].firstFrame + 1];
tree2.save(helmut2);
helmut2.hotSpotX = 0;
helmut2.hotSpotY = 0;
// foreground piece 1 pixelmap:
jjPIXELMAP fore1("JungUlt-Foreground1.png");
jjANIMFRAME@ helmut3 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim].firstFrame + 2];
fore1.save(helmut3);
helmut3.hotSpotX = 0;
helmut3.hotSpotY = 0;
// foreground piece 2 pixelmap:
jjPIXELMAP fore2("JungUlt-Foreground2.png");
jjANIMFRAME@ helmut4 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame];
fore2.save(helmut4);
helmut4.hotSpotX = 0;
helmut4.hotSpotY = 0;
// foreground piece 3 pixelmap:
jjPIXELMAP fore3("JungUlt-Foreground3.png");
jjANIMFRAME@ helmut5 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame + 1];
fore3.save(helmut5);
helmut5.hotSpotX = 0;
helmut5.hotSpotY = 0;
// foreground piece 4 pixelmap:
jjPIXELMAP fore4("JungUlt-Foreground4.png");
jjANIMFRAME@ helmut6 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame + 2];
fore4.save(helmut6);
helmut6.hotSpotX = 0;
helmut6.hotSpotY = 0;
// foreground piece 5 pixelmap:
jjPIXELMAP fore5("JungUlt-Foreground5.png");
jjANIMFRAME@ helmut7 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame + 3];
fore5.save(helmut7);
helmut7.hotSpotX = 0;
helmut7.hotSpotY = 0;
jjLAYER@ water = MLLE::GetLayer("Water");
water.spriteMode = SPRITE::TRANSLUCENT;
// canopy sunrays layer:
jjLAYER@ sunrays = MLLE::GetLayer("Sunrays");
sunrays.spriteParam = 192;
sunrays.spriteMode = SPRITE::BLEND_LIGHTEN;
sunrays.textureSurface = SURFACE::FULLSCREEN;
sunrays.textureStyle = TEXTURE::WARPHORIZON;
sunrays.warpHorizon.setFadeColor(jjPALCOLOR(7,219,223));
sunrays.xSpeed = sunrays.ySpeed = 0.05;
sunrays.warpHorizon.fadePositionX = 0.7;
sunrays.warpHorizon.fadePositionY = 0;
jjLAYER@ sky = jjLayers[8];
sky.texture = TEXTURE::NORMAL; // this version of the tileset doesn't include the original sky texture cuz you can simply do this
sky.xSpeed = 0.02;
sky.ySpeed = 0.02;
sky.xAutoSpeed = -0.04;
sky.xSpeedModel = LAYERSPEEDMODEL::NORMAL;
sky.ySpeedModel = LAYERSPEEDMODEL::NORMAL;
// final layer setup for boss
backupLayerOrder = jjLayerOrderGet();
bossLayerOrder = backupLayerOrder;
bossLayerOrder.removeRange(1,3);
}
void onLevelBegin() {
MLLE::SpawnOffgrids();
setTimer();
}
void onLevelReload() {
MLLE::SpawnOffgridsLocal();
copterSpawned = false;
copterDefeated = false;
beenBirdie = false;
DDevan(jjObjectPresets[OBJECT::DEVILDEVAN]);
boemFloor = false;
customBossHealthBar.ready = false;
scoreSeconds = storeScoreSeconds;
if (foughtDevan) {
jjMusicLoad("DD-Jungle.it");
jjSampleLoad(SOUND::DEVILDEVAN_PHASER2, "phaser2.wav"); // revert ghetto solution
}
jjChat("/smhealth " + startHP);
for (int i = 0; i < 8; ++i) jjAlert("");
}
void onMain() {
if (jjDifficulty == 0) oneUpsIntoBlueGems();
if ((jjGameTicks % 70) == 0 && !levelOver && CurrentPopup is null) scoreSeconds++;
rankingSetup();
if (levelOver) updateFade();
int boem;
if ((jjGameTicks % 5) == 0 && boemFloor == true) {
boem = jjAddObject(OBJECT::RFBULLET, (474 * 32) + (jjRandom()%256 * 4), (26 * 32) + (jjRandom()%24 * 4));
jjObjects[boem].state = STATE::EXPLODE;
}
if (jjTriggers[1] && !layer2Destroyed) {
jjLayerOrderSet(bossLayerOrder);
layer2Destroyed = true;
}
else if (!jjTriggers[1] && layer2Destroyed) {
jjLayerOrderSet(backupLayerOrder);
layer2Destroyed = false;
}
if (jjTriggers[2] == true && emoteFade < 255) {
emoteFade++;
if (emoteFade == 1) {
jjSamplePriority(SOUND::DOG_WAF1);
jjLocalPlayers[1].showText("@@@@#MY MAN", STRING::LARGE); // to do someday: make co-op friendly
}
}
else if (jjTriggers[2] == false) emoteFade = 0;
if (copterDefeated) {
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ scen = jjObjects[i];
if (scen.eventID == OBJECT::DESTRUCTSCENERYBOMB && scen.state == STATE::SLEEP) {
scen.state = STATE::KILL;
// copterDefeated = false;
}
}
}
}
void onPlayer(jjPLAYER@ play) {
dialogueInterface(play);
rankingInterface(play);
if (play.bossActivated) {
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.eventID == OBJECT::DEVILDEVAN) {
if (obj.state == STATE::DONE) {
play.keyLeft = play.keyRight = play.keyDown = play.keyUp = play.keyRun = play.keyFire = play.keyJump = play.keySelect = false;
}
if (obj.state == STATE::WALK && devanWait) {
obj.state = STATE::STILL;
obj.determineCurAnim(ANIM::DEVILDEVAN, 8);
}
if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 6) {
boemFloor = true;
jjMusicStop();
}
if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 5) {
jjMusicLoad("damn-akane.mp3");
if (!jjTriggers[1]) {
for (int a = 0; a < 8; a++) {
jjOBJ@ lavaBlub = jjObjects[jjAddObject(OBJECT::AMBIENTSOUND, (476 * 32) + (a * 7 * 32) + 16, (28 * 32) + 16)];
lavaBlub.var[1] = 96;
}
}
jjTriggers[1] = true;
boemFloor = false;
}
if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 1) {
if (play.bossActivated == true) { endFlag = true; }
play.activateBoss(false);
@CurrentPopup = Conversation(array<Screen@> = {
Screen(
top: Line("okay buster, now TALK! where's bubba?!", left: Jazz, direction: -1)
),
Screen(
top: Line(left: Jazz),
bottom: Line("h-he's on that hill behind you, doing a RITUAL of some sort! now leave me ALONE!", right: Devan, direction: 1, color: 80)
),
Screen(
top: Line("yeah, sure, like i'll FALL for that.", left: Jazz, direction: -1),
bottom: Line(right: Devan, direction: 1, color: 80)
),
Screen(
top: Line("jazz, wait. that does explain why they came to this jungle in the first place. given, you know... its TEMPORALLY DISPLACED aura.", left: Jazz, right: Lori, direction: 1, color: 40),
bottom: Line("what she said. but that's really ALL i know!", right: Devan, direction: 1, color: 80)
),
Screen(
top: Line("so... that HILL over there? behind the VILLAGE?", left: Jazz, right: Lori, direction: -1),
bottom: Line("yes, YES!!", right: Devan, direction: 1, color: 80)
),
Screen(
top: Line(left: Jazz, right: Lori),
bottom: Line("well, break a leg or WHATEVER! my personal translocator is all charged up now, so i'm OUTTA HERE!", right: Devan, direction: 1, color: 80),
finish: function(bool skipping) {
jjLocalPlayers[0].boss = 0;
jjSamplePriority(SOUND::INTRO_MONSTER2);
endFlag = false;
endLevel(jjLocalPlayers[0]);
}
),
});
}
}
}
}
if (play.charCurr == CHAR::BIRD) {
if (play.shieldTime == 0) {
endBirdie(play);
}
else { jjSetModTempo(uint8(-1.75 * int(play.shieldTime / 70) + 210)); }
}
}
void onPlayerDraw(jjPLAYERDRAW& draw) {
jjPLAYER@ play = draw.player;
if (play.charCurr == CHAR::BIRD) {
draw.shield[SHIELD::PLASMA] = false;
return;
}
}
bool onDrawScore(jjPLAYER@ player, jjCANVAS@ canvas) {
if (jillNear) {
canvas.drawSprite((jjSubscreenWidth / 2) - 16, jjSubscreenHeight - (jjSubscreenHeight / 32), ANIM::CUSTOM[7], 16, 0);
canvas.drawString(jjSubscreenWidth / 2, jjSubscreenHeight - (jjSubscreenHeight / 32), "x" + player.gems[GEM::PURPLE], STRING::MEDIUM);
}
if (CurrentPopup !is null) {
CurrentPopup.Draw(canvas);
return !CurrentPopup.DrawHUD();
}
if (levelOver) {
drawFade(player, canvas);
if (rankLine < 6) rankingDisplay(player, canvas);
}
if (rankLine >= 6) return true;
else return false;
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
if (jjColorDepth < 16) {
canvas.drawRectangle(0, 0, jjSubscreenWidth, jjSubscreenHeight, 79);
canvas.drawString((jjSubscreenWidth / 16), (jjSubscreenHeight / 2) - (jjSubscreenHeight / 8), "This level does not", smallScreen ? STRING::SMALL : STRING::MEDIUM);
canvas.drawString((jjSubscreenWidth / 16), (jjSubscreenHeight / 2) - (jjSubscreenHeight / 16), "support 8-bit color depth.", smallScreen ? STRING::SMALL : STRING::MEDIUM);
canvas.drawString((jjSubscreenWidth / 16), (jjSubscreenHeight / 2) + (jjSubscreenHeight / 16), "Please go to options menu", smallScreen ? STRING::SMALL : STRING::MEDIUM);
canvas.drawString((jjSubscreenWidth / 16), (jjSubscreenHeight / 2) + (jjSubscreenHeight / 8), "and switch to 16-bit color.", smallScreen ? STRING::SMALL : STRING::MEDIUM);
}
if (rankLine >= 6) return true;
else return CurrentPopup !is null && !CurrentPopup.DrawHUD();
}
bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
if (rankLine >= 6) return true;
else return CurrentPopup !is null && !CurrentPopup.DrawHUD();
}
bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {
if (customBossHealthBar.ready) customBossHealthBar.draw(canvas);
if (jjDifficulty == 0 && CurrentPopup is null) {
return true;
}
if (rankLine >= 6) return true;
else return CurrentPopup !is null && !CurrentPopup.DrawHUD();
}
bool onDrawPlayerTimer(jjPLAYER@ player, jjCANVAS@ canvas) { return CurrentPopup !is null && !CurrentPopup.DrawHUD(); }
// putting foreground eyecandy on layer 1:
void onDrawLayer1(jjPLAYER@ player, jjCANVAS@ canvas) {
canvas.drawSprite(0, (14 * 32), ANIM::HELMUT, 0, 2);
canvas.drawSprite(269, (34 * 32), ANIM::HELMUT, 1, 0, -1);
canvas.drawSprite(0, (94 * 32), ANIM::HELMUT, 0, 2);
canvas.drawSprite((416 * 32), (38 * 32), ANIM::HELMUT, 0, 2);
canvas.drawSprite((415 * 32), (11 * 32), ANIM::HELMUT, 0, 2, -1);
canvas.drawSprite((100 * 32), 0, ANIM::HELMUT, 1, 1);
canvas.drawSprite((200 * 32), 0, ANIM::HELMUT, 1, 1, -1);
canvas.drawSprite((400 * 32), 0, ANIM::HELMUT, 1, 1, -1);
canvas.drawSprite((124 * 32), (109 * 32), ANIM::HELMUT, 1, 2);
canvas.drawSprite((520 * 32), (102 * 32), ANIM::HELMUT, 1, 2);
canvas.drawSprite((604 * 32), (105 * 32), ANIM::HELMUT, 1, 2, -1);
canvas.drawSprite((294 * 32), 0, ANIM::HELMUT, 1, 1);
canvas.drawSprite((250 * 32), 0, ANIM::HELMUT, 1, 3);
canvas.drawSprite((340 * 32), 0, ANIM::HELMUT, 1, 3, -1);
canvas.drawSprite((550 * 32), 0, ANIM::HELMUT, 1, 3);
canvas.drawSprite((500 * 32), 0, ANIM::HELMUT, 1, 1);
canvas.drawSprite((610 * 32), 0, ANIM::HELMUT, 0, 2, SPRITE::FLIPV);
canvas.drawSprite((747 * 32) + 20, (97 * 32), ANIM::HELMUT, 1, 0);
if (player.charCurr == CHAR::BIRD) canvas.drawSprite((746 * 32), (47 * 32) - 10, ANIM::HELMUT, 1, 1, -1);
}
void onDrawLayer4(jjPLAYER@ player, jjCANVAS@ canvas) {
canvas.drawSprite((432 * 32), (35 * 32) + 16, ANIM::CUSTOM[5], 0, 0, 0, SPRITE::BLEND_NORMAL, emoteFade);
if (drawBirdie) canvas.drawSprite((447 * 32), (71 * 32) + 10, ANIM::CHUCK, 18, (jjGameTicks/12) & 3, -1);
}
// applying canopy to tree layer 1:
void onDrawLayer6(jjPLAYER@ player, jjCANVAS@ canvas) {
for (int i = -1; i < 9; i++) canvas.drawSprite(0 + (960 * i), 0, ANIM::HELMUT, 0, 0); // you might want a bigger number of loops if your level is very wide
}
// applying canopy to tree layer 2:
void onDrawLayer7(jjPLAYER@ player, jjCANVAS@ canvas) {
for (int i = -1; i < 7; i++) canvas.drawSprite(0 + (800 * i), 64, ANIM::HELMUT, 0, 1); // two tiles down for this level
}
class CustomBossHealthBar {
int maxHealth;
bool ready = false;
jjOBJ@ boss;
jjPIXELMAP healthBar(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::BOSS].firstAnim].firstFrame + 1]);
void initialize(jjOBJ@ boss)
{
@this.boss = @boss;
maxHealth = boss.energy;
ready = true;
}
void draw(jjCANVAS@ canvas)
{
if (boss.energy <= 0 || devanWait == true) return;
if (bossPhase <= 3) canvas.drawString(jjResolutionWidth / 2 + 90, 20, "x" + (5 - bossPhase), STRING::SMALL, STRING::BOUNCE);
for (int x = 0; x < 1 + int(ceil((healthBar.width * bossHealth) / maxHealth)); ++x)
for (uint y = 0; y < healthBar.height; ++y)
if (healthBar[x, y] != 0)
canvas.drawPixel(jjResolutionWidth / 2 + x - 69, 4 + y + 17, healthBar[x, y] + 16 * (4 - bossPhase));
}
}
void tweakDevanRemoteAnim() {
for (uint i = 0; i < jjAnimations[jjAnimSets[ANIM::DEVAN].firstAnim + 1].frameCount; ++i) {
jjAnimFrames[jjAnimations[jjAnimSets[ANIM::DEVAN].firstAnim + 1].firstFrame + i].hotSpotY += 12;
}
}
const array<SOUND::Sample> ButtstompSounds = {SOUND::COMMON_SPLAT1, SOUND::COMMON_SPLAT2, SOUND::COMMON_SPLAT3, SOUND::COMMON_SPLAT4};
class DDevan : jjBEHAVIORINTERFACE {
int prevFrameID, physMoveIFrames, turnAroundCounter, cutsceneCounter;
int crouchShootCounter, crouchShootFireCounter;
jjOBJ@ bigGunProp;
bool isLizardInitialized;
float lizardRelXPos, lizardRelYPos, lizardTargetXPos, lizardTargetYPos;
int lizardDirection, lizardTimer;
int turnAroundTime = 50, animSpeed = 6, lizardWait = 128;
DDevan(jjOBJ@ preset) {
preset.behavior = this;
preset.playerHandling = HANDLING::SPECIAL;
preset.bulletHandling = HANDLING::DETECTBULLET;
preset.scriptedCollisions = true;
preset.isTarget = true;
preset.isFreezable = true;
preset.triggersTNT = true;
initializeBoss();
}
void initializeBoss() {
bossPhase = 1;
bossHealth = 100;
prevFrameID = -1;
physMoveIFrames = 0;
turnAroundCounter = 0;
cutsceneCounter = 0;
crouchShootCounter = 0;
crouchShootFireCounter = 0;
isLizardInitialized = false;
@bigGunProp = null;
}
void initializeLizard(jjOBJ@ obj) {
if (isLizardInitialized) return;
lizardRelXPos = 512 * obj.direction;
lizardRelYPos = lizardRelXPos * lizardRelXPos / 3072 - 160;
lizardTargetXPos = obj.xPos;
lizardTargetYPos = obj.yPos;
lizardDirection = -obj.direction;
lizardTimer = 0;
isLizardInitialized = true;
}
void processLizard(jjOBJ@ obj) {
if (!isLizardInitialized) return;
lizardRelYPos = -lizardRelXPos * lizardRelXPos / 3072 - 160;
if (lizardRelXPos > -96 && lizardRelXPos < 96 && lizardTimer <= lizardWait) lizardTimer++;
else lizardRelXPos += 4 * lizardDirection;
if (lizardTimer == 12 * animSpeed) {
@bigGunProp = jjObjects[jjAddObject(OBJECT::BOMB, lizardRelXPos + lizardTargetXPos + 28 * lizardDirection, lizardRelYPos + lizardTargetYPos - 40)];
bigGunProp.behavior = GunProp();
bigGunProp.var[1] = ANIM::CUSTOM[19];
bigGunProp.var[2] = 3;
bigGunProp.direction = lizardDirection;
bigGunProp.xSpeed = float(3 * lizardDirection) / 4;
bigGunProp.ySpeed = -2;
}
}
void drawLizard(jjOBJ@ obj) {
if (!isLizardInitialized) return;
jjDrawSprite(lizardRelXPos + lizardTargetXPos, lizardRelYPos + lizardTargetYPos, ANIM::LIZARD, 3, jjGameTicks >> 2, lizardDirection, SPRITE::BRIGHTNESS, 110, -1);
if (lizardTimer > 0 && lizardTimer < 13 * animSpeed)
jjDrawSprite(lizardRelXPos + lizardTargetXPos, lizardRelYPos + lizardTargetYPos, ANIM::CUSTOM[19], 11, lizardTimer / animSpeed, lizardDirection, SPRITE::BRIGHTNESS, 110, -1);
else
jjDrawSprite(lizardRelXPos + lizardTargetXPos, lizardRelYPos + lizardTargetYPos, ANIM::LIZARD, 2, jjGameTicks >> 3, lizardDirection, SPRITE::BRIGHTNESS, 110, -1);
}
void usePhaseTwoAnims(jjOBJ@ obj) {
if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 13) { obj.determineCurAnim(ANIM::CUSTOM[19], 1); }
else if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 14) { obj.determineCurAnim(ANIM::CUSTOM[19], 2); }
else if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 16) { obj.determineCurAnim(ANIM::CUSTOM[19], 4); }
else if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 18) { obj.determineCurAnim(ANIM::CUSTOM[19], 5); }
else if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 19) { obj.determineCurAnim(ANIM::CUSTOM[19], 6); }
else if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 20) { obj.determineCurAnim(ANIM::CUSTOM[19], 7); }
else if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 21) { obj.determineCurAnim(ANIM::CUSTOM[19], 8); }
else if (uint(obj.curAnim) == jjAnimSets[ANIM::CUSTOM[19]].firstAnim + 9) { obj.determineCurAnim(ANIM::CUSTOM[19], 10); }
}
void replacePhaseOneProjectiles() {
for (int i = 0; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (uint(obj.curAnim) == jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 17 && jjGameTicks % 1 == 0) {
jjOBJ@ laser = jjObjects[obj.fireBullet(OBJECT::LASER)];
laser.behavior = BEHAVIOR::BULLET;
laser.direction = jjObjects[obj.creatorID].direction;
if (jjObjects[i].creatorID == uint(obj.objectID)) jjObjects[i].state = STATE::KILL;
laser.creatorID = obj.creatorID;
laser.creatorType = CREATOR::OBJECT;
laser.playerHandling = HANDLING::ENEMYBULLET;
int playerID = obj.findNearestPlayer(2000000);
if (playerID >= 0 && laser.direction * jjLocalPlayers[playerID].xPos > laser.direction * laser.xPos && abs(jjLocalPlayers[playerID].yPos - laser.yPos) <= 32.f)
jjLocalPlayers[playerID].hurt();
obj.delete();
}
}
}
void replacePhaseThreeProjectiles() {
for (int i = 0; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.curAnim == int(jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 4) && obj.counter == 0 && jjGameTicks % 1 == 0) {
jjOBJ@ bilsyFire = jjObjects[obj.fireBullet(OBJECT::BULLET)];
bilsyFire.behavior = BEHAVIOR::BILSYBULLET;
bilsyFire.direction = obj.direction;
bilsyFire.creatorID = obj.objectID;
bilsyFire.creatorType = CREATOR::OBJECT;
bilsyFire.playerHandling = HANDLING::ENEMYBULLET;
bilsyFire.animSpeed = 2;
bilsyFire.determineCurAnim(ANIM::BILSBOSS, 3);
obj.delete();
}
}
}
void onBehave(jjOBJ@ obj) {
// 1 - SLEEP - Defeated, sitting
// 2 - WAKE - Transform into D. Devan
// 5 - WALK - Walking
// 6 - JUMP - Jumping
// 7 - FIRE - Shooting
// 8 - FLY - (DD) Fly
// 12 - STILL - Idle
// 17 - DONE - Defeated, falling
// 19 - FALL - Falling
// 22 - ATTACK - (DD) Shoot fireballs
// 24 - FADEIN - Teleporting in
// 31 - WAIT - ?
// 29 - EXTRA - Recover from falling
// 33 - DELAYEDSTART - Starting
// 35 - DUCK - Ducking
// Bonus states:
// 9 - BOUNCE - Crouch shooting
// 13 - FLOAT - Move to final position, wait for end cutscene
// 27 - TURN - Phase 1 to 2 cutscene
int playerID = obj.findNearestPlayer(1000000);
float finalXPos, dirX, dirY, length;
float finalYPos = 19 * 32;
obj.behave(BEHAVIOR::DEVILDEVAN, false);
if (physMoveIFrames > 0) physMoveIFrames--;
if (obj.freeze > 35) obj.freeze = 35;
if (obj.state != STATE::WALK) turnAroundCounter = 0;
if (bossPhase == 1 && bossHealth <= 0) {
float yAux = obj.yPos;
obj.putOnGround();
// Making extra sure that Devan is on the ground when he's losing his gun.
// Hopefully this approach doesn't cause any issues :P
if (yAux == obj.yPos) {
obj.state = STATE::TURN;
obj.isFreezable = false;
bossPhase = 2;
bossHealth = 100;
crouchShootCounter = 0;
crouchShootFireCounter = 0;
}
else obj.yPos = yAux;
}
obj.energy = (bossHealth <= 0) ? 1 : bossHealth;
if (bossPhase == 4 && bossHealth <= 0) {
if (obj.state == STATE::ATTACK) { // Dying while shooting fireballs seems to cause issues.
obj.determineCurAnim(ANIM::DEVILDEVAN, 5);
obj.state = STATE::FLY;
}
//bossPhase = 5;
obj.energy = 0;
obj.state = STATE::FLOAT;
if (playerID >=0) givePlayerPointsForObject(jjLocalPlayers[playerID], obj);
}
if (bossPhase == 5) {
obj.var[2] = 69; // Nice.
if (playerID >= 0 && jjLocalPlayers[playerID].boss == 0) {
obj.determineCurAnim(ANIM::DEVILDEVAN, 11);
if (prevFrameID == -1) {
obj.frameID = 0;
prevFrameID = 0;
obj.determineCurFrame();
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_TELPORT2);
}
if (prevFrameID == 6 && obj.frameID != prevFrameID) obj.delete();
prevFrameID = obj.frameID;
}
}
switch (obj.state) {
case STATE::WALK:
if (playerID >= 0) {
if (obj.direction * jjLocalPlayers[playerID].xPos < obj.direction * obj.xPos)
turnAroundCounter++;
if (turnAroundCounter >= turnAroundTime) {
turnAroundCounter = 0;
obj.direction *= -1;
}
}
break;
case STATE::DUCK:
if (obj.frameID == 5) {
if (bossPhase == 1) obj.determineCurAnim(ANIM::CUSTOM[19], 9);
else obj.determineCurAnim(ANIM::CUSTOM[19], 10);
obj.frameID = 0;
obj.state = STATE::BOUNCE;
}
break;
case STATE::FIRE:
if (playerID >= 0 && (obj.frameID == 4 || obj.frameID == 5) && (jjLocalPlayers[playerID].curAnim - jjAnimSets[jjLocalPlayers[playerID].setID].firstAnim == RABBIT::DIVE) || (jjLocalPlayers[playerID].curAnim - jjAnimSets[jjLocalPlayers[playerID].setID].firstAnim == RABBIT::DIVEFIRERIGHT)) {
obj.frameID = 0;
obj.state = STATE::DUCK;
}
break;
case STATE::BOUNCE:
if (obj.frameID < 4) obj.frameID = int(crouchShootCounter / (animSpeed - 2));
else if (obj.frameID == 4 || obj.frameID == 5) {
if (crouchShootCounter % (animSpeed - 2) == 0) {
if (playerID >= 0 && crouchShootFireCounter < 11 && ((obj.direction * jjLocalPlayers[playerID].xPos > obj.direction * obj.xPos && abs(jjLocalPlayers[playerID].yPos - obj.yPos) < 50) || crouchShootFireCounter < 1)) {
if (obj.frameID == 4) obj.frameID = 5;
else if (obj.frameID == 5) {
obj.frameID = 4;
jjSample(obj.xPos, obj.yPos, SOUND::DEVILDEVAN_PHASER2);
jjOBJ@ blast = jjObjects[obj.fireBullet(OBJECT::BLASTERBULLET)];
blast.determineCurAnim(ANIM::DEVILDEVAN, 17);
blast.playerHandling = HANDLING::ENEMYBULLET;
blast.state = STATE::FLY;
blast.counterEnd = 63;
blast.direction = obj.direction;
blast.xSpeed = 6 * obj.direction;
blast.xAcc = 0;
blast.determineCurFrame();
crouchShootFireCounter++;
}
}
else obj.frameID++;
}
}
else if (crouchShootCounter % (animSpeed - 2) == 0) obj.frameID++;
crouchShootCounter++;
if (obj.frameID == 9 && crouchShootCounter % (animSpeed - 2) == 3) {
crouchShootCounter = 0;
crouchShootFireCounter = 0;
obj.determineCurAnim(ANIM::DEVILDEVAN, 13);
obj.frameID = 7;
obj.state = STATE::DUCK;
}
break;
case STATE::TURN:
if (bossPhase == 2) {
if (cutsceneCounter == 0 && playerID >= 0) jjLocalPlayers[playerID].cameraFreeze(obj.xPos, obj.yPos, true, false);
if (cutsceneCounter < animSpeed * 7) {
obj.determineCurAnim(ANIM::DEVILDEVAN, 12);
obj.frameID = cutsceneCounter / animSpeed;
if (cutsceneCounter == animSpeed) {
jjOBJ@ gun = jjObjects[jjAddObject(OBJECT::BOMB, obj.xPos + 25 * -obj.direction, obj.yPos - 10)];
gun.behavior = GunProp();
gun.var[1] = ANIM::DEVILDEVAN;
gun.var[2] = 15;
gun.direction = -obj.direction;
gun.xSpeed = -3 * obj.direction;
gun.ySpeed = -2;
}
}
else if (cutsceneCounter < animSpeed * 7 + animSpeed * 14) {
obj.determineCurAnim(ANIM::CUSTOM[19], 0);
obj.frameID = cutsceneCounter / animSpeed - 7;
}
else {
initializeLizard(obj);
obj.determineCurAnim(ANIM::DEVAN, 1);
obj.frameID = jjGameTicks >> 3;
}
cutsceneCounter++;
}
break;
case STATE::FLOAT:
if (obj.xPos >= 488 * 32) finalXPos = 493 * 32;
else finalXPos = 481 * 32;
if (obj.xPos > jjLocalPlayers[playerID].xPos) obj.direction = -1;
else obj.direction = 1;
obj.determineCurAnim(ANIM::DEVILDEVAN, 5);
obj.frameID = ((jjGameTicks/10) % jjAnimations[obj.curAnim].frameCount);
if (jjGameTicks % 10 == 0) obj.justHit = 5;
if (abs(obj.xPos - finalXPos) <= 32.f && abs(obj.yPos - finalYPos) <= 32.f && jjLocalPlayers[playerID].yPos < 26 * 32 && (jjLocalPlayers[playerID].curAnim - jjAnimSets[jjLocalPlayers[playerID].setID].firstAnim == RABBIT::STAND || jjLocalPlayers[playerID].curAnim - jjAnimSets[jjLocalPlayers[playerID].setID].firstAnim == RABBIT::RUN1 || jjLocalPlayers[playerID].curAnim - jjAnimSets[jjLocalPlayers[playerID].setID].firstAnim == RABBIT::RUN2 || jjLocalPlayers[playerID].curAnim - jjAnimSets[jjLocalPlayers[playerID].setID].firstAnim == RABBIT::RUN3)) {
bossPhase = 5;
obj.state = STATE::DONE;
}
else {
dirX = finalXPos - obj.xPos;
dirY = finalYPos - obj.yPos;
length = sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
dirX /= length;
dirY /= length;
}
obj.xPos += dirX;
obj.yPos += dirY;
}
break;
}
if (bossPhase == 2) {
usePhaseTwoAnims(obj);
if (cutsceneCounter > 200) {
replacePhaseOneProjectiles();
}
if (obj.state != STATE::STILL && bigGunProp !is null && obj.doesCollide(bigGunProp)) {
bigGunProp.deactivate();
@bigGunProp = null;
jjSample(obj.xPos, obj.yPos, SOUND::DEVILDEVAN_LAUGH);
jjSampleLoad(SOUND::DEVILDEVAN_PHASER2, "laser.wav"); // ghetto solution
obj.determineCurAnim(ANIM::CUSTOM[19], 8);
obj.state = STATE::STILL;
obj.isFreezable = true;
obj.counter = 0;
if (playerID >= 0) jjLocalPlayers[playerID].cameraUnfreeze(false);
}
if (bossHealth <= 0) {
bossHealth = 100;
obj.state = STATE::WAKE;
obj.frameID = 0;
bossPhase = 3;
}
}
if (bossPhase == 3 && bossHealth <= 0 && uint(obj.curAnim) != jjAnimSets[ANIM::DEVILDEVAN].firstAnim + 6) {
bossHealth = 100;
bossPhase = 4;
}
if (bossPhase == 4) replacePhaseThreeProjectiles();
processLizard(obj);
}
void onDraw(jjOBJ@ obj) {
if (obj.state == STATE::DELAYEDSTART) return;
obj.determineCurFrame();
drawLizard(obj);
if (obj.state == STATE::FREEZE) {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos + ((jjGameTicks % 8 < 4) ? 0 : 3), obj.curFrame, obj.direction, (jjGameTicks % 8 < 4) ? SPRITE::FROZEN : ((bossPhase == 4) ? SPRITE::PALSHIFT : SPRITE::NORMAL), (bossPhase == 4) ? 8 : 0);
}
else {
if (obj.justHit == 0)
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, (bossPhase == 4) ? SPRITE::PALSHIFT : SPRITE::NORMAL, (bossPhase == 4) ? 8 : 0);
else jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::SINGLECOLOR, 15);
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
// Edited version of the onObjectHit function of the Flower class from plus52Scripting.j2as
if (bossPhase == 5 || obj.state == STATE::FLOAT) return true;
if (bullet !is null) {
//recreation of HANDLING::HURTBYBULLET with HANDLING::ENEMY
if (obj.causesRicochet) {
if ((bullet.var[6] & 6) == 0) //not fire-based, not a laser beam
bullet.ricochet();
else if ((bullet.var[6] & 4) == 0) //not a laser beam
bullet.delete();
} else if ((bullet.var[6] & 16) == 0) //not a fireball
bullet.state = STATE::EXPLODE;
if (obj.freeze > 0 && force < 3)
force = 3;
if (obj.state != STATE::TURN) {
bossHealth -= force;
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.
}
if (bossHealth <= 0) { //killed
bossHealth = 0;
if (obj.freeze > 0)
obj.unfreeze(0);
if (player !is null) {
obj.grantPickup(player, (uint(bullet.curAnim) == jjAnimSets[ANIM::AMMO].firstAnim + 17) ? 5 : 10);
}
} else
obj.freeze = 0;
} else { //recreation of HANDLING::ENEMY; player guaranteed to be non-null
if (force != 0 && physMoveIFrames == 0) { //attacking via special attack, e.g. buttstomp
if (obj.state != STATE::TURN) bossHealth -= 4; //constant amount of damage for special attacks
physMoveIFrames = 10;
if (obj.state != STATE::TURN && bossHealth > 0) { //only wounded
obj.justHit = 5;
}
if (obj.freeze > 0) {
obj.unfreeze(1);
} else {
jjSample(obj.xPos, obj.yPos, ButtstompSounds[jjRandom() % ButtstompSounds.length]);
}
if (force > 0) { //buttstomp or sugar rush
player.buttstomp = 50; //landing
player.ySpeed = player.ySpeed / -2 - 8;
player.yAcc = 0;
player.extendInvincibility(-70);
} else if (force == -101) { //running into frozen enemy
player.xAcc = 0;
player.xSpeed /= -2;
player.ySpeed = -6;
player.extendInvincibility(-10);
}
} else if (!(obj.freeze > 0)) { //not attacking
player.hurt();
}
}
return true;
}
}
class GunProp : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
if (obj.var[0] == 0) initialize(obj);
if (obj.state == STATE::FLY && obj.counter < 200) {
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
obj.ySpeed += 0.125;
obj.counter++;
// jjPrint(""+obj.counter);
}
else obj.delete();
}
void initialize(jjOBJ@ obj) {
obj.var[0] = 1;
obj.playerHandling = HANDLING::PARTICLE;
obj.bulletHandling = HANDLING::IGNOREBULLET;
obj.isTarget = false;
obj.isFreezable = false;
obj.isBlastable = false;
obj.triggersTNT = false;
obj.state = STATE::FLY;
obj.lightType = LIGHT::NONE;
}
void onDraw(jjOBJ@ obj) {
jjDrawSprite(obj.xPos, obj.yPos, obj.var[1], obj.var[2], 8 - jjGameTicks >> 2, obj.direction, SPRITE::NORMAL, 0, -1);
}
}
bool givePlayerPointsForObject(jjPLAYER@ player, jjOBJ@ obj) {
if (player is null)
return false;
if (obj.points != 0 && (jjGameMode == GAME::SP || jjGameMode == GAME::COOP)) {
player.score += obj.points;
jjPARTICLE@ particle = jjAddParticle(PARTICLE::STRING);
if (particle !is null) {
particle.xPos = obj.xPos;
particle.yPos = obj.yPos;
particle.xSpeed = (-32768 - int(jjRandom() & 0x3FFF)) / 65536.f;
particle.ySpeed = (-65536 - int(jjRandom() & 0x7FFF)) / 65536.f;
particle.string.text = formatInt(obj.points);
}
obj.points = 0;
return true;
}
return false;
}
class shamanDemon : jjBEHAVIORINTERFACE {
int curseTarget;
int curseDistance = (256 * 256) + (jjDifficulty * 256 * 64);
int lastHitID;
void onBehave(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.putOnGround();
curseTarget = -2; // clear targets after reloading
obj.state = STATE::WALK;
case STATE::WALK:
obj.behave(BEHAVIOR::WALKINGENEMY);
obj.var[3] = 0; // for summon animation
obj.var[4] = 0; // for bonk animation
if (obj.direction >= 0) {
obj.xSpeed = 0.2;
} else { obj.xSpeed = -0.2; }
if (obj.findNearestPlayer(curseDistance) > -1 && obj.findNearestPlayer(curseDistance) != curseTarget) { // only one cloud per player can be active FROM THE SAME SHAMAN DEMON
curseTarget = obj.findNearestPlayer(curseDistance);
obj.state = STATE::EXTRA;
}
obj.var[2] = obj.var[2] - 1;
if (jjRandom() % 512 == 0 && obj.var[2] <= 0) {
obj.var[1] = 70 + (jjRandom() % 140); // time of idling
obj.state = STATE::IDLE;
}
if (obj.findNearestPlayer(5000) > -1) {
if (obj.xPos > jjLocalPlayers[obj.findNearestPlayer(5000)].xPos) { obj.direction = -1; }
else { obj.direction = 1; }
obj.state = STATE::ATTACK;
}
break;
case STATE::IDLE:
if (obj.var[1] == 0) {
int rand = jjRandom()%2;
obj.direction = rand - 1; // randomly change direction
if (obj.direction >= 0) {
obj.xSpeed = 0.2;
} else { obj.xSpeed = -0.2; } // have to declare movement vector again or direction will never change here for some reason...
obj.var[2] = 140; // idle cooldown
obj.state = STATE::WALK;
} else {
obj.var[1] = obj.var[1] - 1;
}
break;
case STATE::ATTACK:
obj.var[4] = obj.var[4] + 1;
if (obj.var[4] == 42) jjSample(obj.xPos, obj.yPos, SOUND::INTRO_HITTURT);
if (obj.var[4] == 70) obj.state = STATE::WALK;
break;
case STATE::EXTRA:
obj.var[3] = obj.var[3] + 1;
if (obj.var[3] == 20) {
int rand = jjRandom()%2;
if (rand==0)
jjSample(obj.xPos, obj.yPos, SOUND::INTRO_INHALE);
else jjSample(obj.xPos, obj.yPos, SOUND::INTRO_UHTURT);
}
if (obj.var[3] == 90) {
jjOBJ@ cloud = jjObjects[jjAddObject(OBJECT::RAPIER, jjLocalPlayers[curseTarget].xPos, jjLocalPlayers[curseTarget].yPos - 128, obj.objectID)];
cloud.var[2] = curseTarget;
int rand = jjRandom()%2;
if (rand==0)
jjSample(cloud.xPos, cloud.yPos, SOUND::AMMO_BLUB1);
else jjSample(cloud.xPos, cloud.yPos, SOUND::AMMO_BLUB2);
for( int n = 0; n < 2 + int(jjRandom()%3); n++ ) {
jjOBJ@ spark = jjObjects[jjAddObject(OBJECT::SHARD, cloud.xPos, cloud.yPos, cloud.objectID, CREATOR::OBJECT)];
spark.determineCurAnim(ANIM::PICKUPS, 27);
}
}
if (obj.var[3] == 112) {
obj.state = STATE::WALK;
}
break;
case STATE::FREEZE:
if (obj.freeze == 0) obj.state = obj.oldState;
else obj.freeze--;
break;
case STATE::DEACTIVATE:
for (int i = 0; i < jjObjectCount; ++i)
if (int(jjObjects[i].creatorID) == obj.objectID) jjObjects[i].state = STATE::KILL;
obj.deactivate();
break;
case STATE::KILL:
for (int i = 0; i < jjObjectCount; ++i)
if (int(jjObjects[i].creatorID) == obj.objectID) jjObjects[i].state = STATE::KILL;
givePlayerPointsForObject(jjLocalPlayers[lastHitID], obj);
obj.particlePixelExplosion(0);
obj.delete();
}
}
void onDraw(jjOBJ@ obj) {
switch (obj.state) {
case STATE::IDLE:
obj.determineCurAnim(ANIM::CUSTOM[9], 0);
obj.frameID = ((jjGameTicks/9) % jjAnimations[obj.curAnim].frameCount);
break;
case STATE::WALK:
obj.determineCurAnim(ANIM::CUSTOM[9], 1);
obj.frameID = ((jjGameTicks/9) % jjAnimations[obj.curAnim].frameCount);
break;
case STATE::ATTACK:
obj.determineCurAnim(ANIM::CUSTOM[9], 2);
obj.frameID = int8(obj.var[4] / 7);
break;
case STATE::EXTRA:
obj.determineCurAnim(ANIM::CUSTOM[9], 3);
obj.frameID = int8(obj.var[3] / 7);
break;
}
obj.determineCurFrame();
obj.draw();
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet !is null) {
if ((bullet.var[6] & 16) == 0) { //not a fireball
bullet.state = STATE::EXPLODE;
}
if (obj.freeze != 0) {
if (force < 3)
force = 3;
obj.unfreeze(0);
}
if (bullet.var[3] == 8) obj.energy -= (bullet.animSpeed * 2); // super effective
else obj.energy -= bullet.animSpeed;
obj.justHit = 5;
} else {
if (force == 0) { //collision
if (obj.state != STATE::FREEZE)
player.hurt();
} else {
if (force == 1) { //sugar rush/buttstomp
bool bounce = true;
if (player.buttstomp == 41) //actually stomping
bounce = !player.hurt(); //stomping but protected by something, including a sugar rush
if (bounce) {
player.buttstomp = 50; //landing
player.ySpeed = player.ySpeed / -2 - 8;
player.yAcc = 0;
player.extendInvincibility(-70);
}
} else if (force == -101) { //frozen+running
player.xAcc = 0;
player.xSpeed /= -2;
player.ySpeed = -6;
player.extendInvincibility(-10);
}
if (obj.var[7] < jjGameTicks) { //hasn't been hurt recently
obj.var[7] = jjGameTicks + 70; //next time this can be hurt by a physical attack
if (obj.freeze != 0)
obj.unfreeze(1);
else
jjSample(obj.xPos, obj.yPos, ButtstompSounds[jjRandom() % ButtstompSounds.length]);
obj.energy -= 4;
obj.justHit = 5;
}
}
}
lastHitID = player.playerID;
if (obj.energy <= 0) obj.state = STATE::KILL;
return true;
}
}
class stormCloud : jjBEHAVIORINTERFACE {
float followSpeed = 5 + jjDifficulty;
int attackCD = 240 - (jjDifficulty * 40);
void onBehave(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.var[1] = attackCD;
obj.state = STATE::IDLE;
case STATE::IDLE:
obj.var[3] = 50 - (jjDifficulty * 10);
obj.frameID = (jjGameTicks/5) & 3;
obj.determineCurFrame();
if (abs(obj.xPos - jjLocalPlayers[obj.var[2]].xPos) > 8.f || abs(obj.yPos - (jjLocalPlayers[obj.var[2]].yPos - 128)) > 8.f) {
float dirX = jjLocalPlayers[obj.var[2]].xPos - obj.xPos;
float dirY = jjLocalPlayers[obj.var[2]].yPos - 128 - obj.yPos;
float length = sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
dirX /= length;
dirY /= length;
}
obj.xPos += dirX * followSpeed;
obj.yPos += dirY * followSpeed;
}
obj.var[1] = obj.var[1] - 1;
if (obj.var[1] <= 0) {
obj.var[2] = 40;
obj.state = STATE::ATTACK;
}
break;
case STATE::ATTACK:
obj.frameID = (jjGameTicks/3) & 3;
obj.determineCurFrame();
if (jjGameTicks % 10 == 0) { obj.justHit = 5; }
obj.var[3] = obj.var[3] - 1;
if (obj.var[3] <= 0) {
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ELECTRIC1);
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::LIGHTNINGSHIELDBULLET)];
bullet.ySpeed = 5;
bullet.creatorID = obj.objectID;
bullet.creatorType = CREATOR::OBJECT;
bullet.playerHandling = HANDLING::ENEMYBULLET;
obj.var[1] = attackCD;
obj.state = STATE::IDLE;
}
break;
case STATE::DEACTIVATE:
obj.delete();
break;
case STATE::KILL:
if (obj.isActive) {
jjOBJ@ poof = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos)];
poof.determineCurAnim(ANIM::AMMO, 2);
}
obj.delete();
break;
}
}
void onDraw(jjOBJ@ obj) {
if (obj.state == STATE::ATTACK) {
if (obj.justHit > 0) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, SPRITE::SINGLECOLOR, 15, 1);
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, SPRITE::NORMAL, 0, 1);
}
else jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, SPRITE::TRANSLUCENT, 0, 1);
}
}
class guerrillaDoofus : jjBEHAVIORINTERFACE {
// OBJECT VARIABLES:
// var[1] = jump stamina
// var[2] = blaster power
// var[3] = animation counter (start/end jump, start/end laying down)
// var[4] = animation counter (peak of jump)
// var[5] = idle counter
// var[6] = turning stuck check
void onBehave(jjOBJ@ obj) {
int count = jjRandom() % 3 + 8;
switch (obj.state) {
case STATE::START:
obj.var[6] = 0;
obj.state = STATE::SLEEP;
case STATE::SLEEP: // hiding in bush
obj.playerHandling = HANDLING::PARTICLE;
obj.bulletHandling = HANDLING::IGNOREBULLET;
obj.determineCurAnim(ANIM::CUSTOM[22], 12);
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
obj.xPos = obj.xOrg;
obj.yPos = obj.yOrg;
if (obj.findNearestPlayer(256 * 128) > -1) {
if (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos < obj.xPos) obj.direction = -1;
else obj.direction = 1;
for (int i = 0; i < count; i++) {
jjPARTICLE@ particle = jjAddParticle(PARTICLE::LEAF);
particle.xPos = obj.xPos;
particle.yPos = obj.yPos;
particle.xSpeed = int(jjRandom() % 9 - 4);
particle.ySpeed = int(jjRandom() % 4 - 4);
}
jjSample(obj.xPos, obj.yPos, SOUND::INTRO_GREN2);
obj.xSpeed = obj.direction * 1.2;
obj.ySpeed = -3;
obj.state = STATE::JUMP;
}
break;
case STATE::WALK:
if (obj.var[6] > 0) obj.var[6] = obj.var[6] - 1;
obj.xPos = obj.xPos + obj.xSpeed;
obj.determineCurAnim(ANIM::CUSTOM[22], 8);
obj.var[3] = 0;
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
// turning check:
if (!jjMaskedVLine(int(obj.xPos + obj.xSpeed), int(obj.yPos) + 27, 4) || jjMaskedVLine(int(obj.xPos + (obj.xSpeed * 8)), int(obj.yPos) - 8, 30) || jjEventGet(uint16(obj.xPos / 32), uint16(obj.yPos / 32)) == 14) {
obj.var[4] = 14;
obj.state = STATE::TURN;
}
// idling check:
obj.var[5] = obj.var[5] - 1;
if (jjRandom() % 512 == 0 && obj.var[5] <= 0) {
obj.var[5] = 70 + (jjRandom() % 140); // time of idling
obj.state = STATE::IDLE;
}
// duck check:
if ((jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos >= obj.yPos - 48 && obj.direction * (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - obj.xPos) > 0 && (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].curAnim - jjAnimSets[jjLocalPlayers[obj.findNearestPlayer(256 * 128)].setID].firstAnim == RABBIT::DIVE) || (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].curAnim - jjAnimSets[jjLocalPlayers[obj.findNearestPlayer(256 * 128)].setID].firstAnim == RABBIT::DIVEFIRERIGHT)) || (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos > obj.yPos + 90 && obj.direction * (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - obj.xPos) > 0)) {
obj.var[3] = 0;
obj.state = STATE::DUCK;
}
// jump check:
if (obj.var[1] > 0) obj.var[1] = obj.var[1] - 1;
if (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos < obj.yPos - 48 && obj.var[1] == 0 && obj.direction * (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - obj.xPos) > 0) obj.state = STATE::SPRING;
// fire check:
if (obj.var[2] < 6 && jjGameTicks % 22 == 0) obj.var[2] = obj.var[2] + 1;
else if ((jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos < obj.yPos + 80 || jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos >= obj.yPos + 1600) && obj.var[2] > 0 && obj.direction * (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - obj.xPos) > 0) {
if (jjGameTicks % 10 == 0) {
jjSample(obj.xPos, obj.yPos, SOUND::DEVILDEVAN_PHASER2, 63, 13081);
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::FIREBALLBULLET)];
jjOBJ@ flash = jjObjects[jjAddObject(OBJECT::EXPLOSION, bullet.xPos, bullet.yPos)];
flash.determineCurAnim(ANIM::AMMO, 16);
bullet.determineCurAnim(ANIM::DEVAN, 0);
bullet.creatorID = obj.objectID;
bullet.creatorType = CREATOR::OBJECT;
bullet.playerHandling = HANDLING::ENEMYBULLET;
bullet.direction = obj.direction;
bullet.animSpeed = 1;
bullet.xSpeed = obj.xSpeed * 2;
if (bullet.direction < 0) {
bullet.xSpeed *= -1;
bullet.xAcc *= -1;
flash.direction = -1;
}
else flash.direction = 1;
obj.var[2] = obj.var[2] - 1;
}
}
break;
case STATE::IDLE:
// jump check:
if (obj.var[1] > 0) obj.var[1] = obj.var[1] - 1;
if (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos < obj.yPos - 48 && obj.var[1] == 0 && obj.direction * (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - obj.xPos) > 0) obj.state = STATE::SPRING;
// fire check:
if (obj.var[2] < 6 && jjGameTicks % 22 == 0) obj.var[2] = obj.var[2] + 1;
if (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos < obj.yPos + 80 && obj.var[2] > 0 && obj.direction * (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - obj.xPos) > 0) {
obj.determineCurAnim(ANIM::CUSTOM[22], 7);
if (jjGameTicks % 10 == 0) {
jjSample(obj.xPos, obj.yPos, SOUND::DEVILDEVAN_PHASER2, 63, 13081);
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::FIREBALLBULLET)];
jjOBJ@ flash = jjObjects[jjAddObject(OBJECT::EXPLOSION, bullet.xPos, bullet.yPos)];
flash.determineCurAnim(ANIM::AMMO, 16);
bullet.determineCurAnim(ANIM::DEVAN, 0);
bullet.creatorID = obj.objectID;
bullet.creatorType = CREATOR::OBJECT;
bullet.playerHandling = HANDLING::ENEMYBULLET;
bullet.direction = obj.direction;
bullet.animSpeed = 1;
bullet.xSpeed = obj.xSpeed * 2;
if (bullet.direction < 0) {
bullet.xSpeed *= -1;
bullet.xAcc *= -1;
flash.direction = -1;
}
else flash.direction = 1;
obj.var[2] = obj.var[2] - 1;
}
}
else obj.determineCurAnim(ANIM::CUSTOM[22], 5);
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
// walk check:
if (obj.var[5] <= 0 && uint(obj.curAnim) != jjAnimations[jjAnimSets[ANIM::CUSTOM[22] + 7]]) { // not busy shooting?
obj.var[5] = 140 + (jjRandom() % 140); // time until next idle
obj.state = STATE::WALK;
}
else obj.var[5] = obj.var[5] - 1;
break;
case STATE::DUCK: // start of laying down
obj.determineCurAnim(ANIM::CUSTOM[22], 9);
obj.frameID = int8(obj.var[3] / 7);
obj.var[3] = obj.var[3] + 1;
if (obj.var[3] == 20) {
obj.state = STATE::HIDE;
}
break;
case STATE::HIDE: // laying down
if (obj.var[3] > 0 && jjGameTicks % 10 == 0) obj.var[3] = obj.var[3] - 1; // don't get up too fast
obj.determineCurAnim(ANIM::CUSTOM[22], 10);
// fire check:
if (obj.var[2] < 6 && jjGameTicks % 22 == 0) obj.var[2] = obj.var[2] + 1;
if (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos >= obj.yPos - 4 && jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos < obj.yPos + 90 && obj.var[2] > 0 && obj.direction * (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - obj.xPos) > 0) {
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
if (jjGameTicks % 10 == 0) {
jjSample(obj.xPos, obj.yPos, SOUND::DEVILDEVAN_PHASER2, 63, 13081);
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::FIREBALLBULLET)];
jjOBJ@ flash = jjObjects[jjAddObject(OBJECT::EXPLOSION, bullet.xPos, bullet.yPos)];
flash.determineCurAnim(ANIM::AMMO, 16);
bullet.determineCurAnim(ANIM::DEVAN, 0);
bullet.creatorID = obj.objectID;
bullet.creatorType = CREATOR::OBJECT;
bullet.playerHandling = HANDLING::ENEMYBULLET;
bullet.direction = obj.direction;
bullet.animSpeed = 1;
bullet.xSpeed = obj.xSpeed * 2;
if (bullet.direction < 0) {
bullet.xSpeed *= -1;
bullet.xAcc *= -1;
flash.direction = -1;
}
else flash.direction = 1;
obj.var[2] = obj.var[2] - 1;
}
}
else obj.frameID = 0;
// get up check:
if ((jjLocalPlayers[obj.findNearestPlayer(256 * 128)].yPos < obj.yPos - 4 || obj.findNearestPlayer(256 * 160) < 0 || (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos > obj.xPos && obj.direction < 0) || (jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos < obj.xPos && obj.direction >= 0)) && jjGameTicks % 22 == 0 && obj.var[3] <= 0) {
obj.var[3] = 0;
obj.state = STATE::WAKE;
}
break;
case STATE::WAKE: // get up
obj.determineCurAnim(ANIM::CUSTOM[22], 11);
obj.frameID = int8(obj.var[3] / 7);
obj.var[3] = obj.var[3] + 1;
if (obj.var[3] == 13) {
obj.state = STATE::WALK;
}
break;
case STATE::TURN:
obj.determineCurAnim(ANIM::CUSTOM[22], 6);
obj.frameID = 0;
if (obj.var[4] == 7) {
obj.direction = obj.direction * -1;
obj.xSpeed = obj.xSpeed * -1;
}
if (obj.var[4] == 0) obj.state = STATE::WALK;
else {
obj.var[4] = obj.var[4] - 1;
obj.var[6] = obj.var[6] + 2;
}
if (obj.var[6] >= 50) obj.state = STATE::SPRING; // help me stepbro I'm stuck!
break;
case STATE::SPRING: // a.k.a. start jump
obj.determineCurAnim(ANIM::CUSTOM[22], 0);
obj.frameID = int8(obj.var[3] / 7);
obj.var[3] = obj.var[3] + 1;
if (obj.var[3] == 41) {
obj.ySpeed = -5;
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_JUMP2);
obj.state = STATE::JUMP;
}
break;
case STATE::JUMP:
if (obj.var[6] > 0) obj.var[6] = obj.var[6] - 1;
obj.playerHandling = HANDLING::ENEMY;
obj.bulletHandling = HANDLING::HURTBYBULLET;
obj.var[3] = 0;
if (jjGameTicks % 10 == 0) obj.ySpeed += 1;
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
if (obj.ySpeed < -0.1) {
obj.determineCurAnim(ANIM::CUSTOM[22], 1);
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
}
else if (obj.var[4] < 20) { // peak of jump
obj.determineCurAnim(ANIM::CUSTOM[22], 2);
obj.frameID = int8(obj.var[4] / 7);
obj.var[4] = obj.var[4] + 1;
}
else {
obj.determineCurAnim(ANIM::CUSTOM[22], 3);
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
}
if (obj.ySpeed > 0 && jjMaskedPixel(int(obj.xPos + obj.xSpeed), int(obj.yPos + obj.ySpeed) + 18)) { // landing detection
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_LANDPOP);
obj.state = STATE::LAND;
}
break;
case STATE::LAND:
obj.var[4] = 0;
obj.determineCurAnim(ANIM::CUSTOM[22], 4);
obj.frameID = int8(obj.var[3] / 7);
obj.var[3] = obj.var[3] + 1;
if (obj.var[3] == 41) {
obj.var[1] = 120; // tired from jump
obj.state = STATE::WALK;
}
break;
case STATE::FREEZE:
if (obj.freeze == 0) obj.state = obj.oldState;
else obj.freeze--;
break;
case STATE::DEACTIVATE:
obj.deactivate();
break;
case STATE::KILL:
obj.delete();
break;
}
}
void onDraw(jjOBJ@ obj) {
obj.determineCurFrame();
if (obj.state == STATE::SLEEP) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 32); // I made the bush sprite to be universally usable, but for THESE levels I want it to blend in more
else obj.draw();
}
}
enum HeavyCopterVars {
hcNextPhysicalInjury,
hcNextNearbyPlayerCheck,
hcTargetPlayerID,
hcCurrentAngle
}
class HeavyCopter : jjBEHAVIORINTERFACE {
int lastHitID;
void onBehave(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.state = STATE::IDLE;
case STATE::IDLE: //fall through from START
obj.yPos = obj.yOrg + jjSin(obj.counterEnd << 2) * 5;
obj.frameID = (obj.counterEnd) >> 2;
break;
case STATE::FLY: {
const int desiredDistanceFromPlayer = 150;
const jjPLAYER@ targetPlayer = jjPlayers[obj.var[hcTargetPlayerID]];
float targetX = targetPlayer.xPos;
const float targetY = targetPlayer.yPos - 150;
int desiredAngle = 0; //forwards
int desiredDirection = obj.direction;
copterChannel = jjSampleLooped(obj.xPos, obj.yPos, SOUND::ORANGE_BUBBELSR, copterChannel);
if (abs(obj.xPos - targetPlayer.xPos) > desiredDistanceFromPlayer / 2) {
if (obj.xPos < targetPlayer.xPos) {
targetX -= desiredDistanceFromPlayer;
desiredDirection = 1;
} else {
targetX += desiredDistanceFromPlayer;
desiredDirection = -1;
}
} else {
desiredAngle = 17; //down
}
if (desiredDirection != obj.direction) {
if (obj.var[hcCurrentAngle] != 17)
desiredAngle = 17;
else
obj.direction = desiredDirection;
}
if (desiredAngle > obj.var[hcCurrentAngle])
obj.var[hcCurrentAngle] = obj.var[hcCurrentAngle] + 1;
else if (desiredAngle < obj.var[hcCurrentAngle])
obj.var[hcCurrentAngle] = obj.var[hcCurrentAngle] - 1;
if (obj.var[hcCurrentAngle] == 0 || obj.var[hcCurrentAngle] == 17) {
obj.frameID = (obj.counterEnd) >> 2;
obj.determineCurAnim(ANIM::CUSTOM[23], obj.var[hcCurrentAngle] == 0 ? 3 : 5);
} else {
obj.frameID = (obj.var[hcCurrentAngle] - 1) / 4;
obj.determineCurAnim(ANIM::CUSTOM[23], 4);
}
float dx = targetX - obj.xPos;
float dy = targetY - obj.yPos;
if (abs(dx) < 7 && abs(dy) < 16) {
if (desiredDirection == obj.direction && desiredAngle == obj.var[hcCurrentAngle]) {
obj.state = STATE::FIRE;
obj.counter = 70;
obj.counterEnd = 15;
}
obj.xSpeed = obj.ySpeed = 0; //only use xSpeed/ySpeed instead of temporary variables because the STATE::FALL code modifies them, just to be cute
} else {
int maxSpeed = (jjDifficulty >= 2) ? 3 : 2;
dx /= 32;
if (dx > maxSpeed) obj.xSpeed = maxSpeed;
else if (dx < -maxSpeed) obj.xSpeed = -maxSpeed;
else obj.xSpeed = dx;
obj.xPos += obj.xSpeed;
maxSpeed -= 1; //slower max speed than X
dy /= 32;
if (dy > maxSpeed) obj.ySpeed = maxSpeed;
else if (dy < -maxSpeed) obj.ySpeed = -maxSpeed;
else obj.ySpeed = dy;
obj.yPos += obj.ySpeed;
}
break; }
case STATE::FIRE:
copterChannel = jjSampleLooped(obj.xPos, obj.yPos, SOUND::ORANGE_BUBBELSR, copterChannel);
if (obj.counterEnd & 15 == 0 && (jjDifficulty != 0 || obj.counterEnd & 31 == 0)) { //the animation is in the firing frame
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PISTOL1);
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::BLASTERBULLET)];
bullet.ySpeed = 2 + jjDifficulty;
bullet.xSpeed = obj.var[hcCurrentAngle] == 0 ? 2 + jjDifficulty : 0;
bullet.xAcc = 0;
bullet.playerHandling = HANDLING::ENEMYBULLET;
bullet.counterEnd = 64;
bullet.determineCurAnim(ANIM::AMMO, 12);
}
obj.frameID = (obj.counterEnd) >> 2;
if (--obj.counter == 0) //done firing, for now anyway
obj.state = STATE::FLY;
break;
case STATE::FALL:
copterChannel = jjSampleLooped(obj.xPos, obj.yPos, SOUND::PICKUPS_HELI2, copterChannel);
if (obj.var[4] == 1) {
float dirX = (255 * 32) - obj.xPos;
float dirY = (46 * 32) - obj.yPos;
float length = sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
dirX /= length;
dirY /= length;
}
obj.direction = (obj.xPos - (255 * 32) > 0) ? -1 : 1;
obj.xPos += dirX * (2 + obj.xAcc);
obj.yPos += dirY * (2 + obj.yAcc);
obj.xAcc = obj.xAcc + 0.2;
obj.yAcc = obj.yAcc + 0.2;
}
else {
obj.xPos += obj.xSpeed;
obj.yPos += (obj.ySpeed += 0.08);
}
if (jjGameTicks % 20 == 0) {
jjOBJ@ smoke = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos)];
smoke.determineCurAnim(ANIM::AMMO, 72);
}
if ((jjMaskedHLine(int(obj.xPos) - 20, 40, int(obj.yPos) + 20) && obj.var[4] == 0) || (obj.var[4] == 1 && obj.yPos >= 45 * 32)) {
obj.particlePixelExplosion(2);
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_EXPL_TNT);
jjOBJ@ boom = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos)];
boom.determineCurAnim(ANIM::AMMO, 5);
int rand = jjRandom() % 4;
for( int n = 0; n < rand + 3; n++ ) {
jjOBJ@ shard = jjObjects[jjAddObject(OBJECT::SHARD, obj.xPos, obj.yPos)];
shard.behavior = BEHAVIOR::SHARD;
shard.determineCurAnim(ANIM::AMMO, (jjRandom()%2 + 78));
}
if (obj.var[4] == 1) copterDefeated = true;
obj.delete();
} else {
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction);
}
return; //skip usual drawing code
case STATE::FREEZE:
if (obj.freeze == 0) {
obj.state = obj.oldState;
break;
} else {
--obj.freeze;
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, mode: SPRITE::FROZEN); //copter
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame - 12, obj.direction, mode: SPRITE::FROZEN); //lizard
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, jjAnimations[obj.curAnim + 3], obj.direction, mode: SPRITE::FROZEN); //gun
return;
}
case STATE::DEACTIVATE:
if (obj.var[4] == 1) copterDefeated = true; // this can probably be abused but it's the only working solution I can come up with
obj.deactivate();
return;
}
//common code for IDLE, FLY, and FIRE: find a target player, and do drawing
if (jjGameTicks > obj.var[hcNextNearbyPlayerCheck]) {
const int nearestPlayerID = obj.findNearestPlayer(192 * 192); //same radius as float lizards
if (nearestPlayerID >= 0 && obj.var[hcTargetPlayerID] != nearestPlayerID) {
obj.var[hcNextNearbyPlayerCheck] = jjGameTicks + 3 * 70; //next time to start looking for a nearest player
obj.var[hcTargetPlayerID] = nearestPlayerID;
if (obj.state == STATE::IDLE)
obj.state = STATE::FLY;
}
}
if (obj.state == STATE::IDLE && obj.var[4] == 1) obj.yOrg = obj.yOrg + 2;
++obj.counterEnd;
obj.determineCurFrame();
const SPRITE::Mode mode = obj.justHit == 0 ? SPRITE::NORMAL : SPRITE::SINGLECOLOR;
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, mode: mode, param: 15); //copter... the copter is a bigger sprite than the lizard, so it might as well have the collision detection
if (obj.var[hcCurrentAngle] == 0 || obj.var[hcCurrentAngle] == 17) {
jjDrawSpriteFromCurFrame( //lizard
obj.xPos, obj.yPos,
jjAnimations[obj.curAnim - 3] + ((obj.frameID >> 1) & 3), //slower animation speed (>>3) than the copter (>>2)
obj.direction, mode: mode, param: 15
);
jjDrawSpriteFromCurFrame( //gun
obj.xPos, obj.yPos,
obj.state == STATE::FIRE ?
jjAnimations[obj.curAnim + (obj.var[hcCurrentAngle] == 0 ? 6 : 5)] + (obj.frameID & 3) :
jjAnimations[obj.curAnim + 3],
obj.direction, mode: mode, param: 15
);
} else {
jjDrawSpriteFromCurFrame( //lizard
obj.xPos, obj.yPos,
jjAnimations[obj.curAnim - 3] + obj.frameID, //when turning, lizard frameID and gun frameID match copter frameID
obj.direction, mode: mode, param: 15
);
jjDrawSpriteFromCurFrame( //gun
obj.xPos, obj.yPos,
jjAnimations[obj.curAnim + 3] + obj.frameID,
obj.direction, mode: mode, param: 15
);
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { //lots taken from plus52Scripting.j2as
if (bullet !is null) {
if ((bullet.var[6] & 16) == 0) { //not a fireball
bullet.state = STATE::EXPLODE;
}
if (obj.freeze != 0) {
if (force < 3)
force = 3;
obj.unfreeze(0);
}
hurtObject(obj, force);
} else {
if (force == 0) { //collision
if (obj.state != STATE::FREEZE)
player.hurt();
} else {
if (force == 1) { //sugar rush/buttstomp
bool bounce = true;
if (player.buttstomp == 41) //actually stomping
bounce = !player.hurt(); //stomping but protected by something, including a sugar rush
if (bounce) {
player.buttstomp = 50; //landing
player.ySpeed = player.ySpeed / -2 - 8;
player.yAcc = 0;
player.extendInvincibility(-70);
}
} else if (force == -101) { //frozen+running
player.xAcc = 0;
player.xSpeed /= -2;
player.ySpeed = -6;
player.extendInvincibility(-10);
}
if (obj.var[hcNextPhysicalInjury] < jjGameTicks) { //hasn't been hurt recently
obj.var[hcNextPhysicalInjury] = jjGameTicks + 70; //next time this can be hurt by a physical attack
if (obj.freeze != 0)
obj.unfreeze(1);
else
jjSample(obj.xPos, obj.yPos, ButtstompSounds[jjRandom() % ButtstompSounds.length]);
hurtObject(obj, 4);
}
}
}
lastHitID = player.playerID;
return true;
}
void hurtObject(jjOBJ@ obj, int force) const {
obj.energy -= force;
if (obj.energy <= 10) { //killed, but with a probably-unnecessary buffer
givePlayerPointsForObject(jjLocalPlayers[lastHitID], obj);
obj.state = STATE::FALL;
obj.playerHandling = HANDLING::PARTICLE;
jjOBJ@ lizard = jjObjects[jjAddObject(OBJECT::FLOATLIZARD, obj.xPos, obj.yPos, 1,CREATOR::LEVEL, BEHAVIOR::FLOATLIZARD)]; //mostly taken from g_hit.c
lizard.state = STATE::FALL;
lizard.xSpeed = obj.direction;
lizard.ySpeed = 0.5;
lizard.curAnim = jjAnimSets[ANIM::LIZARD] + 2;
lizard.counter = 0;
lizard.frameID = 0;
lizard.playerHandling = HANDLING::ENEMY;
lizard.bulletHandling = HANDLING::HURTBYBULLET;
} else {
obj.justHit = 5;
}
}
}
class CustomDragonfly : jjBEHAVIORINTERFACE {
jjOBJ@ rockTarget;
float triggerDistance = 200;
float extraStateSpeed = 2;
CustomDragonfly(jjOBJ@ preset) {
preset.behavior = this;
}
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::DRAGONFLY, true);
// 28 - IDLE
// 8 - START FOLLOW
// 13 - FOLLOW
// 22 - ATTACK
// 30 - RETURN TO SPAWN
// 3 - DEAD
if (obj.state != STATE::EXTRA) {
obj.age = 0;
obj.bulletHandling = HANDLING::HURTBYBULLET;
obj.playerHandling = HANDLING::ENEMY;
}
if (obj.state == STATE::FLOAT) {
for (int i = 0; i < jjObjectCount; i++) {
jjOBJ@ rock = jjObjects[i];
if (rock.eventID == 179 && rock.state != STATE::DEACTIVATE) {
float distance = sqrt(pow(rock.xPos - obj.xPos, 2) + pow(rock.yPos - obj.yPos, 2));
if (distance <= triggerDistance) {
@rockTarget = rock;
obj.state = STATE::EXTRA;
obj.bulletHandling = HANDLING::IGNOREBULLET;
obj.playerHandling = HANDLING::PARTICLE;
}
}
}
}
else if (obj.state == STATE::EXTRA) {
if (rockTarget !is null)
{
if (obj.doesCollide(rockTarget)) {
obj.determineCurAnim(ANIM::CUSTOM[2], 1);
obj.frameID = (int8(obj.age / 12));
obj.determineCurFrame();
if (obj.age == 30) jjSample(obj.xPos, obj.yPos, SOUND::DOG_AGRESSIV);
else if (obj.age == 47) {
rockTarget.clearPlatform();
rockTarget.particlePixelExplosion(0);
rockTarget.state = STATE::DEACTIVATE;
rockTarget.delete();
obj.state = STATE::STOP;
obj.determineCurAnim(ANIM::DRAGFLY, 0);
}
if (obj.age > 47) obj.age = 0;
else obj.age++;
}
else {
// Trigonometry. Don't worry about it, I don't understand it either :)
float dirX = rockTarget.xPos - obj.xPos;
float dirY = rockTarget.yPos - obj.yPos;
float length = sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
dirX /= length;
dirY /= length;
}
obj.direction = (obj.xPos - rockTarget.xPos > 0) ? -1 : 1;
obj.xPos += dirX * extraStateSpeed;
obj.yPos += dirY * extraStateSpeed;
}
}
}
}
}
class bigGrub : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::MONITOR);
obj.beSolid();
obj.causesRicochet = true;
obj.bulletHandling = HANDLING::DETECTBULLET;
obj.playerHandling = HANDLING::SPECIAL;
obj.scriptedCollisions = true;
obj.xPos = obj.xOrg;
}
void onDraw(jjOBJ@ obj) {
if (obj.state != STATE::FREEZE) obj.frameID = ((jjGameTicks/10) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurFrame();
obj.draw();
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (player.doesCollide(obj) && abs(player.xPos - obj.xPos) <= 32.f) player.xPos = player.xPos - (player.xSpeed * 2); // nope.avi
return true;
}
}
class grub : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
jjANIMFRAME@ frame = jjAnimFrames[obj.curFrame];
switch(jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,2)) {
case 0:
obj.behave(BEHAVIOR::WALKINGENEMY);
obj.xSpeed = obj.direction * 0.1;
break;
case 1:
switch (obj.state) {
case STATE::START:
while (!jjMaskedPixel(int(obj.xPos) - (frame.coldSpotY - frame.hotSpotY) - 4, int(obj.yPos))) obj.xPos = obj.xPos + 1; // fall right
if (jjRandom()%2 == 0) obj.direction = -1;
else obj.direction = 1;
obj.ySpeed = obj.direction * 0.1;
obj.state = STATE::WALK;
break; // need to make sure relocation is done
case STATE::WALK:
if (
!jjMaskedHLine(int(obj.xPos) - (frame.coldSpotY - frame.hotSpotY), 8, int(obj.yPos + (obj.ySpeed * (frame.width / 2))))
||
jjMaskedHLine(int(obj.xPos - (frame.height / 4)), int(frame.height / 2), int(obj.yPos + (obj.ySpeed * (frame.width / 2))))
||
jjEventGet(uint16(obj.xPos / 32), uint16((obj.yPos + obj.ySpeed) / 32)) == 14
) {
obj.direction = obj.direction * -1;
obj.ySpeed = obj.ySpeed * -1;
}
else obj.yPos = obj.yPos + obj.ySpeed;
break;
case STATE::FREEZE:
if (obj.freeze == 0) obj.state = obj.oldState;
else obj.freeze--;
break;
case STATE::DEACTIVATE:
obj.deactivate();
break;
case STATE::KILL:
obj.delete();
break;
}
break;
case 2:
switch (obj.state) {
case STATE::START:
while (!jjMaskedPixel(int(obj.xPos), int(obj.yPos) + (frame.coldSpotY - frame.hotSpotY) + 4)) obj.yPos = obj.yPos - 1; // fall up
if (jjRandom()%2 == 0) obj.direction = -1;
else obj.direction = 1;
obj.xSpeed = obj.direction * 0.1;
obj.state = STATE::WALK;
break; // need to make sure relocation is done
case STATE::WALK:
if (
!jjMaskedVLine(int(obj.xPos + (obj.xSpeed * (frame.width / 2))), int(obj.yPos) + (frame.coldSpotY - frame.hotSpotY) - 7, 10)
||
jjMaskedVLine(int(obj.xPos + (obj.xSpeed * (frame.width / 2))), int(obj.yPos - (frame.height / 4)), int(frame.height / 2))
||
jjEventGet(uint16(obj.xPos / 32), uint16(obj.yPos / 32)) == 14
) {
obj.direction = obj.direction * -1;
obj.xSpeed = obj.xSpeed * -1;
}
else obj.xPos = obj.xPos + obj.xSpeed;
break;
case STATE::FREEZE:
if (obj.freeze == 0) obj.state = obj.oldState;
else obj.freeze--;
break;
case STATE::DEACTIVATE:
obj.deactivate();
break;
case STATE::KILL:
obj.delete();
break;
}
break;
case 3:
switch (obj.state) {
case STATE::START:
while (!jjMaskedPixel(int(obj.xPos) + (frame.coldSpotY - frame.hotSpotY) + 4, int(obj.yPos))) obj.xPos = obj.xPos - 1; // fall left
if (jjRandom()%2 == 0) obj.direction = -1;
else obj.direction = 1;
obj.ySpeed = obj.direction * 0.1;
obj.state = STATE::WALK;
break; // need to make sure relocation is done
case STATE::WALK:
if (
!jjMaskedHLine(int(obj.xPos) + (frame.coldSpotY - frame.hotSpotY) - 7, 10, int(obj.yPos + (obj.ySpeed * (frame.width / 2))))
||
jjMaskedHLine(int(obj.xPos + (frame.height / 4)), int(frame.height / 2), int(obj.yPos + (obj.ySpeed * (frame.width / 2))))
||
jjEventGet(uint16(obj.xPos / 32), uint16((obj.yPos + obj.ySpeed) / 32)) == 14
) {
obj.direction = obj.direction * -1;
obj.ySpeed = obj.ySpeed * -1;
}
else obj.yPos = obj.yPos + obj.ySpeed;
break;
case STATE::FREEZE:
if (obj.freeze == 0) obj.state = obj.oldState;
else obj.freeze--;
break;
case STATE::DEACTIVATE:
obj.deactivate();
break;
case STATE::KILL:
obj.delete();
break;
}
break;
}
}
void onDraw(jjOBJ@ obj) {
obj.determineCurFrame();
switch(jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,2)) {
case 0:
obj.draw(); break;
case 1:
if (obj.state == STATE::FREEZE) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 256, (obj.direction > 0) ? 1 : -1, 1, SPRITE::FROZEN);
else {
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurFrame();
if (obj.justHit > 0) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 256, (obj.direction > 0) ? 1 : -1, 1, SPRITE::SINGLECOLOR, 15);
else jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 256, (obj.direction > 0) ? -1 : 1, 1);
}
break;
case 2:
if (obj.state == STATE::FREEZE) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, (obj.direction > 0) ? SPRITE::FLIPV : SPRITE::FLIPHV, SPRITE::FROZEN);
else {
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurFrame();
if (obj.justHit > 0) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, (obj.direction > 0) ? SPRITE::FLIPV : SPRITE::FLIPHV, SPRITE::SINGLECOLOR, 15);
else jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, (obj.direction > 0) ? SPRITE::FLIPV : SPRITE::FLIPHV);
}
break;
case 3:
if (obj.state == STATE::FREEZE) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 768, (obj.direction > 0) ? 1 : 1, -1, SPRITE::FROZEN);
else {
obj.frameID = ((jjGameTicks/7) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurFrame();
if (obj.justHit > 0) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 768, (obj.direction > 0) ? 1 : 1, -1, SPRITE::SINGLECOLOR, 15);
else jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 768, (obj.direction > 0) ? 1 : -1, 1);
}
break;
}
}
}
void loriBlock(jjOBJ@ obj) {
obj.behave(BEHAVIOR::DESTRUCTSCENERY);
int nearPlayerID = obj.findNearestPlayer(256);
if (obj.state != STATE::KILL && obj.state != STATE::DONE) {
if (jjLocalPlayers[nearPlayerID].charCurr == CHAR::LORI && jjLocalPlayers[obj.findNearestPlayer(256)].doesCollide(obj) && jjLocalPlayers[nearPlayerID].specialMove > 0) {
givePlayerPointsForObject(jjLocalPlayers[nearPlayerID], obj);
obj.state = STATE::KILL;
}
}
}
void flickerWrapper(jjOBJ@ obj) {
if (jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3) > 3) {
obj.eventID = 157;
obj.behave(BEHAVIOR::FLICKERGEM);
obj.points = 50;
obj.var[0] = 0;
if (jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3) == 5) obj.determineCurAnim(ANIM::CUSTOM[7], 43);
else obj.determineCurAnim(ANIM::PICKUPS, 92);
}
else {
obj.behave(BEHAVIOR::FLICKERGEM);
obj.var[0] = jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3) + 1;
int points = jjObjectPresets[OBJECT::REDGEM + obj.var[0] - 1].points;
obj.points = points;
obj.determineCurAnim(ANIM::PICKUPS, 22);
}
}
void SuperGemWrapper(jjOBJ@ obj) {
if (obj.state == STATE::START) {
if (jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,3) < 4)
obj.var[0] = jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,3) + 1; //color
else {
obj.var[0] = 0;
obj.age = jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,3) - 3;
}
}
obj.behave(BEHAVIOR::SUPERGEM);
if (obj.state == STATE::SLEEP && obj.var[0] == 0) {
if (obj.age == 1) obj.determineCurAnim(ANIM::CUSTOM[7], 6);
if (obj.age == 2) obj.determineCurAnim(ANIM::CUSTOM[7], 42);
}
if (obj.state == STATE::ACTION) {
if (jjParameterGet(uint(obj.xPos) >> 5, uint(obj.yPos) >> 5, 0,3) < 4) jjSample(obj.xOrg, obj.yOrg, SOUND::DOG_SNIF1);
else jjSample(obj.xOrg, obj.yOrg, SOUND::MONKEY_SPLUT);
}
}
class parsley : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::PICKUP);
obj.points = 500;
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ player, int) {
increaseMaxHP(player);
player.invincibility = 70;
player.blink = 0;
obj.behavior = BEHAVIOR::EXPLOSION2;
obj.scriptedCollisions = false;
obj.frameID = 0;
return true;
}
}
void increaseMaxHP(jjPLAYER@ player) {
int HPmem = player.health; // store HP so it doesn't increase
jjChat("/smhealth " + (jjMaxHealth + 1));
for (int i = 0; i < 8; ++i) {
jjAlert("");
}
player.health = HPmem;
}
class specialApple : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.eventID = 66;
obj.behave(BEHAVIOR::PICKUP);
obj.points = 50;
obj.var[0] = 0;
}
}
class jill : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.putOnGround();
farPlayerID = obj.findNearestPlayer(256 * 32);
nearPlayerID = obj.findNearestPlayer(256 * 16);
if (obj.state == STATE::START) obj.state = STATE::STILL;
if (farPlayerID > -1 && obj.state == STATE::DEACTIVATE) obj.state = STATE::STILL;
if (nearPlayerID > -1 && obj.state == STATE::STILL && jjLocalPlayers[nearPlayerID].charCurr != CHAR::FROG) {
obj.state = STATE::WAKE;
jillNear = true;
}
if (obj.state == STATE::WAKE) {
if (farPlayerID > -1) {
if (obj.xPos > jjLocalPlayers[farPlayerID].xPos) obj.direction = -1;
else obj.direction = 1;
}
if (jjKey[0x54] && CurrentPopup is null) jillSpeaks(jjLocalPlayers[nearPlayerID]);
else if (nearPlayerID < 0) {
obj.state = STATE::STILL;
jillNear = false;
}
}
}
void onDraw(jjOBJ@ obj) {
obj.frameID = ((jjGameTicks/10) % jjAnimations[obj.curAnim].frameCount);
obj.determineCurAnim(ANIM::CUSTOM[3], jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 0, 2));
obj.determineCurFrame();
obj.draw();
if (obj.state == STATE::WAKE) jjDrawSprite(obj.xPos - 40, obj.yPos - 48, ANIM::CUSTOM[4], 0, (jjGameTicks/12) & 1);
}
}
void jillSpeaks(jjPLAYER@ play) {
if (beenBirdie) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("thanks, hope you had a good haul!", right: Jill, direction: 1, color: 16)),
});
} else {
if (!jillMet && play.gems[GEM::PURPLE] < 50) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("hi, i'm jill! i'm looking for the rare and elusive PURPLE APPLES.", right: Jill, direction: 1, color: 16)),
Screen(
top: Line("if you happen to find 50 of those and bring them to me, i'll let you have my BIRDIE FRIEND for a while!", right: Jill, direction: 1, color: 16),
finish: function(bool skipping) { jillMet = true; } )
});
}
if (!jillMet && play.gems[GEM::PURPLE] >= 50) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("hi, i'm jill! i'm looking for the rare and elusive PURPLE APPLES.", right: Jill, direction: 1, color: 16)),
Screen(top: Line("if you happen to find 50 of those and bring them to me, i'll let you have my BIRDIE FRIEND for a while!", right: Jill, direction: 1, color: 16), choices: array<Choice@> = {
Choice("give her the apples", effect: function() { jillMet = true; startBirdie(jjLocalPlayers[nearPlayerID]); }),
Choice("not yet", style: DefaultIfSkippedChoice, effect: function() { jillMet = true; })
})
});
}
if (jillMet && play.gems[GEM::PURPLE] < 50) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("find 50 PURPLE APPLES and bring them to me, and in return i'll let you have my BIRDIE FRIEND for a while!", right: Jill, direction: 1, color: 16)),
});
}
if (jillMet && play.gems[GEM::PURPLE] >= 50) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("find 50 PURPLE APPLES and bring them to me, and in return i'll let you have my BIRDIE FRIEND for a while!", right: Jill, direction: 1, color: 16), choices: array<Choice@> = {
Choice("give her the apples", effect: function() { jillMet = true; startBirdie(jjLocalPlayers[nearPlayerID]); }),
Choice("not yet", style: DefaultIfSkippedChoice, effect: function() { jillMet = true; })
}),
});
}
}
}
void startBirdie(jjPLAYER@ play) {
play.gems[GEM::PURPLE] = play.gems[GEM::PURPLE] - 50;
jjMusicLoad("CD-Normal.mod");
play.cameraFreeze((447 * 32), (71 * 32), true, false);
play.shieldTime = 2800;
play.shieldType = SHIELD::PLASMA;
play.warpToID(3, true);
play.morphTo(CHAR::BIRD, false);
drawBirdie = false;
play.cameraUnfreeze(false);
}
void endBirdie(jjPLAYER@ play) {
beenBirdie = true;
jjMusicLoad("DD-Jungle.it");
play.cameraFreeze((441 * 32), (71 * 32), true, false);
play.revertMorph(false);
play.warpToID(4, true);
drawBirdie = true;
play.cameraUnfreeze(false);
}
void SyncedVine(jjOBJ@ obj) {
if (jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 3, 4) == 0) {
obj.var[1] = 128; } //default length in case parameter isn't specifically defined
else { obj.var[1] = jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 3, 4) * 32; }
if (lastSwingingVineLUTLength != obj.var[1]) { //need to generate LUT (LookUp Table) by doing the same math swinging vine objects do
lastSwingingVineLUTLength = obj.var[1];
PossibleVineVariableConfigurations = array<array<int>> = {{obj.var[1] * 256, 0}};
while (true) {
const array<int>@ oldConfiguration = @PossibleVineVariableConfigurations[PossibleVineVariableConfigurations.length-1];
array<int> newConfiguration(2);
newConfiguration[1] = oldConfiguration[1] + ((oldConfiguration[0] > 0) ? -32 : 32);
newConfiguration[0] = oldConfiguration[0] + newConfiguration[1];
if (newConfiguration[1] == 0 && newConfiguration[0] == obj.var[1] * 256) //gone full circle
break;
PossibleVineVariableConfigurations.insertLast(newConfiguration);
}
}
const array<int>@ syncedConfiguration = PossibleVineVariableConfigurations[(jjGameTicks + (jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0, NumberOfBitsDevotedToSyncParameter) * (PossibleVineVariableConfigurations.length / (1 << NumberOfBitsDevotedToSyncParameter)))) % PossibleVineVariableConfigurations.length];
for (uint i = 0; i < 2; ++i)
obj.var[2 + i] = syncedConfiguration[i];
//clean up:
obj.state = STATE::ACTION;
obj.behavior = BEHAVIOR::SWINGINGVINE;
obj.behave();
}
int lastSwingingVineLUTLength = -1;
array<array<int>> PossibleVineVariableConfigurations;
class LogSpawner : jjBEHAVIORINTERFACE {
/* int maxAmount = 4; // of logs
float timeInSeconds = 8; // delay between each log spawn
replaced these variables with event parameters -GG */
LogSpawner(jjOBJ@ preset) {
preset.behavior = this;
preset.bulletHandling = HANDLING::IGNOREBULLET;
preset.playerHandling = HANDLING::PARTICLE;
}
void onBehave(jjOBJ@ obj) {
if (obj.special >= jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 0, 4)) return;
if (obj.counter % (jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 4, 4) * 50) == 0) {
jjAddObject(204, obj.xOrg, obj.yOrg);
obj.special++;
}
obj.counter++;
}
}
class Log : jjBEHAVIORINTERFACE {
float xOld, yOld;
int width = 70, height = 5; // 160 is the height of the pole sprite. 30 is the width, using that value however leads to some buggy platforms. [added new sprite, disregard height -GG]
float downSpeed = 0.7, arcSpeed = 3, arcRange = 26;
// downSpeed - The speed at which the platform goes down after finishing its initial arc.
// arcSpeed - The speed of the initial arc "animation" (when it goes up and down at the start).
// arcRange - The range of the arc. Note that higher arcRange values will also raise the platform speed; lower arcSpeed to counteract this effect.
Log(jjOBJ@ preset) {
preset.behavior = this;
preset.bulletHandling = HANDLING::IGNOREBULLET;
xOld = preset.xPos;
yOld = preset.yPos;
}
void onBehave(jjOBJ@ obj) {
int sinValue = int(-obj.counter * arcSpeed);
int layer = 5; // Draw layer
if (sinValue % 1024 <= -256) // When it's no longer moving up, change layer
layer = 4;
if (sinValue % 1024 >= -512) { // Before the initial arc is finished, move in sin pattern
float movement = jjSin(sinValue);
obj.yPos = obj.yOrg + movement * arcRange;
obj.counter++;
}
else { // After the initial arc
obj.yPos += downSpeed;
}
int xTile = int(obj.xPos) / 32;
int yTile = int(obj.yPos) / 32;
if (jjEventGet(xTile, yTile) == AREA::FLYOFF || yTile > jjLayers[4].height) obj.counter = 0; // Make sure the area event is directly below the pole event [added a failsafe -GG]
jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[6], 1, 0, 255, 1, 1, SPRITE::NORMAL, 0, layer);
xOld = obj.xPos - width + 34;
yOld = obj.yPos - height;
if (sinValue % 1024 <= -256) // When it's not moving up, make the object a platform
obj.bePlatform(xOld, yOld, width, height);
if (obj.counter == 0) obj.clearPlatform();
}
}
class totemTurret : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.behave(BEHAVIOR::POLE);
if (jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 11, 1) == 0) obj.direction = -1;
else obj.direction = 1;
obj.frameID = 0;
obj.determineCurFrame();
obj.bulletHandling = HANDLING::IGNOREBULLET;
obj.playerHandling = HANDLING::SPECIAL; // whatever doesn't interact at all
obj.isBlastable = obj.isFreezable = obj.isTarget = obj.triggersTNT = false;
if (jjGameTicks % (288 - (jjDifficulty * 48) / jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 12, 3)) == 0 && obj.isActive) {
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::FIRESHIELDBULLET)];
bullet.determineCurAnim(ANIM::BUBBA, 4);
bullet.killAnim = bullet.determineCurAnim(ANIM::AMMO, 3, false);
bullet.playerHandling = HANDLING::ENEMYBULLET;
bullet.state = STATE::FLY;
bullet.xPos = obj.xPos;
bullet.yPos = obj.yPos;
bullet.counterEnd = 63;
bullet.animSpeed = 1;
}
jjPARTICLE@ particle = jjAddParticle(PARTICLE::SMOKE);
if (particle !is null) {
particle.xPos = obj.xPos;
particle.yPos = obj.yPos;
}
}
void onDraw (jjOBJ@ obj) {
// don't draw
}
[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.