Downloads containing HH17_Guardian.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Holiday Hare '17Featured Download ShadowGPW Single player 8.8 Download file

File preview

  1. #include "MLLE-Include-1.6.asc" ///@MLLE-Generated
  2. const bool MLLESetupSuccessful = MLLE::Setup();
  3. #pragma require "HH17_Guardian.j2l"
  4. #pragma require "Icicle.j2a"
  5. #pragma require "HH17_Dragon.j2a"
  6. #pragma require "SEXmas.j2a"
  7. #pragma require "HH17_Roar.wav"
  8.  
  9. bool dragonActivated = false;
  10. enum dragonStates {Intro, Idle, Roar, Homing_Fire, Flamethrower, Flame_Burst, Firebombs, Spawn_FireOrb, Shockwave};
  11. uint currState = Intro;
  12.  
  13. int randcar = 20;
  14. int frameRate = 7;
  15. int shockSpeed = 3;
  16. int healthFactor = 7;
  17. int elapsed = 0;
  18. bool charging = false;
  19. bool defeated = false;
  20.  
  21. void singleColorLayer(jjLAYER@ layer, int color) {
  22.         array<int> tileIDs, uniqueTileIDs;
  23.         for (int i = 0; i < layer.height; i++) {
  24.                 for (int j = 0; j < layer.width; j++) {
  25.                         int tileID = layer.tileGet(j, i);
  26.                         if (tileID != 0)
  27.                                 tileIDs.insertLast(tileID);
  28.                 }
  29.         }
  30.         int prev = 0;
  31.         tileIDs.sortAsc();
  32.         for (uint i = 0; i < tileIDs.length(); i++) {
  33.                 if (tileIDs[i] != prev)
  34.                         uniqueTileIDs.insertLast(prev = tileIDs[i]);
  35.         }
  36.         uint firstNewTile = jjTileCount;
  37.         jjTilesFromTileset(jjTilesetFileName, 1, uniqueTileIDs.length());
  38.         for (uint i = 0; i < uniqueTileIDs.length(); i++) {
  39.                 jjPIXELMAP tile(uniqueTileIDs[i]);
  40.                 for (int j = 0; j < 32; j++) {
  41.                         for (int k = 0; k < 32; k++) {
  42.                                 uint8 pixel = tile[k, j];
  43.                                 if (pixel != 0) {
  44.                                         tile[k, j] = color;
  45.                                 }
  46.                         }
  47.                 }
  48.                 tile.save(firstNewTile + i);
  49.         }
  50.         layer.generateSettableTileArea();
  51.         for (int i = 0; i < layer.height; i++) {
  52.                 for (int j = 0; j < layer.widthReal; j++) {
  53.                         int tileID = layer.tileGet(j, i);
  54.                         if (tileID != 0)
  55.                                 layer.tileSet(j, i, firstNewTile + uniqueTileIDs.find(tileID));
  56.                 }
  57.         }
  58. }
  59.  
  60. void onLevelLoad() {
  61.         jjUseLayer8Speeds = true;
  62.         jjTexturedBGFadePositionY = 0.52;
  63.        
  64.         singleColorLayer(jjLayers[6], 176);
  65.         singleColorLayer(jjLayers[7], 204);
  66.        
  67.         jjAnimSets[ANIM::CUSTOM[0]].load(0, "HH17_Dragon.j2a");
  68.         jjOBJ@ preset = jjObjectPresets[OBJECT::LIZARD];
  69.         preset.determineCurAnim(ANIM::CUSTOM[0], 0);
  70.         preset.behavior = DragonBoss();
  71.         preset.energy = 100;
  72.         preset.points = 50000;          
  73.         preset.scriptedCollisions = true;
  74.         preset.playerHandling = HANDLING::SPECIAL;
  75.         preset.bulletHandling = HANDLING::DETECTBULLET;
  76.         preset.isBlastable = false;
  77.         preset.isTarget = false;
  78.         preset.special = 0;
  79.         preset.animSpeed = 2;
  80.        
  81.         switch (jjDifficulty) {
  82.                 case 0: preset.age = 500; healthFactor = 5; break;
  83.                 case 1: preset.age = 700; healthFactor = 7; break;
  84.                 case 2: preset.age = 900; healthFactor = 9; break;
  85.                 default: preset.age = 900; healthFactor = 9; break;
  86.         }
  87.        
  88.         jjAnimSets[ANIM::CUSTOM[2]].load(0, "HH17_Icicle.j2a");
  89.         jjSampleLoad(SOUND::P2_FOEW1, "HH17_Glass2.wav");
  90.         jjSampleLoad(SOUND::P2_FOEW4, "HH17_Glass3.wav");
  91.         jjSampleLoad(SOUND::P2_FOEW5, "HH17_Glass4.wav");
  92.        
  93.         jjSampleLoad(SOUND::INTRO_MONSTER, "HH17_Roar.wav");
  94.        
  95.         jjAnimSets[ANIM::CUSTOM[1]].load(2, "SExmas.j2a");
  96.         jjObjectPresets[OBJECT::BOMBCRATE].behavior = GiftBox();
  97.         jjObjectPresets[OBJECT::BOMBCRATE].determineCurAnim(ANIM::CUSTOM[1], 0);
  98.        
  99.         jjObjectPresets[OBJECT::ICEBULLET].var[3] = jjObjectPresets[OBJECT::ICEBULLETPU].var[3] = 3;
  100. }
  101.  
  102. void onLevelReload() {
  103.         onLevelLoad();
  104.         currState = Intro;
  105. }
  106.  
  107. bool inView(const jjOBJ@ obj) {
  108.         for (int i = 0; i < jjLocalPlayerCount; i++) {
  109.                 const jjPLAYER@ player = jjLocalPlayers[i];
  110.                 if (obj.xPos > player.cameraX - 64 && obj.yPos > player.cameraY - 64 && obj.xPos < player.cameraX + jjSubscreenWidth + 64 && obj.yPos < player.cameraY + jjSubscreenHeight + 64)
  111.                         return true;
  112.         }
  113.         return false;
  114. }
  115.  
  116. void onLevelBegin() {
  117.         jjPIXELMAP blackTile(243);
  118.         for (uint x = 0; x < blackTile.width; ++x) {
  119.                 for (uint y = 0; y < blackTile.height; ++y) {
  120.                         blackTile[x,y] = 180;
  121.                 }
  122.         }
  123.         blackTile.save(243, true);
  124. }
  125.  
  126. class GiftBox : jjBEHAVIORINTERFACE {
  127.         void destroy(jjOBJ@ obj) {
  128.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_WOOD1);
  129.                 {
  130.                         int id = jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos);
  131.                         if (id != 0) {
  132.                                 jjOBJ@ other = jjObjects[id];
  133.                                 other.determineCurAnim(ANIM::PICKUPS, 4);
  134.                         }
  135.                 }
  136.                 for (int i = jjRandom() & 7 | 8; i-- != 0;) {
  137.                         int id = jjAddObject(OBJECT::SHARD, obj.xPos, obj.yPos);
  138.                         if (id != 0) {
  139.                                 jjOBJ@ other = jjObjects[id];
  140.                                 other.determineCurAnim(ANIM::PICKUPS, 93 + (jjRandom() & 1));
  141.                         }
  142.                 }
  143.                 obj.yPos -= 8.f;
  144.                 for (int i = obj.var[1]; i-- != 0;) {
  145.                         int id = jjAddObject(obj.var[0], obj.xPos, obj.yPos);
  146.                         if (id != 0) {
  147.                                 jjOBJ@ other = jjObjects[id];
  148.                                 if (other.playerHandling == HANDLING::PICKUP) {
  149.                                         int angle = (jjRandom() & 255) + 128;
  150.                                         other.xSpeed = jjCos(angle) * 5.f;
  151.                                         other.ySpeed = jjSin(angle) * -3.f;
  152.                                 } else if (other.playerHandling == HANDLING::SPECIAL) {
  153.                                         other.deactivates = false;
  154.                                 }
  155.                         }
  156.                 }
  157.                 obj.clearPlatform();
  158.                 obj.delete();
  159.         }
  160.         void onBehave(jjOBJ@ obj) override {
  161.                 switch (obj.state) {
  162.                         case STATE::START:
  163.                                 {
  164.                                         uint16 x = int(obj.xOrg) >>> 5;
  165.                                         uint16 y = int(obj.yOrg) >>> 5;
  166.                                         obj.var[0] = jjParameterGet(x, y, 0, 8);
  167.                                         obj.var[1] = jjParameterGet(x, y, 8, 4);
  168.                                         obj.curAnim += jjParameterGet(x, y, 12, 2);
  169.                                         obj.determineCurFrame();
  170.                                         obj.bulletHandling = HANDLING::DESTROYBULLET;
  171.                                         obj.scriptedCollisions = true;
  172.                                 }
  173.                                 break;
  174.                         case STATE::FALL:
  175.                                 obj.var[2] = 1;
  176.                                 break;
  177.                         case STATE::FREEZE:
  178.                         case STATE::SLEEP:
  179.                                 if (obj.var[2] != 0) {
  180.                                         destroy(obj);
  181.                                         return;
  182.                                 }
  183.                 }
  184.                 obj.behave(BEHAVIOR::MONITOR);
  185.         }
  186.         bool onObjectHit(jjOBJ@, jjOBJ@, jjPLAYER@, int) {
  187.                 return true;
  188.         }
  189.         bool onIsSolid(jjOBJ@) {
  190.                 return true;
  191.         }
  192. }
  193.  
  194. class DragonBoss : jjBEHAVIORINTERFACE {
  195.         void onBehave(jjOBJ@ obj) {
  196.                 obj.determineCurFrame();
  197.  
  198.                 if (obj.freeze == 0 && obj.var[5] == 0) {
  199.                         if (jjGameTicks % frameRate == 0) obj.frameID++;
  200.                         obj.special++;
  201.                 }
  202.                
  203.                 frameRate = obj.energy > 50? 7 : obj.energy < 25? 4 : 5;
  204.                 shockSpeed = obj.energy < 50? 4:3;
  205.                
  206.                 if (obj.energy < 25 && obj.var[5] == 0) {
  207.                         dragonShout(obj, 36);
  208.                        
  209.                         if (obj.special % 250 == 0) jjSamplePriority(SOUND::INTRO_MONSTER);
  210.                 }
  211.                
  212.                 if (obj.energy < 25 && obj.var[9] == 0) {
  213.                         jjLocalPlayers[0].showText("@@@@@@@Uh-oh! You've made the dragon very angry indeed!",  STRING::MEDIUM);
  214.                         obj.var[9] = 1;
  215.                 }
  216.                
  217.                 if (obj.frameID > 20) obj.frameID = currState == Idle? 15:0;
  218.  
  219.                 if (obj.state != STATE::KILL) {
  220.                         if (obj.var[5] == 0) {
  221.                                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : obj.freeze > 0? SPRITE::FROZEN : SPRITE::SINGLEHUE, obj.justHit > 0? 15:71);
  222.                                 if (obj.justHit == 0 && obj.freeze == 0 && jjColorDepth == 16) {
  223.                                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NEONGLOW, 3);
  224.                                         if (charging && obj.special % 5 == 0) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TRANSLUCENTCOLOR, 15);
  225.                                         if (obj.energy < 75) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NEONGLOW, 0);
  226.                                         if (obj.energy < 50) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NEONGLOW, 0);
  227.                                         if (obj.energy < 25) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NEONGLOW, 0);
  228.                                 }
  229.                         } else if (obj.var[5] % 10 > 3) {
  230.                                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : obj.freeze > 0? SPRITE::FROZEN : SPRITE::SINGLEHUE, obj.justHit > 0? 15:71);
  231.                                 if (obj.justHit == 0 && obj.freeze == 0 && jjColorDepth == 16) {
  232.                                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NEONGLOW, 0);
  233.                                         if (obj.energy < 75) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NEONGLOW, 0);
  234.                                         if (obj.energy < 50) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NEONGLOW, 0);
  235.                                         if (obj.energy < 25) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NEONGLOW, 0);
  236.                                 }
  237.                         }
  238.                 }
  239.                 obj.direction = 1;
  240.                 obj.yPos = obj.yOrg + 8;
  241.                
  242.                 for (int i = 0; i < jjLocalPlayerCount; i++) {
  243.                         if (jjLocalPlayers[i].xPos > obj.xPos) jjLocalPlayers[i].xPos = obj.xPos;
  244.                 }
  245.                
  246.                 if (obj.var[1] == 0) {
  247.                         jjOBJ@ target = jjObjects[jjAddObject(OBJECT::FLICKERLIGHT, obj.xPos - 32, obj.yPos - 140)];
  248.                         target.isTarget = true;
  249.                         target.light = 0;
  250.                         target.lightType = LIGHT::NONE;
  251.                         obj.var[1] = 1;
  252.                 }
  253.                
  254.                 if (obj.state == STATE::FREEZE) {
  255.                         obj.var[10] = obj.var[10] - 1;
  256.                         if (obj.var[10] == 0) {
  257.                                 obj.unfreeze(1);
  258.                                 obj.state = obj.oldState;
  259.                         }
  260.                 } else {
  261.                         obj.var[10] = 105;
  262.                 }
  263.                
  264.                 switch (jjDifficulty) {
  265.                         case 0: randcar = obj.energy < 25? 15:10; break;
  266.                         case 1: randcar = obj.energy < 25? 25:15; break;
  267.                         case 2: randcar = obj.energy < 25? 30:20; break;
  268.                         default: randcar = obj.energy < 25? 30:20; break;
  269.                 }
  270.                
  271.                 switch (currState) {
  272.                         case Intro:
  273.                                 for (int i = 0; i < jjLocalPlayerCount; i++) {
  274.                                         if (inView(obj)) {
  275.                                                 obj.var[11] = 1;
  276.                                                 if (obj.frameID == 7 && obj.special % 7 == 0) {
  277.                                                         jjSamplePriority(SOUND::INTRO_MONSTER);
  278.                                                 }
  279.                                         }
  280.                                         if (obj.var[11] == 0) {
  281.                                                 obj.frameID = 0;
  282.                                                 obj.special = 0;
  283.                                         }
  284.                                         if (obj.special >= 185 && obj.frameID >= 15 && obj.frameID <= 20) {
  285.                                                 currState = Flamethrower;
  286.                                                 obj.special = 0;
  287.                                         }
  288.                                 }
  289.                         break;
  290.                         case Idle:
  291.                                 if (obj.frameID < 15) obj.frameID = 15;
  292.                                 if (obj.frameID == 20) obj.frameID = 15;
  293.                                
  294.                                 if (obj.special >= 105) {
  295.                                         randomAttack(obj);
  296.                                 }
  297.                         break;
  298.                         case Roar:
  299.                                 if (obj.frameID == 8 && obj.special % 7 == 1) {
  300.                                         jjSamplePriority(SOUND::INTRO_MONSTER);
  301.                                         jjSample(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, CollapsingIceSounds[jjRandom()%3]);
  302.                                         obj.var[4] = 1;
  303.                                 }
  304.                                 if (obj.frameID >= 9 && obj.var[4] == 1) {
  305.                                         dragonShout(obj, obj.energy < 50? 7:9);
  306.                                         int playerID = obj.findNearestPlayer(100000);
  307.                                         if (playerID >= -1) {
  308.                                                 jjPlayers[playerID].xSpeed = jjPlayers[playerID].xSpeed - 1;
  309.                                         }
  310.                                 }
  311.                                
  312.                                 if ((obj.special >= 70 && obj.frameID == 0) || obj.energy < 25) {
  313.                                         randomAttack(obj);
  314.                                         obj.var[4] = 0;
  315.                                         jjLocalPlayers[0].cameraUnfreeze();
  316.                                 }
  317.                                
  318.                         break;
  319.                         case Homing_Fire:
  320.                                 if (obj.frameID >= 7 && obj.frameID <= 10) {   
  321.                                         if (obj.counter < 7) {
  322.                                                 if (obj.counter % 7  == 0 && obj.freeze == 0 && obj.var[5] == 0) {
  323.                                                         jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::BULLET)];
  324.                                                         bullet.determineCurAnim(ANIM::BILSBOSS, 3);
  325.                                                         bullet.animSpeed = 1;
  326.                                                         bullet.behavior = HomingBullet();
  327.                                                         bullet.playerHandling = HANDLING::ENEMYBULLET;
  328.                                                         bullet.triggersTNT = true;
  329.                                                         bullet.isBlastable = true;
  330.                                                 }
  331.                                                        
  332.                                                 if (obj.counter == 1) {
  333.                                                         int playerID = obj.findNearestPlayer(40000);
  334.                                                         if (playerID >= -1) {
  335.                                                                 jjSamplePriority(SOUND::DEVILDEVAN_DRAGONFIRE);
  336.                                                                 jjSamplePriority(SOUND::BILSBOSS_FIRE);
  337.                                                         }
  338.                                                 }
  339.                                         }
  340.                                         obj.counter++;
  341.                                 }
  342.                                 else obj.counter = 0;
  343.                                
  344.                                 if (obj.special >= 140 && obj.frameID >= 15 && obj.frameID <= 20) {
  345.                                         randomAttack(obj);
  346.                                 }
  347.                         break;
  348.                         case Flamethrower:
  349.                                 if (obj.frameID == 5 && obj.special % 7 == 0 && obj.var[5] == 0) jjSamplePriority(SOUND::BILSBOSS_FIRESTART);
  350.                                 if (obj.frameID >= 7 && obj.frameID <= 10) {
  351.                                         obj.var[4] = 1;
  352.                                         int playerID = obj.findNearestPlayer(1000000);
  353.                                         int sine = obj.energy > 50? 1: obj.energy < 25? 3:2;
  354.                                         if (obj.counter % 1 == 0 && obj.freeze == 0 && obj.var[5] == 0) {
  355.                                                 jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::TOASTERBULLETPU)];
  356.                                                 bullet.behavior = Firebreather();
  357.                                                 bullet.lightType = LIGHT::POINT;
  358.                                                 bullet.playerHandling = HANDLING::ENEMYBULLET;
  359.                                                 bullet.animSpeed = 1;
  360.                                                 bullet.xSpeed = -16;
  361.                                                 float ys = jjRandom()%sine + (jjCos(obj.counter*32)) + jjSin(obj.counter*2)*2;
  362.                                                 bullet.ySpeed = ys;
  363.                                                 bullet.counterEnd = 165;
  364.                                         }
  365.                                        
  366.                                                 if (obj.counter == 1) {
  367.                                                         if (playerID >= -1) {
  368.                                                                 jjSamplePriority(SOUND::BILSBOSS_FIRE);
  369.                                                         }
  370.                                                 }
  371.                                        
  372.                                         if (obj.counter % 35 == 0 && obj.counter > 1 && obj.freeze == 0 && obj.var[5] == 0) jjSamplePriority(SOUND::COMMON_FLAMER);
  373.                                        
  374.                                         obj.counter++;
  375.                                 }
  376.                                 else obj.counter = 0;
  377.                                
  378.                                 if (obj.var[4] == 1 && obj.special < 490 && obj.frameID > 9) obj.frameID = 7;
  379.                                
  380.                                 if (obj.special >= 490 && obj.frameID >= 15 && obj.frameID <= 20) {
  381.                                         randomAttack(obj);
  382.                                         obj.var[4] = 0;
  383.                                 }
  384.                         break;
  385.                         case Flame_Burst:
  386.                                 if (obj.frameID >= 7 && obj.frameID <= 10 && obj.var[5] == 0) {
  387.                                         obj.var[4] = 1;
  388.                                         int playerID = obj.findNearestPlayer(1000000);
  389.                                         int fireRate = obj.energy < 50? 6:10;
  390.                                         if (obj.counter % fireRate == 0 && obj.freeze == 0) {
  391.                                                 jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::FIRESHIELDBULLET)];
  392.                                                 bullet.behavior = BEHAVIOR::BULLET;
  393.                                                 bullet.lightType = LIGHT::POINT;
  394.                                                 bullet.playerHandling = HANDLING::ENEMYBULLET;
  395.                                                 bullet.animSpeed = 1;
  396.                                                 bullet.xSpeed = -6;
  397.                                                 bullet.xAcc = 0;
  398.                                                 bullet.yAcc = 0;
  399.                                                 float ys = jjRandom()%4 + (jjCos(obj.counter*32));
  400.                                                 bullet.ySpeed = ys;
  401.                                                 bullet.counterEnd = 200;
  402.                                         }
  403.                                        
  404.                                                 if (obj.counter == 1) {
  405.                                                         if (playerID >= -1) {
  406.                                                                 jjSamplePriority(SOUND::BILSBOSS_FIRE);
  407.                                                         }
  408.                                                 }
  409.                                        
  410.                                         if (obj.counter % fireRate == 0 && obj.counter > 1 && obj.freeze == 0) jjSamplePriority(jjRandom()%2 > 0? SOUND::AMMO_FIREGUN1A : SOUND::AMMO_FIREGUN2A);
  411.                                        
  412.                                         obj.counter++;
  413.                                 }
  414.                                 else obj.counter = 0;
  415.                                
  416.                                 if (obj.var[4] == 1 && obj.special < 350 && obj.frameID > 9) obj.frameID = 7;
  417.                                
  418.                                 if (obj.special >= 350 && obj.frameID >= 15 && obj.frameID <= 20) {
  419.                                         randomAttack(obj);
  420.                                         obj.var[4] = 0;
  421.                                 }
  422.                         break;
  423.                         case Firebombs:
  424.                                 if (obj.frameID >= 7 && obj.frameID <= 10 && obj.var[5] == 0) {
  425.                                         obj.var[4] = 1;
  426.                                         int playerID = obj.findNearestPlayer(1000000);
  427.                                         int fireRate = obj.energy < 50? 9:14;
  428.                                         if (obj.counter % fireRate == 0 && obj.freeze == 0) {
  429.                                                 jjOBJ@ bullet = jjObjects[obj.fireBullet(OBJECT::FIRESHIELDBULLET)];
  430.                                                 jjSamplePriority(SOUND::BILSBOSS_FIRE);
  431.                                                 bullet.behavior = Firebomb();
  432.                                                 bullet.special = bullet.determineCurAnim(ANIM::BUBBA, 4);
  433.                                                 bullet.lightType = LIGHT::POINT;
  434.                                                 bullet.playerHandling = HANDLING::ENEMYBULLET;
  435.                                                 bullet.animSpeed = 1;
  436.                                                 bullet.xSpeed = -(4 + jjRandom()%3);
  437.                                                 bullet.ySpeed = -(6 + jjRandom()%3);
  438.                                                 bullet.xAcc = 0;
  439.                                                 bullet.yAcc = 0;
  440.                                                 bullet.counterEnd = 200;
  441.                                                 bullet.killAnim = jjObjectPresets[OBJECT::SEEKERBULLET].killAnim;
  442.                                         }
  443.                                        
  444.                                                 if (obj.counter == 1) {
  445.                                                         if (playerID >= -1) {
  446.                                                                 jjSamplePriority(SOUND::BILSBOSS_FIRESTART);
  447.                                                         }
  448.                                                 }
  449.                                        
  450.                                         obj.counter++;
  451.                                 }
  452.                                 else obj.counter = 0;
  453.                                
  454.                                 if (obj.var[4] == 1 && obj.special < 350 && obj.frameID > 9) obj.frameID = 7;
  455.                                
  456.                                 if (obj.special >= 350 && obj.frameID >= 15 && obj.frameID <= 20) {
  457.                                         randomAttack(obj);
  458.                                         obj.var[4] = 0;
  459.                                 }
  460.                         break;
  461.                         case Spawn_FireOrb:
  462.                                 if (obj.frameID >= 7 && obj.frameID <= 10 && obj.var[5] == 0) {
  463.                                         if (obj.counter < 7) {
  464.                                                 if (obj.counter % 7 == 0 && obj.freeze == 0) {
  465.                                                         jjOBJ@ orb = jjObjects[obj.fireBullet(OBJECT::SPARK)];
  466.                                                         orb.determineCurAnim(ANIM::AMMO, 77);
  467.                                                         orb.behavior = FireOrb();
  468.                                                         orb.playerHandling = HANDLING::SPECIAL;
  469.                                                         orb.bulletHandling = HANDLING::DETECTBULLET;
  470.                                                         orb.energy = 4;
  471.                                                         orb.triggersTNT = true;
  472.                                                         orb.isBlastable = true;
  473.                                                         orb.scriptedCollisions = true;
  474.                                                 }
  475.                                                        
  476.                                                 if (obj.counter == 1) {
  477.                                                         int playerID = obj.findNearestPlayer(40000);
  478.                                                         if (playerID >= -1) {
  479.                                                                 jjSamplePriority(SOUND::DEVILDEVAN_DRAGONFIRE);
  480.                                                                 jjSamplePriority(SOUND::BILSBOSS_FIRE);
  481.                                                         }
  482.                                                 }
  483.                                         }
  484.                                         obj.counter++;
  485.                                 }
  486.                                 else obj.counter = 0;
  487.                                
  488.                                 if (obj.special >= 140 && obj.frameID >= 15 && obj.frameID <= 20) {
  489.                                         randomAttack(obj);
  490.                                 }
  491.                         break;
  492.                         case Shockwave:
  493.                                 if (obj.frameID >= 7 && obj.frameID <= 10 && obj.var[5] == 0) {
  494.                                         if (obj.counter == 1 && obj.freeze == 0) {
  495.                                                 jjOBJ@ blast = jjObjects[obj.fireBullet(OBJECT::BULLET)];
  496.                                                 blast.behavior = DragonShockwave();
  497.                                                 blast.lightType = LIGHT::RING;
  498.                                                 blast.light = 100;
  499.                                         }
  500.                                         obj.counter++;
  501.                                 }
  502.                                 else obj.counter = 0;
  503.                                
  504.                                 if (obj.special >= 140 && obj.frameID >= 15 && obj.frameID <= 20) {
  505.                                         randomAttack(obj);
  506.                                 }
  507.                         break;
  508.                 }
  509.  
  510.                 if (obj.freeze == 0 && dragonActivated) obj.special++;
  511.  
  512.                 obj.energy = int(obj.age / healthFactor);
  513.                
  514.                 if (obj.animSpeed <= 0) {
  515.                         obj.age -= 1;
  516.                         obj.animSpeed = 2;
  517.                 }
  518.                
  519.                 if (obj.age < 1) {
  520.                         obj.var[5] = obj.var[5] + 1;
  521.                         if (obj.var[5] < 350) {
  522.                                 jjMusicStop();
  523.                                
  524.                                 for (int i = 1; i < jjObjectCount; i++) {
  525.                                         if (jjObjects[i].eventID == OBJECT::SPARK) jjObjects[i].state = STATE::KILL;
  526.                                 }
  527.                                
  528.                                 if (obj.var[5] == 10) {
  529.                                         jjSample(jjLocalPlayers[0].xPos, jjLocalPlayers[0].yPos, SOUND::INTRO_MONSTER, 63, 45000);
  530.                                         jjLocalPlayers[0].cameraUnfreeze(true);
  531.                                 }
  532.                                 if (obj.var[5] % 7 == 0 && obj.var[5] > 35) {
  533.                                         jjOBJ@ boem = jjObjects[jjAddObject(OBJECT::EXPLOSION, int(obj.xPos - 130) + jjRandom()%260, int(obj.yPos) - jjRandom()%160)];
  534.                                         boem.determineCurAnim(ANIM::AMMO, 5);
  535.                                         jjSample(boem.xPos, boem.yPos, SOUND::COMMON_EXPL_TNT, 48, 0);
  536.                                 }
  537.                         } else {
  538.                                 for (int i = 0; i < 30; i++) {
  539.                                         obj.particlePixelExplosion(1);
  540.                                         obj.particlePixelExplosion(2);
  541.                                 }
  542.                                 for (int j = 0; j < jjLocalPlayerCount; j++) {
  543.                                         givePlayerPointsForObject(jjLocalPlayers[j], obj);
  544.                                         jjLocalPlayers[j].bossActivated = false;
  545.                                 }
  546.                                 jjLocalPlayers[0].showText("@@@@@@@@#||||~DRAGON SLAIN", STRING::LARGE);
  547.                                 defeated = true;
  548.                                 obj.delete();
  549.                         }
  550.                 } else obj.var[5] = 0;
  551.                 if (jjLocalPlayers[0].bossActivated) {
  552.                         jjLocalPlayers[0].boss = obj.objectID;
  553.                 }
  554.         }
  555.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
  556.                 if (bull !is null) {
  557.                         if (bull.playerHandling == HANDLING::PLAYERBULLET && obj.var[5] == 0) {
  558.                                 if (bull.var[3] != 6) obj.age -= (bull.var[3] == 9? bull.animSpeed + 1 : bull.animSpeed) * ((bull.yPos < obj.yPos - 92) && (bull.xPos < obj.xPos + 8)? 2:1);
  559.                                 else obj.animSpeed -= bull.animSpeed;
  560.                                 obj.justHit = (bull.yPos < obj.yPos - 92) && (bull.xPos < obj.xPos + 8)? 7:5;
  561.                                 if (obj.freeze > 0) {
  562.                                         obj.unfreeze(1);
  563.                                         obj.energy -= bull.animSpeed;
  564.                                 }
  565.                         }
  566.                         if ((bull.var[6] & 16) == 0 || obj.var[5] == 0) {
  567.                                 bull.state = STATE::EXPLODE;
  568.                         }
  569.                 } else if (play !is null) {
  570.                         if (obj.var[5] == 0) play.hurt();
  571.                         play.xSpeed = -6 * obj.direction;
  572.                 }
  573.                 return true;
  574.         }
  575. }
  576.  
  577. const array<SOUND::Sample> CollapsingIceSounds = {
  578.         SOUND::P2_FOEW1,
  579.         SOUND::P2_FOEW4,
  580.         SOUND::P2_FOEW5
  581. };
  582.  
  583. class Firebreather : jjBEHAVIORINTERFACE {
  584.         void onBehave(jjOBJ@ obj) {
  585.                 obj.behave(BEHAVIOR::TOASTERBULLET, false);
  586.                 if (obj.state != STATE::EXPLODE) {
  587.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
  588.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::ALPHAMAP, 25);
  589.                         obj.xPos -= 3;
  590.                 }
  591.         }
  592. }
  593.  
  594. class HomingBullet : jjBEHAVIORINTERFACE {
  595.         void onBehave(jjOBJ@ obj) {
  596.                 obj.behave(BEHAVIOR::BILSYBULLET);
  597.                
  598.                 if (obj.counter > 70) obj.counter = 1;
  599.                 for (int i = 1; i < jjObjectCount; i++) {
  600.                         if (jjObjects[i].playerHandling == HANDLING::PLAYERBULLET && jjObjects[i].doesCollide(obj, true)) {
  601.                                 jjObjects[i].state = STATE::EXPLODE;
  602.                         }
  603.                 }
  604.         }
  605. }
  606.  
  607. class Firebomb : jjBEHAVIORINTERFACE {
  608.         void onBehave(jjOBJ@ obj) {
  609.                 obj.behave(BEHAVIOR::BULLET, obj.state == STATE::EXPLODE? true:false);
  610.                 obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
  611.                 if (obj.state != STATE::EXPLODE) {
  612.                         jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.var[0], 1, 1, SPRITE::NORMAL);
  613.                         if (obj.ySpeed < 8) obj.ySpeed += 0.15;
  614.                 }
  615.         }
  616. }
  617.  
  618. class FireOrb : jjBEHAVIORINTERFACE {
  619.         void onBehave(jjOBJ@ obj) {
  620.                 obj.behave(BEHAVIOR::SPARK, false);
  621.                 obj.special++;
  622.                 if (obj.special < 35) obj.xPos -= 4;
  623.                 else {
  624.                         obj.xPos += jjSin(obj.special*8);
  625.                         obj.yPos += jjCos(obj.special*8);
  626.                 }
  627.                
  628.                 if (obj.energy < 1) obj.state = STATE::KILL;
  629.                
  630.                 int carDropRate = jjDifficulty == 0? 1 : jjDifficulty == 1? 2 : 4;                                              
  631.                
  632.                 if (obj.state != STATE::KILL) {
  633.                         jjDrawSprite(obj.xPos, obj.yPos, ANIM::AMMO, 77, 5, obj.direction, obj.justHit > 0? SPRITE::SINGLECOLOR : SPRITE::NORMAL, 15);
  634.                 } else {
  635.                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BURN, 0, 0);
  636.                         obj.particlePixelExplosion(1);
  637.                         if (jjRandom()%carDropRate == 0) {
  638.                                 jjOBJ@ carrot = jjObjects[jjAddObject(OBJECT::CARROT, int(obj.xPos), int(obj.yPos), obj.objectID, CREATOR::OBJECT)];
  639.                                 carrot.state = STATE::FLOATFALL;
  640.                         }
  641.                         obj.delete();
  642.                 }
  643.                
  644.                 int playerID = obj.findNearestPlayer(60000);
  645.                 if (playerID > -1) {
  646.                         if (obj.special % 60 == 0 && !jjMaskedPixel(int(obj.xPos), int(obj.yPos), 4)) {
  647.                                 jjOBJ@ bullet = jjObjects[jjAddObject(OBJECT::FIRESHIELDBULLET, obj.xPos, obj.yPos)];
  648.                                 bullet.xSpeed = jjPlayers[playerID].xPos > obj.xPos? 4:-4;
  649.                                 bullet.state = STATE::FLY;
  650.                                 bullet.playerHandling = HANDLING::ENEMYBULLET;
  651.                                 bullet.animSpeed = 1;
  652.                                 bullet.counterEnd = 105;
  653.                                 bullet.xAcc = 0;
  654.                         }
  655.                 }
  656.                
  657.                 for (int i = 1; i < jjObjectCount; i++) {
  658.                         if (jjObjects[i].var[3] == 3 && jjObjects[i].doesCollide(obj, true)) obj.state = STATE::KILL;
  659.                 }
  660.                
  661.                 jjPARTICLE@ cinders = jjAddParticle(PARTICLE::SMOKE);
  662.                 cinders.xPos = int(obj.xPos - 8) + jjRandom()%16;
  663.                 cinders.yPos = obj.yPos;
  664.                 cinders.ySpeed = -0.5;
  665.         }
  666.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
  667.                 if (bull !is null) {
  668.                         if (bull.playerHandling == HANDLING::PLAYERBULLET) {
  669.                                 if (bull.var[3] != 6) {
  670.                                         obj.energy -= bull.animSpeed;
  671.                                         obj.justHit = 5;
  672.                                 }
  673.                         }
  674.                         if ((bull.var[6] & 16) == 0) {
  675.                                 bull.state = STATE::EXPLODE;
  676.                         }
  677.                 } else if (play !is null) {
  678.                         play.hurt();
  679.                 }
  680.                 return true;
  681.         }
  682. }
  683.  
  684. class Icicle : jjBEHAVIORINTERFACE {
  685.         void onBehave(jjOBJ@ obj) {
  686.                 obj.behave(BEHAVIOR::BULLET, false);
  687.                 obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
  688.                 if (obj.state == STATE::EXPLODE) {
  689.                         obj.unfreeze(1);
  690.                         obj.delete();
  691.                 } else {
  692.                         obj.xAcc = 0;
  693.                         obj.yAcc = 0;
  694.                         jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[2], 0, 0, obj.var[0], 1, 1, SPRITE::SINGLEHUE, 32);
  695.                 }
  696.         }
  697. }
  698.  
  699. class DragonShockwave : jjBEHAVIORINTERFACE {
  700.         void onBehave(jjOBJ@ obj) {
  701.                 obj.behave(BEHAVIOR::STEADYLIGHT, false);
  702.                 obj.xPos = obj.xOrg + 24;
  703.                 obj.lightType = obj.var[0] == 1? LIGHT::RING2 : LIGHT::RING;
  704.                 obj.var[1] = obj.light * 4;
  705.                 obj.counter++;
  706.                 if (obj.counter == 1) {
  707.                         obj.light = 100;
  708.                         jjSamplePriority(SOUND::ORANGE_SWEEP0L);
  709.                 }
  710.                 if (obj.counter > 150) obj.delete();
  711.                 if (obj.var[0] == 0) {
  712.                         if (obj.light > 0) {
  713.                                 obj.light--;
  714.                                 charging = true;
  715.                         } else {
  716.                                 obj.var[0] = 1;
  717.                                 for (int i = 0; i < 5; i++) {
  718.                                         jjSamplePriority(SOUND::ORANGE_BOEMR);
  719.                                 }
  720.                                 charging = false;
  721.                         }
  722.                 } else {
  723.                         obj.light += 3;
  724.                 }
  725.                
  726.                 for (int i = 0; i < jjLocalPlayerCount; i++) {
  727.                         jjPLAYER@ play = jjLocalPlayers[i];
  728.                         float dx = play.xPos - obj.xPos, dy = play.yPos - obj.yPos;
  729.                         if (dx * dx + dy * dy < obj.var[1] * obj.var[1]) {
  730.                                 if (obj.var[8] & 1 << i == 0 && obj.var[0] == 1) {
  731.                                         play.hurt(2, play.blink == 0 && play.buttstomp < 41? true:false);
  732.                                         obj.var[8] = obj.var[8] | 1 << i;
  733.                                 }
  734.                         }
  735.                 }
  736.                 for (int j = 1; j < jjObjectCount; j++) {
  737.                         jjOBJ@ bull = jjObjects[j];
  738.                         float dx = bull.xPos - obj.xPos, dy = bull.yPos - obj.yPos;
  739.                         if (dx * dx + dy * dy < obj.var[1] * obj.var[1]) {
  740.                                 if (bull.playerHandling == HANDLING::PLAYERBULLET && bull.var[9] == 0 && obj.var[0] == 1) {
  741.                                         bull.ricochet();
  742.                                 }
  743.                         }
  744.                 }
  745.         }
  746. }
  747.  
  748. const array<dragonStates> closeRangeAttacks = {Idle, Roar, Roar, Homing_Fire, Homing_Fire, Spawn_FireOrb, Shockwave};
  749. const array<dragonStates> longRangeAttacks = {Idle, Idle, Roar, Homing_Fire, Flame_Burst, Flame_Burst, Flamethrower, Firebombs};
  750.  
  751. array<dragonStates> closeRangeQueue;
  752. array<dragonStates> longRangeQueue;
  753.  
  754. void shuffle(array<dragonStates>& input) {
  755.     for (int i = input.length(); i != 0;) {
  756.         int index = jjRandom() % i;
  757.         i--;
  758.         dragonStates temp = input[index];
  759.         input[index] = input[i];
  760.         input[i] = temp;
  761.     }
  762. }
  763.  
  764. dragonStates pop(array<dragonStates>& input) {
  765.     dragonStates result = input[input.length() - 1];
  766.     input.removeLast();
  767.     return result;
  768. }
  769.  
  770. dragonStates getNextAttack(array<dragonStates>& queue, const array<dragonStates>& reserve) {
  771.     if (queue.isEmpty()) {
  772.         queue = reserve;
  773.         shuffle(queue);
  774.     }
  775.     return pop(queue);
  776. }
  777.  
  778. void randomAttack(jjOBJ@ obj) {
  779.     int playerID = obj.findNearestPlayer(100000);
  780.     if (playerID > -1)
  781.         currState = getNextAttack(closeRangeQueue, closeRangeAttacks);
  782.     else
  783.         currState = getNextAttack(longRangeQueue, longRangeAttacks);
  784.     obj.special = 0;
  785.     obj.counter = 0;
  786. }
  787.  
  788. void dragonShout(jjOBJ@ obj, int rate) {
  789.         uint random = jjRandom();
  790.         int magnitude = (2 << (random & 3)) - 1;
  791.         int halfMagnitude = magnitude >> 1;
  792.                                
  793.         if (jjGameTicks & 1 == 0)
  794.                 jjLocalPlayers[0].cameraFreeze(jjLocalPlayers[0].cameraX + (random >> 8 & magnitude) - halfMagnitude, jjLocalPlayers[0].cameraY + (random >> 2 & magnitude) - halfMagnitude, false, true);
  795.         else
  796.                 jjLocalPlayers[0].cameraUnfreeze();
  797.                                                
  798.         int fireRate = rate;
  799.         if (obj.special % fireRate == 0) {
  800.                 if (jjRandom()%randcar < (randcar - 1)) {
  801.                         Icicle temp;
  802.                         jjOBJ@ icicle = jjObjects[jjAddObject(OBJECT::BLASTERBULLET, (int(obj.xPos - 890) + jjRandom()%830), int(obj.yPos - 580), obj.objectID, CREATOR::OBJECT, jjVOIDFUNCOBJ(temp.onBehave))];
  803.                         icicle.xSpeed = 0;
  804.                         icicle.ySpeed = 6;
  805.                         icicle.direction = 1;
  806.                         icicle.counterEnd = 200;
  807.                         icicle.state = STATE::FLY;
  808.                         icicle.playerHandling = HANDLING::ENEMYBULLET;
  809.                         icicle.lightType = LIGHT::NONE;
  810.                 } else {
  811.                         jjOBJ@ carrot = jjObjects[jjAddObject(OBJECT::CARROT, (int(obj.xPos - 890) + jjRandom()%830), int(obj.yPos - 580), obj.objectID, CREATOR::OBJECT)];
  812.                         carrot.state = STATE::FLOATFALL;
  813.                 }
  814.         }
  815. }
  816.  
  817. bool givePlayerPointsForObject(jjPLAYER@ player, jjOBJ@ obj) { //This will probably be made a jjOBJ method as part of the real JJ2+ API eventually, because it shows up all the time in the native code, but that hasn't happened yet, so here you go. Increases the player's jjPLAYER::score to match the object's jjOBJ::points, and creates a string particle with that number which flies up to the top left corner of the screen.
  818.         if (player is null)
  819.                 return false;
  820.         if (obj.points != 0 && (jjGameMode == GAME::SP || jjGameMode == GAME::COOP)) {
  821.                 player.score += obj.points;
  822.                 jjPARTICLE@ particle = jjAddParticle(PARTICLE::STRING);
  823.                 if (particle !is null) {
  824.                         particle.xPos = obj.xPos;
  825.                         particle.yPos = obj.yPos;
  826.                         particle.xSpeed = (-32768 - int(jjRandom() & 0x3FFF)) / 65536.f;
  827.                         particle.ySpeed = (-65536 - int(jjRandom() & 0x7FFF)) / 65536.f;
  828.                         particle.string.text = formatInt(obj.points);
  829.                 }
  830.                 obj.points = 0; //Ensures that JJ2 won't end up increasing the player's score TWICE, since a call to (the native version of) this function is actually part of the standard pickup-handling code and would otherwise therefore get you in trouble with the FiveUp onObjectHit code.
  831.                 return true;
  832.         }
  833.         return false;
  834. }
  835.  
  836. void onPlayer(jjPLAYER@ play) {
  837.         if (jjGameTicks % 350 == 0 && !dragonActivated) jjSample(play.xPos + 128, play.yPos, SOUND::INTRO_MONSTER, play.xPos > 67*32? 32:16, 0);
  838. }
  839.  
  840. void onMain() {
  841.         for (int i = 1; i < jjObjectCount; i++) {
  842.                 if (jjObjects[i].eventID == OBJECT::BOMBCRATE) {
  843.                         jjObjects[i].yPos = jjObjects[i].yOrg;
  844.                 }
  845.         }
  846.         if (defeated) elapsed++;
  847.         if (elapsed == 350) jjNxt("HH17_Ending.j2l", false, false);
  848. }
  849. void onFunction0(jjPLAYER@ play) {
  850.         play.activateBoss(true);
  851.         if (!dragonActivated) {
  852.                 dragonActivated = true;
  853.                 play.showText("@@@@@@@A big, bad dragon blocks your path!", STRING::MEDIUM);
  854.         }
  855. }