Downloads containing Lori Fortress.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Lori FortressFeatured Download Primpy Single player 8.7 Download file

File preview

  1. const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
  2. #include "MLLE-Include-1.5.asc" ///@MLLE-Generated
  3. #pragma require "Lori Fortress-MLLE-Data-3.j2l" ///@MLLE-Generated
  4. #pragma require "Lori Fortress-MLLE-Data-2.j2l" ///@MLLE-Generated
  5. #pragma require "Lori Fortress-MLLE-Data-1.j2l" ///@MLLE-Generated
  6. #pragma require "Lori Fortress.j2as" ///@MLLE-Generated
  7. #pragma require "Meteor.j2a"
  8. #pragma require "CosmicDust.j2a"
  9. #pragma require "Mortar.j2a"
  10. #pragma require "Lightningrod.j2a"
  11. #pragma require "CloneMachine.j2a"
  12. #pragma require "expmine.wav"
  13. #pragma require "lowind.wav"
  14. #pragma require "f_gren4.wav"
  15. #pragma require "ZAPFIZZ1.wav"
  16. #pragma require "ZAPFIZZ2.wav"
  17. #pragma require "kaze_loop.ogg"
  18. #pragma require "fireeye.ogg"
  19.  
  20. #include "ArcaneWeapon2.asc"
  21. #include "ArcaneWeapon3.asc"
  22. #include "ArcaneWeapon4.asc"
  23. #include "ArcaneWeapon7.asc"
  24. #include "ArcaneWeapon8.asc"
  25. #include "LFgems.asc"
  26.  
  27. CosmicDust Duster();
  28.  
  29. uint FriendColor(uint8 a, uint8 b, uint8 c, uint8 d) { return a | (b << 8) | (c << 16) | (d << 24); }
  30. uint8 loriCounter = 0;
  31. bool bossDefeated = false, bossStarted = false;
  32.        
  33. void onLevelLoad()
  34. {
  35.         jjPLAYER@ Player = jjLocalPlayers[0];
  36.         Player.morphTo(Player.charOrig = CHAR::LORI, false);
  37.         jjAnimSets[ANIM::CUSTOM[22]].load(0, "Meteor.j2a");
  38.         jjAnimSets[ANIM::CUSTOM[23]].load(0, "CosmicDust.j2a");
  39.         jjAnimSets[ANIM::CUSTOM[24]].load(0, "Mortar.j2a");
  40.         jjAnimSets[ANIM::CUSTOM[27]].load(0, "Lightningrod.j2a");
  41.         jjAnimSets[ANIM::CUSTOM[28]].load(0, "CloneMachine.j2a");
  42.         Foo(jjObjectPresets[OBJECT::MORPH]);
  43.         CloneMachine(jjObjectPresets[OBJECT::ROBOT]);
  44.         jjPlayers[28].fur = FriendColor(64,24,64,72);
  45. }
  46.  
  47. void onMain() {
  48.         gem::deleteCollectedGems();
  49.         jjDrawSprite(1083.646, 4012.51, ANIM::JAZZ, RABBIT::CORPSE, 0, 0, SPRITE::NORMAL);
  50.         jjDrawSprite(1597.939, 4072.51, ANIM::SPAZ, RABBIT::CORPSE, 0, 0, SPRITE::NORMAL);
  51.         if (jjTriggers[1] == true && bossStarted == false) {
  52.                 jjDrawSpriteFromCurFrame(5007, 387, jjAnimations[jjAnimSets[ANIM::CUSTOM[28]]].firstFrame + 2, 0, SPRITE::NORMAL);
  53.                 }
  54. }
  55.  
  56. void onPlayer(jjPLAYER@ play)
  57. {
  58.         gem::trackPlayerGems(play);
  59.         gem::upgradeHealth(play);
  60. }
  61.  
  62. void onLevelReload()
  63. {
  64.         gem::restorePlayerGems();
  65.         jjLocalPlayers[0].lives++;
  66.         loriCounter = 0;
  67.         bossDefeated = false;
  68.         bossStarted = false;
  69.         jjMusicLoad("kaze_loop.ogg");
  70.         CloneMachine(jjObjectPresets[OBJECT::ROBOT]);
  71. }
  72.  
  73. bool onDrawLives(jjPLAYER@ play, jjCANVAS@ canvas)  { return true; }
  74.  
  75.  
  76. enum FooWeapon { None, Meteor, Duster, Mortar, Rod, Sanguine };
  77. bool FooSoundsLoaded = false;
  78. array<MLLEWeapons::WeaponInterface@> ArcaneWeapons = {null, ArcaneWeapons::MeteorGun::Weapon(), ArcaneWeapons::CosmicDuster::Weapon(), ArcaneWeapons::MortarLauncher::Weapon(), ArcaneWeapons::LightningRod::Weapon(), ArcaneWeapons::SanguineSpear::Weapon()};
  79.  
  80. class Foo : jjBEHAVIORINTERFACE {
  81.  
  82.         Foo(jjOBJ@ preset) {
  83.                 preset.behavior = this;
  84.                 preset.playerHandling = HANDLING::ENEMY;
  85.                 preset.scriptedCollisions = true;
  86.                 preset.bulletHandling = HANDLING::HURTBYBULLET;
  87.                 preset.isTarget = true;
  88.                 preset.isFreezable = true;
  89.                 preset.isBlastable = false;
  90.                 preset.energy = 88; // 3 + 45 buffer (Lori does an absurd amount of damage with her special kick, just making sure...)
  91.                 preset.determineCurAnim(ANIM::LORI, RABBIT::STAND);
  92.                 preset.determineCurFrame();
  93.                 preset.direction = -1;
  94.                 preset.triggersTNT = false;
  95.         }
  96.         void onBehave(jjOBJ@ obj) override {
  97.                 uint frameID;
  98.  
  99.                 const auto lastCurFrame = obj.curFrame;
  100.                 jjPLAYER@ Player = jjLocalPlayers[0];
  101.                 uint loriHurtFreq;
  102.                 const auto@ anim = jjAnimations[jjAnimSets[obj.special] + obj.curAnim];
  103.                
  104.                 if ( (obj.energy <= 85 || bossDefeated == true) && obj.state != STATE::EXTRA) {
  105.                         obj.frameID = 0;
  106.                         obj.bulletHandling = HANDLING::IGNOREBULLET;
  107.                         obj.playerHandling = HANDLING::DYING;
  108.                         obj.isTarget = false;
  109.                         obj.isFreezable = false;
  110.                         obj.state = STATE::EXTRA;
  111.                         }
  112.                        
  113.                 switch (obj.state) {
  114.                         case STATE::START: {
  115.                                 if (!FooSoundsLoaded) {
  116.                                         FooSoundsLoaded = true;
  117.                                         jjSampleLoad(SOUND::ORANGE_BOEMR, "expmine.wav");
  118.                                         jjSampleLoad(SOUND::ORANGE_BOEML, "lowind.wav");
  119.                                         jjSampleLoad(SOUND::ORANGE_MERGE, "f_gren4.wav");
  120.                                         jjSampleLoad(SOUND::COMMON_ELECTRICHIT, "ZAPFIZZ1.wav");
  121.                                         jjSampleLoad(SOUND::COMMON_ELECTRIC2, "ZAPFIZZ2.wav");
  122.                                 }
  123.                                         obj.special = ANIM::LORI;
  124.                                 switch (obj.doesHurt = (HANDLING::getParameterAtOrigin(obj, 0, 3) % 6)) { //weapon
  125.                                         case FooWeapon::Meteor:
  126.                                                 obj.state = STATE::BOUNCE;
  127.                                                 obj.curAnim = RABBIT::JUMPFIRERIGHT;
  128.                                                 break;
  129.                                         case FooWeapon::Duster:
  130.                                                 obj.state = STATE::JUMP;
  131.                                                 obj.curAnim = RABBIT::JUMPFIRERIGHT;
  132.                                                 break;
  133.                                         case FooWeapon::Mortar:
  134.                                                 obj.state = STATE::ATTACK;
  135.                                                 obj.curAnim = RABBIT::DIVEFIRERIGHT;
  136.                                                 obj.counter = jjRandom();
  137.                                                 break;
  138.                                         case FooWeapon::Rod:
  139.                                                 obj.state = STATE::FLY;
  140.                                                 obj.curAnim = RABBIT::AIRBOARD;
  141.                                                 break;
  142.                                         case FooWeapon::Sanguine:
  143.                                         default:
  144.                                                 obj.state = STATE::WALK;
  145.                                                 break;
  146.                                 }
  147.                                 if (obj.doesHurt != FooWeapon::Rod)
  148.                                         obj.putOnGround(true);
  149.                                 break; }
  150.                         case STATE::BOUNCE: //Meteor
  151.                                 if (obj.counter == 0) //starting out
  152.                                         obj.yAcc = obj.yPos;
  153.                                 obj.yPos = obj.yAcc - abs(jjSin(obj.counter += 8) * 128);
  154.                                 if (obj.counter & 511 == 0) {
  155.                                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_JUMP);
  156.                                         jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos + 12)].determineCurAnim(ANIM::AMMO, 72);
  157.                                 } else if (obj.counter & 511 == 256) {
  158.                                         jjOBJ@ meteor = fireBullet(obj, lastCurFrame, OBJECT::BOUNCERBULLET);
  159.                                         jjSample(meteor.xPos, meteor.yPos, SOUND::ORANGE_BOEMR, 42, 20000);
  160.                                         meteor.determineCurAnim(ANIM::CUSTOM[22], 1);
  161.                                         meteor.ySpeed = 0;
  162.                                         meteor.killAnim = jjObjectPresets[OBJECT::SEEKERBULLET].killAnim;
  163.                                         meteor.lightType = LIGHT::POINT;
  164.                                         meteor.light = 10;
  165.                                         meteor.xSpeed = abs(meteor.xSpeed);
  166.                                         meteor.xAcc = abs(meteor.xAcc);
  167.                                         meteor.behavior = Meteor;
  168.                                 }
  169.                                 obj.direction = (obj.xPos < Player.xPos) ? 1 : -1;
  170.                                 frameID = obj.counter >> 6;
  171.                                 break;
  172.                         case STATE::JUMP: //Duster
  173.                                 if (obj.ySpeed < 0 && jjMaskedPixel(int(obj.xPos), int(obj.yPos + obj.ySpeed) - 12))
  174.                                         obj.ySpeed = 0;
  175.                                 else
  176.                                         obj.yPos += obj.ySpeed += 0.25;
  177.                                 if (jjMaskedPixel(int(obj.xPos), int(obj.yPos) + 12)) {
  178.                                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_JUMP);
  179.                                         jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos + 12)].determineCurAnim(ANIM::AMMO, 72);
  180.                                         obj.ySpeed = -8;
  181.                                 } else {
  182.                                         const float targetX = obj.xPos + obj.direction * 1.25 * (89 - obj.energy);
  183.                                         if (jjMaskedVLine(int(targetX), int(obj.yPos) - 12, 24))
  184.                                                 obj.direction = -obj.direction;
  185.                                         else
  186.                                                 obj.xPos = targetX;
  187.                                 }
  188.                                 if (++obj.counter % 50 == 1) {
  189.                                         jjOBJ@ duster = fireBullet(obj, lastCurFrame, OBJECT::ICEBULLET);
  190.                                         duster.determineCurAnim(ANIM::CUSTOM[23], 1);
  191.                                         duster.counterEnd = 180;
  192.                                         duster.lightType = LIGHT::BRIGHT;
  193.                                         duster.light = 8;
  194.                                         duster.playerHandling = HANDLING::PICKUP;
  195.                                         duster.scriptedCollisions = true;
  196.                                         duster.behavior = Duster;
  197.                                 }
  198.                                 obj.var[1] = jjSampleLooped(obj.xPos,obj.yPos,SOUND::ORANGE_BOEML,obj.var[1]);
  199.                                 frameID = obj.counter >> 6;
  200.                                 break;
  201.                         case STATE::ATTACK: //Mortar
  202.                                 if (++obj.counter & (obj.creatorType == CREATOR::LEVEL ? 63 : 127) == 5) {
  203.                                         jjOBJ@ mortar = fireBullet(obj, lastCurFrame, OBJECT::SEEKERBULLET);
  204.                                         jjSample(mortar.xPos, mortar.yPos, SOUND::ORANGE_MERGE, 0, 0);
  205.                                         mortar.determineCurAnim(ANIM::CUSTOM[22], 1);
  206.                                         mortar.xSpeed = 7;
  207.                                         mortar.xAcc = abs(mortar.xAcc);
  208.                                         mortar.ySpeed = -9 + (jjRandom() & ((4 << (88 - obj.energy)) - 1)) / 2.f;
  209.                                         mortar.counterEnd = 90;
  210.                                         mortar.behavior = Mortar;
  211.                                 }
  212.                                 obj.direction = (obj.xPos < Player.xPos) ? 1 : -1;
  213.                                 frameID = obj.counter >> 6;
  214.                                 break;
  215.                         case STATE::FLY: //Rod
  216.                                 obj.xPos = obj.xOrg + jjSin((++obj.counterEnd) << 2) * 100;
  217.                                 obj.yPos = obj.yOrg + 8 * (1 - jjSin(jjGameTicks * 12));
  218.                                 obj.direction = (((obj.counterEnd + 64) & 255) < 128) ? 1 : -1;
  219.                                 if (obj.counterEnd & 127 == 64) {
  220.                                         obj.var[0] = 22;
  221.                                         obj.curAnim = RABBIT::AIRBOARDTURN;
  222.                                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_AIRBTURN, 100, 16537);
  223.                                 }
  224.                                 if (obj.var[0] != 0) {
  225.                                         if ((obj.var[0] = obj.var[0] - 1) == 0) {
  226.                                                 obj.curAnim = RABBIT::AIRBOARD;
  227.                                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_AIRBTURN2, 0, 16537);
  228.                                         } else
  229.                                                 frameID = obj.var[0] / 3;
  230.                                 }
  231.                                 if (obj.var[0] == 0) { //not turning
  232.                                         frameID = obj.counterEnd >> 3;
  233.                                         if (obj.age < -60 && (obj.energy < 88 || (Player.yPos > obj.yPos && abs(Player.xPos - obj.xPos) < 50)) && !jjMaskedHLine(int(obj.xPos) - 5, 10, int(obj.yPos))) {
  234.                                                 jjOBJ@ rod = fireBullet(obj, lastCurFrame, OBJECT::TNT);
  235.                                                 jjSample(rod.xPos, rod.yPos, SOUND::COMMON_MONITOR, 0, 12500);
  236.                                                 rod.determineCurAnim(ANIM::CUSTOM[27], 0);
  237.                                                 rod.playerHandling = HANDLING::PARTICLE;
  238.                                                 rod.bulletHandling = HANDLING::IGNOREBULLET;
  239.                                                 rod.counterEnd = 255;
  240.                                                 rod.var[4] = 1; //color
  241.                                                 rod.behavior = Lightningrod;
  242.                                         }
  243.                                 }
  244.                                 obj.var[1] = jjSampleLooped(obj.xPos,obj.yPos,SOUND::COMMON_AIRBOARD,obj.var[1]);
  245.                                 break;
  246.                         case STATE::WALK: //Sanguine/None
  247.                                 obj.xSpeed = (89 - obj.energy) * 1.5 * obj.direction;
  248.                                 obj.behave(BEHAVIOR::WALKINGENEMY, false);
  249.                                 if (obj.doesHurt == FooWeapon::Sanguine) {
  250.                                         if (obj.counter % 70 == 10) {
  251.                                                 for (uint i = obj.energy; i < 89; ++i) {
  252.                                                         jjOBJ@ spear = fireBullet(obj, lastCurFrame, OBJECT::FIREBALLBULLET);
  253.                                                         jjSample(spear.xPos, spear.yPos, SOUND::HATTER_PTOEI, 0, 20000);
  254.                                                         spear.xSpeed = abs(obj.xSpeed) * 4;
  255.                                                         spear.xAcc = abs(spear.xAcc);
  256.                                                         spear.ySpeed = -2;
  257.                                                         spear.counterEnd = 95;
  258.                                                         spear.var[6] = 8;
  259.                                                         spear.behavior = BloodSpear;
  260.                                                         if (obj.energy != 88 && i == 88)
  261.                                                                 spear.ySpeed *= 2;
  262.                                                         else if (i == 87)
  263.                                                                 spear.ySpeed = 0;
  264.                                                 }
  265.                                         }
  266.                                 }
  267.                                 if (obj.energy == 88) obj.curAnim = RABBIT::RUN1;
  268.                                 else obj.curAnim = RABBIT::RUN2 + (87 - obj.energy);
  269.                                 frameID = ++obj.counter / int(10 - abs(obj.xSpeed));
  270.                                 break;
  271.                         case STATE::EXTRA: //dying
  272.                                 if (obj.justHit == 1 || bossDefeated == true) {
  273.                                         if (Player.bossActivated == true && loriCounter != 0)
  274.                                                 loriCounter--;
  275.                                         obj.curAnim = RABBIT::DIE;
  276.                                         loriHurtFreq = jjRandom()%3;
  277.                                         if (bossDefeated == false) {
  278.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_DIE1, 0, (loriHurtFreq == 0) ? 35000 : (loriHurtFreq == 1) ? 30000 : 25000);
  279.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_SHOOT); } }
  280.                                 if (not ((jjMaskedPixel(int(obj.xPos), int(obj.yPos)+22)) or (jjMaskedPixel(int(obj.xPos)+11, int(obj.yPos)+22)) or (jjMaskedPixel(int(obj.xPos)-11, int(obj.yPos)+22))))
  281.                                         obj.yPos = obj.yPos + 2 + ((obj.curAnim == RABBIT::CORPSE) ? 23 : (obj.frameID * 2));
  282.  
  283.                                 if (uint(obj.frameID) < 23) {
  284.                                         if (jjGameTicks % 7 == 0) obj.frameID++; }
  285.                                 else {
  286.                                         obj.curAnim = RABBIT::CORPSE;
  287.                                         //obj.delete();
  288.                                         return;
  289.                                 }
  290.                                 break;
  291.                         default: //deactivate/freeze/done
  292.                                 obj.behave(BEHAVIOR::TUBETURTLE, false);
  293.                                 return;
  294.                 }
  295.                 if (obj.justHit == 1 && obj.energy > 85) {
  296.                         uint loriHurt = jjRandom()%8;
  297.                         loriHurtFreq = jjRandom()%3;
  298.                         switch (loriHurtFreq) {
  299.                         case 0: loriHurtFreq = 35000; break;
  300.                         case 1: loriHurtFreq = 30000; break;
  301.                         case 2: loriHurtFreq = 25000; break;
  302.                         }
  303.                         switch (loriHurt) {
  304.                                 case 0:
  305.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_HURT0, 0, loriHurtFreq);
  306.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_HYDRO, 40);
  307.                                         break;
  308.                                 case 1:
  309.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_HURT1, 0, loriHurtFreq);
  310.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_HYDRO2, 40);
  311.                                         break;
  312.                                 case 2:
  313.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_HURT2, 0, loriHurtFreq);
  314.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_HYDROFIL, 40);
  315.                                         break;
  316.                                 case 3:
  317.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_HURT3, 0, loriHurtFreq);
  318.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_HYDRO, 40);
  319.                                         break;
  320.                                 case 4:
  321.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_HURT4, 0, loriHurtFreq);
  322.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_HYDRO2, 40);
  323.                                         break;
  324.                                 case 5:
  325.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_HURT5, 0, loriHurtFreq);
  326.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_HYDROFIL, 40);
  327.                                         break;
  328.                                 case 6:
  329.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_HURT6, 0, loriHurtFreq);
  330.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_HYDRO, 40);
  331.                                         break;
  332.                                 case 7:
  333.                                         jjSample(obj.xPos, obj.yPos, SOUND::LORISOUNDS_HURT7, 0, loriHurtFreq);
  334.                                         jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_HYDROFIL, 40);
  335.                                         break;
  336.                         }
  337.                 }
  338.                 if (obj.state != STATE::EXTRA)
  339.                         obj.curFrame = anim.firstFrame + (frameID % anim.frameCount); // hell if I know what was Violet doing with frameID...
  340.                 else obj.curFrame = anim.firstFrame + (obj.frameID % anim.frameCount);
  341.         }
  342.  
  343.         jjOBJ@ fireBullet(jjOBJ@ obj, int lastCurFrame, OBJECT::Object eventID) const {
  344.                 obj.age = 5;
  345.                 obj.curFrame = lastCurFrame; //for gunspot, etc.
  346.                 jjOBJ@ bullet = jjObjects[obj.fireBullet(eventID)];
  347.                 bullet.playerHandling = HANDLING::ENEMYBULLET;
  348.                 bullet.animSpeed = 1;
  349.                 return bullet;
  350.         }
  351.        
  352.         void onDraw(jjOBJ@ obj) {
  353.                 const SPRITE::Mode mode = obj.state == STATE::FREEZE ? SPRITE::FROZEN : obj.justHit == 0 ? SPRITE::PLAYER : SPRITE::SINGLECOLOR;
  354.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, mode, 28);
  355.                 if (obj.state == STATE::DONE)
  356.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TRANSLUCENTCOLOR, 28); //got to do it this way, because TINTED would erase the player colors and make it look like jazz instead of purplejazz
  357.                 else if (obj.age-- > 0) { //flare time
  358.                         const jjANIMFRAME@ frame = jjAnimFrames[obj.curFrame];
  359.                         jjDrawSprite(obj.xPos + (frame.hotSpotX - frame.gunSpotX) * obj.direction, obj.yPos + frame.hotSpotY - frame.gunSpotY, ANIM::AMMO, 16, 0, obj.direction, SPRITE::PLAYER, 28); //flare
  360.                 }
  361.         }
  362. }
  363.  
  364. int getParameterAtOrigin(const jjOBJ@ obj, int offset, int length) /*const*/ {
  365.         return jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, offset, length);
  366. }
  367.  
  368. void Meteor(jjOBJ@ obj) {
  369.         obj.behave(BEHAVIOR::BULLET, obj.state == STATE::EXPLODE? true:false);
  370.        
  371.         if (obj.state != STATE::EXPLODE) {
  372.                 obj.var[2] = 0;
  373.                 obj.age += obj.direction == 0? 10 : 10 * obj.direction;
  374.                
  375.                 jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[22], obj.eventID == OBJECT::BOUNCERBULLET? 1:0, 0, -obj.age, 1, 1, obj.eventID == OBJECT::BOUNCERBULLET || obj.var[4] == 1? SPRITE::SINGLEHUE : SPRITE::NORMAL, 72);
  376.                
  377.                 jjPARTICLE@ smoke = jjAddParticle(PARTICLE::SMOKE);
  378.                 if (smoke !is null) {
  379.                         smoke.xPos = smoke.xPos;
  380.                         smoke.yPos = smoke.yPos;
  381.                 }
  382.                
  383.                 if (obj.eventID == OBJECT::BOUNCERBULLETPU && obj.var[4] == 0) {
  384.                         jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[22], 0, 0, -obj.age, 1, 1, SPRITE::TRANSLUCENTSINGLEHUE, 40);
  385.                         jjPARTICLE@ cinders = jjAddParticle(PARTICLE::FIRE);
  386.                         if (cinders !is null) {
  387.                                 cinders.xPos = int(obj.xPos - 8) + jjRandom()%17;
  388.                                 cinders.yPos = int(obj.yPos - 8) + jjRandom()%17;
  389.                         }
  390.                 }
  391.                
  392.                 if (obj.yPos > jjWaterLevel) {
  393.                         obj.var[4] = 1;
  394.                         obj.xSpeed = obj.xSpeed * 0.875;
  395.                         obj.ySpeed = obj.ySpeed * 0.875;
  396.                 }
  397.        
  398.                 switch (obj.direction) {
  399.                         case 1: obj.xSpeed -= obj.eventID == OBJECT::BOUNCERBULLET? 0.1:0.15; obj.ySpeed += obj.eventID == OBJECT::BOUNCERBULLET? 0.15:0.2; break;
  400.                         case -1: obj.xSpeed += obj.eventID == OBJECT::BOUNCERBULLET? 0.1:0.15; obj.ySpeed += obj.eventID == OBJECT::BOUNCERBULLET? 0.15:0.2; break;
  401.                 }
  402.                
  403.                 if (obj.xSpeed == 0) obj.ySpeed += 0.4;
  404.                 if (obj.ySpeed > 8) obj.ySpeed = 8;
  405.                
  406.  
  407.                
  408.         } else {
  409.                 obj.age = 0;
  410.                 if (obj.var[2] == 0) {
  411.                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BENZIN1, 0, 0);
  412.                         obj.var[2] = 1;
  413.                        
  414.                         if (obj.eventID == OBJECT::BOUNCERBULLETPU) {
  415.                                 for (int i = -1; i <= 1; i+= 2) {
  416.                                         jjOBJ@ rock = jjObjects[jjAddObject(OBJECT::SHARD, int(obj.xPos + (i * 12)), int(obj.yPos - 8), obj.creatorID, CREATOR::OBJECT, Rock)];
  417.                                         rock.determineCurAnim(ANIM::CUSTOM[22], 1);
  418.                                         rock.playerHandling = HANDLING::ENEMYBULLET;
  419.                                         rock.var[3] = 2;
  420.                                         rock.var[4] = obj.var[4];
  421.                                         rock.var[6] = 8;
  422.                                         rock.animSpeed = 1;
  423.                                         rock.direction = i;
  424.                                         rock.xSpeed = 6 * i;
  425.                                         rock.ySpeed = -3;
  426.                                         rock.state = STATE::FLY;
  427.                                         rock.lightType = LIGHT::POINT;
  428.                                         rock.light = 10;
  429.                                         rock.counterEnd = jjObjectPresets[OBJECT::BOUNCERBULLET].counterEnd;
  430.                                         rock.killAnim = jjObjectPresets[OBJECT::BOUNCERBULLET].killAnim;
  431.                                 }
  432.                         }
  433.                 }
  434.         }
  435. }
  436.  
  437. void Rock(jjOBJ@ obj) {
  438.         obj.behave(BEHAVIOR::BULLET, obj.state == STATE::EXPLODE? true:false);
  439.        
  440.         if (obj.state == STATE::FLY) {
  441.                 obj.age += obj.direction == 0? 10 : 10 * obj.direction;
  442.                 jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[22], 1, 0, -obj.age, 1, 1, obj.var[4] == 1? SPRITE::SINGLEHUE : SPRITE::NORMAL, 72);
  443.                
  444.                 switch (obj.direction) {
  445.                         case 1: obj.xSpeed -= 0.05; obj.ySpeed += 0.1; break;
  446.                         case -1: obj.xSpeed += 0.05; obj.ySpeed += 0.1; break;
  447.                 }
  448.                
  449.                 if (obj.yPos > jjWaterLevel) {
  450.                         obj.var[4] = 1;
  451.                         obj.xSpeed = obj.xSpeed * 0.875;
  452.                         obj.ySpeed = obj.ySpeed * 0.875;
  453.                 }
  454.                
  455.                 jjPARTICLE@ smoke = jjAddParticle(PARTICLE::SMOKE);
  456.                 if (smoke !is null) {
  457.                         smoke.xPos = obj.xPos;
  458.                         smoke.yPos = obj.yPos;
  459.                 }
  460.                
  461.         }
  462.        
  463. }
  464.  
  465. class CosmicDust : jjBEHAVIORINTERFACE {
  466.         void onBehave(jjOBJ@ obj) {
  467.                 const jjOBJ@ creator = jjObjects[obj.creatorID];
  468.                 if (creator.eventID != OBJECT::MORPH) { //ded
  469.                         obj.delete();
  470.                         return;
  471.                 }
  472.                 obj.behave(obj.state == STATE::EXPLODE? BEHAVIOR::BULLET : BEHAVIOR::TNT, false);
  473.                 obj.var[0] = obj.var[0] + (5 * obj.direction);
  474.                
  475.                 if (obj.state != STATE::EXPLODE) {
  476.                         obj.xPos = creator.xPos + (obj.eventID == OBJECT::ICEBULLETPU? 160:120)*jjSin((obj.counter + 1)*12) * (obj.direction != 0? obj.direction : 1);
  477.                         obj.yPos = creator.yPos - (obj.eventID == OBJECT::ICEBULLETPU? 80:60)*jjCos((obj.counter + 1)*12);
  478.                 } else {
  479.                         obj.delete();
  480.                 }
  481.         }
  482.         void onDraw(jjOBJ@ obj) {
  483.                 if (obj.state != STATE::EXPLODE && obj.counter > 1) {
  484.                         jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[23], obj.eventID == OBJECT::ICEBULLETPU? 0:1, 0, -obj.var[0], 1, 1, SPRITE::ALPHAMAP, obj.eventID == OBJECT::ICEBULLETPU? 34:72);
  485.                         jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[23], obj.eventID == OBJECT::ICEBULLETPU? 0:1, 0, -obj.var[0], 1, 1, SPRITE::ALPHAMAP, obj.eventID == OBJECT::ICEBULLETPU? 34:72);
  486.                 }
  487.         }
  488.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  489.                 player.frozen = obj.freeze;
  490.                 obj.delete();
  491.                 return true;
  492.         }
  493. }
  494.  
  495. void Mortar(jjOBJ@ obj) {
  496.         obj.behave(obj.state == STATE::EXPLODE? BEHAVIOR::RFBULLET : BEHAVIOR::BULLET, false);
  497.         if (obj.yPos <= 0) obj.state = STATE::EXPLODE;
  498.         obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
  499.        
  500.         switch (obj.state) {
  501.                 case STATE::START:
  502.                         obj.state = STATE::FLY;
  503.                         obj.lightType = LIGHT::POINT;
  504.                         obj.var[2] = 0;
  505.                 break;
  506.                
  507.                 case STATE::FLY:
  508.                         jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[24], obj.eventID == OBJECT::SEEKERBULLETPU? 2:0, jjGameTicks >> 2, obj.var[0], 0.75, 0.75, SPRITE::NORMAL);
  509.                        
  510.                         if (obj.counter % 5 == 0 && !jjLowDetail) {
  511.                                 jjOBJ@ trail = jjObjects[jjAddObject(OBJECT::EXPLOSION, int(obj.xPos - jjCos(obj.var[0])), int(obj.yPos - jjSin(obj.var[0])))];
  512.                                 trail.determineCurAnim(ANIM::AMMO, 3);
  513.                                 trail.lightType = LIGHT::NONE;
  514.                                 trail.playerHandling = HANDLING::PARTICLE;
  515.                                 trail.bulletHandling = HANDLING::IGNOREBULLET;
  516.                                 trail.isBlastable = false;
  517.                         }
  518.                        
  519.                         switch (obj.direction) {
  520.                                 case 1: obj.xSpeed -= 0.275; obj.ySpeed += 0.225; break;
  521.                                 case -1: obj.xSpeed += 0.275; obj.ySpeed += 0.225; break;
  522.                         }
  523.                         if (obj.xSpeed == 0) obj.ySpeed += 0.3;
  524.                         if (obj.ySpeed > 12) obj.ySpeed = 12;
  525.                 break;
  526.                
  527.                 case STATE::EXPLODE:
  528.                         jjDrawResizedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 5, obj.curFrame + 5, 2, 2, SPRITE::NORMAL);
  529.                
  530.                         if (obj.var[2] == 0) {
  531.                                 jjOBJ@ blast = jjObjects[jjAddObject(OBJECT::BULLET, obj.xPos, obj.yPos, obj.creatorID, CREATOR::OBJECT, MortarShockwave)];
  532.                                 obj.var[2] = 1;
  533.                                 blast.var[2] = 1;
  534.                                 blast.animSpeed = obj.animSpeed;
  535.                         }
  536.                 break;
  537.         }
  538. }
  539.  
  540. void MortarShockwave(jjOBJ@ obj) {
  541.         obj.playerHandling = HANDLING::PARTICLE;
  542.         obj.bulletHandling = HANDLING::IGNOREBULLET;
  543.        
  544.         if (obj.var[2] == 1) {
  545.                 obj.lightType = obj.var[2] == 1? LIGHT::RING2 : LIGHT::NONE;
  546.                 obj.var[1] = obj.var[1] + 1;
  547.                 obj.light += 2;
  548.                 obj.var[4] = obj.light * 4;
  549.                
  550.                 jjPLAYER@ player = jjLocalPlayers[0];
  551.                 float dx = player.xPos - obj.xPos, dy = player.yPos - obj.yPos;
  552.                 if (dx * dx + dy * dy < obj.var[4] * obj.var[4])
  553.                         player.hurt(obj.animSpeed);
  554.         }
  555.        
  556.         if (obj.var[1] == 14) {
  557.                 obj.var[1] = 0;
  558.                 obj.var[2] = 0;
  559.                 obj.var[4] = 0;
  560.                 obj.delete();
  561.         }
  562. }
  563.                
  564. void BloodSpear(jjOBJ@ obj) {
  565.         obj.behave(BEHAVIOR::BULLET, false);
  566.         obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
  567.        
  568.         if (obj.state == STATE::FLY) {
  569.                 jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::HATTER, 3, jjGameTicks >> 2, obj.var[0], 2, 1, SPRITE::SINGLEHUE, obj.eventID == OBJECT::FIREBALLBULLETPU? 15:24);
  570.                 switch (obj.direction) {
  571.                         case 1: obj.xSpeed -= 0.1; obj.ySpeed += 0.1; break;
  572.                         case -1: obj.xSpeed += 0.1; obj.ySpeed += 0.1; break;
  573.                 }
  574.                 if (obj.xSpeed == 0) obj.ySpeed += 0.15;
  575.                
  576.                 jjPARTICLE@ blood = jjAddParticle(PARTICLE::ICETRAIL);
  577.                 blood.xPos = obj.xPos;
  578.                 blood.yPos = obj.yPos;
  579.                 blood.icetrail.color = obj.eventID == OBJECT::FIREBALLBULLETPU? 16:24;
  580.                 blood.icetrail.colorStop = obj.eventID == OBJECT::FIREBALLBULLETPU? 24:32;
  581.         }
  582.        
  583.         if (obj.state == STATE::EXPLODE) {
  584.                 jjDrawSprite(obj.xPos, obj.yPos, ANIM::MONKEY, 1, obj.curFrame, obj.direction, SPRITE::SINGLEHUE, obj.eventID == OBJECT::FIREBALLBULLETPU? 15:24);
  585.                 if (obj.var[1] < 27) {
  586.                         if (obj.var[1] == 1) jjSample(obj.xPos, obj.yPos, SOUND::HATTER_SPLOUT, 0, 20000);
  587.                         obj.var[1] = obj.var[1] + 1;
  588.                 }
  589.                 if (obj.var[1] == 27) {
  590.                         obj.var[1] = 0;
  591.                         obj.delete();
  592.                 }
  593.         }
  594. }
  595.  
  596. void Lightningrod(jjOBJ@ obj) {
  597.         obj.behave(BEHAVIOR::BULLET, false);
  598.        
  599.         jjDrawSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[27], obj.var[0] == 1? obj.var[4] : 0, obj.var[0] == 1? jjGameTicks / 10 % 6 : 0, obj.direction, SPRITE::NORMAL);
  600.        
  601.         if (obj.counter == 2) {
  602.                 obj.age = 0;
  603.                 obj.var[1] = 0;
  604.         }
  605.        
  606.         if (obj.counter == 127) obj.counter = 2;
  607.        
  608.         if (!jjMaskedHLine(int(obj.xPos - 16), 24, int(obj.yPos + 8))) {
  609.                 obj.yPos += 6;
  610.                 obj.var[0] = 0;
  611.                 obj.lightType = LIGHT::POINT;
  612.         } else {
  613.                 obj.var[0] = 1;
  614.         }
  615.        
  616.         if (obj.var[0] == 1) {
  617.                 obj.age++;
  618.                 if (obj.var[1] == 0) {
  619.                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_LANDCAN1, 0, 30000);
  620.                         obj.var[1] = 1;
  621.                 }
  622.                 obj.lightType = LIGHT::BRIGHT;
  623.                 obj.light = 10;
  624.                 if (obj.age % 20 == 0) {
  625.                         for (int i = -4; i <= 4; i += 4) {
  626.                                 int id = jjAddObject(OBJECT::LIGHTNINGSHIELDBULLET, obj.xPos, obj.yPos - 12, obj.creatorID, CREATOR::OBJECT, Electricity);
  627.                                 if (id != 0) {
  628.                                         jjOBJ@ zap = jjObjects[id];
  629.                                         zap.lightType = LIGHT::NONE;
  630.                                         zap.counterEnd = 6;
  631.                                         zap.direction = obj.direction;
  632.                                         zap.lightType = LIGHT::NONE;
  633.                                         zap.playerHandling = HANDLING::ENEMYBULLET;
  634.                                         zap.var[3] = 1;
  635.                                         zap.var[6] = 8;
  636.                                         zap.xSpeed = i;
  637.                                         zap.xAcc = 0;
  638.                                         zap.animSpeed = 1;
  639.                                         if (i == 0) zap.ySpeed = -4;
  640.                                 }
  641.                         }
  642.                 }
  643.         }
  644.        
  645.         if (obj.age == 420) {
  646.                 obj.var[0] = obj.var[1] = 0;
  647.                 obj.particlePixelExplosion(0);
  648.                 obj.delete();
  649.         }
  650. }
  651.  
  652. void Electricity(jjOBJ@ obj) {
  653.         obj.behave(BEHAVIOR::BULLET, false);
  654. }
  655.  
  656. void onFunction0(jjPLAYER@ player) {
  657.        player.activateBoss();
  658.            jjTriggers[1] = true;
  659.            player.invincibility = 0;
  660.            jjMusicLoad("fireeye.ogg");
  661.            player.limitXScroll(uint(136), uint(26));
  662. }
  663.  
  664. class CloneMachine: jjBEHAVIORINTERFACE {
  665.         uint recharge, maxHP;
  666.         int currentHP;
  667.         CloneMachine(jjOBJ@ preset) {
  668.                 preset.behavior = this;
  669.                 preset.playerHandling = HANDLING::SPECIAL;
  670.                 preset.bulletHandling = HANDLING::DETECTBULLET;
  671.                 preset.scriptedCollisions = true;
  672.                 preset.isTarget = false;
  673.                 preset.isFreezable = false;
  674.                 preset.isBlastable = false;
  675.                 preset.triggersTNT = false;
  676.                 preset.deactivates = false;
  677.                 preset.determineCurAnim(ANIM::CUSTOM[28], 0);
  678.                 //preset.determineCurFrame();
  679.                 preset.curFrame = 2;
  680.                 preset.direction = 1;
  681.                 preset.special = 0;
  682.                 preset.counter = 0;
  683.                 preset.frameID = 2;
  684.                 preset.energy = 100;
  685.                 recharge = (jjDifficulty <= 0) ? 320 : (jjDifficulty == 1) ? 280 : 240;
  686.                 maxHP = (jjDifficulty <= 0) ? 150 : (jjDifficulty == 1) ? 225 : 300;
  687.                 currentHP = maxHP;
  688.         }
  689.        
  690.     void onBehave(jjOBJ @ obj) {
  691.           obj.energy = 100 * currentHP / maxHP;
  692.           int playerID = obj.findNearestPlayer(8000000);
  693.           if (jjPlayers[playerID].bossActivated == false && bossDefeated == false) return;
  694.           if (uint(obj.special) < recharge && obj.state != STATE::EXTRA) {
  695.                 obj.special++;
  696.           }
  697.           else if (uint(obj.special) >= recharge && obj.state != STATE::KILL && obj.state != STATE::EXTRA) obj.special = 0;
  698.       if (obj.state == STATE::START) {
  699.                 jjPlayers[playerID].boss = obj.objectID;
  700.                 obj.putOnGround();
  701.           }
  702.           if (obj.special == 140 && loriCounter < 5 && obj.state != STATE::EXTRA) {
  703.                 if (obj.counter == 0) { // preventing double Lori spawn
  704.                         switch (jjRandom()%4) {
  705.                         case 0: jjParameterSet(151, 11, 0, 3, 0);       break;
  706.                         case 1: jjParameterSet(151, 11, 0, 3, 1);       break;
  707.                         case 2: jjParameterSet(151, 11, 0, 3, 2);       break;
  708.                         case 3: jjParameterSet(151, 11, 0, 3, 5);       break;
  709.                         }
  710.                         jjObjects[jjAddObject(OBJECT::MORPH, obj.xPos - 150, obj.yPos - 20)];
  711.                         jjSample(jjPlayers[playerID].xPos, jjPlayers[playerID].yPos, SOUND::COMMON_TELPORT2);
  712.                         loriCounter++;
  713.                         obj.counter = 1;
  714.                         }
  715.                 }
  716.                 else obj.counter = 0;
  717.                
  718.           if (playerID > -1) {
  719.                 if (obj.age > 0) obj.age--;
  720.                 jjPLAYER@ play;
  721.                 float dx = jjPlayers[playerID].xPos - obj.xPos + 80, dy = jjLocalPlayers[playerID].yPos + 50 - obj.yPos;
  722.                 if (dx * dx + dy * dy < 140 * 140) {
  723.                         obj.age = 35;
  724.                         jjPlayers[playerID].xSpeed = -8 * obj.direction;
  725.                         //jjPlayers[playerID].ySpeed = -8 * obj.direction;
  726.                         //jjPlayers[playerID].hurt(0, false);
  727.                 }
  728.           }
  729.       if (obj.justHit == 0 && obj.state != STATE::EXTRA) {
  730.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL, 0, 5);
  731.       } else if (obj.state != STATE::EXTRA)
  732.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::SINGLECOLOR, 28, 5);
  733.           bossStarted = true;
  734.           if (obj.state == STATE::KILL) {
  735.                 jjPlayers[playerID].activateBoss(false);
  736.                 jjSample(jjPlayers[playerID].xPos, jjPlayers[playerID].yPos, SOUND::INTRO_BOEM2);
  737.                 jjNxt(false, false);
  738.                 bossDefeated = true;
  739.                 obj.special = 254;
  740.                 obj.state = STATE::EXTRA;
  741.           }      
  742.          
  743.           if (obj.state == STATE::EXTRA) {
  744.                 if (obj.special > 0)
  745.                         obj.special--;
  746.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::BLEND_DISSOLVE, obj.special);
  747.           }
  748.           if (obj.justHit == 1) {
  749.                 switch (jjRandom()%5) {
  750.                         case 0: jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_METAL1);      break;
  751.                         case 1: jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_METAL2);      break;
  752.                         case 2: jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_METAL3);      break;
  753.                         case 3: jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_METAL4);      break;
  754.                         case 4: jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_METAL5);      break;
  755.                         }
  756.           }
  757.           if (loriCounter < 5 && obj.state != STATE::EXTRA) {
  758.                 if (obj.special == 120 || obj.special == 200) obj.frameID = 1;
  759.                 if (obj.special == 220) obj.frameID = 2;
  760.                 if (obj.special == 140) obj.frameID = 0;
  761.           }
  762.           else obj.frameID = 2;
  763.           obj.curFrame = jjAnimations[jjAnimSets[ANIM::CUSTOM[28]]].firstFrame + (obj.frameID % jjAnimations[jjAnimSets[ANIM::CUSTOM[28]]].frameCount);
  764.      }
  765.        
  766.          bool onObjectHit(jjOBJ @ obj, jjOBJ @ bullet, jjPLAYER @ player, int force) {
  767.         if (bullet !is null) {
  768.                         bullet.state = STATE::EXPLODE;
  769.                         obj.justHit = 5; //flash white for 5 ticks--jjOBJ::justHit is automatically deincremented by the JJ2 engine, so individual behavior functions don't need to worry about doing that.
  770.                         currentHP -= bullet.animSpeed;
  771.                         if (currentHP <= 0) { //killed
  772.                                 currentHP = 0;
  773.                                 obj.state = STATE::KILL;
  774.                         }              
  775.       }
  776.       return true;
  777.     }
  778.   }
  779.  
  780. bool onCheat(string &in cheat) {
  781.         jjPLAYER@ Player = jjLocalPlayers[0];
  782.         if (cheat == "jjgems")
  783.                 Player.gems[GEM::RED] = Player.gems[GEM::RED] + 100;
  784.         else if (cheat == "jjmorph" || cheat == "jjcolor")
  785.                 return true; // forcing the player to play as canon Lori
  786.         else if ((cheat == "jjk" || cheat == "jjkill") && Player.health <= 0) { ; }
  787.         else if (cheat == "jjlori")
  788.                 jjSample(Player.xPos, Player.yPos, SOUND::LORISOUNDS_TOUCH);
  789.         else
  790.                 return false;
  791.         jjAlert(cheat, false, STRING::MEDIUM);
  792.         return true;
  793. }