Downloads containing Ozymandius.j2as

Downloads
Name Author Game Mode Rating
JJ2+ Only: OzymandiusFeatured Download Violet CLM Single player 9.8 Download file

File preview

  1. array<int> firespeeds = {3, 20, 3, 6, 16, 1, 0, 14, 5};
  2. array<bool> hasAlreadyCollectedWeapon = {true, false, false, false, false, false, false, false, false};
  3. const array<uint8> gunProfilePics =             {18, 25, 29, 34, 49, 57, 59, 61, 68};
  4. array<string> weaponNames = {
  5.         "Blaster",
  6.         "Ricochet Fire",
  7.         " Shield Cubes",
  8.         "  Jet Missile",
  9.         " Crimson Candy",
  10.         "Flaming Footprint",
  11.         "Remote Detonator",
  12.         "Chain Launcher",
  13.         "    Chakram"
  14. };
  15. array<string> weaponDescriptions = {
  16.         "Rarity is best pony.",
  17.         "Great balls of fire fly from your gun! The immense@destructive force of these fireballs is marred only by@their slow speeds. If a fireball hits a wall, it will@bounce, and yet not forever...",
  18.         "Little frozen cubes spring to your defense, circling@around you as long as you hold the fire button. Let@go, and they'll fly off in search of enemies to destroy,@though they are most damaging at a shorter range...",
  19.         "Missiles pound through the air and do great damage to@all but the fiercest. Each launch will knock you back,@however, even leading to flight. It is up to you to@turn this recoil into a blessing, or a curse...",
  20.         "Festive green and red orbs fly madly forth, picking up@speed until they are little more than a sparkly blur@to behold. And yet, the faster they fly, the less@effective they are at harming your foes...",
  21.         "A jet of flame hugs the ground beneath your feet,@incinerating all that dares stand in its way. If your@foe flies above you, however, your fate is sealed, for@this fire is beholden less to you than to gravity...",
  22.         "An explosion fills the air, and your ears tingle@despite the immense distance. These bombs hurt any foe@dumb enough to stand in the way, yet despite their@great maneuverability, they inflict little harm...",
  23.         "A tiny metal ring slips from the muzzle of your blaster,@soon joined by another, and yet another, zipping through@the air. As a melee weapon, the chain is useless,@for its damage increases with its own length...",
  24.         "Glowing blue rings spin from your weapon, but soon lose@their appetite for battle and yearn to return to you.@Chakrams do not fly far, despite their power, but you@may be able to put them to use in your defense..."
  25. };
  26. bool paused = true, pausedForApproachingPlanet = true, pausedForGunStory = false, pausedForChatting = false, pausedForWatchingTheGoddess = false;
  27. int describedWeapon = 0;
  28. int elapsedPauseTime = 0;
  29. enum conversations {chatEVA, chatEVA2, chatSTATUE, chatGODDESS};
  30. int currentPauseChat = chatEVA;
  31.  
  32. bool barrierDestroyed = false, statueDefeated = false, scrolly = false, whiteWorld = false, showScore = false;
  33. const float SCROLLSPEED = 3;
  34.  
  35. const int BOLLYHEALTH1 = 300;
  36. const int BOLLYHEALTH2 = 200;
  37. const int BOLLYHEALTH3 = 620;
  38. const int BOLLYHEALTH4 = 275;
  39.  
  40. const string MUSICFILENAME = "zarathus.mo3";
  41.  
  42.  
  43. void onLevelReload() {
  44.         barrierDestroyed = statueDefeated = scrolly = whiteWorld = false;
  45.         paused = pausedForGunStory = pausedForChatting = false; //just in case
  46.         jjMusicLoad(MUSICFILENAME);
  47.         jjEnableEachASFunction();
  48.         if (jjTileGet(5, 185, 65) == 1) jjEnabledASFunctions[4] = false; //text sign
  49.         jjLocalPlayers[0].food = 0; //whatever
  50.         jjObjectPresets[OBJECT::BOLLY].energy = BOLLYHEALTH1 / 10;
  51.         jjLayerHasTiles[7] = jjLayerHasTiles[5] = jjLayerHasTiles[4] = jjLayerHasTiles[3] = true;
  52.         jjLayerHasTiles[6] = false;
  53.         jjTileSet(4, 156, 0, 0);
  54.         jjTileSet(4, 157, 0, 0);
  55.         jjTileSet(4, 158, 0, 0);
  56.         jjTileSet(4, 159, 0, 0);
  57.        
  58.         jjLayerYAutoSpeed[8] = 0;
  59.         jjLayerXAutoSpeed[8] = 0;
  60.         jjTexturedBGStyle = TEXTURE::MENU;
  61.         jjTexturedBGStars = true;
  62.         jjTexturedBGUsed = false;
  63.         jjSetFadeColors(164, 164, 162);
  64.        
  65.         //jjTileType[7] = jjTileType[8] = jjTileType[17] = jjTileType[18] = jjTileType[424] = jjTileType[425] = 0; //normal
  66. }
  67. void onLevelLoad() {
  68.        
  69.         if (jjResolutionWidth != 640 || jjResolutionHeight != 480)
  70.                 jjAlert("||WARNING: This level is designed to be played in 640x480, not " + formatInt(jjResolutionWidth, "1") +"x" + formatInt(jjResolutionHeight, "1"));
  71.         if (jjLocalPlayerCount > 1)
  72.                 jjAlert("||WARNING: This level is not designed to be played in splitscreen.");
  73.         if ((jjGameMode != GAME::SP )|| (jjGameConnection != GAME::LOCAL))
  74.                 jjAlert("||WARNING: This level is only designed for local single player.");
  75.        
  76.         jjTexturedBGStyle = TEXTURE::MENU;
  77.         jjTexturedBGStars = true;
  78.         jjSetFadeColors(164, 164, 162);
  79.        
  80.         jjLayerHasTiles[6] = false;
  81.        
  82.         jjPalette.fill(0,0,0, 10, 235); //blackness blackness blackness
  83.         jjPalette.gradient(176,92,0, 76,28,0, 24, 8); //planet pits
  84.         jjPalette.gradient(255,0,0, 0,0,0, 32, 8); //warning text
  85.         jjPalette.gradient(240,196,108, 72,44,4, 40, 8); //planet base
  86.         jjPalette.gradient(255,255,255, 0,0,0, 72, 8); //stars
  87.         jjPalette.apply();
  88.        
  89.         for (int i = 1; i < 10; ++i)
  90.                 jjWeapons[i].infinite = true;
  91.         for (int i = 0; i < jjLocalPlayerCount; ++i) {
  92.                 jjLocalPlayers[i].lives = 999; //that should be enough
  93.                 jjLocalPlayers[i].powerup[WEAPON::GUN8] = true;
  94.         }
  95.        
  96.         for (int x = 183; x < 197; ++x)
  97.                 jjTileSet(4, x, 0, jjTileGet(4, x, 0) | TILE::VFLIPPED); //spikes above chakram ammo
  98.         for (int x = 173; x < 176; ++x)
  99.                 jjTileSet(5, x, 79, jjTileGet(5, x, 79) | TILE::VFLIPPED); //barrier above jet missile ammo
  100.         for (int x = 157; x < 161; ++x)
  101.                 jjTileSet(4, x, 63, jjTileGet(4, x, 63) | TILE::VFLIPPED); //platform to the right of goddess
  102.        
  103.         jjObjectPresets[OBJECT::BLASTERBULLET].special = jjObjectPresets[OBJECT::BLASTERBULLET].determineCurAnim(ANIM::AMMO, 10);
  104.         jjObjectPresets[OBJECT::BLASTERBULLET].lightType = LIGHT::NONE;
  105.         jjObjectPresets[OBJECT::BLASTERBULLET].animSpeed = 2; //damage
  106.         jjObjectPresets[OBJECT::BLASTERBULLET].counterEnd += 35;
  107.         jjObjectPresets[OBJECT::BLASTERBULLETPU].animSpeed = 3; //damage
  108.        
  109.         jjObjectPresets[OBJECT::BOUNCERBULLET].special = jjObjectPresets[OBJECT::BOUNCERBULLET].determineCurAnim(ANIM::AMMO, 77);
  110.         jjObjectPresets[OBJECT::BOUNCERBULLET].frameID = 4;
  111.         jjObjectPresets[OBJECT::BOUNCERBULLET].determineCurFrame();
  112.         jjObjectPresets[OBJECT::BOUNCERBULLET].killAnim = jjObjectPresets[OBJECT::RFBULLET].killAnim;
  113.         jjObjectPresets[OBJECT::BOUNCERBULLET].counterEnd = 0;
  114.         jjObjectPresets[OBJECT::BOUNCERBULLET].var[6] = 2; //firey
  115.         jjObjectPresets[OBJECT::BOUNCERBULLET].lightType = LIGHT::NONE;
  116.         jjObjectPresets[OBJECT::BOUNCERBULLET].behavior = BallBullet;
  117.         jjObjectPresets[OBJECT::BOUNCERBULLET].ySpeed = 0;
  118.         jjObjectPresets[OBJECT::BOUNCERBULLET].animSpeed = 10; //damage
  119.        
  120.         jjObjectPresets[OBJECT::ICEBULLET].special = jjObjectPresets[OBJECT::ICEBULLET].determineCurAnim(ANIM::AMMO, 29);
  121.         jjObjectPresets[OBJECT::ICEBULLET].freeze = 0;
  122.         jjObjectPresets[OBJECT::ICEBULLET].lightType = LIGHT::NONE;
  123.         jjObjectPresets[OBJECT::ICEBULLET].counterEnd = 255;
  124.         jjObjectPresets[OBJECT::ICEBULLET].behavior = ShieldBullet;
  125.         jjObjectPresets[OBJECT::ICEBULLET].animSpeed = 5; //damage
  126.        
  127.         jjObjectPresets[OBJECT::SEEKERBULLET].special = jjObjectPresets[OBJECT::SEEKERBULLET].determineCurAnim(ANIM::SONCSHIP, 0);
  128.         jjObjectPresets[OBJECT::SEEKERBULLET].behavior = ReboundMissile;
  129.         jjObjectPresets[OBJECT::SEEKERBULLET].xSpeed = 4.5;
  130.         jjObjectPresets[OBJECT::SEEKERBULLET].animSpeed = 8; //damage
  131.         jjWeapons[WEAPON::SEEKER].style = WEAPON::NORMAL;
  132.        
  133.         jjObjectPresets[OBJECT::RFBULLET].special = jjObjectPresets[OBJECT::RFBULLET].determineCurAnim(ANIM::AMMO, 57);
  134.         jjObjectPresets[OBJECT::RFBULLET].behavior = PassOnThrough;
  135.         jjObjectPresets[OBJECT::RFBULLET].var[6] = 16; //passes through enemies
  136.         jjObjectPresets[OBJECT::RFBULLET].killAnim = jjObjectPresets[OBJECT::BLASTERBULLET].killAnim;
  137.         jjObjectPresets[OBJECT::RFBULLET].animSpeed = 1; //damage
  138.         jjWeapons[WEAPON::RF].style = WEAPON::POPCORN;
  139.         jjObjectPresets[OBJECT::RFBULLETPU].animSpeed = 11; //damage
  140.         jjObjectPresets[OBJECT::RFBULLETPU].counterEnd = 100;
  141.        
  142.         jjObjectPresets[OBJECT::TOASTERBULLET].behavior = PathOfFire;
  143.         jjObjectPresets[OBJECT::TOASTERBULLET].var[6] = 2 + 16; //passes through enemies and burns them to death
  144.         jjObjectPresets[OBJECT::TOASTERBULLET].lightType = LIGHT::BRIGHT;
  145.         jjObjectPresets[OBJECT::TOASTERBULLET].light = 8;
  146.         jjWeapons[WEAPON::TOASTER].style = WEAPON::MISSILE;
  147.        
  148.         jjObjectPresets[OBJECT::TNT].counterEnd = 1;
  149.         jjObjectPresets[OBJECT::TNT].behavior = RepositionTNT;
  150.        
  151.         jjObjectPresets[OBJECT::FIREBALLBULLETPU].determineCurAnim(ANIM::SONCSHIP, 1);
  152.         jjObjectPresets[OBJECT::FIREBALLBULLETPU].determineCurFrame();
  153.         jjObjectPresets[OBJECT::FIREBALLBULLETPU].behavior = ChainGang;
  154.         jjObjectPresets[OBJECT::FIREBALLBULLETPU].xSpeed = 8;
  155.         jjObjectPresets[OBJECT::FIREBALLBULLETPU].var[6] = 0; //nothing special
  156.        
  157.         jjObjectPresets[OBJECT::ELECTROBULLET].special = jjObjectPresets[OBJECT::ELECTROBULLET].determineCurAnim(ANIM::AMMO, 76);
  158.         jjObjectPresets[OBJECT::ELECTROBULLET].behavior = Chakram;
  159.         jjObjectPresets[OBJECT::ELECTROBULLET].var[6] = 0; //nothing special
  160.         jjObjectPresets[OBJECT::ELECTROBULLET].killAnim = jjObjectPresets[0].determineCurAnim(ANIM::AMMO, 2, false);
  161.         jjObjectPresets[OBJECT::ELECTROBULLET].animSpeed = 9; //damage
  162.        
  163.         for (int i = 0; i < 8; ++i) {
  164.                 jjObjectPresets[OBJECT::ICEAMMO3 + i].lightType = LIGHT::RING2;
  165.                 jjObjectPresets[OBJECT::ICEAMMO3 + i].light = 7;
  166.         }
  167.         jjObjectPresets[OBJECT::SEEKERAMMO3].behavior = RisingAmmo;
  168.         jjObjectPresets[OBJECT::SEEKERAMMO3].isBlastable = false;
  169.         jjObjectPresets[OBJECT::DESTRUCTSCENERY].scriptedCollisions = true;
  170.         jjObjectPresets[OBJECT::SAVEPOST].scriptedCollisions = true;
  171.         jjObjectPresets[OBJECT::EXTRALIFE].scriptedCollisions = true;
  172.         jjObjectPresets[OBJECT::TRIGGERCRATE].scriptedCollisions = true;
  173.        
  174.         jjObjectPresets[OBJECT::FASTFIRE].curAnim = jjObjectPresets[OBJECT::CARROT].curAnim;
  175.         jjObjectPresets[OBJECT::FASTFIRE].eventID = OBJECT::CARROT;
  176.        
  177.        
  178.         jjObjectPresets[OBJECT::EVA].behavior = MyEva;
  179.         jjObjectPresets[OBJECT::EVA].scriptedCollisions = true;
  180.        
  181.         jjObjectPresets[OBJECT::LEMON].behavior = Stars;
  182.         jjObjectPresets[OBJECT::LEMON].determineCurAnim(ANIM::AMMO, 3);
  183.         jjObjectPresets[OBJECT::LEMON].playerHandling = HANDLING::PARTICLE;
  184.        
  185.         jjObjectPresets[OBJECT::BIGROCK].behavior = RockBarrier;
  186.         jjObjectPresets[OBJECT::BIGROCK].scriptedCollisions = true;
  187.         jjObjectPresets[OBJECT::BIGROCK].energy = 100;
  188.        
  189.         jjObjectPresets[OBJECT::FRIES].behavior = TempleLock;
  190.         jjObjectPresets[OBJECT::FRIES].scriptedCollisions = true;
  191.         jjObjectPresets[OBJECT::FRIES].playerHandling = HANDLING::SPECIAL;
  192.        
  193.         jjObjectPresets[OBJECT::STRAWBERRY].behavior = Acolyte;
  194.         jjObjectPresets[OBJECT::STRAWBERRY].bulletHandling = HANDLING::IGNOREBULLET;
  195.         jjObjectPresets[OBJECT::STRAWBERRY].playerHandling = HANDLING::PARTICLE;
  196.         jjObjectPresets[OBJECT::STRAWBERRY].determineCurAnim(ANIM::EVA, 3);
  197.         jjObjectPresets[OBJECT::STRAWBERRY].determineCurFrame();
  198.         jjObjectPresets[OBJECT::STRAWBERRY].light = 0;
  199.         jjObjectPresets[OBJECT::STRAWBERRY].lightType = LIGHT::BRIGHT;
  200.        
  201.        
  202.         jjObjectPresets[OBJECT::CRAB].energy = 30;
  203.         jjObjectPresets[OBJECT::CRAB].behavior = InvisibleCrab;
  204.        
  205.         jjObjectPresets[OBJECT::NORMTURTLE].energy = 25;
  206.         jjObjectPresets[OBJECT::NORMTURTLE].behavior = Turtle;
  207.         jjObjectPresets[OBJECT::NORMTURTLE].playerHandling = HANDLING::ENEMY;
  208.         jjObjectPresets[OBJECT::NORMTURTLE].bulletHandling = HANDLING::DESTROYBULLET;
  209.         jjObjectPresets[OBJECT::NORMTURTLE].determineCurAnim(ANIM::TURTLE, 5);
  210.         jjObjectPresets[OBJECT::NORMTURTLE].determineCurFrame();
  211.         jjObjectPresets[OBJECT::NORMTURTLE].isBlastable = false;
  212.        
  213.         jjObjectPresets[OBJECT::PEAR].energy = 50;
  214.         jjObjectPresets[OBJECT::PEAR].behavior = UFO;
  215.         jjObjectPresets[OBJECT::PEAR].playerHandling = HANDLING::ENEMY;
  216.         jjObjectPresets[OBJECT::PEAR].points = 500;
  217.         jjObjectPresets[OBJECT::PEAR].determineCurAnim(ANIM::FRUITPLAT, 0);
  218.         jjObjectPresets[OBJECT::PEAR].determineCurFrame();
  219.        
  220.         jjObjectPresets[OBJECT::STEAM].energy = 1;
  221.         jjObjectPresets[OBJECT::STEAM].behavior = JumpyGuy;
  222.         jjObjectPresets[OBJECT::STEAM].playerHandling = HANDLING::ENEMY;
  223.         jjObjectPresets[OBJECT::STEAM].bulletHandling = HANDLING::HURTBYBULLET;
  224.         jjObjectPresets[OBJECT::STEAM].points = 10;
  225.         jjObjectPresets[OBJECT::STEAM].frameID = 2;
  226.         jjObjectPresets[OBJECT::STEAM].determineCurFrame();
  227.         jjObjectPresets[OBJECT::STEAM].special = 224;
  228.        
  229.         jjObjectPresets[OBJECT::BEEBOY].energy = 1;
  230.         jjObjectPresets[OBJECT::BEEBOY].points = 20;
  231.         jjObjectPresets[OBJECT::BEEBOY].behavior = KamikazBee;
  232.        
  233.         jjObjectPresets[OBJECT::BILSY].energy = 120;
  234.         jjObjectPresets[OBJECT::BILSY].points = 1000;
  235.         jjObjectPresets[OBJECT::BILSY].playerHandling = HANDLING::ENEMY;
  236.         jjObjectPresets[OBJECT::BILSY].bulletHandling = HANDLING::HURTBYBULLET;
  237.         jjObjectPresets[OBJECT::BILSY].behavior = MiniBilsy;
  238.         jjObjectPresets[OBJECT::BILSY].direction = -1;
  239.        
  240.         jjObjectPresets[OBJECT::HELMUT].energy = 100;
  241.         jjObjectPresets[OBJECT::HELMUT].special = 400;
  242.         jjObjectPresets[OBJECT::HELMUT].behavior = StatueBoss;
  243.         jjObjectPresets[OBJECT::HELMUT].state = STATE::DELAYEDSTART;
  244.         jjObjectPresets[OBJECT::HELMUT].direction = 1;
  245.         jjObjectPresets[OBJECT::HELMUT].playerHandling = HANDLING::SPECIAL;
  246.         jjObjectPresets[OBJECT::HELMUT].bulletHandling = HANDLING::IGNOREBULLET;
  247.         jjObjectPresets[OBJECT::HELMUT].scriptedCollisions = true;
  248.         jjObjectPresets[OBJECT::HELMUT].determineCurAnim(ANIM::AMMO, 8);
  249.         jjObjectPresets[OBJECT::HELMUT].frameID = 7;
  250.         jjObjectPresets[OBJECT::HELMUT].determineCurFrame();
  251.        
  252.         jjObjectPresets[OBJECT::MILK].behavior = Goddess;
  253.         jjObjectPresets[OBJECT::MILK].determineCurAnim(ANIM::VINE, 1);
  254.         jjObjectPresets[OBJECT::MILK].determineCurFrame();
  255.         jjObjectPresets[OBJECT::MILK].bulletHandling = HANDLING::IGNOREBULLET; //to start off with
  256.         jjObjectPresets[OBJECT::MILK].playerHandling = HANDLING::PARTICLE; //to start off with
  257.         jjObjectPresets[OBJECT::MILK].energy = 60;
  258.         jjObjectPresets[OBJECT::MILK].special = 4800;
  259.         jjObjectPresets[OBJECT::MILK].scriptedCollisions = true;
  260.        
  261.         jjObjectPresets[OBJECT::BOLLY].behavior = Bolly1;
  262.         jjObjectPresets[OBJECT::BOLLY].determineCurAnim(ANIM::SONCSHIP, 2); //it probably already is, but can't hurt
  263.         jjObjectPresets[OBJECT::BOLLY].determineCurFrame();
  264.         jjObjectPresets[OBJECT::BOLLY].age = BOLLYHEALTH1;
  265.         jjObjectPresets[OBJECT::BOLLY].energy = BOLLYHEALTH1 / 10;
  266.         jjObjectPresets[OBJECT::BOLLY].bulletHandling = HANDLING::DESTROYBULLET;
  267.         jjObjectPresets[OBJECT::BOLLY].deactivates = false;
  268.         jjObjectPresets[OBJECT::BOLLY].scriptedCollisions = false;
  269.         jjObjectPresets[OBJECT::BOLLY].isBlastable = false; //don't mess with the speeds, please
  270.        
  271. }
  272.  
  273. void BallBullet(jjOBJ@ obj) {
  274.         if (obj.state == STATE::START) {
  275.                 obj.state = STATE::FLY;
  276.                 obj.xAcc = obj.yAcc = 0;
  277.         } else if (obj.state == STATE::EXPLODE) {
  278.                 obj.counterEnd = 99;
  279.         }
  280.         if (jjMaskedPixel(obj.xPos + obj.xSpeed, obj.yPos)) {
  281.                 obj.xSpeed = -obj.xSpeed;
  282.                 ++obj.counterEnd;
  283.         } else if (jjMaskedPixel(obj.xPos, obj.yPos + obj.ySpeed)) {
  284.                 obj.ySpeed = -obj.ySpeed;
  285.                 ++obj.counterEnd;
  286.         }
  287.         if (obj.counterEnd > 4) {
  288.                 obj.state = STATE::EXPLODE;
  289.                 obj.behavior = BEHAVIOR::RFBULLET;
  290.                 obj.frameID = 7;
  291.                 obj.counter = 0;
  292.                 return;
  293.         }
  294.         obj.xPos = obj.xPos + obj.xSpeed;
  295.         obj.yPos = obj.yPos + obj.ySpeed;
  296.         if (++obj.counter >= 8) {
  297.                 obj.counter = 0;
  298.                 if (++obj.frameID > 6) obj.frameID = 4;
  299.                 obj.determineCurFrame();
  300.                 uint rand = jjRandom();
  301.                 //if ((rand & 3) == 0) {
  302.                         jjPARTICLE@ fire = jjAddParticle(PARTICLE::FIRE);
  303.                         if (fire !is null) {
  304.                                 fire.xPos = obj.xPos;
  305.                                 fire.yPos = obj.yPos;
  306.                                 fire.fire.size = (rand >> 2) & 3;
  307.                         }
  308.                 //}
  309.         }
  310.         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 0, SPRITE::PALSHIFT, obj.var[0], 3);
  311. }
  312.  
  313. void ShieldBullet(jjOBJ@ obj) {
  314.         if (obj.state == STATE::START)
  315.                 obj.state = STATE::FLY;
  316.         jjPLAYER@ play = jjPlayers[obj.creatorID];
  317.         float xChange = (jjRandom() & 63) / 128.0 - 0.25;
  318.         float yChange = (jjRandom() & 63) / 128.0 - 0.25;
  319.         if (play.xPos < obj.xPos)
  320.                 xChange -= .25;
  321.         else
  322.                 xChange += .25;
  323.         if (play.yPos < obj.yPos)
  324.                 yChange -= .25;
  325.         else
  326.                 yChange += .25;
  327.         obj.xSpeed = obj.xSpeed + xChange;
  328.         obj.ySpeed = obj.ySpeed + yChange;
  329.         if (!play.keyFire) obj.behavior = BEHAVIOR::BULLET;
  330.         obj.behave(BEHAVIOR::BULLET); //eh
  331. }
  332.  
  333. void ReboundMissile(jjOBJ@ missile) {
  334.         jjPLAYER@ play = jjPlayers[missile.creatorID];
  335.         play.xSpeed = play.xSpeed - missile.xSpeed;
  336.         play.ySpeed = play.ySpeed - missile.ySpeed;
  337.         play.alreadyDoubleJumped = missile.ySpeed < 0; //so that Spaz can get the same height increases that Jazz and Lori do by limiting their gravity through coptering
  338.         missile.var[0] = atan2(missile.ySpeed, missile.xSpeed) * 180;
  339.         missile.behavior = BEHAVIOR::BOLLYBULLET; //on with the show
  340. }
  341.  
  342. void PassOnThrough(jjOBJ@ missile) {
  343.         if (missile.state == STATE::START) missile.state = STATE::ROCKETFLY;
  344.         else if (missile.state == STATE::DEACTIVATE) missile.delete();
  345.         missile.xPos = missile.xPos + missile.xSpeed + missile.xAcc;
  346.         missile.yPos = missile.yPos + missile.ySpeed + missile.yAcc;
  347.         if (jjMaskedPixel(missile.xPos, missile.yPos)) {
  348.                 missile.state = STATE::EXPLODE;
  349.                 missile.behavior = BEHAVIOR::EXPLOSION2;
  350.                 missile.frameID = 0;
  351.         }
  352.         missile.xAcc = missile.xAcc * 1.1;
  353.         //missile.yAcc = missile.yAcc * 1.1;
  354.         if ((jjRandom() & 7) == 0) {
  355.                 jjPARTICLE@ spark = jjAddParticle(PARTICLE::SPARK);
  356.                 if (spark !is null) {
  357.                         spark.xPos = missile.xPos;
  358.                         spark.yPos = missile.yPos;
  359.                         spark.xSpeed = -missile.xSpeed;
  360.                         spark.ySpeed = -missile.ySpeed;
  361.                 }
  362.         }
  363.         missile.counter += 45 + (jjRandom() & 31);
  364.         jjDrawSprite(missile.xPos, missile.yPos + jjCos(missile.counter) * 3, ANIM::AMMO, 57, jjGameTicks % 10, missile.direction, SPRITE::PALSHIFT, 248);
  365. }
  366.  
  367. void PathOfFire(jjOBJ@ flame) {
  368.         if (flame.state == STATE::START) {
  369.                 jjSample(flame.xPos, flame.yPos, SOUND::AMMO_FIREGUN2A);
  370.                 if (jjMaskedPixel(flame.xPos, flame.yPos)) {
  371.                         flame.delete();
  372.                         return;
  373.                 }
  374.                 flame.state = STATE::FLY;
  375.                 if (flame.direction == 0) flame.direction = (flame.xSpeed < 0) ? -1 : 1;
  376.                 float nuY = flame.yPos + (jjRandom() & 7);
  377.                 if (!jjMaskedPixel(flame.xPos, nuY))
  378.                         flame.yPos = nuY;
  379.                 if (flame.creatorType == CREATOR::PLAYER) {// && jjRandom() & 7 == 0) {
  380.                         jjPARTICLE@ smoke = jjAddParticle(PARTICLE::SMOKE);
  381.                         if (smoke !is null) {
  382.                                 smoke.xPos = flame.xPos;
  383.                                 smoke.yPos = flame.yPos;
  384.                         }
  385.                 } else
  386.                         flame.animSpeed = 2;
  387.         }
  388.         if (++flame.counter >= 2) {
  389.                 flame.counter = 0;
  390.                 if (++flame.frameID == 2) {
  391.                         jjOBJ@ nuFlame = jjObjects[jjAddObject(OBJECT::TOASTERBULLET, flame.xPos + 10 * flame.direction, flame.yPos, flame.creatorID, flame.creatorType)];
  392.                         nuFlame.direction = flame.direction;
  393.                         nuFlame.playerHandling = flame.playerHandling;
  394.                 } else if (flame.frameID == 8)
  395.                         flame.delete();
  396.         }
  397.         flame.determineCurFrame();
  398.         flame.draw();
  399. }
  400.  
  401. void RepositionTNT(jjOBJ@ tnt) {
  402.         jjPLAYER@ play = jjPlayers[tnt.creatorID];
  403.         tnt.xPos = jjMouseX + play.cameraX;
  404.         tnt.yPos = jjMouseY + play.cameraY;
  405.         tnt.behavior = BEHAVIOR::TNT; //all done
  406. }
  407.  
  408. void ChainGang(jjOBJ@ chain) {
  409.         if (chain.state == STATE::START) chain.state = STATE::FLY;
  410.         else if (chain.state == STATE::DEACTIVATE || chain.state == STATE::EXPLODE || chain.yPos < 0 || chain.xPos < 0) {
  411.                 if (--chain.counter <= 0) chain.delete();
  412.         } else {
  413.                 chain.animSpeed = ++chain.counter >> 2;
  414.                 //if (chain.animSpeed > 15) chain.animSpeed = 15; //maximum strength
  415.                 chain.xPos = chain.xPos + chain.xSpeed;
  416.                 chain.yPos = chain.yPos + chain.ySpeed;
  417.                 if (jjMaskedPixel(chain.xPos, chain.yPos))
  418.                         chain.state = STATE::EXPLODE;
  419.         }
  420.         for (int i = 0; i < chain.counter - 1; ++i)
  421.                 jjDrawSpriteFromCurFrame(chain.xPos - chain.xSpeed * i, chain.yPos - chain.ySpeed * i, chain.curFrame, 0, SPRITE::TINTED, (i <= 4) ? 24 : 215);
  422.                
  423. }
  424.  
  425. void Chakram(jjOBJ@ ring) {
  426.         if (ring.state == STATE::START) ring.state = STATE::FLY;
  427.         jjPLAYER@ play = jjPlayers[ring.creatorID];
  428.         if (play.xPos < ring.xPos) ring.xAcc = ring.xAcc - .03;
  429.         else ring.xAcc = ring.xAcc + .03;
  430.         if (play.yPos < ring.yPos) ring.yAcc = ring.yAcc - .03;
  431.         else ring.yAcc = ring.yAcc + .03;
  432.         ring.xPos = ring.xPos + ring.xSpeed + ring.xAcc;
  433.         ring.yPos = ring.yPos + ring.ySpeed + ring.yAcc;
  434.         if (jjMaskedPixel(ring.xPos, ring.yPos))
  435.                 ring.state = STATE::EXPLODE;
  436.         if (ring.state == STATE::EXPLODE) {
  437.                 ring.behavior = BEHAVIOR::EXPLOSION2;
  438.         }
  439.         ring.frameID = (++ring.counter >> 1) % 9;
  440.         ring.determineCurFrame();
  441.         ring.draw();
  442.        
  443. }
  444.  
  445.  
  446.  
  447.  
  448.  
  449.  
  450.  
  451. void DisplayHealth(jjOBJ@ enemy, int yOffset = -30) {
  452.         int health = enemy.energy;
  453.         float xPos = enemy.xPos - (jjObjectPresets[enemy.eventID].energy/2);
  454.         float yPos = enemy.yPos + yOffset;
  455.         while (health > 0) {
  456.                 jjDrawSprite(xPos, yPos, ANIM::PICKUPS, 41, 0, 0, SPRITE::NORMAL, 0, 3); //heart
  457.                 xPos += 10;
  458.                 health -= 10;
  459.         }
  460. }
  461.  
  462.  
  463. void InvisibleCrab(jjOBJ@ enemy) {
  464.         if (enemy.state == STATE::KILL) enemy.deactivate(); //by using deactivate instead of delete, enemies will reappear when left alone, like if spawned by a generator, yet they won't regenerate while you still haven't left the spot where they died
  465.         else {
  466.                 enemy.behave(BEHAVIOR::CRAB, (enemy.justHit > 0));
  467.                 if (enemy.justHit == 0)
  468.                 jjDrawSpriteFromCurFrame(enemy.xPos, enemy.yPos, enemy.curFrame, enemy.direction, SPRITE::NEONGLOW, 1);
  469.                 DisplayHealth(enemy);
  470.         }
  471.         //jjDrawTile(enemy.xPos - 32, enemy.yPos - 32, 737, TILE::TOPLEFT);
  472.         //jjDrawTile(enemy.xPos + 16, enemy.yPos - 32, 737, TILE::TOPRIGHT);
  473.         //jjDrawTile(enemy.xPos - 32, enemy.yPos + 16, 737, TILE::BOTTOMLEFT);
  474.         //jjDrawTile(enemy.xPos + 16, enemy.yPos + 16, 737, TILE::BOTTOMRIGHT);
  475. }
  476.  
  477. void Turtle(jjOBJ@ enemy) {
  478.         switch (enemy.state) {
  479.                 case STATE::START:
  480.                         enemy.state = STATE::FALL;
  481.                         break;
  482.                 case STATE::FALL:
  483.                         if (jjMaskedPixel(enemy.xPos, enemy.yPos + enemy.ySpeed)) {
  484.                                 enemy.state = STATE::FADEIN;
  485.                                 enemy.yPos = enemy.yPos - 32;
  486.                                 enemy.putOnGround(true);
  487.                                 enemy.determineCurAnim(ANIM::TURTLE, 3);
  488.                                 enemy.frameID = 0;
  489.                                 enemy.counter = 0;
  490.                                 if (jjLocalPlayers[0].xPos < enemy.xPos) enemy.direction = -1;
  491.                                 else enemy.direction = 1;
  492.                         } else {
  493.                                 enemy.yPos = enemy.yPos + enemy.ySpeed;
  494.                                 enemy.ySpeed = enemy.ySpeed + .08;
  495.                         }
  496.                         break;
  497.                 case STATE::FADEIN:
  498.                         if (++enemy.counter > 5) {
  499.                                 enemy.counter = 0;
  500.                                 if (++enemy.frameID >= 6) {
  501.                                         enemy.determineCurAnim(ANIM::TURTLE, 1);
  502.                                         enemy.state = STATE::IDLE;
  503.                                         enemy.bulletHandling = HANDLING::HURTBYBULLET;
  504.                                 }
  505.                                 enemy.determineCurFrame();
  506.                         }
  507.                         break;
  508.                 case STATE::IDLE:
  509.                         if ((jjRandom() & 31) == 0) {
  510.                                 enemy.state = STATE::ATTACK;
  511.                                 enemy.determineCurAnim(ANIM::TURTLE, 0);
  512.                                 enemy.frameID = 0;
  513.                                 enemy.counter = 0;
  514.                                 jjSample(enemy.xPos, enemy.yPos, SOUND::SPAZSOUNDS_BURP);
  515.                         } else {
  516.                                 if (++enemy.counter > 4) {
  517.                                         enemy.counter = 0;
  518.                                         if (++enemy.frameID >= 11)
  519.                                                 enemy.frameID = 0;
  520.                                         enemy.determineCurFrame();
  521.                                 }
  522.                         }
  523.                         break;
  524.                 case STATE::ATTACK:
  525.                         if (++enemy.counter > 3) {
  526.                                 enemy.counter = 0;
  527.                                 if (++enemy.frameID >= 11) {
  528.                                         enemy.determineCurAnim(ANIM::TURTLE, 1);
  529.                                         enemy.state = STATE::IDLE;
  530.                                         enemy.frameID = 0;
  531.                                 } else if (enemy.frameID >= 3 && enemy.frameID <= 6) {
  532.                                         jjOBJ@ obj = jjObjects[jjAddObject(OBJECT::BULLET, enemy.xPos + 45 * enemy.direction, enemy.yPos - 10, enemy.objectID, CREATOR::OBJECT)];
  533.                                         obj.behavior = BEHAVIOR::BULLET;
  534.                                         obj.determineCurAnim(ANIM::AMMO, 14);
  535.                                         obj.killAnim = jjObjectPresets[OBJECT::BLASTERBULLET].killAnim;
  536.                                         obj.playerHandling = HANDLING::ENEMY;
  537.                                         obj.energy = 1;
  538.                                         obj.points = 5;
  539.                                         obj.lightType = LIGHT::POINT;
  540.                                         obj.xAcc = .2;
  541.                                         obj.xSpeed = 1.2;
  542.                                         int ys = (jjRandom() & 3);
  543.                                         obj.ySpeed = ys - 2;
  544.                                         obj.counterEnd = 25;
  545.                                 }
  546.                                 enemy.determineCurFrame();
  547.                         }
  548.                         break;
  549.                 case STATE::DEACTIVATE:
  550.                 case STATE::KILL:
  551.                         enemy.deactivate();
  552.                         break;
  553.         }
  554.         enemy.draw();
  555.         if (enemy.bulletHandling == HANDLING::HURTBYBULLET)
  556.                 DisplayHealth(enemy);
  557. }
  558.  
  559.  
  560. void UFO(jjOBJ@ enemy) {
  561.         if (enemy.state == STATE::KILL || enemy.state == STATE::DEACTIVATE) {
  562.                 enemy.deactivate();
  563.         } else { //nothing complicated enough to bother disabling STATE::START
  564.                 ++enemy.counter; //use counter instead of gameticks so different enemies will be in different places
  565.                 enemy.xPos = jjSin(enemy.counter*5)*120 + enemy.xOrg;
  566.                 enemy.yPos = jjSin(enemy.counter*15 + 256)*20 + enemy.yOrg;
  567.                 if (jjRandom() & 63 == 0) {
  568.                         jjOBJ@ obj = jjObjects[enemy.fireBullet(OBJECT::BULLET)];
  569.                         obj.behavior = BEHAVIOR::BULLET;
  570.                         obj.determineCurAnim(ANIM::FRUITPLAT, 1);
  571.                         obj.playerHandling = HANDLING::ENEMY;
  572.                         obj.energy = 1;
  573.                         obj.points = 5;
  574.                         obj.ySpeed = 6;
  575.                         obj.counterEnd = 100;
  576.                 }
  577.         }
  578.         if (enemy.justHit == 0)
  579.                 jjDrawSpriteFromCurFrame(enemy.xPos, enemy.yPos, enemy.curFrame, 0, SPRITE::PALSHIFT, 112);
  580.         else
  581.                 enemy.draw();
  582.         DisplayHealth(enemy);
  583. }
  584.  
  585. array<SOUND::Sample> BoingNoises = {SOUND::AMMO_BMP1, SOUND::AMMO_BMP2, SOUND::AMMO_BMP3, SOUND::AMMO_BMP4, SOUND::AMMO_BMP5, SOUND::AMMO_BMP6};
  586. void JumpyGuy(jjOBJ@ enemy) {
  587.         if (enemy.state == STATE::START) {
  588.                 jjSample(enemy.xPos, enemy.yPos, SOUND::STEAM_STEAM);
  589.                 enemy.state = STATE::FALL;
  590.                 enemy.xSpeed = (int(jjRandom() & 15) - 8) / 3;
  591.                 if (enemy.creatorType == CREATOR::LEVEL) {
  592.                         enemy.special = 208;
  593.                         for (int i = (jjRandom() & 7) + 6; i > 0; --i) {
  594.                                 jjOBJ@ obj = jjObjects[jjAddObject(OBJECT::STEAM, enemy.xPos + (jjRandom() & 63) - 32, enemy.yPos - (jjRandom() & 63), enemy.objectID)];
  595.                         }
  596.                 }
  597.         } else if (enemy.state == STATE::DEACTIVATE) {
  598.                 enemy.deactivate();
  599.         } else if (enemy.state == STATE::KILL) {
  600.                 enemy.grantPickup(jjLocalPlayers[0], 1);
  601.                 enemy.deactivate();
  602.         } else {
  603.                 if (jjMaskedPixel(enemy.xPos + enemy.xSpeed, enemy.yPos)) {
  604.                         enemy.xSpeed = -enemy.xSpeed;
  605.                 } else if (jjMaskedPixel(enemy.xPos, enemy.yPos + enemy.ySpeed)) {
  606.                         enemy.ySpeed = -enemy.ySpeed;
  607.                         jjSample(enemy.xPos, enemy.yPos, BoingNoises[jjRandom() % BoingNoises.length()], 48);
  608.                 }
  609.                 enemy.xPos = enemy.xPos + enemy.xSpeed;
  610.                 enemy.yPos = enemy.yPos + enemy.ySpeed;
  611.                 enemy.ySpeed = enemy.ySpeed + .3;
  612.                 if (enemy.ySpeed < -7) enemy.ySpeed = -7;
  613.                 if (enemy.justHit == 0)
  614.                         jjDrawSpriteFromCurFrame(enemy.xPos, enemy.yPos, enemy.curFrame, 0, SPRITE::PALSHIFT, enemy.special);
  615.                 else
  616.                         enemy.draw();
  617.                 //DisplayHealth(enemy);
  618.         }
  619. }
  620.  
  621. void KamikazBee(jjOBJ@ enemy) {
  622.         if (enemy.state == STATE::KILL || enemy.state == STATE::DEACTIVATE || jjTriggers[5]) {
  623.                 enemy.deactivate();
  624.         } else { //nothing complicated enough to bother disabling STATE::START
  625.                 if (enemy.var[0] == 0) { //not triggered yet
  626.                         enemy.counter += 10; //use counter instead of gameticks so different enemies will be in different places
  627.                         enemy.xPos = jjSin(enemy.counter)*24 + enemy.xOrg;
  628.                         enemy.yPos = jjCos(enemy.counter)*24 + enemy.yOrg;
  629.                         int playerID = enemy.findNearestPlayer(50000);
  630.                         if (playerID > -1) {
  631.                                 jjSample(enemy.xPos, enemy.yPos, SOUND::COMMON_DOWN);
  632.                                 jjPLAYER@ play = jjPlayers[playerID];
  633.                                 float angle = atan2(enemy.xPos - play.xPos, enemy.yPos - play.yPos);
  634.                                 enemy.var[0] = 1;
  635.                                 enemy.lightType = LIGHT::POINT;
  636.                                 enemy.counter = 40;
  637.                                 enemy.xSpeed = -8 * sin(angle);
  638.                                 enemy.ySpeed = -8 * cos(angle);
  639.                                 enemy.direction = enemy.xSpeed;
  640.                         } else {
  641.                                 enemy.direction = (((enemy.counter + 256) & 1023) < 512) ? 1 : -1;
  642.                                 /*if ((jjRandom() & 511) == 0 && enemy.var[1] == 0) {
  643.                                         enemy.var[1] = 1;
  644.                                         jjAddObject(OBJECT::BEEBOY, enemy.xPos, enemy.yPos, enemy.objectID);
  645.                                         jjSample(enemy.xPos, enemy.yPos, SOUND::BUMBEE_BEELOOP);
  646.                                 }*/
  647.                         }
  648.                 } else {
  649.                         if (enemy.counter == 0) { //slight delay before attacking
  650.                                 enemy.xPos = enemy.xPos + enemy.xSpeed;
  651.                                 enemy.yPos = enemy.yPos + enemy.ySpeed;
  652.                         }
  653.                         else --enemy.counter;
  654.                 }
  655.                 enemy.frameID = (jjGameTicks >> 1) & 3;
  656.                 enemy.determineCurFrame();
  657.         }
  658.         jjDrawSpriteFromCurFrame(enemy.xPos, enemy.yPos + jjSin(jjGameTicks << 4) * 3, enemy.curFrame, enemy.direction);
  659. }
  660.  
  661. void MiniBilsy(jjOBJ@ enemy) {
  662.         switch (enemy.state) {
  663.         case STATE::START:
  664.                 enemy.state = STATE::IDLE;
  665.                 enemy.putOnGround(true);
  666.                 enemy.determineCurAnim(ANIM::BILSBOSS, 4); //idle
  667.                 break;
  668.         case STATE::KILL:
  669.         case STATE::DEACTIVATE:
  670.                 enemy.deactivate();
  671.                 break;
  672.         case STATE::IDLE:
  673.                 enemy.frameID = (jjGameTicks >> 2) & 7;
  674.                 enemy.determineCurFrame();
  675.                 if (enemy.energy < 120) { //injured
  676.                         if (enemy.var[0] == 0) { jjSample(enemy.xPos, enemy.yPos, SOUND::BILSBOSS_THUNDER); enemy.var[0] = 1; }
  677.                         switch (jjRandom() & 127) {
  678.                                 case 0:
  679.                                         enemy.state = STATE::ATTACK;
  680.                                         enemy.determineCurAnim(ANIM::BILSBOSS, 0);
  681.                                         enemy.counter = 0;
  682.                                         break;
  683.                                 case 1: //teleport
  684.                                 case 2:
  685.                                 case 3:
  686.                                         enemy.state = STATE::FADEOUT;
  687.                                         enemy.determineCurAnim(ANIM::BILSBOSS, 2);
  688.                                         enemy.counter = 0;
  689.                                         if (jjDifficulty > 1) { //hard/turbo: invincible while transportalizing
  690.                                                 enemy.playerHandling = HANDLING::PARTICLE;
  691.                                                 enemy.bulletHandling = HANDLING::IGNOREBULLET;
  692.                                         } {
  693.                                                 jjPLAYER@ play = jjLocalPlayers[0];
  694.                                                 enemy.xAcc = play.xPos;
  695.                                                 enemy.yAcc = play.yPos;
  696.                                         }
  697.                                         break;
  698.                         }
  699.                 }
  700.                 break;
  701.         case STATE::FADEOUT:
  702.                 if (++enemy.counter < 68) {
  703.                         enemy.frameID = enemy.counter >> 2;
  704.                         enemy.determineCurFrame();
  705.                 } else {
  706.                         enemy.frameID = enemy.counter = 0;
  707.                         enemy.state = STATE::FADEIN;
  708.                         jjSample(enemy.xPos, enemy.yPos, SOUND::BILSBOSS_BILLAPPEAR);
  709.                         enemy.determineCurAnim(ANIM::BILSBOSS, 1);
  710.                         enemy.xPos = enemy.xAcc;
  711.                         enemy.yPos = enemy.yAcc - 32;
  712.                         enemy.direction = (jjLocalPlayers[0].xPos < enemy.xPos) ? -1 : 1;
  713.                         enemy.determineCurFrame();
  714.                         enemy.putOnGround(true);
  715.                         return;
  716.                 }
  717.                 break;
  718.         case STATE::FADEIN:
  719.                 if (++enemy.counter < 40) {
  720.                         enemy.frameID = enemy.counter >> 1;
  721.                         enemy.determineCurFrame();
  722.                 } else {
  723.                         enemy.frameID = enemy.counter = 0;
  724.                         enemy.determineCurAnim(ANIM::BILSBOSS, 4);
  725.                         enemy.state = STATE::IDLE;
  726.                         enemy.playerHandling = HANDLING::ENEMY;
  727.                         enemy.bulletHandling = HANDLING::HURTBYBULLET;
  728.                 }
  729.                 break;
  730.         case STATE::ATTACK:
  731.                 if (++enemy.counter < 36) {
  732.                         enemy.frameID = enemy.counter >> 1;
  733.                         enemy.determineCurFrame();
  734.                         if (enemy.counter == 32) {
  735.                                 jjObjects[enemy.fireBullet(OBJECT::TOASTERBULLET)].playerHandling = HANDLING::ENEMYBULLET;
  736.                         }
  737.                 } else {
  738.                         enemy.frameID = enemy.counter = 0;
  739.                         enemy.determineCurAnim(ANIM::BILSBOSS, 4);
  740.                         enemy.state = STATE::IDLE;
  741.                 }
  742.                 break;
  743.         }
  744.         enemy.draw();
  745.         DisplayHealth(enemy, -45);
  746. }
  747.  
  748.  
  749. void Stars(jjOBJ@ star) {
  750.         if (star.state == STATE::START) {
  751.                 star.behave(BEHAVIOR::BIRDFEATHER); //get some nice random speeds
  752.                 star.frameID = 0;
  753.                 star.determineCurFrame();
  754.                 star.xSpeed = star.xSpeed * 1.3; //faster x dimension
  755.         } else if (star.state == STATE::DEACTIVATE) {
  756.                 star.delete();
  757.         } else {
  758.                 star.xPos = star.xPos + star.xSpeed;
  759.                 star.yPos = star.yPos + star.ySpeed;
  760.                 uint8 size = uint(abs(star.xPos - star.xOrg) + abs(star.yPos - star.yOrg)) >> 4;
  761.                 if (size < 5) size = 5;
  762.                 jjDrawSpriteFromCurFrame(star.xPos, star.yPos, star.curFrame, 0, SPRITE::RESIZED, size, 3);
  763.         }
  764. }
  765.  
  766. void RockBarrier(jjOBJ@ rock) {
  767.         if (rock.energy < 100) rock.energy += 5;
  768.         if (rock.state == STATE::DEACTIVATE) rock.deactivate();
  769.         else if (rock.state == STATE::START) {
  770.                 rock.xPos = rock.xPos - 16;
  771.                 rock.yPos = rock.yPos + 16;
  772.                 rock.state = STATE::IDLE;
  773.         }
  774.         else if (rock.state == STATE::IDLE) {
  775.                 rock.beSolid();
  776.                 rock.draw();
  777.                 DisplayHealth(rock);
  778.         }
  779. }
  780. void RisingAmmo(jjOBJ@ obj) {
  781.         if (!jjMaskedPixel(obj.xPos, obj.yPos - 2))
  782.                 obj.yPos = obj.yPos - 2;
  783.         obj.xSpeed = obj.ySpeed = 0; //just in case
  784.         obj.behave(BEHAVIOR::PICKUP);
  785. }
  786. void TrackingAmmo(jjOBJ@ obj) {
  787.         if (obj.yPos < jjLocalPlayers[0].yPos)
  788.                 obj.yPos = obj.yPos + 1;
  789.         else
  790.                 obj.yPos = obj.yPos - 1;
  791.         obj.behave(BEHAVIOR::PICKUP);
  792. }
  793.  
  794.  
  795. void FallingSpikes(jjOBJ@ spike) {
  796.         if (spike.state == STATE::START) {
  797.                 spike.determineCurAnim(ANIM::AMMO, 3);
  798.                 spike.frameID = 3;
  799.                 spike.determineCurFrame();
  800.                 spike.state = STATE::FALL;
  801.                 spike.lightType = LIGHT::POINT;
  802.                 spike.energy = 1;
  803.                 spike.points = 5;
  804.                 spike.playerHandling = HANDLING::ENEMY;
  805.         }
  806.         else if (spike.state == STATE::KILL || spike.state == STATE::DEACTIVATE)
  807.                 spike.delete();
  808.         spike.yPos = spike.yPos + 5;
  809.         jjDrawTile(spike.xPos - 16, spike.yPos - 16, 752);
  810.         //spike.draw();
  811. }
  812.  
  813. array<float> xRockOffsets = {-40, -30, -41, 10};
  814. array<float> yRockOffsets = {2, 57, 112, 112};
  815. void StatueBoss(jjOBJ@ boss) {
  816.         uint8 eyecolor = 8;
  817.         if (boss.state == STATE::DELAYEDSTART || boss.state == STATE::EXPLODE) {
  818.                 if (++boss.counter < 140) {
  819.                         eyecolor = (boss.state == STATE::DELAYEDSTART) ? 70 : 56;
  820.                         boss.xPos = boss.xOrg + (jjRandom() & 3) - 2;
  821.                         boss.yPos = boss.yOrg + (jjRandom() & 3) - 2;
  822.                         if ((jjRandom() & 7) == 0) {
  823.                                 jjObjects[
  824.                                         jjAddObject(
  825.                                                 OBJECT::EXPLOSION,
  826.                                                 boss.xPos + (jjRandom() & 63) - 48,
  827.                                                 boss.yPos + (jjRandom() & 127) - 16,
  828.                                                 boss.objectID
  829.                                         )
  830.                                 ].determineCurAnim(ANIM::AMMO, 2);
  831.                                 jjSample(boss.xPos, boss.yPos, SOUND::COMMON_DAMPED1);
  832.                         }
  833.                 } else if (boss.counter >= 200) {
  834.                         if (boss.state == STATE::DELAYEDSTART) {
  835.                                 boss.counter = 0;
  836.                                 boss.state = STATE::WAKE;
  837.                                 boss.bulletHandling = HANDLING::DETECTBULLET;
  838.                                 boss.lightType = LIGHT::BRIGHT;
  839.                                 boss.light = 9;
  840.                                 jjMusicLoad("darkness.mod") || jjMusicLoad("boss intro.it") || jjMusicLoad("boss.j2b");
  841.                         } else {
  842.                                 for (int i = 0; i < 4; ++i) {
  843.                                         jjOBJ@ rock = jjObjects[boss.var[i]];
  844.                                         if (i < 2) rock.particlePixelExplosion(0);
  845.                                         rock.delete();
  846.                                 }
  847.                                 boss.delete();
  848.                         }
  849.                 }
  850.         } else {
  851.                 float yOffset = jjSin(boss.counter += 4) * 250;
  852.                 if (yOffset > 0)
  853.                         yOffset = 0;
  854.                 else {
  855.                         if (yOffset > -2)
  856.                                 jjSample(boss.xPos, boss.yPos, SOUND::COMMON_SPRING1, 63, 6000);
  857.                         if (boss.direction == 1) {
  858.                                 if (boss.xPos >= boss.xOrg + 560)
  859.                                         boss.direction = -1;
  860.                         } else {
  861.                                 if (boss.xPos <= boss.xOrg)
  862.                                         boss.direction = 1;
  863.                         }
  864.                         boss.xPos = boss.xPos + (jjRandom() & 3) * boss.direction;
  865.                         if (yOffset < -249) {
  866.                                 if (jjDifficulty < 2 || (jjRandom() & 3) > 0) {
  867.                                         jjAddObject(OBJECT::BULLET, (61 + (jjRandom() % 20)) * 32, 30*32, boss.objectID, CREATOR::OBJECT, FallingSpikes);
  868.                                         jjSample(boss.xPos, boss.yPos, SOUND::COMMON_SPLAT2);
  869.                                 } else
  870.                                         jjAddObject(OBJECT::STEAM, (61 + (jjRandom() % 20)) * 32, 31*32, boss.objectID);
  871.                         }
  872.                 }
  873.                 boss.yPos = boss.yOrg + yOffset;
  874.                 for (int i = 0; i < 4; ++i) {
  875.                         jjOBJ@ rock = jjObjects[boss.var[i]];
  876.                         rock.xPos = boss.xPos + xRockOffsets[i] * boss.direction;
  877.                         rock.yPos = boss.yPos + yRockOffsets[i];
  878.                         //rock.draw();
  879.                 }
  880.         }
  881.        
  882.         for (int x = 0; x < 4; ++x)
  883.                 for (int y = 0; y < 6; ++y) {
  884.                         if (boss.direction == 1)
  885.                                 jjDrawTile(boss.xPos - 66 + x*32, boss.yPos - 55 + y*32, 733 + x + y*10);
  886.                         else
  887.                                 jjDrawTile(boss.xPos - 62 + x*32, boss.yPos - 55 + y*32, 736 - x + y*10 + TILE::HFLIPPED);
  888.                 }
  889.        
  890.         if (boss.justHit > 0) eyecolor = 64;
  891.         jjDrawSpriteFromCurFrame(boss.xPos, boss.yPos, boss.curFrame, 0, SPRITE::PALSHIFT, eyecolor);
  892.         jjDrawSpriteFromCurFrame(boss.xPos + 20 * boss.direction, boss.yPos - 4, boss.curFrame - 7, 0, SPRITE::PALSHIFT, eyecolor);
  893. }
  894.  
  895.  
  896. void Acolyte(jjOBJ@ obj) {
  897.         if (obj.state == STATE::START) {
  898.                 if (jjTriggers[5]) { obj.delete(); return; }
  899.                 obj.direction = (obj.xPos > 134*32) ? 1 : -1;
  900.                 obj.state = STATE::WAIT;
  901.         } else if (obj.state == STATE::DEACTIVATE) {
  902.                 obj.deactivate();
  903.         } else if (obj.state == STATE::WAIT) {
  904.                 if (pausedForWatchingTheGoddess) obj.state = STATE::SPRING;
  905.                 else obj.draw();
  906.         } else if (obj.state == STATE::SPRING) {
  907.                 uint rand = jjRandom();
  908.                 if (obj.yPos == obj.yOrg && obj.xPos < 132*32) //first one, hasn't lifted up yet
  909.                         jjSamplePriority(SOUND::BILSBOSS_SCARY3);
  910.                 if (obj.counterEnd < 64) {
  911.                         if ((rand & 8) == 0) {
  912.                                 obj.yPos = obj.yPos - 1;
  913.                                 obj.light = (++obj.counterEnd) / 3;
  914.                                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame + (jjRandom() & 7), obj.direction, ((rand & 16) == 0) ? SPRITE::NORMAL : SPRITE::TRANSLUCENT);
  915.                         }
  916.                 } else {
  917.                         obj.state = STATE::FLOAT;
  918.                         obj.counterEnd = 0;
  919.                 }
  920.         } else if (obj.state == STATE::FLOAT) {
  921.                 uint rand = jjRandom();
  922.                 jjDrawSpriteFromCurFrame(obj.xPos + (rand & 7) - 4, obj.yPos + ((rand >> 3) & 7) - 4, obj.curFrame, obj.direction);
  923.                 if ((rand >> 6) & 3 == 0) {
  924.                         jjOBJ@ spark = jjObjects[jjAddObject(OBJECT::SHARD, obj.xPos, obj.yPos, obj.objectID, CREATOR::OBJECT, BEHAVIOR::BIRDFEATHER)];
  925.                         spark.determineCurAnim(ANIM::AMMO, 12);
  926.                         spark.lightType = LIGHT::POINT2;
  927.                 }
  928.                 if (elapsedPauseTime >= 1080 + ((int(obj.xOrg / 32) - 130) << 2)) {
  929.                         obj.particlePixelExplosion(1);
  930.                         obj.delete();
  931.                         jjSample(obj.xPos, obj.yPos, SOUND::JAZZSOUNDS_BALANCE, 63, 16000 + obj.xOrg);
  932.                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ELECTRIC1);
  933.                 }
  934.         }
  935. }
  936.  
  937. const float GODDESSSPEED = .12;
  938. const float GODDESSMAXSPEED = 5;
  939. const array<float> GoddessTargetsX = {137*32, 175*32, 159*32, 161*32, 189*32, 147*32};
  940. const array<float> GoddessTargetsY = {64*32,  63*32,  70*32,  47*32,  70*32,  63*32 };
  941. void Goddess(jjOBJ@ enemy) {
  942.         if (enemy.state == STATE::START) {
  943.                 if (jjTriggers[5]) { enemy.delete(); return; }
  944.                 enemy.state = STATE::DELAYEDSTART;
  945.                 enemy.xPos = enemy.xOrg + 17;
  946.                 enemy.yPos = enemy.yOrg - 5;
  947.         } else if (enemy.state == STATE::DEACTIVATE) {
  948.                 enemy.deactivate();
  949.         } else if (enemy.state == STATE::DELAYEDSTART) {
  950.                 if (pausedForWatchingTheGoddess && elapsedPauseTime == 1120) {
  951.                         enemy.lightType = LIGHT::RING2;
  952.                         enemy.light = 80;
  953.                         jjLocalPlayers[0].activateBoss();
  954.                         jjLocalPlayers[0].boss = enemy.objectID;
  955.                         enemy.playerHandling = HANDLING::SPECIAL;
  956.                         enemy.bulletHandling = HANDLING::DETECTBULLET;
  957.                         enemy.deactivates = false;
  958.                         enemy.var[0] = 7;
  959.                 } else if (enemy.light > 0) {
  960.                         if (--enemy.light == 30)
  961.                                 enemy.state = STATE::WAKE;
  962.                 }
  963.         } else if (enemy.state == STATE::EXPLODE) {
  964.                 if (++enemy.light == -80) {
  965.                         paused = false;
  966.                         jjTriggers[5] = true;
  967.                         jjLocalPlayers[0].cameraUnfreeze();
  968.                         enemy.delete();
  969.                         jjSample(enemy.xPos, enemy.yPos, SOUND::AMMO_MISSILE, 63, 16000);
  970.                         jjAddParticlePixelExplosion(enemy.xPos, enemy.yPos + 32, jjObjectPresets[OBJECT::BILSY].curFrame, 0, 0);
  971.                         jjObjects[jjAddObject(OBJECT::BOUNCERAMMO3, enemy.xPos, enemy.yPos + 32, enemy.objectID, CREATOR::OBJECT, TrackingAmmo)].deactivates = false;
  972.                         jjMusicLoad(MUSICFILENAME);
  973.                 } else {
  974.                         jjPARTICLE@ part = jjAddParticle(PARTICLE::STRING);
  975.                         if ((jjGameTicks & 3) == 0)jjSample(enemy.xPos, enemy.yPos, SOUND::COMMON_PISTOL1);
  976.                         if (part !is null) {
  977.                                 part.string.text = "|O";
  978.                                 part.xPos = enemy.xPos;
  979.                                 part.yPos = enemy.yPos;
  980.                                 part.xSpeed = float(jjRandom() & 31) - 16;
  981.                                 part.ySpeed = float(jjRandom() & 31) - 16;
  982.                         }
  983.                 }
  984.         } else {
  985.                 if (enemy.justHit > 0) {
  986.                         --enemy.justHit;
  987.                         enemy.lightType = LIGHT::BRIGHT;
  988.                         if (enemy.special <= 0) { //dead
  989.                                 paused = true;
  990.                                 enemy.state = STATE::EXPLODE;
  991.                                 enemy.bulletHandling = HANDLING::IGNOREBULLET; //for what it's worth
  992.                                 enemy.playerHandling = HANDLING::PARTICLE;
  993.                                 jjLocalPlayers[0].activateBoss(false);
  994.                                 jjLocalPlayers[0].boss = 0;
  995.                                 jjLocalPlayers[0].cameraFreeze(enemy.xPos, enemy.yPos, true, false);
  996.                         } else {
  997.                                 jjOBJ@ spark = jjObjects[jjAddObject(OBJECT::SHARD, enemy.xPos, enemy.yPos, enemy.objectID, CREATOR::OBJECT, BEHAVIOR::BIRDFEATHER)];
  998.                                 spark.determineCurAnim(ANIM::AMMO, 7);
  999.                                 spark.lightType = LIGHT::POINT;
  1000.                         }
  1001.                 }
  1002.                 else {
  1003.                         enemy.lightType = LIGHT::RING2;
  1004.                         if (enemy.special < 4800) {
  1005.                                 enemy.special += jjDifficulty;
  1006.                                 enemy.energy = (enemy.special / 80);
  1007.                         }
  1008.                 }
  1009.                 if ((jjGameTicks & 511) == 0) //new choice of target sometimes
  1010.                         enemy.var[0] = (jjRandom()) & 7;
  1011.                 else if (jjDifficulty > 1 && (jjRandom() & 511) == 0) {
  1012.                         jjAddObject(OBJECT::BEEBOY, enemy.xPos, enemy.yPos - 32, enemy.objectID);
  1013.                         jjSample(enemy.xPos, enemy.yPos, SOUND::BUMBEE_BEELOOP);
  1014.                 }
  1015.                 int targetID = enemy.var[0];
  1016.                 float target = (targetID >= 6) ? jjLocalPlayers[0].xPos : GoddessTargetsX[targetID];
  1017.                 if (enemy.xPos > target) {
  1018.                         enemy.xSpeed = enemy.xSpeed - GODDESSSPEED;
  1019.                         if (enemy.xSpeed < -GODDESSMAXSPEED) enemy.xSpeed = -GODDESSMAXSPEED;
  1020.                 } else {
  1021.                         enemy.xSpeed = enemy.xSpeed + GODDESSSPEED;
  1022.                         if (enemy.xSpeed > GODDESSMAXSPEED) enemy.xSpeed = GODDESSMAXSPEED;
  1023.                 }
  1024.                 enemy.direction = enemy.xSpeed;
  1025.                 target = (targetID >= 6) ? jjLocalPlayers[0].yPos : GoddessTargetsY[targetID];
  1026.                 if (enemy.yPos > target) {
  1027.                         enemy.ySpeed = enemy.ySpeed - GODDESSSPEED;
  1028.                         if (enemy.ySpeed < -GODDESSMAXSPEED) enemy.ySpeed = -GODDESSMAXSPEED;
  1029.                 } else {
  1030.                         enemy.ySpeed = enemy.ySpeed + GODDESSSPEED;
  1031.                         if (enemy.ySpeed > GODDESSMAXSPEED) enemy.ySpeed = GODDESSMAXSPEED;
  1032.                 }
  1033.                 enemy.xPos = enemy.xPos + enemy.xSpeed;
  1034.                 enemy.yPos = enemy.yPos + enemy.ySpeed;
  1035.         }
  1036.         for (int x = 0; x < 2; ++x)
  1037.                 for (int y = 0; y < 4; ++y)
  1038.                         jjDrawTile(enemy.xPos - 29 + x*32, enemy.yPos - 41 + y*32, 228 + x + y*10, TILE::ALLQUADRANTS, 5);
  1039.         //enemy.draw();
  1040. }
  1041.  
  1042.  
  1043. void BollySpikeBall(jjOBJ@ obj) {
  1044.         obj.energy = 100;
  1045.         if (obj.doesHurt == 2) {
  1046.                 obj.behave(BEHAVIOR::FLICKERGEM, false);
  1047.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, (obj.playerHandling == HANDLING::PARTICLE) ? SPRITE::TRANSLUCENT : SPRITE::NORMAL);
  1048.         } else {
  1049.                 if (obj.state == STATE::START) {
  1050.                         obj.killAnim = obj.determineCurAnim(ANIM::SONCSHIP, 4, false);
  1051.                         obj.determineCurAnim(ANIM::SONCSHIP, 5);
  1052.                         obj.determineCurFrame();
  1053.                         obj.state = STATE::ROTATE;
  1054.                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_METALHIT);
  1055.                 } else if (obj.state == STATE::DEACTIVATE) {
  1056.                         obj.delete();
  1057.                 } else if (obj.state == STATE::KILL) {
  1058.                         obj.behavior = BEHAVIOR::EXPLOSION2;
  1059.                         return;
  1060.                 }
  1061.                 obj.counter += 4;
  1062.                 jjOBJ@ bolly = jjObjects[obj.creatorID];
  1063.                 if (obj.doesHurt == 1) {
  1064.                         ++obj.special;
  1065.                         obj.xPos = obj.xOrg + jjSin(obj.counter) * obj.special;
  1066.                         obj.yPos = obj.yOrg + jjCos(obj.counter) * obj.special;
  1067.                 } else {
  1068.                         obj.xPos = bolly.xPos - 11*bolly.direction + jjSin(obj.counter) * obj.special;
  1069.                         obj.yPos = bolly.yPos + 11 + jjCos(obj.counter) * obj.special;
  1070.                 obj.justHit = bolly.justHit; //not sure how I feel about this effect
  1071.                 }
  1072.                 //jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame);
  1073.                 obj.draw();
  1074.         }
  1075. }
  1076. void BollyBird(jjOBJ@ obj) {
  1077.         if (obj.state == STATE::START) {
  1078.                 obj.determineCurAnim(ANIM::SPAZ2, 2);
  1079.                 obj.state = STATE::FLY;
  1080.         } else if (obj.state == STATE::DEACTIVATE || obj.state == STATE::KILL) {
  1081.                 obj.delete();
  1082.         } else {
  1083.                 obj.xPos = obj.xPos + obj.xSpeed;
  1084.                 obj.yPos = obj.yPos + obj.ySpeed;
  1085.                 obj.frameID = (++obj.counter >> 2) & 7;
  1086.                 obj.determineCurFrame();
  1087.                 obj.draw();
  1088.                 if ((jjRandom() & 31) == 0) {
  1089.                         jjOBJ@ feather = jjObjects[jjAddObject(OBJECT::SHARD, obj.xPos, obj.yPos, obj.creatorID)];
  1090.                         feather.determineCurAnim(ANIM::BIRD, 6);
  1091.                         feather.behavior = BEHAVIOR::BIRDFEATHER;
  1092.                         feather.playerHandling = HANDLING::ENEMY;
  1093.                         feather.energy = 1;
  1094.                         feather.lightType = LIGHT::POINT2;
  1095.                         feather.light = 1;
  1096.                         jjSample(feather.xPos, feather.yPos, SOUND::COMMON_BIRDFLY);
  1097.                 }
  1098.         }
  1099. }
  1100. void BollyTargetMissile(jjOBJ@ obj) {
  1101.         if (obj.state == STATE::START) {
  1102.                 obj.ySpeed = 6.6;
  1103.                 obj.xSpeed = jjObjects[obj.creatorID].direction;
  1104.                 obj.var[0] = 768;
  1105.                 obj.playerHandling = HANDLING::ENEMYBULLET;
  1106.                 obj.animSpeed = 2;
  1107.                 obj.determineCurAnim(ANIM::BILSBOSS, 3);
  1108.                 obj.killAnim = obj.determineCurAnim(ANIM::AMMO, 7, false);
  1109.                 obj.lightType = LIGHT::BRIGHT;
  1110.                 obj.light = 21;
  1111.                 obj.deactivates = false;
  1112.                 jjSample(obj.xPos, obj.yPos, SOUND::BILSBOSS_FIRE);
  1113.         } else if (obj.doesHurt == 0) {
  1114.                 if (obj.yPos < -200) {
  1115.                         obj.doesHurt = 1;
  1116.                         obj.xPos = jjLocalPlayers[0].xPos;
  1117.                         jjSamplePriority(SOUND::SONCSHIP_TARGETLOCK);
  1118.                         obj.xSpeed = 0;
  1119.                         obj.var[0] = 256;
  1120.                         obj.ySpeed = 0;
  1121.                 } else
  1122.                         obj.ySpeed = obj.ySpeed - 0.15;
  1123.         } else {
  1124.                 jjOBJ@ bolly = jjObjects[obj.creatorID];
  1125.                 if (abs(bolly.yPos - obj.yPos) < 10 && abs(bolly.xPos - obj.xPos) < 30) {
  1126.                         obj.behavior = BEHAVIOR::EXPLOSION2;
  1127.                         bolly.energy = 50; //100-50
  1128.                         jjSamplePriority(SOUND::AMMO_BOEM1);
  1129.                 } else {
  1130.                         if (obj.ySpeed < 8.8) obj.ySpeed = obj.ySpeed + 0.15;
  1131.                         jjDrawSprite(obj.xPos, 10*32, ANIM::SONCSHIP, 7, 0, 0);
  1132.                 }
  1133.         }
  1134.         if (obj.playerHandling != HANDLING::EXPLOSION) { //ded
  1135.                 obj.frameID = (jjGameTicks >> 2) % 3;
  1136.                 obj.determineCurFrame();
  1137.         }
  1138.         obj.behave(BEHAVIOR::BOLLYBULLET, true);
  1139. }
  1140. void BollyBouncingLaser(jjOBJ@ obj) {
  1141.         if (obj.state == STATE::START) {
  1142.                 obj.determineCurAnim(ANIM::AMMO, 2);
  1143.                 obj.determineCurFrame();
  1144.                 obj.lightType = LIGHT::POINT2;
  1145.                 obj.playerHandling = (obj.scriptedCollisions) ? HANDLING::SPECIAL : HANDLING::ENEMYBULLET;
  1146.                 obj.animSpeed = 2; //let's be dangerous
  1147.                 obj.state = STATE::FLY;
  1148.         } else if (obj.state == STATE::FLY) { //same stuff as BallBullet, really
  1149.                 if (jjMaskedPixel(obj.xPos + obj.xSpeed, obj.yPos)) {
  1150.                         obj.xSpeed = -obj.xSpeed;
  1151.                         ++obj.counterEnd;
  1152.                 } else if (obj.yPos < 0 || jjMaskedPixel(obj.xPos, obj.yPos + obj.ySpeed)) {
  1153.                         obj.ySpeed = -obj.ySpeed;
  1154.                         ++obj.counterEnd;
  1155.                 }
  1156.                 if (obj.counterEnd == 4) {
  1157.                         obj.delete();
  1158.                         return;
  1159.                 }
  1160.                 obj.xPos = obj.xPos + obj.xSpeed;
  1161.                 obj.yPos = obj.yPos + obj.ySpeed;
  1162.                 if (obj.counterEnd > 4) obj.draw();
  1163.                 if (obj.special > 0) { //do this after moving
  1164.                         jjOBJ@ laser = jjObjects[jjAddObject(OBJECT::BULLET, obj.xOrg, obj.yOrg, obj.creatorID)];
  1165.                         laser.xSpeed = obj.xSpeed;
  1166.                         laser.ySpeed = obj.ySpeed;
  1167.                         laser.behavior = BollyBouncingLaser;
  1168.                         laser.special = obj.special - 1;
  1169.                         laser.counterEnd = obj.counterEnd;
  1170.                         laser.scriptedCollisions = obj.scriptedCollisions;
  1171.                         obj.special = 0;
  1172.                 }
  1173.         } else
  1174.                 obj.delete();
  1175. }
  1176. void BollyMovingRockPlatform(jjOBJ@ obj) {
  1177.         if (obj.state == STATE::DEACTIVATE) obj.delete();
  1178.         obj.xPos = obj.xPos + obj.xSpeed;
  1179.         if (obj.beSolid() != 0) {
  1180.                 jjPARTICLE@ part = jjAddParticle(PARTICLE::ICETRAIL);
  1181.                 if (part !is null) {
  1182.                         part.xPos = obj.xPos;
  1183.                         part.yPos = obj.yPos;
  1184.                 }
  1185.         }
  1186.         obj.draw();
  1187. }
  1188.  
  1189.  
  1190. void DestroyAllBollyProjectiles(uint16 bollyID) {
  1191.         for (int i = 1; i < jjObjectCount; ++i) {
  1192.                 jjOBJ@ obj = jjObjects[i];
  1193.                 if (obj.isActive){
  1194.                         if (obj.behavior == BollySpikeBall) {
  1195.                                 obj.playerHandling = HANDLING::PARTICLE;
  1196.                                 obj.ySpeed = 1;
  1197.                                 obj.doesHurt = 2;
  1198.                                 obj.counter = 35;
  1199.                                 obj.state = STATE::FLOAT;
  1200.                                 obj.lightType = LIGHT::NONE;
  1201.                         } else if (obj.creatorID == bollyID && obj.eventID != OBJECT::BOLLY) {
  1202.                                 if (obj.eventID == OBJECT::BIGROCK) obj.clearPlatform();
  1203.                                 obj.behavior = BEHAVIOR::EXPLOSION2; //that might work
  1204.                         }
  1205.                 }
  1206.         }
  1207.         if (jjDifficulty <= 0) //this is only ever called between Bolly phases, so good opportunity to heal players up
  1208.                 for (int i = 0; i < jjLocalPlayerCount; ++i)
  1209.                         jjLocalPlayers[i].health = jjMaxHealth;
  1210. }
  1211. void BollyTop(jjOBJ@ obj) {
  1212.         //just sit there and be controlled by DrawAndManageBolly and the Bolly# behaviors. does have some code of its own, but all in onObjectHit.
  1213. }
  1214. void DrawAndManageBolly(jjOBJ@ boss) {
  1215.         jjOBJ@ top = jjObjects[boss.special];
  1216.         boss.justHit = top.justHit;
  1217.         if (top.energy < 50) { //been hurt
  1218.                 boss.age -= (50 - top.energy);
  1219.                 top.energy = 50;
  1220.         }
  1221.         boss.energy = (boss.age + 9) / 10;
  1222.         if (boss.age <= 0 && boss.state != STATE::DONE && boss.state != STATE::START) {
  1223.                 boss.counter = 0;
  1224.                 boss.state = STATE::DONE;
  1225.                 boss.playerHandling = HANDLING::EXPLOSION;
  1226.                 top.playerHandling = HANDLING::EXPLOSION;
  1227.         } else if (boss.behavior != Bolly1 && (jjGameTicks & 7) == 0) { //start sparking after taking a little damage,
  1228.                 jjPARTICLE@ spark = jjAddParticle(PARTICLE::SPARK);     //to provide an excuse for that big hole in the sprite
  1229.                 if (spark !is null) {
  1230.                         spark.xPos = boss.xPos - 11 * boss.direction;
  1231.                         spark.yPos = boss.yPos + 11;
  1232.                         uint rand = jjRandom();
  1233.                         spark.xSpeed = (rand & 0xFFFF) / 32768.f - 1;
  1234.                         spark.ySpeed = (rand >> 16) / 32768.f - 1;
  1235.                 }
  1236.         }
  1237.         boss.frameID = (jjGameTicks >> 1) & 7;
  1238.         boss.determineCurFrame();
  1239.         SPRITE::Mode mode = SPRITE::NORMAL;
  1240.         if (boss.justHit > 0) mode = SPRITE::SINGLECOLOR;
  1241.         else if (boss.playerHandling == HANDLING::EXPLOSION) mode = SPRITE::TRANSLUCENT;
  1242.         jjDrawSpriteFromCurFrame(boss.xPos, boss.yPos, boss.curFrame, boss.direction, mode, 15, 5);
  1243.         //jjDrawString(boss.xPos - 10, boss.yPos - 50, "" + boss.age + ", " + boss.energy);
  1244.         top.xPos = boss.xPos;
  1245.         top.yPos = boss.yPos;
  1246.         top.direction = boss.direction;
  1247.         top.frameID = (jjGameTicks >> 3) % 6;
  1248.         top.determineCurFrame();
  1249.         jjDrawSpriteFromCurFrame(top.xPos, top.yPos, top.curFrame, top.direction, mode, 15, 5);
  1250.         if ((jjGameTicks & 63) == 0)
  1251.                 jjObjects[jjAddObject(OBJECT::EXPLOSION, boss.xPos - 50 * boss.direction, boss.yPos + 12, boss.objectID)].determineCurAnim(ANIM::SONCSHIP, 4);
  1252. }
  1253. void Bolly1(jjOBJ@ boss) {
  1254.         switch (boss.state) {
  1255.                 case STATE::START:
  1256.                         if (boss.special == 0) {
  1257.                                 boss.special = jjAddObject(OBJECT::BOLLY, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollyTop);
  1258.                                 jjOBJ@ top = jjObjects[boss.special];
  1259.                                 top.determineCurAnim(ANIM::SONCSHIP, 3);
  1260.                                 top.playerHandling = HANDLING::SPECIAL;
  1261.                                 top.bulletHandling = HANDLING::DETECTBULLET;
  1262.                                 top.scriptedCollisions = true;
  1263.                         }
  1264.                         if (boss.yPos < 5*32)
  1265.                                 boss.yPos = boss.yPos + 1;
  1266.                         else {
  1267.                                 jjLocalPlayers[0].activateBoss();
  1268.                                 boss.state = STATE::DELAYEDSTART;
  1269.                                 boss.yOrg = boss.yPos;
  1270.                                 boss.xOrg = boss.xPos;
  1271.                                 boss.counterEnd = 0;
  1272.                                 jjMusicLoad("sonicdrm.s3m") || jjMusicLoad("boss.j2b");
  1273.                         }
  1274.                         boss.direction = (boss.xPos < jjLocalPlayers[0].xPos) ? 1 : -1;
  1275.                         break;
  1276.                 case STATE::DELAYEDSTART:
  1277.                         boss.yPos = jjSin(jjGameTicks << 2) * 5 + boss.yOrg;
  1278.                         if ((++boss.counterEnd & 15) == 7) {
  1279.                                 jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1280.                                 spikeball.playerHandling = HANDLING::ENEMY;
  1281.                                 spikeball.bulletHandling = HANDLING::DESTROYBULLET;
  1282.                                 spikeball.special = boss.counterEnd - 6;
  1283.                                 spikeball.counter = 512;
  1284.                                 spikeball.lightType = LIGHT::POINT2;
  1285.                         } else if (boss.counterEnd == 200)
  1286.                                 boss.state = STATE::FLOAT;
  1287.                         break;
  1288.                 case STATE::FLOAT:
  1289.                         boss.yPos = jjSin(jjGameTicks << 3) * 5 + boss.yOrg;
  1290.                         if (boss.direction == -1) {
  1291.                                 boss.xPos = boss.xPos - 1.3;
  1292.                                 if (boss.xPos < 159*32) boss.direction = 1;
  1293.                         } else {
  1294.                                 boss.xPos = boss.xPos + 1.3;
  1295.                                 if (boss.xPos > 171*32) boss.direction = -1;
  1296.                         }
  1297.                         break;
  1298.                 case STATE::DONE:
  1299.                         if (++boss.counter == 1) {
  1300.                                 DestroyAllBollyProjectiles(boss.objectID);
  1301.                         } else {
  1302.                                 boss.xPos = boss.xPos + (jjRandom() & 7) - 3.5;
  1303.                                 boss.yPos = boss.yPos + (jjRandom() & 7) - 3.5;
  1304.                                 if ((boss.counter & 3) == 0) {
  1305.                                         jjOBJ@ expl = jjObjects[jjAddObject(OBJECT::EXPLOSION, (157 + (jjRandom() & 15)) * 32, (jjRandom() & 7) * 32, boss.objectID)];
  1306.                                         expl.determineCurAnim(ANIM::AMMO, 3);
  1307.                                         expl.lightType = LIGHT::BRIGHT;
  1308.                                         expl.light = 9;
  1309.                                         jjSample(expl.xPos, expl.yPos, SOUND::AMMO_BOEM1);
  1310.                                 }
  1311.                                 if (boss.counter == 350) {
  1312.                                         jjLayerHasTiles[5] = false;
  1313.                                         jjLayerHasTiles[6] = true;
  1314.                                         jjLayerXSpeed[7] = 0;
  1315.                                         jjLayerYSpeed[7] = 0;
  1316.                                         jjLayerYOffset[7] = 7*32;
  1317.                                         boss.age = 1;
  1318.                                         boss.state = STATE::START;
  1319.                                         boss.behavior = Bolly2;
  1320.                                         jjObjectPresets[OBJECT::BOLLY].energy = BOLLYHEALTH2 / 10;
  1321.                                         for (uint16 x = 156; x < 173; ++x)
  1322.                                                 for (uint16 y = 0; y < 10; ++y)
  1323.                                                         jjAddParticleTileExplosion(x, y, jjTileGet(5, x, y), false);
  1324.                                         jjSamplePriority(SOUND::COMMON_DAMPED1);
  1325.                                         jjTileSet(4, 156, 0, 420 | TILE::VFLIPPED);
  1326.                                         jjTileSet(4, 157, 0, 421 | TILE::VFLIPPED);
  1327.                                         jjTileSet(4, 158, 0, 420 | TILE::VFLIPPED);
  1328.                                         jjTileSet(4, 159, 0, 421 | TILE::VFLIPPED);
  1329.                                         for (int i = 0; i < jjLocalPlayerCount; ++i)
  1330.                                                 if (jjLocalPlayers[i].yPos < 36) jjLocalPlayers[i].yPos = 36;
  1331.                                 }
  1332.                         }
  1333.                         break;
  1334.         }
  1335.         DrawAndManageBolly(boss);
  1336. }
  1337. array<uint16> ColumnsToBlowAway = {154, 155, 174, 175};
  1338. void Bolly2(jjOBJ@ boss) {
  1339.         switch (boss.state) {
  1340.                 case STATE::START: {
  1341.                         bool xMov = true, yMov = true;
  1342.                         if (boss.xPos < boss.xOrg - 1) { boss.xPos = boss.xPos + 1; boss.direction = 1; }
  1343.                         else if (boss.xPos > boss.xOrg + 1) { boss.xPos = boss.xPos - 1; boss.direction = -1; }
  1344.                         else xMov = false;
  1345.                         if (boss.yPos < boss.yOrg - 1) boss.yPos = boss.yPos + 1;
  1346.                         else if (boss.yPos > boss.yOrg + 1) boss.yPos = boss.yPos - 1;
  1347.                         else yMov = false;
  1348.                         if (boss.age < BOLLYHEALTH2) {
  1349.                                 boss.age += 2;
  1350.                                 jjSample(boss.xPos, boss.yPos, SOUND::COMMON_MONITOR, boss.age & 63);
  1351.                         }
  1352.                         else if (!xMov && !yMov) {
  1353.                                 boss.state = STATE::FLOAT;
  1354.                                 boss.counter = 0;
  1355.                                 boss.playerHandling = HANDLING::ENEMY;
  1356.                                 jjObjects[boss.special].playerHandling = HANDLING::SPECIAL;
  1357.                                 jjSamplePriority(SOUND::SONCSHIP_MISSILE2);
  1358.                         }
  1359.                         break; }
  1360.                 case STATE::FLOAT:
  1361.                         boss.yPos = jjSin(jjGameTicks << 3) * 5 + boss.yOrg;
  1362.                         if (boss.direction == -1) {
  1363.                                 boss.xPos = boss.xPos - 1.3;
  1364.                                 if (boss.xPos < 159*32) boss.direction = 1;
  1365.                         } else {
  1366.                                 boss.xPos = boss.xPos + 1.3;
  1367.                                 if (boss.xPos > 171*32) boss.direction = -1;
  1368.                         }
  1369.                         if ((++boss.counter & 255) == 30) {
  1370.                                 uint rand = jjRandom();
  1371.                                 for (int i = 0; i < 1024; i += 256) if ((rand & 7) != 0) {
  1372.                                         jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1373.                                         spikeball.playerHandling = HANDLING::ENEMY;
  1374.                                         spikeball.bulletHandling = HANDLING::DESTROYBULLET;
  1375.                                         spikeball.doesHurt = 1;
  1376.                                         spikeball.counter = i;
  1377.                                         spikeball.lightType = LIGHT::POINT2;
  1378.                                         rand >>= 3;
  1379.                                 }
  1380.                         } else if (jjDifficulty >= 2 && (jjRandom() & 127) == 13) {
  1381.                                 jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1382.                                 spikeball.playerHandling = HANDLING::ENEMY;
  1383.                                 spikeball.bulletHandling = HANDLING::DESTROYBULLET;
  1384.                                 spikeball.ySpeed = 1;
  1385.                                 spikeball.doesHurt = 2;
  1386.                                 spikeball.counter = 80;
  1387.                                 spikeball.state = STATE::FLOAT;
  1388.                                 spikeball.lightType = LIGHT::POINT2;
  1389.                         }
  1390.                         break;
  1391.                 case STATE::DONE:
  1392.                         if (++boss.counter == 1) {
  1393.                                 DestroyAllBollyProjectiles(boss.objectID);
  1394.                         } else {
  1395.                                 boss.xPos = boss.xPos + 1 * boss.direction;
  1396.                                 boss.yPos = boss.yPos + 1.5;
  1397.                                 if ((boss.counter & 3) == 0) {
  1398.                                         jjOBJ@ expl = jjObjects[jjAddObject(OBJECT::EXPLOSION, boss.xPos + (jjRandom() & 31) - 16, boss.yPos + (jjRandom() & 31) - 16, boss.objectID)];
  1399.                                         expl.determineCurAnim(ANIM::AMMO, 3);
  1400.                                         expl.lightType = LIGHT::BRIGHT;
  1401.                                         expl.light = 9;
  1402.                                         jjSample(expl.xPos, expl.yPos, SOUND::AMMO_BOEM1);
  1403.                                 }
  1404.                                 if (boss.counter == 400) {
  1405.                                         jjLayerHasTiles[4] = jjLayerHasTiles[3] = false;
  1406.                                         jjTriggers[6] = scrolly = true;
  1407.                                         jjMusicLoad("GYRATION.MOD") || jjMusicLoad("neurocycle.mod") || jjMusicLoad("challenger.s3m") || jjMusicLoad("fastrack.j2b");
  1408.                                         jjTileSet(4, 156, 0, 0); //get rid of the spikes
  1409.                                         jjTileSet(4, 157, 0, 0);
  1410.                                         jjTileSet(4, 158, 0, 0);
  1411.                                         jjTileSet(4, 159, 0, 0);
  1412.                                         boss.age = 1;
  1413.                                         boss.counter = 0;
  1414.                                         boss.state = STATE::START;
  1415.                                         boss.behavior = Bolly3;
  1416.                                         boss.light = 1;
  1417.                                         boss.xOrg = boss.xAcc = 140*32;
  1418.                                         boss.yOrg = boss.yAcc = 7*32;
  1419.                                         jjObjectPresets[OBJECT::BOLLY].energy = BOLLYHEALTH3 / 10;
  1420.                                         jjSamplePriority(SOUND::COMMON_DAMPED1);
  1421.                                         for (uint16 y = 0; y < 10; ++y) {
  1422.                                                 for (uint16 x = 0; x < 4; ++x) {
  1423.                                                         uint16 tile = jjTileGet(3, ColumnsToBlowAway[x], y);
  1424.                                                         for (uint8 quadrant = 0; quadrant < 4; ++quadrant) {
  1425.                                                                 jjPARTICLE@ particle = jjAddParticle(PARTICLE::TILE);
  1426.                                                                 if (particle !is null) {
  1427.                                                                         particle.xPos = (ColumnsToBlowAway[x]*32) + (quadrant & 1) * 16;
  1428.                                                                         particle.yPos = y*32 + (quadrant & 2) * 16;
  1429.                                                                         particle.tile.tileID = tile;
  1430.                                                                         particle.tile.quadrant = quadrant;
  1431.                                                                         particle.ySpeed = (jjRandom() & 7) - 3.5;
  1432.                                                                         particle.xSpeed = jjRandom() & 7;
  1433.                                                                 }
  1434.                                                         }
  1435.                                                 }
  1436.                                         }
  1437.                                 }
  1438.                         }
  1439.                         break;
  1440.         }
  1441.         DrawAndManageBolly(boss);
  1442. }
  1443. array<STATE::State> PossibleScrollyStates = {STATE::FADEIN, STATE::FADEOUT, STATE::BOUNCE, STATE::ATTACK, STATE::FLOATFALL, STATE::ROTATE};
  1444. float BollyAngle = -1;
  1445. void Bolly3(jjOBJ@ boss) {
  1446.         //pos: where sprite is drawn to and collisions happen with
  1447.         //org: current logical position, which pos only approximates
  1448.         //acc: current target, which org tries to reach
  1449.         //speed: how acc tries to reach org
  1450.         if (boss.state != STATE::DONE) {
  1451.                 if (boss.state != STATE::START) {
  1452.                         if (++boss.counter == 300) {
  1453.                                 boss.counter = 0;
  1454.                                 boss.xSpeed = 3;
  1455.                                 boss.ySpeed = 2;
  1456.                                 boss.xAcc = 157*32;
  1457.                                 boss.lightType = LIGHT::NONE;
  1458.                                 STATE::State nuState = PossibleScrollyStates[jjRandom() % PossibleScrollyStates.length()];
  1459.                                 switch (nuState) {
  1460.                                         case STATE::FADEOUT:
  1461.                                                 if (boss.state == STATE::FADEOUT) boss.counter = 299; //try again
  1462.                                                 else {
  1463.                                                         boss.xAcc = 178*32;
  1464.                                                         boss.xSpeed = 6;
  1465.                                                         boss.ySpeed = 1;
  1466.                                                 }
  1467.                                                 break;
  1468.                                         case STATE::FADEIN:
  1469.                                                 break;
  1470.                                         case STATE::BOUNCE:
  1471.                                                 boss.yAcc = 140;
  1472.                                                 BollyAngle = -1;
  1473.                                                 break;
  1474.                                         case STATE::ATTACK:
  1475.                                                 boss.lightType = LIGHT::LASER;
  1476.                                                 break;
  1477.                                         case STATE::FLOATFALL:
  1478.                                                 boss.yAcc = 40;
  1479.                                                 boss.xSpeed = 3.5;
  1480.                                                 break;
  1481.                                         case STATE::ROTATE:
  1482.                                                 boss.xAcc = 165*32;
  1483.                                                 boss.yAcc = 320;
  1484.                                                 break;
  1485.                                 }
  1486.                                 boss.state = nuState;
  1487.                         }
  1488.                         if (abs(boss.xAcc - boss.xOrg) > 4) {
  1489.                                 if (boss.xAcc < boss.xOrg) boss.xOrg = boss.xOrg - boss.xSpeed;
  1490.                                 else boss.xOrg = boss.xOrg + boss.xSpeed;
  1491.                         }
  1492.                         if (abs(boss.yAcc - boss.yOrg) > 4) {
  1493.                                 if (boss.yAcc < boss.yOrg) boss.yOrg = boss.yOrg - boss.ySpeed;
  1494.                                 else boss.yOrg = boss.yOrg + boss.ySpeed;
  1495.                         }
  1496.                         boss.yPos = jjSin(jjGameTicks << 3) * 5 + boss.yOrg;
  1497.                         boss.xPos = jjCos(jjGameTicks << 4) * 8 + boss.xOrg;
  1498.                 }
  1499.                 switch (boss.state) {
  1500.                         case STATE::START:
  1501.                                 if (boss.age < BOLLYHEALTH3) {
  1502.                                         boss.age += 2;
  1503.                                         jjSample(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::COMMON_MONITOR, boss.age & 63);
  1504.                                 }else {
  1505.                                         boss.state = STATE::FADEOUT;
  1506.                                         boss.direction = 1;
  1507.                                         boss.counter = 0;
  1508.                                         boss.playerHandling = HANDLING::ENEMY;
  1509.                                         boss.xAcc = 178*32;
  1510.                                         boss.yAcc = jjLocalPlayers[0].yPos;
  1511.                                         boss.ySpeed = 2;
  1512.                                         boss.xSpeed = 6;
  1513.                                         jjObjects[boss.special].playerHandling = HANDLING::SPECIAL;
  1514.                                         jjSamplePriority(SOUND::SONCSHIP_MISSILE2);
  1515.                                 }
  1516.                                 break;
  1517.                         case STATE::FADEOUT:
  1518.                                 if (boss.xOrg < boss.xAcc - 10) {
  1519.                                         boss.yAcc = jjLocalPlayers[0].yPos;
  1520.                                         if ((jjRandom() & 31) == 13) {
  1521.                                                 jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1522.                                                 spikeball.playerHandling = HANDLING::ENEMY;
  1523.                                                 spikeball.bulletHandling = HANDLING::DESTROYBULLET;
  1524.                                                 spikeball.ySpeed = 1;
  1525.                                                 spikeball.xSpeed = SCROLLSPEED;
  1526.                                                 spikeball.doesHurt = 2;
  1527.                                                 spikeball.counter = 20;
  1528.                                                 spikeball.state = STATE::FLOAT;
  1529.                                                 spikeball.lightType = LIGHT::POINT2;
  1530.                                         }
  1531.                                 }
  1532.                                 break;
  1533.                         case STATE::BOUNCE:
  1534.                                 if (boss.yOrg < 150 && boss.xOrg < 158*32) {
  1535.                                         if (BollyAngle == -1) {
  1536.                                                 BollyAngle = atan2(boss.xOrg - jjLocalPlayers[0].xPos, boss.yOrg - jjLocalPlayers[0].yPos);
  1537.                                         } else {
  1538.                                                 if ((boss.counter & 15) == 0) {
  1539.                                                         jjOBJ@ ball = jjObjects[boss.fireBullet(OBJECT::BOUNCERBULLET)];
  1540.                                                         float angle = BollyAngle + (jjRandom() & 255) / 512.;
  1541.                                                         ball.xSpeed = sin(angle) * -7;
  1542.                                                         ball.ySpeed = cos(angle) * -7;
  1543.                                                         ball.counterEnd = 4;
  1544.                                                         ball.playerHandling = HANDLING::ENEMYBULLET;
  1545.                                                         ball.animSpeed = 2;
  1546.                                                         ball.var[0] = 8;
  1547.                                                         jjSample(ball.xPos, ball.yPos, SOUND::COMMON_EXPSM1);
  1548.                                                 }
  1549.                                         }
  1550.                                 }
  1551.                                 break;
  1552.                         case STATE::ATTACK:
  1553.                                 if (boss.counter < 200) boss.yAcc = jjLocalPlayers[0].yPos;
  1554.                                 else {
  1555.                                         jjOBJ@ ball = jjObjects[boss.fireBullet(OBJECT::BLASTERBULLET)];
  1556.                                         jjSample(boss.xPos, boss.yPos, SOUND::AMMO_LASER);
  1557.                                         ball.xSpeed = 24;
  1558.                                         ball.direction = 1;
  1559.                                         ball.state = STATE::FLY;
  1560.                                         ball.playerHandling = HANDLING::ENEMYBULLET;
  1561.                                         ball.animSpeed = 1;
  1562.                                         ball.lightType = LIGHT::POINT2;
  1563.                                         ball.determineCurAnim(ANIM::AMMO, 9);
  1564.                                         ball.killAnim = 0;
  1565.                                 }
  1566.                                 break;
  1567.                         case STATE::FLOATFALL:
  1568.                                 if ((boss.counter & 63) < 32)
  1569.                                         boss.xAcc = jjLocalPlayers[0].xPos;
  1570.                                 else {
  1571.                                         jjOBJ@ ball = jjObjects[boss.fireBullet(OBJECT::BLASTERBULLET)];
  1572.                                         jjSample(boss.xPos, boss.yPos, SOUND::AMMO_LASER);
  1573.                                         ball.ySpeed = 13;
  1574.                                         ball.xSpeed = SCROLLSPEED;
  1575.                                         ball.var[7] = ball.xAcc = 0;
  1576.                                         ball.direction = 1;
  1577.                                         ball.state = STATE::FLY;
  1578.                                         ball.playerHandling = HANDLING::ENEMYBULLET;
  1579.                                         ball.animSpeed = 1;
  1580.                                         ball.lightType = LIGHT::POINT2;
  1581.                                         ball.determineCurAnim(ANIM::AMMO, 9);
  1582.                                         ball.killAnim = 0;
  1583.                                 }
  1584.                                 break;
  1585.                         case STATE::ROTATE:
  1586.                                 if ((boss.counter & 127) == 127) {
  1587.                                         for (int i = 0; i < 1024; i += 256) {
  1588.                                                 jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1589.                                                 spikeball.playerHandling = HANDLING::ENEMY;
  1590.                                                 spikeball.bulletHandling = HANDLING::DESTROYBULLET;
  1591.                                                 spikeball.doesHurt = 1;
  1592.                                                 spikeball.counter = i;
  1593.                                                 spikeball.lightType = LIGHT::POINT2;
  1594.                                         }
  1595.                                 }
  1596.                                 break;
  1597.                 }
  1598.                 if (boss.xPos < 155*32) {
  1599.                         if (((jjGameTicks >> 4) & 1) == 0)
  1600.                                 jjDrawSprite(156.5 * 32, boss.yPos, ANIM::FONT, 2, 1, 0, SPRITE::PALSHIFT, 216, 2);
  1601.                 } else if (boss.xPos > 175 * 32) {
  1602.                         if (((jjGameTicks >> 4) & 1) == 0)
  1603.                                 jjDrawSprite(173.5 * 32, boss.yPos, ANIM::FONT, 2, 1, 0, SPRITE::PALSHIFT, 216, 2);
  1604.                 }
  1605.         } else { //STATE::DONE
  1606.                 jjOBJ@ top = jjObjects[boss.special];
  1607.                 if (++boss.counter <= 2) {
  1608.                         DestroyAllBollyProjectiles(boss.objectID);
  1609.                         scrolly = false;
  1610.                         top.justHit = 255;
  1611.                         boss.particlePixelExplosion(0);
  1612.                         top.particlePixelExplosion(0);
  1613.                         boss.lightType = LIGHT::RING2;
  1614.                         top.lightType = LIGHT::RING2;
  1615.                         jjLocalPlayers[0].activateBoss(false); //no more health bar
  1616.                         jjMusicStop();
  1617.                 } else if (boss.counter == 200) {
  1618.                         whiteWorld = true;
  1619.                         boss.age = 1;
  1620.                         boss.counter = 255;
  1621.                         boss.state = STATE::START;
  1622.                         boss.behavior = Bolly4;
  1623.                         boss.xPos = 165*32;
  1624.                         boss.yPos = 5*32;
  1625.                         boss.lightType = LIGHT::NONE;
  1626.                         top.lightType = LIGHT::NONE;
  1627.                         jjObjectPresets[OBJECT::BOLLY].energy = BOLLYHEALTH4 / 10;
  1628.                         jjPalette.fill(255,255,255, 10, 166);
  1629.                         jjPalette.fill(255,255,255, 208, 37);
  1630.                         //jjPalette.fill(0,0,0, 64, 8);
  1631.                         jjPalette.gradient(232,176,88, 88,60,8);
  1632.                         jjPalette.color[244].green = jjPalette.color[244].blue = 0; //red
  1633.                         jjPalette.color[245].red = 0; jjPalette.color[245].green = 128; //blue
  1634.                         jjPalette.apply();
  1635.                         //jjTileType[7] = jjTileType[8] = jjTileType[17] = jjTileType[18] = jjTileType[424] = jjTileType[425] = 6; //frozen
  1636.                         jjTexturedBGStyle = TEXTURE::TUNNEL;
  1637.                         jjTexturedBGUsed = true;
  1638.                         jjTexturedBGStars = false;
  1639.                         jjTexturedBGTexture = TEXTURE::XARGON;
  1640.                         jjSetFadeColors();
  1641.                         jjLayerYAutoSpeed[8] = 3;
  1642.                         jjLayerXAutoSpeed[8] = -1.2;
  1643.                         jjTexturedBGFadePositionY = .35;
  1644.                         jjLayerHasTiles[6] = jjLayerHasTiles[7] = false;
  1645.                         jjLayerHasTiles[4] = true;
  1646.                         jjSamplePriority(SOUND::BILSBOSS_THUNDER);
  1647.                         return;
  1648.                 }
  1649.                 boss.light = (boss.counter & 31) << 2;
  1650.                 top.light = ((boss.counter + 12) & 31) << 2;
  1651.                 jjSamplePriority(SOUND::COMMON_RINGGUN);
  1652.                 for (int i = 0; i < jjLocalPlayerCount; ++i)
  1653.                         jjLocalPlayers[i].lighting = boss.counter;
  1654.         }
  1655.         DrawAndManageBolly(boss);
  1656. }
  1657. array<STATE::State> PossibleWhiteStates = {STATE::FLOATFALL, STATE::ROTATE, STATE::FLY, STATE::ROCKETFLY, STATE::FIRE, STATE::JUMP};
  1658. array<uint8> WhiteStateReds =   {255, 128, 0,   250, 0,   164};
  1659. array<uint8> WhiteStateGreens = {0,   0,   255, 153, 210, 220};
  1660. array<uint8> WhiteStateBlues =  {0,   128, 64,  39,  250, 32};
  1661. void Bolly4(jjOBJ@ boss) {
  1662.         if (boss.state == STATE::START) {
  1663.                 if (--boss.counter > 99) {
  1664.                         for (int i = 0; i < jjLocalPlayerCount; ++i)
  1665.                                 jjLocalPlayers[i].lighting = boss.counter;
  1666.                 } else {
  1667.                         boss.counter = 0;
  1668.                         boss.doesHurt = 0;
  1669.                         boss.age = 1;
  1670.                         boss.energy = 95;
  1671.                         boss.xOrg = boss.xAcc = boss.xPos;
  1672.                         boss.yOrg = boss.yAcc = boss.yPos;
  1673.                         boss.playerHandling = HANDLING::ENEMY;
  1674.                         boss.bulletHandling = HANDLING::HURTBYBULLET;
  1675.                         jjObjects[boss.special].playerHandling = HANDLING::PARTICLE;
  1676.                         jjObjects[boss.special].bulletHandling = HANDLING::IGNOREBULLET;
  1677.                         boss.state = STATE::DELAYEDSTART;
  1678.                         jjEcho = 20; //I don't know how echo works :(
  1679.                         jjMusicLoad("onewingedangel.j2b");
  1680.                 }
  1681.                 DrawAndManageBolly(boss);
  1682.         } else {
  1683.                 jjOBJ@ top = jjObjects[boss.special];
  1684.                 if (boss.energy < 100) {
  1685.                         boss.justHit = (100 - boss.energy);
  1686.                         if ((boss.age -= boss.justHit) < 0) {
  1687.                                 DestroyAllBollyProjectiles(boss.objectID);
  1688.                                 if (++boss.doesHurt > uint8(4 + jjDifficulty)) { //start at 1, so ~four encounters
  1689.                                         boss.state = STATE::DONE;
  1690.                                         showScore = true;
  1691.                                         boss.playerHandling = HANDLING::EXPLOSION;
  1692.                                         boss.bulletHandling = HANDLING::IGNOREBULLET;
  1693.                                         boss.counter = 0;
  1694.                                         jjMusicStop();
  1695.                                 } else {
  1696.                                         boss.age = BOLLYHEALTH4;
  1697.                                         boss.counter = 0;
  1698.                                         jjSamplePriority(SOUND::SONCSHIP_MISSILE2);
  1699.                                         for (int i = 0; i < jjLocalPlayerCount; ++i) {
  1700.                                                 jjLocalPlayers[i].invincibility = 150; //let's be nice
  1701.                                                 if (jjLocalPlayers[i].health < jjMaxHealth) jjLocalPlayers[i].health = jjLocalPlayers[i].health + 1;
  1702.                                                 switch (jjLocalPlayers[i].charCurr) {
  1703.                                                         case CHAR::JAZZ:
  1704.                                                                 jjSamplePriority(SOUND::JAZZSOUNDS_JUMMY);
  1705.                                                                 break;
  1706.                                                         case CHAR::SPAZ:
  1707.                                                                 jjSamplePriority(SOUND::SPAZSOUNDS_HAPPY);
  1708.                                                                 break;
  1709.                                                         default:
  1710.                                                                 jjSamplePriority(SOUND::COMMON_EAT1);
  1711.                                                                 break;
  1712.                                                 }
  1713.                                         }
  1714.                                         boss.xSpeed = 3;
  1715.                                         boss.ySpeed = 2;
  1716.                                         uint nuStateIndex;
  1717.                                         do {
  1718.                                                 nuStateIndex = jjRandom() % PossibleWhiteStates.length();
  1719.                                         } while (PossibleWhiteStates[nuStateIndex] == boss.state || (PossibleWhiteStates[nuStateIndex] == STATE::FLOATFALL && boss.doesHurt == uint(4 + jjDifficulty))); //must be something DIFFERENT
  1720.                                         boss.state = PossibleWhiteStates[nuStateIndex];
  1721.                                         switch (boss.state) {
  1722.                                                 case STATE::FLOATFALL: //drop steam puppies
  1723.                                                         boss.yAcc = -200;
  1724.                                                         boss.ySpeed = 2;
  1725.                                                         break;
  1726.                                                 case STATE::ROTATE: //four spike balls on chains
  1727.                                                 case STATE::FIRE: //follow you around firing lasers
  1728.                                                         boss.xSpeed = 1.5;
  1729.                                                         boss.ySpeed = 1.3;
  1730.                                                         break;
  1731.                                                 case STATE::FLY: //bird swarms
  1732.                                                         boss.xAcc = boss.xOrg;
  1733.                                                         boss.yAcc = 4*32;
  1734.                                                         boss.xSpeed = 2.5;
  1735.                                                         break;
  1736.                                                 case STATE::ROCKETFLY: //targetted missiles
  1737.                                                         boss.yAcc = 100;
  1738.                                                         boss.xAcc = boss.xOrg;
  1739.                                                         boss.xSpeed = 2.5;
  1740.                                                         break;
  1741.                                         }
  1742.                                         jjPalette.gradient(
  1743.                                                 WhiteStateReds[nuStateIndex],
  1744.                                                 WhiteStateGreens[nuStateIndex],
  1745.                                                 WhiteStateBlues[nuStateIndex],
  1746.                                                 0,0,0
  1747.                                         );
  1748.                                         jjPalette.apply();
  1749.                                         jjSetFadeColors();
  1750.                                         jjSample(boss.xPos, boss.yPos, SOUND::COMMON_BELL_FIRE);
  1751.                                 }
  1752.                         }
  1753.                         boss.energy = 100;
  1754.                 }
  1755.                
  1756.                 bool xMov = false, yMov = false;
  1757.                 if (abs(boss.xAcc - boss.xOrg) > 4) {
  1758.                         xMov = true;
  1759.                         if (boss.xAcc < boss.xOrg) { boss.xOrg = boss.xOrg - boss.xSpeed; boss.direction = -1; }
  1760.                         else { boss.xOrg = boss.xOrg + boss.xSpeed; boss.direction = 1; }
  1761.                 }
  1762.                 if (abs(boss.yAcc - boss.yOrg) > 4) {
  1763.                         yMov = true;
  1764.                         if (boss.yAcc < boss.yOrg) boss.yOrg = boss.yOrg - boss.ySpeed;
  1765.                         else boss.yOrg = boss.yOrg + boss.ySpeed;
  1766.                 }
  1767.                 boss.yPos = jjSin(jjGameTicks << 3) * 5 + boss.yOrg;
  1768.                 boss.xPos = jjCos(jjGameTicks << 3) * 5 + boss.xOrg;
  1769.                
  1770.                 ++boss.counter;
  1771.                 switch (boss.state) {
  1772.                         case STATE::FLOATFALL:
  1773.                                 if (boss.yPos > -80) boss.counter = 0;
  1774.                                 else {
  1775.                                         if (boss.counter < 500) {
  1776.                                                 uint rand = jjRandom();
  1777.                                                 if ((rand & 15) == 0) {
  1778.                                                         jjOBJ@ obj = jjObjects[jjAddObject(OBJECT::STEAM, (155*32) + ((rand >> 5) % 640), -3, boss.objectID)];
  1779.                                                         obj.points = 0;
  1780.                                                         obj.ySpeed = (rand & 31) / 16.;
  1781.                                                 }
  1782.                                         }
  1783.                                         if ((boss.counter > 100) && (boss.counter & 15) == 0) { //don't need to check _constantly_
  1784.                                                 bool remainingMushrooms = false;
  1785.                                                 for (int i = 0; i < jjObjectCount; ++i) {
  1786.                                                         jjOBJ@ obj = jjObjects[i];
  1787.                                                         if (obj.isActive && obj.eventID == OBJECT::STEAM)
  1788.                                                                 remainingMushrooms = true;
  1789.                                                 }
  1790.                                                 if (!remainingMushrooms) {
  1791.                                                         boss.yAcc = 30;
  1792.                                                         boss.energy = 95;
  1793.                                                         boss.age = 1;
  1794.                                                 } else boss.age = BOLLYHEALTH4;
  1795.                                         }
  1796.                                 }
  1797.                                 break;
  1798.                         case STATE::ROTATE:
  1799.                                 if (boss.counter < 160) {
  1800.                                         if ((boss.counter & 7) == 7) for (int i = 0; i < 1024; i += 256) {
  1801.                                                 jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1802.                                                 spikeball.playerHandling = HANDLING::PARTICLE;
  1803.                                                 spikeball.bulletHandling = HANDLING::IGNOREBULLET;
  1804.                                                 spikeball.special = boss.counter - 6;
  1805.                                                 spikeball.counter = i;
  1806.                                                 spikeball.determineCurAnim(ANIM::SONCSHIP, 1); //chain
  1807.                                                 spikeball.determineCurFrame();
  1808.                                                 spikeball.lightType = LIGHT::POINT;
  1809.                                         }
  1810.                                 } else if (boss.counter == 160) {
  1811.                                         for (int i = 0; i < 1024; i += 256) {
  1812.                                                 jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1813.                                                 spikeball.playerHandling = HANDLING::ENEMY;
  1814.                                                 spikeball.bulletHandling = HANDLING::DESTROYBULLET;
  1815.                                                 spikeball.special = boss.counter - 6;
  1816.                                                 spikeball.counter = i;
  1817.                                                 spikeball.determineCurAnim(ANIM::AMMO, 77); //spike ball-like shape
  1818.                                                 spikeball.frameID = 7;
  1819.                                                 spikeball.determineCurFrame();
  1820.                                                 spikeball.lightType = LIGHT::RING2;
  1821.                                                 spikeball.light = 7;
  1822.                                         }
  1823.                                 } else {
  1824.                                         if (!xMov && !yMov) {
  1825.                                                 boss.xAcc = jjLocalPlayers[0].xPos;
  1826.                                                 boss.yAcc = jjLocalPlayers[0].yPos;
  1827.                                         }
  1828.                                 }
  1829.                                 break;
  1830.                         case STATE::FLY:
  1831.                                 if (xMov) {
  1832.                                         boss.counter = 0;
  1833.                                 } else {
  1834.                                         if (boss.counter == 210) {
  1835.                                                 if (boss.xOrg > 165*32)
  1836.                                                         boss.xAcc = 157*32;
  1837.                                                 else
  1838.                                                         boss.xAcc = 172*32;
  1839.                                         } else if (boss.counter > 40) {
  1840.                                                 uint rand = jjRandom();
  1841.                                                 if ((rand & 7) == 0) {
  1842.                                                         rand >>= 3;
  1843.                                                         jjOBJ@ bird = jjObjects[jjAddObject(OBJECT::BULLET, jjLocalPlayers[0].xPos - 640*boss.direction + (float(rand & 255) - 127.5), (float((rand >> 8) & 255) - 127.5), boss.objectID, CREATOR::OBJECT, BollyBird)];
  1844.                                                         bird.ySpeed = 4.6;
  1845.                                                         bird.xSpeed = 7.2 * boss.direction;
  1846.                                                         bird.direction = -boss.direction; //bird sprite faces left
  1847.                                                         bird.playerHandling = HANDLING::ENEMY;
  1848.                                                         bird.energy = 1;
  1849.                                                         bird.lightType = LIGHT::POINT2;
  1850.                                                         bird.light = 1;
  1851.                                                         jjSample(bird.xPos, bird.yPos, SOUND::SPAZSOUNDS_BIRDSIT);
  1852.                                                 }
  1853.                                         }
  1854.                                 }
  1855.                                 break;
  1856.                         case STATE::ROCKETFLY:
  1857.                                 if (yMov) {
  1858.                                         boss.counter = 0;
  1859.                                 } else {
  1860.                                         if (boss.counter == 1) {
  1861.                                                 for (int angle = 0; angle < 1024; angle += 128) {
  1862.                                                         jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1863.                                                         spikeball.playerHandling = HANDLING::ENEMY;
  1864.                                                         spikeball.bulletHandling = HANDLING::DESTROYBULLET;
  1865.                                                         spikeball.counter = angle;
  1866.                                                         spikeball.special = 60;
  1867.                                                         spikeball.lightType = LIGHT::POINT2;
  1868.                                                 }
  1869.                                         } else {
  1870.                                                 if (!xMov)
  1871.                                                         boss.xAcc = jjLocalPlayers[0].xPos;
  1872.                                                 if ((boss.counter & 127) == 0)
  1873.                                                         jjAddObject(OBJECT::BULLET, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollyTargetMissile);
  1874.                                                 else if ((boss.counter & 127) == 64) {
  1875.                                                         jjOBJ@ spikeball = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BollySpikeBall)];
  1876.                                                         spikeball.playerHandling = HANDLING::ENEMY;
  1877.                                                         spikeball.bulletHandling = HANDLING::DESTROYBULLET;
  1878.                                                         spikeball.ySpeed = 1;
  1879.                                                         spikeball.doesHurt = 2;
  1880.                                                         spikeball.counter = 5;
  1881.                                                         spikeball.state = STATE::FLOAT;
  1882.                                                         spikeball.lightType = LIGHT::POINT2;
  1883.                                                 }
  1884.                                         }
  1885.                                 }
  1886.                                 break;
  1887.                         case STATE::FIRE:
  1888.                                 if (!xMov && !yMov) {
  1889.                                         boss.xAcc = jjLocalPlayers[0].xPos;
  1890.                                         boss.yAcc = jjLocalPlayers[0].yPos;
  1891.                                 }
  1892.                                 if ((boss.counter & 127) == 70) {
  1893.                                         jjSamplePriority(SOUND::AMMO_LASER);
  1894.                                         jjOBJ@ laser = jjObjects[jjAddObject(OBJECT::BULLET, boss.xPos, boss.yPos, boss.objectID)];
  1895.                                         uint rand = jjRandom();
  1896.                                         laser.xSpeed = boss.xSpeed * boss.direction * 4.5 + (int(rand & 0xFFFF) - 0x8000) / 21844.;
  1897.                                         rand >>= 16;
  1898.                                         if (boss.yAcc < boss.yOrg) laser.ySpeed = boss.ySpeed * -6 + (int(rand & 0xFFFF) - 0x8000) / 21844.;
  1899.                                         else laser.ySpeed = boss.ySpeed * 4.5 + (int(rand & 0xFFFF) - 0x8000) / 21844.;
  1900.                                         laser.behavior = BollyBouncingLaser;
  1901.                                         laser.special = 10;
  1902.                                 }
  1903.                                 break;
  1904.                         case STATE::JUMP:
  1905.                                 if (!xMov && !yMov) {
  1906.                                         boss.xAcc = jjLocalPlayers[0].xPos;
  1907.                                         boss.yAcc = jjLocalPlayers[0].yPos;
  1908.                                 }
  1909.                                 if (boss.counter == 1) {
  1910.                                         jjSamplePriority(SOUND::AMMO_LASER);
  1911.                                         jjOBJ@ laser = jjObjects[jjAddObject(OBJECT::BULLET, 156*32, 320, boss.objectID)];
  1912.                                         laser.xSpeed = 9;
  1913.                                         laser.ySpeed = 0;
  1914.                                         laser.behavior = BollyBouncingLaser;
  1915.                                         laser.special = 30; //thatsa one longa laser
  1916.                                         laser.counterEnd = 5; //will pretty much never run out
  1917.                                         laser.scriptedCollisions = true;
  1918.                                         @laser = jjObjects[jjAddObject(OBJECT::BULLET, 165*32, 32, boss.objectID)];
  1919.                                         laser.xSpeed = 9;
  1920.                                         laser.ySpeed = 0;
  1921.                                         laser.behavior = BollyBouncingLaser;
  1922.                                         laser.special = 30;
  1923.                                         laser.counterEnd = 5;
  1924.                                         laser.scriptedCollisions = true;
  1925.                                 } else if ((boss.counter & 15) == 3) {
  1926.                                         jjOBJ@ rock = jjObjects[jjAddObject(OBJECT::BIGROCK, 153*32, (jjRandom() % 620) + 20, boss.objectID)];
  1927.                                         rock.xSpeed = 3;
  1928.                                         rock.behavior = BollyMovingRockPlatform;
  1929.                                         rock.scriptedCollisions = false;
  1930.                                         rock.bulletHandling = HANDLING::IGNOREBULLET;
  1931.                                 }
  1932.                                 break;
  1933.                         case STATE::DONE: {
  1934.                                 uint rand = jjRandom();
  1935.                                 if ((rand & 1) == 0) {
  1936.                                         jjOBJ@ expl = jjObjects[jjAddObject(OBJECT::EXPLOSION, boss.xPos + ((rand >> 1) & 31) - 16, boss.yPos + ((rand >> 6) & 31) - 16, boss.objectID)];
  1937.                                         expl.determineCurAnim(ANIM::AMMO, 3);
  1938.                                         expl.lightType = LIGHT::BRIGHT;
  1939.                                         expl.light = 9;
  1940.                                         jjSamplePriority(SOUND::COMMON_GLASS2);
  1941.                                 } else {
  1942.                                         jjOBJ@ spark = jjObjects[jjAddObject(OBJECT::SHARD, boss.xPos, boss.yPos, boss.objectID, CREATOR::OBJECT, BEHAVIOR::BIRDFEATHER)];
  1943.                                         spark.determineCurAnim(ANIM::AMMO, 7);
  1944.                                         spark.lightType = LIGHT::POINT;
  1945.                                 }
  1946.                                 if (boss.counter > 200 && boss.counter < 300)
  1947.                                         for (int i = 0; i < jjLocalPlayerCount; ++i)
  1948.                                                 jjLocalPlayers[i].lighting = -100 + boss.counter;
  1949.                                 else if (boss.counter == 320) {
  1950.                                         jjTexturedBGUsed = false; //no fade colors getting in the way of perfect whiteness
  1951.                                         jjPalette.fill(255,255,255, 10, 235);
  1952.                                         jjPalette.gradient(0,0,0, 255,255,255, 64, 8);
  1953.                                         jjPalette.apply();
  1954.                                         for (int i = 0; i < jjLocalPlayerCount; ++i) {
  1955.                                                 jjLocalPlayers[i].lighting = 100;
  1956.                                                 jjLocalPlayers[i].showText("@@@@@CONGRATULATIONS", STRING::LARGE);
  1957.                                         }
  1958.                                 } else if (boss.counter == 500) {
  1959.                                         jjNxt("ending", true, true);
  1960.                                         boss.delete();
  1961.                                         jjObjects[boss.special].delete(); //thorough
  1962.                                 }
  1963.                                 break; }
  1964.                 }
  1965.                
  1966.                 top.xPos = boss.xPos;
  1967.                 top.yPos = boss.yPos;
  1968.                 top.direction = boss.direction;
  1969.                 if (boss.justHit > 0) {
  1970.                         jjDrawSpriteFromCurFrame(boss.xPos - boss.justHit, boss.yPos, boss.curFrame, boss.direction, SPRITE::SINGLECOLOR, 244);
  1971.                         jjDrawSpriteFromCurFrame(boss.xPos + boss.justHit, boss.yPos, boss.curFrame, boss.direction, SPRITE::SINGLECOLOR, 245);
  1972.                         jjDrawSpriteFromCurFrame(top.xPos - boss.justHit, top.yPos, top.curFrame, top.direction, SPRITE::SINGLECOLOR, 244);
  1973.                         jjDrawSpriteFromCurFrame(top.xPos + boss.justHit, top.yPos, top.curFrame, top.direction, SPRITE::SINGLECOLOR, 245);
  1974.                 }
  1975.                 jjDrawSpriteFromCurFrame(boss.xPos, boss.yPos, boss.curFrame, boss.direction);
  1976.                 jjDrawSpriteFromCurFrame(top.xPos, top.yPos, top.curFrame, top.direction);
  1977.         }
  1978. }
  1979.  
  1980. void MyEva(jjOBJ@ eva) {
  1981.         if (eva.state == STATE::DEACTIVATE) {
  1982.                 if (statueDefeated && jjLocalPlayers[0].ammo[WEAPON::RF] > 0)
  1983.                         eva.eventID = OBJECT::EXTRALIFE;
  1984.                 eva.deactivate();
  1985.         } else {
  1986.                 int playerID = eva.findNearestPlayer(4000);
  1987.                 if (playerID > -1)
  1988.                         eva.direction = (jjPlayers[playerID].xPos < eva.xPos) ? 1 : -1;
  1989.                 eva.draw();
  1990.         }
  1991. }
  1992.  
  1993.  
  1994. void TempleLock(jjOBJ@ lock) {
  1995.         if (lock.state == STATE::START) {
  1996.                 lock.state = STATE::WAIT;
  1997.                 if (statueDefeated) { lock.lightType = LIGHT::LASER; lock.light = 1; }
  1998.         } else if (lock.state == STATE::DEACTIVATE) {
  1999.                 lock.deactivate();
  2000.         }
  2001. }
  2002.  
  2003. string passwordAttempt = "";
  2004. array<bool> letterWasJustTyped(26);
  2005. string chr(uint8 value){ //Returns a one-character string corresponding to an ASCII value. //http://www.jazz2online.com/snippets/68/operations-on-ascii-values/
  2006.   string str="\0";
  2007.   str[0]=value;
  2008.   return str;
  2009. }
  2010. void onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
  2011.         if (obj.eventID == OBJECT::BIGROCK) {
  2012.                 if (bull !is null) {
  2013.                         obj.energy -= bull.animSpeed;
  2014.                         obj.justHit = 5;
  2015.                         if (obj.energy <= 0) { //only reachable through RFs
  2016.                                 obj.clearPlatform();
  2017.                                 obj.particlePixelExplosion(2);
  2018.                                 obj.delete();
  2019.                                 barrierDestroyed = true;
  2020.                                 play.ammo[WEAPON::RF] = 0;
  2021.                                 play.powerup[WEAPON::RF] = false;
  2022.                                 play.currWeapon = WEAPON::BLASTER; //guaranteed to exist, and finding another one sounds boring
  2023.                                 for (int i = (jjRandom() & 15); i > 0; --i)
  2024.                                         jjObjects[
  2025.                                                 jjAddObject(
  2026.                                                         OBJECT::EXPLOSION,
  2027.                                                         obj.xPos + (jjRandom() & 63) - 32,
  2028.                                                         obj.yPos + (jjRandom() & 63) - 32,
  2029.                                                         obj.objectID
  2030.                                                 )
  2031.                                         ].determineCurAnim(ANIM::AMMO, 5);
  2032.                         }
  2033.                         bull.state = STATE::EXPLODE;
  2034.                 } //else if (play !is null) play.hurt();
  2035.         } else if (obj.eventID == OBJECT::HELMUT) {
  2036.                 if (bull !is null) {
  2037.                         obj.special -= bull.animSpeed;
  2038.                         obj.energy = obj.special / 4;
  2039.                         obj.justHit = 5;
  2040.                         bull.state = STATE::EXPLODE;
  2041.                         if (obj.energy <= 0) {
  2042.                                 obj.state = STATE::EXPLODE;
  2043.                                 obj.counter = 0;
  2044.                                 obj.scriptedCollisions = false;
  2045.                                 obj.xOrg = obj.xPos; obj.yOrg = obj.yPos;
  2046.                                 for (int i = 0; i < jjLocalPlayerCount; ++i)
  2047.                                         jjLocalPlayers[i].activateBoss(false);
  2048.                                 paused = pausedForChatting = true;
  2049.                                 elapsedPauseTime = 1;
  2050.                                 currentPauseChat = chatSTATUE;
  2051.                         }
  2052.                 }
  2053.         } else if (obj.eventID == OBJECT::EVA) {
  2054.                 if (bull is null && play.ammo[WEAPON::RF] == 0) {
  2055.                         if ( (!barrierDestroyed) || statueDefeated ) {
  2056.                                 paused = pausedForChatting = true;
  2057.                                 elapsedPauseTime = 1;
  2058.                                 play.ammo[WEAPON::RF] = 3;
  2059.                                 if (statueDefeated) {
  2060.                                         play.powerup[WEAPON::RF] = false;
  2061.                                         currentPauseChat = chatEVA2;
  2062.                                 } else {
  2063.                                         play.powerup[WEAPON::RF] = true;
  2064.                                         currentPauseChat = chatEVA;
  2065.                                 }
  2066.                         }
  2067.                 }
  2068.         } else if (obj.eventID == OBJECT::FRIES) { //temple lock
  2069.                 if (bull is null) { //only interested in contact with the player
  2070.                         if (obj.state == STATE::WAIT && statueDefeated) {
  2071.                                 obj.state = STATE::IDLE;
  2072.                                 jjTileSet(4, 85, 39, 795);
  2073.                                 jjEventSet(85, 39, AREA::TEXT); //install a reminder sign in case the player doesn't have chatlogger open and didn't write down the password
  2074.                         }
  2075.                         //try to type
  2076.                         for (uint8 keyCode = 0; keyCode < 26; ++keyCode) { //A-Z
  2077.                                 if (jjKey[keyCode + 0x41]) {
  2078.                                         if (!letterWasJustTyped[keyCode]) {
  2079.                                                 letterWasJustTyped[keyCode] = true;
  2080.                                                 if (passwordAttempt.length() < 10) {
  2081.                                                         passwordAttempt += chr(keyCode + 0x41);
  2082.                                                         if (statueDefeated && passwordAttempt == "OPENSESAME") {
  2083.                                                                 jjTriggers[2] =