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 "JungUltEx2-MLLE-Data-2.j2l" ///@MLLE-Generated
#pragma require "JungUltEx2-MLLE-Data-1.j2l" ///@MLLE-Generated
#pragma require "JungUltEx2.j2l" ///@MLLE-Generated
#pragma require "DD-Anims.j2a"
#include "DD-Order.asc"
#include "DD-Rank.asc"
#pragma require "JungUlt-MountRange.png"
#pragma require "JungUlt-Savanna.png"
#pragma require "JungUlt-Clouds1.png"
#pragma require "JungUlt-Clouds2.png"
#pragma require "JungUlt-Village.png"
#pragma require "waterfall-loop.wav"
#pragma require "big-bite.wav"
#pragma require "aslime2.wav"
#pragma require "empty.wav"
#pragma require "common-gemsmsh1.wav"
#pragma require "common-eat1.wav"
#pragma require "common-eat2.wav"
#pragma require "common-eat3.wav"
#pragma require "common-eat4.wav"
#pragma require "cicada.wav"
#pragma require "drumchant1.wav"
#pragma require "drumchant2.wav"
#pragma require "birds1.wav"
#pragma require "birds2.wav"
#pragma require "bird-takeoff1.wav"
#pragma require "bird-takeoff2.wav"
#pragma require "bird-takeoff3.wav"
#pragma require "african-drumming.wav"
#pragma require "african-evening.wav"
#pragma require "african-evening.wav"
#pragma require "zebra-calls.wav"
#pragma require "lions-cicadas.wav"
#pragma require "lion-calls.wav"
#pragma require "cicada-loop.wav"
#pragma require "african-evening-pond.wav"
#pragma require "evening-cicadas.wav"
#pragma require "fooby.wav"
#pragma require "nakotak1.wav"
#pragma require "nakotak2.wav"
#pragma require "bonk.wav"
#pragma require "crazy-laugh.wav"
#pragma require "rustle.wav"
#pragma require "pray1n.wav"
#pragma require "bigcopter.wav"
#pragma offer "rain9.wav"
#pragma offer "DD-JungleMix.it"
#pragma offer "TM-RAGE.S3M"
#pragma offer "drgeno-boss2.mp3"
const int bestTime = 510;
const float maxScore = 110000;
const int easyTimer = 0;
const int normalTimer = 0;
const int hardTimer = 75600;
const int turboTimer = 37800;
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.
float layer5_offset = 0.0;
float layer8_offset = 0.0;
array<int> simonSaysSequence = {0,3,0,1,2,3};
uint8 simonSaysRound = 0; //must be zero, but for testing purposes you can cheat!
uint8 simonDelay, simonCounter = 0;
bool simonSaysYourTurn = false;
uint8 simonLastRoundOutcome = 0; // 1 to win, 2 to lose
// NPC stuff:
bool chiefNear = false;
bool gateUnlocked = false;
bool bananasGiven = false;
int gateTimer, gateChannel, farPlayerID, nearPlayerID = 0;
// Bubba stuff:
int8 drawBubba = 1;
bool foughtBubba, bubbaExitStage = false;
int bubbaChannel, totemChannel, rainChannel, copterChannel;
bool totemActive, bubbaWait = false;
CustomBossHealthBar customBossHealthBar;
int bossHealth, bossPhase;
array<array<float>> totemPlacement = {
{331,25},
{331,19},
{335,27},
{327,27}
};
array<int> totemTile = {1052,1043,1041,1040};
void onLevelLoad() {
YomanPalette();
initiateDialogue();
initiateRanking();
initFade();
jjAnimSets[ANIM::HELMUT].load(); // for pixelmaps
jjAnimSets[ANIM::DOG].load(); // for ambient sfx
jjSampleLoad(SOUND::SCIENCE_PLOPKAOS, "waterfall-loop.wav");
jjSampleLoad(SOUND::COMMON_DRINKSPAZZ1, "common-eat1.wav");
jjSampleLoad(SOUND::COMMON_DRINKSPAZZ2, "common-eat2.wav");
jjSampleLoad(SOUND::COMMON_DRINKSPAZZ3, "common-eat3.wav");
jjSampleLoad(SOUND::COMMON_DRINKSPAZZ4, "common-eat4.wav");
jjSampleLoad(SOUND::EPICLOGO_EPIC2, "aslime2.wav");
jjSampleLoad(SOUND::BILSBOSS_BILLAPPEAR, "cicada.wav");
jjSampleLoad(SOUND::BILSBOSS_FINGERSNAP, "drumchant1.wav");
jjSampleLoad(SOUND::BILSBOSS_FIRE, "drumchant2.wav");
jjSampleLoad(SOUND::BILSBOSS_FIRESTART, "birds1.wav");
jjSampleLoad(SOUND::BILSBOSS_SCARY3, "birds2.wav");
jjSampleLoad(SOUND::BILSBOSS_THUNDER, "african-drumming.wav");
jjSampleLoad(SOUND::BILSBOSS_ZIP, "african-evening.wav");
jjSampleLoad(SOUND::BILSBOSS_ZIP, "african-evening.wav");
jjSampleLoad(SOUND::DOG_AGRESSIV, "zebra-calls.wav");
jjSampleLoad(SOUND::DOG_SNIF1, "lions-cicadas.wav");
jjSampleLoad(SOUND::DOG_WAF1, "lion-calls.wav");
jjSampleLoad(SOUND::DOG_WAF2, "cicada-loop.wav");
jjSampleLoad(SOUND::DOG_WAF3, "african-evening-pond.wav");
jjSampleLoad(SOUND::ENDING_OHTHANK, "evening-cicadas.wav");
jjSampleLoad(SOUND::ORANGE_BUBBELSL, "rain9.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");
jjSampleLoad(SOUND::INTRO_GRAB, "common-gemsmsh1.wav");
// not renaming Purple Gem since it's just a reskin
jjObjectPresets[OBJECT::PURPLEGEM].behavior = specialBanana();
jjObjectPresets[OBJECT::PURPLEGEM].determineCurAnim(ANIM::PICKUPS, 2);
///@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 |Man-|go
jjObjectPresets[OBJECT::DONUT].determineCurAnim(ANIM::CUSTOM[7], 14);
///@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 |Dragn |Fruit
jjObjectPresets[OBJECT::HAM].determineCurAnim(ANIM::CUSTOM[7], 18);
///@Event 182=Pomegranate |+|Food |Pome-|grnat
jjObjectPresets[OBJECT::CHEESE].determineCurAnim(ANIM::CUSTOM[7], 19);
///@Event 167=Lychee |+|Food |Lyche
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 166=Apricot |+|Food |Apri-|cot
jjObjectPresets[OBJECT::PIE].determineCurAnim(ANIM::CUSTOM[7], 23);
///@Event 171=Passion Fruit|+|Food |Passn|Fruit
jjObjectPresets[OBJECT::CANDY].determineCurAnim(ANIM::CUSTOM[7], 24);
///@Event 173=Fig |+|Food |Fig
jjObjectPresets[OBJECT::ICECREAM].determineCurAnim(ANIM::CUSTOM[7], 44);
///@Event 179=Lulo |+|Food |Lulo
jjObjectPresets[OBJECT::TACO].determineCurAnim(ANIM::CUSTOM[7], 26);
///@Event 146=Quince |+|Food |Quince
jjObjectPresets[OBJECT::PRETZEL].determineCurAnim(ANIM::CUSTOM[7], 27);
///@Event 160=Papaya |+|Food |Pa-|paya
jjObjectPresets[OBJECT::LETTUCE].determineCurAnim(ANIM::CUSTOM[7], 29);
///@Event 162=Kiwano |+|Food |Ki-|wano
jjObjectPresets[OBJECT::CUCUMB].determineCurAnim(ANIM::CUSTOM[7], 30);
///@Event 172=Cocoa (fruit) |+|Food |Cocoa
jjObjectPresets[OBJECT::CHOCBAR].determineCurAnim(ANIM::CUSTOM[7], 31);
///@Event 163=Plum |+|Food |Plum
jjObjectPresets[OBJECT::COKE].determineCurAnim(ANIM::CUSTOM[7], 32);
///@Event 164=Bergamot |+|Food |Ber-|gamot
jjObjectPresets[OBJECT::PEPSI].determineCurAnim(ANIM::CUSTOM[7], 33);
///@Event 147=Cherimoya |+|Food |Che-|rimoya
jjObjectPresets[OBJECT::STRAWBERRY].determineCurAnim(ANIM::CUSTOM[7], 46);
///@Event 161=Guanabana |+|Food |Guana-|bana
jjObjectPresets[OBJECT::EGGPLANT].determineCurAnim(ANIM::CUSTOM[7], 53);
///@Event 141=Salak |+|Food |Salak
jjObjectPresets[OBJECT::APPLE].determineCurAnim(ANIM::CUSTOM[7], 64);
///@Event 178=Steak |+|Food |Steak
jjObjectPresets[OBJECT::SANDWICH].determineCurAnim(ANIM::CUSTOM[7], 66);
///@Event 165=Meat 'n' Bone |+|Food |Meat|Bone
jjObjectPresets[OBJECT::MILK].determineCurAnim(ANIM::CUSTOM[7], 67);
///@Event 111=Lori Block |+|Trigger |Lori |Scen
jjOBJ@ myDestructSceneryVariant = jjObjectPresets[OBJECT::CHESHIRE1];
myDestructSceneryVariant.points = 50;
myDestructSceneryVariant.playerHandling = HANDLING::SELFCOLLISION;
myDestructSceneryVariant.behavior = loriBlock;
///@Event 243=Big Grub |+|Object |Big |Grub
jjAnimSets[ANIM::CUSTOM[10]].load(15, "DD-Anims.j2a"); // dragonfly extra sprites
jjObjectPresets[OBJECT::AIRBOARD].determineCurAnim(ANIM::CUSTOM[10], 0);
jjObjectPresets[OBJECT::AIRBOARD].behavior = bigGrub();
jjSampleLoad(SOUND::INTRO_BRAKE, "big-bite.wav");
CustomDragonfly(jjObjectPresets[OBJECT::DRAGONFLY]);
///@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[10], 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=NPC |+|Object |NPC | |Type:{Jill,Yoman,Chief,Savage (male),Savage (female)}3 |Order ID:5
jjAnimSets[ANIM::CUSTOM[3]].load(16, "DD-Anims.j2a"); // NPC sprites
jjAnimSets[ANIM::CUSTOM[4]].load(5, "DD-Anims.j2a"); // dialogue prompt sprite
jjObjectPresets[OBJECT::EVA].behavior = NPC();
///@Event 127=Parrot |-|Enemy |Parrot
jjAnimSets[ANIM::CUSTOM[2]].load(18, "DD-Anims.j2a"); // parrot sprite
jjObjectPresets[OBJECT::FISH].determineCurAnim(ANIM::CUSTOM[2], 0);
jjObjectPresets[OBJECT::FISH].behavior = parrot();
jjObjectPresets[OBJECT::FISH].energy = 1;
jjObjectPresets[OBJECT::FISH].points = 20;
///@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)
///@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 191=Gate Log |+|Trigger |Gate|Log |Adjust Y:5|Adjust X:-6
jjObjectPresets[OBJECT::TUBETURTLE].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::TUBETURTLE].bulletHandling = HANDLING::DESTROYBULLET;
jjObjectPresets[OBJECT::TUBETURTLE].scriptedCollisions = true;
jjObjectPresets[OBJECT::TUBETURTLE].beSolid();
jjObjectPresets[OBJECT::TUBETURTLE].behavior = gatePole;
jjObjectPresets[OBJECT::TUBETURTLE].determineCurAnim(ANIM::CUSTOM[6], 1);
///@Event 223=Fooby the Kamikaze Watermelon |+|Object |Fooby
jjObjectPresets[OBJECT::SPIKEBOLL3D].scriptedCollisions = true;
jjObjectPresets[OBJECT::SPIKEBOLL3D].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::SPIKEBOLL3D].behavior = fooby();
jjSampleLoad(SOUND::EPICLOGO_EPIC1, "fooby.wav");
jjAnimSets[ANIM::CUSTOM[5]].load(17, "DD-Anims.j2a");
///@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;
///@Event 106=
stormCloud(jjObjectPresets[OBJECT::RAPIER]);
///@Event 107=
finalTotem(jjObjectPresets[OBJECT::SPARK]);
jjObjectPresets[OBJECT::SPARK].energy = 100;
jjObjectPresets[OBJECT::SPARK].points = 2000;
jjAnimSets[ANIM::BUBBA].load();
jjAnimSets[ANIM::CUSTOM[11]].load(24, "DD-Anims.j2a"); // Bubba extra sprites
jjSampleLoad(SOUND::INTRO_MONSTER, "crazy-laugh.wav");
///@Event 186=Destruct Scenery (TNT) |+|Trigger |Bomb |Scen |ID:3
jjOBJ@ destPreset = jjObjectPresets[OBJECT::DESTRUCTSCENERYBOMB];
destPreset.behavior = bombDestEdit;
destPreset.playerHandling = HANDLING::SPECIAL;
destPreset.bulletHandling = HANDLING::IGNOREBULLET;
destPreset.isFreezable = false;
destPreset.triggersTNT = false; // no TNT in this level but can't hurt
///@Event 38=
jjOBJ@ spinnerPreset = jjObjectPresets[OBJECT::TNTAMMO3];
spinnerPreset.behavior = spinner();
spinnerPreset.playerHandling = HANDLING::SPECIAL;
spinnerPreset.scriptedCollisions = true;
spinnerPreset.bulletHandling = HANDLING::IGNOREBULLET;
spinnerPreset.isTarget = false;
spinnerPreset.isFreezable = false;
spinnerPreset.triggersTNT = false;
spinnerPreset.deactivates = false;
///@Event 105=
jjObjectPresets[OBJECT::BEE].behavior = livingTotem();
jjObjectPresets[OBJECT::BEE].determineCurAnim(ANIM::CUSTOM[11], 1);
jjObjectPresets[OBJECT::BEE].energy = 100;
jjObjectPresets[OBJECT::BEE].points = 2000;
jjObjectPresets[OBJECT::BEE].scriptedCollisions = true;
jjObjectPresets[OBJECT::BEE].playerHandling = HANDLING::SPECIAL;
jjObjectPresets[OBJECT::BEE].var[7] = 0;
jjObjectPresets[OBJECT::BEE].deactivates = false;
jjSampleLoad(SOUND::BUMBEE_BEELOOP, "pray1n.wav");
///@Event 126=Lava Block |+|Scenery |Lava |Block | Adjust Y:5
jjAnimSets[ANIM::CUSTOM[12]].load(9, "DD-Anims.j2a"); // visual fx
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 225=Sparkle |+|Scenery |Sparkle
jjObjectPresets[OBJECT::BEES].behavior = sparkle;
jjObjectPresets[OBJECT::BEES].determineCurAnim(ANIM::CUSTOM[12], 0);
jjObjectPresets[OBJECT::BEES].playerHandling = HANDLING::PARTICLE;
jjObjectPresets[OBJECT::BEES].bulletHandling = HANDLING::IGNOREBULLET;
jjObjectPresets[OBJECT::BEES].triggersTNT = false;
jjObjectPresets[OBJECT::BEES].isTarget = false;
jjObjectPresets[OBJECT::BEES].isFreezable = false;
///@Event 103=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::DRAGON];
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();
if (jjDifficulty > 0) jjObjectPresets[OBJECT::SPIKEBOLL].bulletHandling = HANDLING::IGNOREBULLET;
///@Event 190=Simon Says |+|Trigger |Simon |Says |Color:2
jjSampleLoad(SOUND::INTRO_LAND, "tineep.wav");
jjANIMSET@ simonSaysSet = jjAnimSets[ANIM::CUSTOM[14]];
simonSaysSet.allocate(array<uint>={1,9,9,9,9});
jjPIXELMAP bush((10 * 32), (100 * 32), 32, 32, 4);
jjANIMFRAME@ bushframe = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[14]].firstAnim].firstFrame];
bush.save(bushframe);
bushframe.hotSpotX = -bushframe.width/2;
bushframe.hotSpotY = -bushframe.height/2;
for (int a = 100; a < 104; a++) {
for (int b = 0; b < 10; b++) {
jjPIXELMAP evileye((b * 32), (a * 32), 32, 32, 4);
jjANIMFRAME@ eyeframe = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[14]].firstAnim + a - 99].firstFrame + b];
evileye.save(eyeframe);
eyeframe.hotSpotX = -eyeframe.width/2;
eyeframe.hotSpotY = -eyeframe.height/2;
}
}
jjOBJ@ simonSaysPreset = jjObjectPresets[OBJECT::RAVEN];
simonSaysPreset.behavior = simonSays();
simonSaysPreset.determineCurAnim(ANIM::CUSTOM[14], 0);
simonSaysPreset.playerHandling = HANDLING::SPECIAL;
simonSaysPreset.bulletHandling = HANDLING::HURTBYBULLET;
simonSaysPreset.scriptedCollisions = true;
simonSaysPreset.isTarget = true;
simonSaysPreset.isFreezable = false;
simonSaysPreset.triggersTNT = true;
simonSaysPreset.deactivates = true;
simonSaysPreset.energy = 50;
///@Event 252=Trigger Target |+|Trigger |Trig |Targ |TriggerID:5|Set to:{On,Off}1|Direction:{Down,Right,Up,Left}2
jjAnimSets[ANIM::CUSTOM[15]].load(14, "DD-Anims.j2a");
jjOBJ@ triggerTargetPreset = jjObjectPresets[OBJECT::CAT];
triggerTargetPreset.behavior = triggerTarget();
triggerTargetPreset.determineCurAnim(ANIM::CUSTOM[15], 0);
triggerTargetPreset.playerHandling = HANDLING::SPECIAL;
triggerTargetPreset.scriptedCollisions = true;
triggerTargetPreset.isTarget = true;
triggerTargetPreset.isFreezable = false;
triggerTargetPreset.triggersTNT = true;
triggerTargetPreset.deactivates = true;
// mountain range pixelmap:
jjPIXELMAP mount("JungUlt-MountRange.png");
jjANIMFRAME@ helmut3 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim].firstFrame + 2];
mount.save(helmut3);
helmut3.hotSpotX = 0;
helmut3.hotSpotY = 0;
// savanna pixelmap:
jjPIXELMAP savanna("JungUlt-Savanna.png");
jjANIMFRAME@ helmut4 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim].firstFrame + 3];
savanna.save(helmut4);
helmut4.hotSpotX = 0;
helmut4.hotSpotY = 0;
// clouds near pixelmap:
jjPIXELMAP clouds1("JungUlt-Clouds1.png");
jjANIMFRAME@ helmut6 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame + 1];
clouds1.save(helmut6);
helmut6.hotSpotX = 0;
helmut6.hotSpotY = 0;
// clouds far pixelmap:
jjPIXELMAP clouds2("JungUlt-Clouds2.png");
jjANIMFRAME@ helmut5 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame];
clouds2.save(helmut5);
helmut5.hotSpotX = 0;
helmut5.hotSpotY = 0;
// village silhouette pixelmap:
jjPIXELMAP village("JungUlt-Village.png");
jjANIMFRAME@ helmut7 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame + 2];
village.save(helmut7);
helmut7.hotSpotX = 0;
helmut7.hotSpotY = 0;
// foreground piece 1 pixelmap:
jjPIXELMAP fore1("JungUlt-Foreground1.png");
jjANIMFRAME@ helmut1 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim].firstFrame];
fore1.save(helmut1);
helmut1.hotSpotX = 0;
helmut1.hotSpotY = 0;
// foreground piece 2 pixelmap:
jjPIXELMAP fore2("JungUlt-Foreground2.png");
jjANIMFRAME@ helmut2 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim].firstFrame + 1];
fore2.save(helmut2);
helmut2.hotSpotX = 0;
helmut2.hotSpotY = 0;
// foreground piece 3 pixelmap:
jjPIXELMAP fore3("JungUlt-Foreground3.png");
jjANIMFRAME@ helmut8 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame + 3];
fore3.save(helmut8);
helmut8.hotSpotX = 0;
helmut8.hotSpotY = 0;
// foreground piece 4 pixelmap:
jjPIXELMAP fore4("JungUlt-Foreground4.png");
jjANIMFRAME@ helmut9 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame + 4];
fore4.save(helmut9);
helmut9.hotSpotX = 0;
helmut9.hotSpotY = 0;
// foreground piece 5 pixelmap:
jjPIXELMAP fore5("JungUlt-Foreground5.png");
jjANIMFRAME@ helmut10 = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::HELMUT].firstAnim + 1].firstFrame + 5];
fore5.save(helmut10);
helmut10.hotSpotX = 0;
helmut10.hotSpotY = 0;
jjLAYER@ clouds3 = MLLE::GetLayer("Clouds Low");
clouds3.spriteParam = 224;
clouds3.spriteMode = SPRITE::BLEND_NORMAL;
jjLAYER@ sky = MLLE::GetLayer("Sky");
sky.xSpeed = 0.02;
sky.ySpeed = 0.02;
sky.xAutoSpeed = -0.09;
sky.yAutoSpeed = -0.01;
sky.xSpeedModel = LAYERSPEEDMODEL::NORMAL;
sky.ySpeedModel = LAYERSPEEDMODEL::NORMAL;
jjLAYER@ water = MLLE::GetLayer("Water");
water.spriteMode = SPRITE::TRANSLUCENT;
jjPIXELMAP rain(32,32);
for (uint x = 0; x < rain.width; ++x) {
for (uint y = 0; y < rain.height; ++y) {
if (x == 16) { //draw in the middle of the tile, xPixel 16
// if (y <= 24) rain[x,y] = 75; //if at yPixel 24 or less, use color 75
// else rain[x,y] = 74; //use color 74 for yPixels 25-32
rain[x,y] = 207 - y;
} else {
rain[x,y] = 0;
}
}
}
jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::COMMON].firstAnim + 2];
for (uint frameID = 0; frameID < anim.frameCount; ++frameID) {
jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame + frameID];
rain.save(frame);
frame.hotSpotX = -frame.width/2;
frame.hotSpotY = -frame.height;
}
// totem tile backups:
for (int i = 0; i < 4; i++) {
jjPIXELMAP tile(totemTile[i]);
jjANIMFRAME@ block = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim + 4].firstFrame + i];
tile.save(block);
}
}
void onLevelBegin() {
MLLE::SpawnOffgrids();
setTimer();
}
void onLevelReload() {
MLLE::SpawnOffgridsLocal();
YomanPalette();
scoreSeconds = storeScoreSeconds;
customBossHealthBar.ready = false;
if (foughtBubba) {
jjMusicLoad("DD-JungleMix.it");
bossPhase = 0;
bubbaWait = false;
totemActive = false;
}
simonSaysRound = simonDelay = simonCounter = simonLastRoundOutcome = 0;
simonSaysYourTurn = false;
}
void onMain() {
layer5_offset += 0.29;
layer8_offset += 0.1;
if (layer5_offset > 992) layer5_offset = 0;
if (layer8_offset > 256) layer8_offset = 0;
if (jjDifficulty == 0) oneUpsIntoBlueGems();
if ((jjGameTicks % 70) == 0 && !levelOver && CurrentPopup is null) scoreSeconds++;
rankingSetup();
if (levelOver) updateFade();
if (gateTimer > 0) {
gateChannel = jjSampleLooped(174 * 32, 65 * 32, SOUND::COMMON_LAND1, gateChannel);
gateTimer = gateTimer - 1;
}
if (gateTimer < 0) {
gateChannel = jjSampleLooped(174 * 32, 65 * 32, SOUND::COMMON_LAND1, gateChannel);
gateTimer = gateTimer + 1;
}
if (jjIsSnowing) rainChannel = jjSampleLooped(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::ORANGE_BUBBELSL, rainChannel, 15);
if (simonDelay > 0) {
simonDelay--;
}
else if (jjTriggers[3] && !simonSaysYourTurn) {
if (simonCounter == 0) {
simonDelay = 150;
if (simonSaysRound >= simonSaysSequence.length() ) {
jjSample(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::INTRO_BOEM1);
}
simonCounter = 1;
}
else if (simonCounter == simonSaysRound+2) {
simonSaysYourTurn = true;
simonCounter = 0;
}
else if (simonCounter > 0) {
simonLastRoundOutcome = 0;
if (simonSaysRound >= simonSaysSequence.length() ) {
jjSample(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::COMMON_ITEMTRE);
jjTriggers[2] = true;
jjTriggers[3] = false;
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.eventID == OBJECT::RAVEN) {
obj.state = STATE::KILL;
}
}
}
else {
simonDelay = 80 - (simonSaysRound * 12); //how many ticks before next note, decreases to zero above
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.eventID == OBJECT::RAVEN && obj.var[2] == simonSaysSequence[simonCounter-1]) {
if (obj.state == STATE::ACTION) obj.var[1] = 0;
else obj.state = STATE::ACTION;
}
}
}
simonCounter++;
}
}
}
void onPlayer(jjPLAYER@ player) {
dialogueInterface(player);
rankingInterface(player);
if (jjGameTicks % (288 - (jjDifficulty * 40)) == 0 && jjTriggers[0]) {
jjOBJ@ parr = jjObjects[jjAddObject(OBJECT::FISH, (jjRandom()%2 > 0) ? (player.xPos + 420) : (player.xPos - 420), player.yPos + (jjRandom()%200) - 100)]; // DUDE WEED LMAO
parr.direction = (parr.xPos > player.xPos) ? -1 : 1;
}
if (bossPhase == 6 || bossPhase == 0) player.bossActivated = false;
if (player.bossActivated && totemActive) {
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if ((obj.eventID == OBJECT::BEE || obj.eventID == OBJECT::SPARK) && obj.state == STATE::KILL) {
bossPhase += 1;
totemActive = false;
}
/* if (obj.eventID == OBJECT::BEE || obj.eventID == OBJECT::SPARK) {
if (obj.state == STATE::KILL) {
bossPhase += 1;
totemActive = false;
}
else {
play.boss = obj.objectID;
customBossHealthBar.initialize(obj.objectID);
}
}*/
}
}
for (int i = 0; i < 1024; i++) { //loop through the global array jjParticles[1024]
jjPARTICLE@ particle = jjParticles[i];
if (particle.type == PARTICLE::RAIN) {
particle.xSpeed = 0; //make rain fall straight down
particle.ySpeed = player.ySpeed < 0? 10 : int(10 + player.ySpeed); //the rain speed accounts for differences in the player speed, and so won't appear to fall more slowly when the player is falling
}
}
if (simonCounter == 0 && simonSaysRound >= simonSaysSequence.length() && jjTriggers[3] && !simonSaysYourTurn) {
int groinky = int(jjRandom() % 8 + 8);
for (int i = 0; i < groinky; i++) {
jjPARTICLE@ particle = jjAddParticle(PARTICLE::STAR);
particle.xPos = player.cameraX + (jjSubscreenWidth / 2);
particle.yPos = player.cameraY + jjSubscreenHeight - (jjSubscreenHeight / 18);
particle.xSpeed = int(jjRandom() % 9 - 4); // don't ask me why it needs to be converted to int. it just does
particle.ySpeed = int(jjRandom() % 9 - 4);
particle.star.angularSpeed = int(jjRandom() % 4 + 1);
particle.star.color = int(jjRandom() % 3 + 64);
particle.star.size = int(jjRandom() % 16 + 24);
}
}
}
bool onDrawScore(jjPLAYER@ player, jjCANVAS@ canvas) {
if (jjTriggers[3] && simonSaysRound < simonSaysSequence.length()) {
if (simonSaysYourTurn) bottomTextXD("Repeat", canvas);
// else if (simonCounter < 2 && simonSaysRound > 0 && simonDelay > 50 && simonLastRoundOutcome == 1)
else if (simonLastRoundOutcome == 1 && simonDelay > 50)
bottomTextXD("Good!", canvas);
else if (simonLastRoundOutcome == 2 && simonDelay > 50)
bottomTextXD("Try again!", canvas);
else bottomTextXD("Listen", canvas);
}
if (chiefNear) {
canvas.drawSprite((jjSubscreenWidth / 2) - 16, jjSubscreenHeight - (jjSubscreenHeight / 32), ANIM::PICKUPS, 2, 0);
canvas.drawString(jjSubscreenWidth / 2, jjSubscreenHeight - (jjSubscreenHeight / 32), "x" + player.gems[GEM::PURPLE], smallScreen ? STRING::SMALL : 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 && !bubbaWait) 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@ play, jjCANVAS@ canvas) { return CurrentPopup !is null && !CurrentPopup.DrawHUD(); }
//applying village to layer:
void onDrawLayer2(jjPLAYER@ player, jjCANVAS@ canvas) {
for (int i = 0; i < 2; i++) canvas.drawSprite(192 + (384 * i), 384, ANIM::HELMUT, 1, 2);
}
// putting foreground eyecandy on layer:
void onDrawLayer3(jjPLAYER@ player, jjCANVAS@ canvas) {
canvas.drawSprite(0, (33 * 32), ANIM::HELMUT, 0, 0);
canvas.drawSprite(269, (100 * 32), ANIM::HELMUT, 0, 1, -1);
canvas.drawSprite((440 * 32), (75 * 32), ANIM::HELMUT, 0, 0, -1);
canvas.drawSprite((440 * 32) - 269, (50 * 32), ANIM::HELMUT, 0, 1);
canvas.drawSprite((170 * 32), 0, ANIM::HELMUT, 1, 3);
canvas.drawSprite((298 * 32), 0, ANIM::HELMUT, 1, 3, -1);
canvas.drawSprite((428 * 32), (118 * 32), ANIM::HELMUT, 1, 4);
canvas.drawSprite((202 * 32), (122 * 32), ANIM::HELMUT, 1, 4);
canvas.drawSprite((35 * 32), (115 * 32), ANIM::HELMUT, 1, 4, -1);
canvas.drawSprite((239 * 32), 30, ANIM::HELMUT, 1, 5);
canvas.drawSprite((128 * 32), (11 * 32), ANIM::HELMUT, 1, 5);
}
void onDrawLayer4(jjPLAYER@ player, jjCANVAS@ canvas) {
if (drawBubba == 1) canvas.drawSprite((324 * 32), (30 * 32) + 10, ANIM::CUSTOM[11], 0, (jjGameTicks/10) & 3, 1);
if (drawBubba == 2) canvas.drawSprite((324 * 32), (30 * 32) + 10, ANIM::BUBBA, 5, 0, -1);
}
// applying clouds near to layer:
void onDrawLayer5(jjPLAYER@ player, jjCANVAS@ canvas) {
for (int i = 0; i < 8; i++) canvas.drawSprite((992 * i) + int(layer5_offset) - 1984, 0, ANIM::HELMUT, 1, 1); // Orwell was right
}
// applying savanna to layer:
void onDrawLayer6(jjPLAYER@ player, jjCANVAS@ canvas) { // don't forget there must be at least one normal tile in the layer or the bool will return false
for (int i = -1; i < 9; i++) // you might want a bigger number of loops if your level is very wide
canvas.drawSprite(0 + (384 * i), 80, ANIM::HELMUT, 0, 3); // you don't really HAVE to stick to the 32x32p grid but I just like to do it
}
// applying mountain range to layer:
void onDrawLayer7(jjPLAYER@ player, jjCANVAS@ canvas) {
for (int i = -1; i < 9; i++) canvas.drawSprite(0 + (352 * i), 192, ANIM::HELMUT, 0, 2);
}
// applying clouds far to layer:
void onDrawLayer8(jjPLAYER@ player, jjCANVAS@ canvas) {
for (int i = 0; i < 8; i++) canvas.drawSprite((256 * i) + int(layer8_offset) - 512, 2, ANIM::HELMUT, 1, 0);
}
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 && bubbaExitStage) return;
canvas.drawSprite(jjResolutionWidth / 2, 4, ANIM::BOSS, 0, 0);
if (bossPhase <= 4) canvas.drawString(jjResolutionWidth / 2 + 90, 20, "x" + (6 - 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 * (5 - bossPhase));
}
}
void bubbaEdit(jjOBJ@ obj) {
// STATE::START (0) - start
// STATE::DEACTIVATE (4) - deactivated
// STATE::JUMP (6) - jump up
// STATE::DONE (17) - dead
// STATE::FALL (19) - jump down
// STATE::ATTACK (22) - shoot fireball
// STATE::FREEZE (23) - frozen
// STATE::IDLE (28) - choosing action (jump, shoot or tornado)
// STATE::EXTRA (29) - land
// STATE::ROTATE (34) - tornado
if (totemActive) {
obj.isTarget = false;
if (obj.age <= 24 && bossPhase < 4) {
jjOBJ@ spin = jjObjects[jjAddObject(OBJECT::TNTAMMO3, obj.xPos, obj.yPos)];
spin.creatorID = obj.objectID;
spin.creatorType = CREATOR::OBJECT;
spin.var[2] = bossPhase; // shield type
spin.var[1] = obj.age; // number of object created (need 24 for full circle)
obj.age++;
}
for (int i = 0; i < jjObjectCount; ++i) {
if (jjObjects[i].creatorID == uint(obj.objectID) && jjObjects[i].eventID == 38) {
jjObjects[i].xPos = obj.xPos + jjSin(jjGameTicks * 4 + (48 * jjObjects[i].var[1])) * (1.7 * 32);
jjObjects[i].yPos = obj.yPos + jjCos(jjGameTicks * 4 + (48 * jjObjects[i].var[1])) * (1.7 * 32);
}
}
}
else if (obj.state != STATE::SPRING && bossPhase < 5) {
for (int i = 0; i < jjObjectCount; ++i) {
if (jjObjects[i].creatorID == uint(obj.objectID) && jjObjects[i].eventID == 38) jjObjects[i].state = STATE::KILL;
}
obj.age = 0;
obj.var[6] = 120; // god I hope that's not used
obj.state = STATE::SPRING; // start summoning if no totem is active and there's still totems left
}
if (obj.state == STATE::SPRING) { // summon totem state
obj.putOnGround();
bubbaChannel = jjSampleLooped(obj.xPos, obj.yPos, SOUND::RAPIER_GOSTLOOP, bubbaChannel);
obj.determineCurAnim(ANIM::CUSTOM[11], 0);
obj.frameID = (jjGameTicks/10) & 3;
obj.determineCurFrame();
obj.draw();
jjPIXELMAP tile(totemTile[bossPhase - 1]);
for (int x = 0; x < 32; x++) {
for (int y = 0; y < 32; y++) {
uint8 color = tile[x,y];
if (color > 1) {
if (color >= 88) color -= 72; //loop from purple back to green
else color += 8;
tile[x,y] = color;
}
}
}
tile.save(totemTile[bossPhase - 1]);
if (obj.var[6] == 0) {
jjPIXELMAP backup(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PLUS_SCENERY].firstAnim + 4].firstFrame + (bossPhase - 1)]);
backup.save(totemTile[bossPhase - 1]);
if (bossPhase == 4) {
jjOBJ@ lastTotem = jjObjects[jjAddObject(OBJECT::SPARK, totemPlacement[bossPhase - 1][0] * 32 + 16, totemPlacement[bossPhase - 1][1] * 32 + 16)];
jjLocalPlayers[obj.findNearestPlayer(1024 * 1024)].boss = lastTotem.objectID; // hopefully big enough range to contain the whole arena??
customBossHealthBar.initialize(lastTotem);
}
else {
jjOBJ@ totem = jjObjects[jjAddObject(OBJECT::BEE, totemPlacement[bossPhase - 1][0] * 32 + 16, totemPlacement[bossPhase - 1][1] * 32 + 16)];
totem.var[6] = bossPhase;
jjLocalPlayers[obj.findNearestPlayer(1024 * 1024)].boss = totem.objectID; // hopefully big enough range to contain the whole arena??
customBossHealthBar.initialize(totem);
}
for (int i = 0; i < jjObjectCount; ++i) {
if (jjObjects[i].eventID == OBJECT::DESTRUCTSCENERYBOMB && jjObjects[i].state != STATE::DONE && jjObjects[i].var[2] == bossPhase) jjObjects[i].state = STATE::KILL;
}
totemActive = true;
if (obj.xPos > jjLocalPlayers[obj.findNearestPlayer(1024 * 1024)].xPos) obj.direction = -1;
else obj.direction = 1;
obj.state = STATE::EXTRA;
} else obj.var[6] = obj.var[6] - 1;
}
if (bossPhase == 4 && totemActive) {
obj.light = 15;
obj.lightType = LIGHT::RING;
} else obj.light = 0;
if (bossPhase == 5) { // can only be hurt normally if all totems are defeated and isn't in cutscene
obj.isTarget = true;
obj.bulletHandling = HANDLING::HURTBYBULLET;
obj.playerHandling = HANDLING::ENEMY;
obj.scriptedCollisions = false;
jjLocalPlayers[obj.findNearestPlayer(512 * 512)].boss = obj.objectID;
bossHealth = obj.energy;
} else {
obj.bulletHandling = HANDLING::DETECTBULLET;
obj.playerHandling = HANDLING::SPECIAL;
obj.scriptedCollisions = true;
}
if (obj.energy <= 7 && obj.isActive && bossPhase == 5) {
obj.energy = 7; // just in case
bossPhase = 6; // cutscene & end level
if (!bubbaWait) {
obj.putOnGround();
obj.state = STATE::FADEOUT;
bubbaWait = true;
endDialog();
}
}
if (obj.state == STATE::FADEOUT) {
obj.behave(BEHAVIOR::MONITOR); // stop doing everything and SIT YOUR ASS DOWN
if (bubbaExitStage) {
obj.determineCurAnim(ANIM::BUBBA, 6);
obj.frameID = (jjGameTicks/12) & 11;
obj.direction = 1;
obj.xPos += 4;
bubbaChannel = jjSampleLooped(obj.xPos, obj.yPos, SOUND::BUBBA_TORNADOATTACK2, bubbaChannel);
} else {
if (obj.xPos > jjLocalPlayers[obj.findNearestPlayer(1024 * 1024)].xPos) obj.direction = -1;
else obj.direction = 1;
obj.determineCurAnim(ANIM::BUBBA, 5);
obj.frameID = 0;
obj.putOnGround();
obj.direction = jjLocalPlayers[obj.findNearestPlayer(512 * 256)].direction * -1; // FACE ME!!
}
obj.determineCurFrame();
obj.draw();
}
else obj.behave(BEHAVIOR::BUBBA);
}
class spinner : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.var[2] = bossPhase; // probably redundant
obj.var[3] = jjRandom()%2; // randomize sprite
obj.var[4] = jjRandom()%2; // randomize sprite direction
obj.state = STATE::IDLE;
case STATE::IDLE:
obj.causesRicochet = true;
obj.direction = obj.var[4] - 1;
obj.frameID = (jjGameTicks/5) & 6 + obj.var[1];
obj.determineCurFrame();
if (obj.var[2] != bossPhase) obj.state = STATE::KILL;
break;
case STATE::DEACTIVATE:
obj.deactivate();
break;
case STATE::KILL:
if (obj.isActive) {
jjOBJ@ poof = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos)];
poof.determineCurAnim(ANIM::PICKUPS, 86);
}
obj.delete();
break;
}
}
void onDraw(jjOBJ@ obj) {
if (obj.var[2] == 1) obj.determineCurAnim(ANIM::AMMO, (obj.var[3] == 0) ? 13 : 55);
// if (obj.var[2] == 2) obj.determineCurAnim(ANIM::AMMO, (obj.var[3] == 0) ? 12 : 75); // line backup in case anims below don't look better
if (obj.var[2] == 2) obj.determineCurAnim(ANIM::PICKUPS, (obj.var[3] == 0) ? 27 : 85);
if (obj.var[2] == 3) obj.determineCurAnim(ANIM::AMMO, (obj.var[3] == 0) ? 8: 71);
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TRANSLUCENT);
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
if (bullet is null) {
if (force <= 0) {
player.hurt(2);
}
return true;
}
return false;
}
}
void bombDestEdit(jjOBJ@ obj) {
obj.var[2] = jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0,3);
obj.behave(BEHAVIOR::DESTRUCTSCENERY);
}
class livingTotem : jjBEHAVIORINTERFACE {
int attackCD = 90 - (jjDifficulty * 15);
int lastHitID;
void onBehave(jjOBJ@ obj) {
bossHealth = obj.energy;
switch (obj.state) {
case STATE::START:
obj.var[5] = 0;
obj.state = STATE::IDLE;
case STATE::IDLE:
obj.behave(BEHAVIOR::BEE);
obj.lightType = LIGHT::LASER;
obj.light = 15;
if (obj.xOrg > jjLocalPlayers[obj.findNearestPlayer(1024 * 1024)].xPos) obj.xOrg -= 1;
if (obj.xOrg < jjLocalPlayers[obj.findNearestPlayer(1024 * 1024)].xPos) obj.xOrg += 1;
if (obj.yOrg > jjLocalPlayers[obj.findNearestPlayer(1024 * 1024)].yPos) obj.xOrg -= 1;
if (obj.yOrg < jjLocalPlayers[obj.findNearestPlayer(1024 * 1024)].yPos) obj.xOrg += 1; // this looks pretty stupid but should work
break;
case STATE::ATTACK:
obj.lightType = LIGHT::LASER;
obj.light = 15;
obj.xOrg = jjLocalPlayers[obj.findNearestPlayer(256 * 256)].xPos;
obj.yOrg = jjLocalPlayers[obj.findNearestPlayer(256 * 256)].yPos;
obj.yPos += 128;
obj.behave(BEHAVIOR::BEE, false);
obj.yPos -= 128;
if (obj.counter < 10) obj.counter += 100; // never stop!!!
if (obj.var[5] <= 0 && (obj.xPos > jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - 128 || obj.xPos < jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos + 128)) {
if (obj.var[6] == 1) {
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_FIREGUN1A);
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::FIRESHIELDBULLET)];
bullet.ySpeed = 5;
bullet.xSpeed = 0;
bullet.xAcc = 0;
bullet.yAcc = 0.2;
bullet.creatorID = obj.objectID;
bullet.creatorType = CREATOR::OBJECT;
bullet.playerHandling = HANDLING::ENEMYBULLET;
if (jjDifficulty < 2) bullet.animSpeed = 1;
else bullet.animSpeed = jjDifficulty;
obj.var[5] = attackCD;
}
if (obj.var[6] == 2) {
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ELECTRIC1); // I'll optimize this later (maybe)
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::LIGHTNINGSHIELDBULLET)];
bullet.ySpeed = 5;
bullet.xSpeed = 0;
bullet.xAcc = 0;
bullet.yAcc = 0.2;
bullet.creatorID = obj.objectID;
bullet.creatorType = CREATOR::OBJECT;
bullet.playerHandling = HANDLING::ENEMYBULLET;
if (jjDifficulty < 2) bullet.animSpeed = 1;
else bullet.animSpeed = jjDifficulty;
obj.var[5] = attackCD;
}
}
else obj.var[5] = obj.var[5] - 1;
if (obj.var[6] == 3 && (obj.xPos > jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos - 256 || obj.xPos < jjLocalPlayers[obj.findNearestPlayer(256 * 128)].xPos + 256) && jjGameTicks % 20 == 0) {
int rand = jjRandom()%2;
if (rand==0)
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_BLUB1);
else jjSample(obj.xPos, obj.yPos, SOUND::AMMO_BLUB2);
jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::WATERSHIELDBULLET)];
bullet.ySpeed = 5;
bullet.xSpeed = jjRandom() % 9 - 4;
bullet.xAcc = obj.xAcc;
bullet.yAcc = 0.2;
bullet.creatorID = obj.objectID;
bullet.creatorType = CREATOR::OBJECT;
bullet.playerHandling = HANDLING::ENEMYBULLET;
if (jjDifficulty < 2) bullet.animSpeed = 1;
else bullet.animSpeed = jjDifficulty;
}
break;
case STATE::FREEZE:
if (obj.freeze == 0) {
obj.unfreeze(0);
obj.state = obj.oldState;
}
else {
if (obj.freeze >= 5) obj.freeze -= 5;
else obj.freeze--;
}
break;
case STATE::DEACTIVATE:
obj.deactivate();
break;
case STATE::KILL:
givePlayerPointsForObject(jjLocalPlayers[lastHitID], obj);
obj.particlePixelExplosion(0);
obj.delete();
break;
}
if (obj.energy <= 0) obj.state = STATE::KILL;
}
void onDraw(jjOBJ@ obj) {
if (obj.state == STATE::FREEZE) {
jjPARTICLE@ particle = jjAddParticle(PARTICLE::SMOKE);
if (particle !is null) {
particle.xPos = obj.xPos;
particle.yPos = obj.yPos;
}
}
obj.draw();
/* if ((jjGameTicks & 63) == 0) { // leaving this out I think... too much crap on screen
const float dy = jjObjects[obj.creatorID].yPos - obj.yPos, dx = jjObjects[obj.creatorID].xPos - obj.xPos; //based on Pos, not Org, so we can't precalculate these
const auto angle = atan2(dy, dx);
jjOBJ@ pulse = jjObjects[jjAddObject(OBJECT::SHARD, obj.xPos, obj.yPos, behavior: BEHAVIOR::INACTIVE)];
pulse.var[6] = obj.var[6];
pulse.xSpeed = cos(angle) * 2;
pulse.ySpeed = sin(angle) * 2;
pulse.counter = int(sqrt((dx*dx) + (dy*dy)) / 2);
pulse.curFrame = jjAnimations[jjAnimSets[ANIM::AMMO] + 8] + 1;
pulse.lightType = LIGHT::POINT2;
pulse.behavior = TriggerPulse;
pulse.age = jjRandom() & 511;
}*/
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { // copied from Violet I hope this works lol
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);
}
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;
return true;
}
}
class finalTotem : stormCloud {
finalTotem(jjOBJ@ objectPreset) {
super(objectPreset);
followSpeed = 8 + jjDifficulty;
attackCD = 120 - (jjDifficulty * 30);
isTotem = true;
objectPreset.isTarget = true;
objectPreset.isFreezable = true;
objectPreset.triggersTNT = true;
objectPreset.deactivates = false;
}
void onDraw(jjOBJ@ obj) override {
bossHealth = obj.energy;
obj.determineCurAnim(ANIM::CUSTOM[11], 1);
obj.draw();
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) override {
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);
}
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;
return true;
}
}
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;
}
/*void TriggerPulse(jjOBJ@ obj) {
if (--obj.counter > 0) {
obj.xPos += obj.xSpeed;
obj.yPos += obj.ySpeed;
obj.age += 6;
jjDrawResizedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, abs(jjSin(obj.age)) + 0.65, abs(jjCos(obj.age)) + 0.65, SPRITE::TRANSLUCENTSINGLEHUE, obj.var[6], 4);
} else
obj.delete();
}*/
class shamanDemon : jjBEHAVIORINTERFACE {
int curseTarget;
int curseDistance = (256 * 256) + (jjDifficulty * 256 * 64);
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;
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();
}
}
class stormCloud : jjBEHAVIORINTERFACE {
float followSpeed = 5 + jjDifficulty;
int attackCD = 240 - (jjDifficulty * 40);
bool isTotem = false;
int lastHitID;
stormCloud(jjOBJ@ objectPreset) {
objectPreset.behavior = this;
if (!isTotem) {
objectPreset.determineCurAnim(ANIM::CUSTOM[9], 4);
objectPreset.playerHandling = HANDLING::SPECIAL;
objectPreset.scriptedCollisions = true;
objectPreset.isTarget = false;
objectPreset.isFreezable = false;
objectPreset.triggersTNT = false;
objectPreset.deactivates = true;
}
}
void onBehave(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.var[1] = attackCD;
obj.state = STATE::IDLE;
case STATE::IDLE:
if (isTotem) {
obj.lightType = LIGHT::LASER;
obj.light = 15;
totemChannel = jjSampleLooped(obj.xPos, obj.yPos, SOUND::BUMBEE_BEELOOP, totemChannel);
}
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:
if (isTotem) {
obj.lightType = LIGHT::LASER;
obj.light = 15;
totemChannel = jjSampleLooped(obj.xPos, obj.yPos, SOUND::BUMBEE_BEELOOP, totemChannel);
}
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) {
if (isTotem) {
jjSample(obj.xPos, obj.yPos, SOUND::AMMO_LASER);
jjOBJ@ laser = jjObjects[obj.fireBullet(OBJECT::LASER)];
laser.ySpeed = 5;
laser.creatorID = obj.objectID;
laser.creatorType = CREATOR::OBJECT;
laser.playerHandling = HANDLING::ENEMYBULLET;
int playerID = obj.findNearestPlayer(2000000);
if (playerID >= 0 && jjLocalPlayers[playerID].yPos >= obj.yPos && abs(jjLocalPlayers[playerID].xPos - laser.xPos) <= 32.f) {
if (jjDifficulty < 2) jjLocalPlayers[playerID].hurt(1);
else jjLocalPlayers[playerID].hurt(2);
}
} else {
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;
if (jjDifficulty < 2) bullet.animSpeed = 1;
else bullet.animSpeed = 2;
}
obj.var[1] = attackCD;
obj.state = STATE::IDLE;
}
break;
case STATE::FREEZE: // only used by totem
if (obj.freeze == 0) {
obj.unfreeze(0);
obj.state = obj.oldState;
}
else {
if (obj.freeze >= 5) obj.freeze -= 5;
else obj.freeze--;
}
break;
case STATE::DEACTIVATE:
obj.delete();
break;
case STATE::KILL:
if (isTotem) {
givePlayerPointsForObject(jjLocalPlayers[lastHitID], obj);
obj.particlePixelExplosion(0);
obj.delete();
}
if (obj.isActive) {
jjOBJ@ poof = jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos)];
poof.determineCurAnim(ANIM::AMMO, 2);
}
obj.delete();
break;
}
if (obj.energy <= 0 && isTotem) obj.state = STATE::KILL;
}
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);
}
bool onObjectHit(jjOBJ@, jjOBJ@, jjPLAYER@ player, int) { return true; } // whatever man
}
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
}
const array<SOUND::Sample> ButtstompSounds = {SOUND::COMMON_SPLAT1, SOUND::COMMON_SPLAT2, SOUND::COMMON_SPLAT3, SOUND::COMMON_SPLAT4};
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);
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.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));
}
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:
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;
}
}
++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 == 243 && 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[10], 1);
obj.frameID = (int8(obj.age / 12));
obj.determineCurFrame();
if (obj.age == 30) jjSample(obj.xPos, obj.yPos, SOUND::INTRO_BRAKE);
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;
}
}
}
class parrot : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.playerHandling = HANDLING::ENEMY;
obj.bulletHandling = HANDLING::HURTBYBULLET;
obj.isBlastable = obj.isFreezable = obj.isTarget = obj.triggersTNT = true;
switch (obj.state) {
case STATE::START:
obj.age = jjRandom() % 10;
obj.state = STATE::FLY;
case STATE::FLY:
obj.frameID = (jjGameTicks/7) & 7;
obj.determineCurFrame();
obj.counter += 1;
if (obj.counter > 127) { obj.counter = 0; }
obj.yPos = obj.yOrg + 100 * jjSin((obj.age + jjGameTicks) * 10);
obj.xPos = obj.xPos + (2 * obj.direction);
break;
case STATE::FREEZE:
if (obj.freeze == 0) obj.state = obj.oldState;
else obj.freeze--;
break;
case STATE::DEACTIVATE:
obj.delete(); // will be continuously spawned anyway
break;
case STATE::KILL:
obj.delete(); // don't drop random item
break;
}
}
void onDraw(jjOBJ@ obj) {
obj.draw();
}
}
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::INTRO_GRAB);
else jjSample(obj.xOrg, obj.yOrg, SOUND::MONKEY_SPLUT);
}
}
class specialBanana : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) {
obj.eventID = 66;
obj.behave(BEHAVIOR::PICKUP);
obj.points = 50;
obj.var[0] = 0;
}
}
class NPC : 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;
if (jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 0, 3) == 2) chiefNear = 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) NPCspeaks(jjLocalPlayers[nearPlayerID], jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 3, 5));
else if (nearPlayerID < 0) {
obj.state = STATE::STILL;
if (jjParameterGet(int(obj.xOrg / 32), int(obj.yOrg / 32), 0, 3) == 2) chiefNear = 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, 3));
obj.determineCurFrame();
obj.draw();
if (obj.state == STATE::WAKE) jjDrawSprite(obj.xPos - 40, obj.yPos - 48, ANIM::CUSTOM[4], 0, (jjGameTicks/12) & 1);
}
}
void NPCspeaks(const jjPLAYER@ play, int8 orderID) {
switch (orderID) {
case 0:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("yo check it out kiddos, i gots me a fresh new RHYME i came up with just fer YOU!", right: Yoman, direction: 1, color: 72)),
Screen(top: Line("now feel my MAGIC FLOW and LISTEN:", right: Yoman, direction: 1, color: 72)),
Screen(top: Line("yo, them mean pesky parrots sure are APLENTY, but huntin' them down is worth a mere TWENTY", right: Yoman, direction: 1, color: 72)),
Screen(top: Line("y'all be better off just keepin' yer distance, tho sometimes the birdbrains can be of ASSISTANCE", right: Yoman, direction: 1, color: 72)),
Screen(top: Line("STOMP at da right time and hold down yer JUMP, and if ya don't miss, that'll give ya a BUMP", right: Yoman, direction: 1, color: 72)),
Screen(top: Line("yer butt is a WEAPON, but also a TOOL - use it fer PLATFORMIN' 'n' don't be no fool!", right: Yoman, direction: 1, color: 72)),
Screen(top: Line(". . .that's it fer now, see ya next time!", right: Yoman, direction: 1, color: 72)),
});
break;
case 1:
if (!bananasGiven && play.gems[GEM::PURPLE] < 50) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("you bring goods?", right: Chief, direction: 1, color: 64)),
Screen(
top: Line(right: Chief),
bottom: Line("not yet, but we're working on it.", left: Lori, direction: -1, color: 40)
),
Screen(
top: Line("hey, how about we just quickly go over there, defeat the demon, then we go back and help you with anything you'd like?", left: Jazz, right: Chief, direction: -1, color: 16),
bottom: Line("(bro, come on...)", left: Lori, direction: -1, color: 40)
),
Screen(
top: Line("it'll only take a moment, promise! we're kind of in a hurry!", left: Jazz, right: Chief, direction: -1, color: 16),
bottom: Line(left: Lori)
),
Screen(
top: Line("entire village depend on this task. me cannot leave, must keep people safe from evil spirits.", left: Jazz, right: Chief, direction: 1, color: 64),
bottom: Line(left: Lori)
),
Screen(
top: Line("also, me have feeling you quest far from over. many ways to go you have. the trees and the rivers tell me so.", left: Jazz, right: Chief, direction: 1, color: 64),
bottom: Line(left: Lori)
),
Screen(
top: Line("aren't you an optimistic fellow...", left: Jazz, right: Chief, direction: -1, color: 16),
bottom: Line(left: Lori)
),
});
}
if (!bananasGiven && play.gems[GEM::PURPLE] >= 50) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(bottom: Line("here are the bananas, chief.", left: Lori, direction: -1, color: 40)),
Screen(
top: Line("good. you save village. me open gate for you.", right: Chief, direction: 1, color: 64),
bottom: Line(left: Lori)
),
Screen(
top: Line("go fast. do not linger! when gate open, evil spirits can come inside.", right: Chief, direction: 1, color: 64),
bottom: Line(left: Lori)
),
Screen(
top: Line("ceremonial spot filled with magic. there we speak with gods. you do what you must do, then leave and not disturb MAGIC FORCES no more.", right: Chief, direction: 1, color: 64),
bottom: Line(left: Lori)
),
Screen(
top: Line(right: Chief),
bottom: Line("thank you. we'll do our best. we leave in peace and respect for your duty.", left: Lori, direction: -1, color: 40),
finish: function(bool skipping) { giveBananas(jjLocalPlayers[nearPlayerID]); }
),
});
}
if (bananasGiven) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("what you wait for? GO! me must close gate or evil spirits come to village!", right: Chief, direction: 1, color: 64)),
});
}
// else {
// @CurrentPopup = Conversation(array<Screen@> = {
// Screen(top: Line("you not supposed to see this. something break. very bad!", direction: 1, color: 64)),
// });
// }
break;
case 2:
if (jjTriggers[1]) {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("you open lock?? not good. me not want part of this! chief deal with you!", direction: 1, color: 88)),
});
}
else {
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("village center be closed to outsider by order of CHIEF BUKTOOF, unga!", direction: 1, color: 88)),
});
}
break;
case 3:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("me team unga.", direction: 1, color: 88)),
});
break;
case 4:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("me team bunga.", direction: 1, color: 48)),
});
break;
case 5:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("me ship unga x bunga. LOVE WIN!", direction: 1, color: 88)),
});
break;
case 6:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("chief buktoof grumpy and strict, but he good leader. get village through some hard times. chief not easy job!", direction: 1, color: 48)),
});
break;
case 7:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("maybe you think 'ooo why them not just pick up bananas, bananas right there'.", direction: 1, color: 88)),
Screen(top: Line("well contrary to what many think, not ALL rabbit jump that high! is VERY hard work for some!!", direction: 1, color: 88)),
});
break;
case 8:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("me in charge of put bottomless pit signs.", direction: 1, color: 48)),
Screen(top: Line("is kinda stupid job. everybody can see pit THIS big, yes?", direction: 1, color: 48)),
Screen(top: Line("me think bottomless pit sign putter just job created to fill void in job market created by automation taking many job from people.", direction: 1, color: 48)),
});
break;
case 9:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("village have tradition to not hang out with you outsider. elders say we TRICKED many times before by you people.", direction: 1, color: 88)),
Screen(top: Line("but not ALL in village want to do this. current day not many people like this rule.", direction: 1, color: 88)),
Screen(top: Line("but CHIEF BUKTOOF believe it. so we stay hide. FOR NOW.", direction: 1, color: 88)),
Screen(top: Line("when me have children, me hope they walk the world and meet many friend.", direction: 1, color: 88)),
});
break;
case 10:
@CurrentPopup = Conversation(array<Screen@> = {
Screen(top: Line("rain time never come this fast before. is very bad... me think EVIL SPIRITS do this.", direction: 1, color: 48)),
Screen(top: Line("when you meet big bad spirit, PUNCH HARD IN FACE from me!", direction: 1, color: 48)),
});
[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.