Downloads containing ab24btl04.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Anniversary Bash 24 Battle PurpleJazz Battle N/A Download file

File preview

  1. const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
  2. #include "MLLE-Include-1.5.asc" ///@MLLE-Generated
  3. #pragma require "ab24btl04-MLLE-Data-1.j2l" ///@MLLE-Generated
  4. #pragma require "ab24btl04.j2l" ///@MLLE-Generated
  5. #pragma require "Meteor.j2a"
  6. #pragma require "expmine.wav"
  7. #pragma require "FTURROCK.wav"
  8. #pragma require "EXPSTD3.wav"
  9. #pragma require "redeemer_flight.wav"
  10. #pragma require "jjnetAmmo_retroic.asc"
  11. #include "jjnetAmmo_retroic.asc"
  12. #pragma require "SpringMushroom.j2a"
  13. #pragma require "S3K_87.wav"
  14.  
  15. bool controllingRedeemer, warning = false;
  16. int xPos = 0, yPos = 0, cameraX = 0, cameraY = 0, redeemerAngle = 0, CTFArrowTimer = 0;
  17. uint delay = 0;
  18.  
  19. bool cameraFrozen = false;
  20. float freezeCameraX = 0;
  21. float freezeCameraY = 0;
  22. bool freezeCameraCentered = false;
  23. void pretendFreezeCamera(float x, float y, bool centered) {
  24.         cameraFrozen = true;
  25.         freezeCameraX = x;
  26.         freezeCameraY = y;
  27.         freezeCameraCentered = centered;
  28. }
  29. void pretendUnfreezeCamera() {
  30.         cameraFrozen = false;
  31.  
  32. }
  33. void processFrozenCamera(jjPLAYER@ player) {
  34.         if (cameraFrozen)
  35.                 player.cameraFreeze(freezeCameraX, freezeCameraY, freezeCameraCentered, true);
  36.         else
  37.                 player.cameraUnfreeze();
  38. }
  39.  
  40.  
  41. int redeemerColor(jjPLAYER@ creator) {
  42.         //switch(creator.team) {
  43.                 //case TEAM::BLUE: return 0;
  44.                 //case TEAM::RED: return 1;
  45.                 //case TEAM::GREEN: return 2;
  46.                 //case TEAM::YELLOW: return 3;
  47.         //}
  48.         return 0;
  49. }
  50.  
  51. bool gameIsActive() {
  52.         return jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME;
  53. }
  54.  
  55. bool CTFMode() {
  56.         return (jjGameMode == GAME::CTF && jjGameCustom == GAME::NOCUSTOM) || jjGameCustom == GAME::DCTF;
  57. }
  58.  
  59. bool otherMode() {
  60.         return jjGameMode != GAME::CTF || jjGameCustom == GAME::TB || jjGameCustom == GAME::TLRS || jjGameCustom == GAME::DOM;
  61. }
  62.  
  63. bool isDestructibleItem(jjOBJ@ target) {
  64.         return target.eventID == OBJECT::TNT || target.behavior == BEHAVIOR::MONITOR;
  65. }
  66.  
  67. class vector2i {
  68.         int x, y;
  69. }
  70.  
  71. array<vector2i> oneWays;
  72.  
  73. void onLevelLoad() {      
  74.  
  75.         jjAnimSets[ANIM::CUSTOM[0]].load(0, "SpringMushroom.j2a");
  76.  
  77.         for (int i = 0; i < 9; i++) {
  78.                 jjAnimations[jjAnimSets[ANIM::SPRING] + i] = jjAnimations[jjAnimSets[ANIM::CUSTOM[0]] + i];
  79.         }
  80.  
  81.         jjAnimSets[ANIM::CUSTOM[22]].load(0, "Meteor.j2a");
  82.         jjAnimations[jjAnimSets[ANIM::AMMO] + 24] = jjAnimations[jjAnimSets[ANIM::CUSTOM[22]] + 1];
  83.         jjAnimations[jjAnimSets[ANIM::AMMO] + 25] = jjAnimations[jjAnimSets[ANIM::CUSTOM[22]] + 2];
  84.        
  85.         jjObjectPresets[OBJECT::BOUNCERBULLET].behavior = jjObjectPresets[OBJECT::BOUNCERBULLETPU].behavior = Meteor();
  86.         jjObjectPresets[OBJECT::BOUNCERBULLET].special = jjObjectPresets[OBJECT::BOUNCERBULLET].determineCurAnim(ANIM::CUSTOM[22], 1);
  87.         jjObjectPresets[OBJECT::BOUNCERBULLETPU].special = jjObjectPresets[OBJECT::BOUNCERBULLETPU].determineCurAnim(ANIM::CUSTOM[22], 0);
  88.         jjObjectPresets[OBJECT::BOUNCERBULLET].ySpeed = jjObjectPresets[OBJECT::BOUNCERBULLETPU].ySpeed = jjObjectPresets[OBJECT::BLASTERBULLET].ySpeed;
  89.         jjObjectPresets[OBJECT::BOUNCERBULLETPU].killAnim = jjObjectPresets[OBJECT::SEEKERBULLET].killAnim;
  90.         jjObjectPresets[OBJECT::BOUNCERBULLET].lightType = LIGHT::POINT;
  91.         jjObjectPresets[OBJECT::BOUNCERBULLETPU].lightType = LIGHT::BRIGHT;
  92.         jjObjectPresets[OBJECT::BOUNCERBULLET].light = jjObjectPresets[OBJECT::BOUNCERBULLETPU].light = 10;
  93.        
  94.         jjObjectPresets[OBJECT::BOUNCERAMMO15].determineCurAnim(ANIM::CUSTOM[22], 3);
  95.         jjObjectPresets[OBJECT::BOUNCERAMMO15].determineCurFrame();
  96.        
  97.         jjObjectPresets[OBJECT::BOUNCERPOWERUP].determineCurAnim(ANIM::CUSTOM[22], 4);
  98.         jjObjectPresets[OBJECT::BOUNCERPOWERUP].determineCurFrame();
  99.        
  100.         jjWeapons[WEAPON::BOUNCER].defaultSample = false;
  101.         jjWeapons[WEAPON::BOUNCER].style = WEAPON::MISSILE;
  102.        
  103.         jjSampleLoad(SOUND::ORANGE_BOEMR, "expmine.wav");
  104.  
  105.         jjObjectPresets[OBJECT::TNT].determineCurFrame();
  106.         jjObjectPresets[OBJECT::TNT].behavior = Redeemer();
  107.         jjObjectPresets[OBJECT::TNT].xSpeed = jjObjectPresets[OBJECT::BLASTERBULLET].xSpeed * 1.33;
  108.         jjObjectPresets[OBJECT::TNT].counterEnd = 255;
  109.         jjObjectPresets[OBJECT::TNT].killAnim = jjObjectPresets[OBJECT::SEEKERBULLET].killAnim;
  110.  
  111.  
  112.         jjObjectPresets[OBJECT::REDSPRING].behavior =
  113.         jjObjectPresets[OBJECT::GREENSPRING].behavior =
  114.         jjObjectPresets[OBJECT::BLUESPRING].behavior =
  115.         jjObjectPresets[OBJECT::FROZENSPRING].behavior =
  116.         jjObjectPresets[OBJECT::HORREDSPRING].behavior = SpringMushroom();
  117.        
  118.         jjWeapons[WEAPON::TNT].maximum = 1;
  119.        
  120.         jjAnimSets[ANIM::SONCSHIP].load();
  121.         jjAnimSets[ANIM::VINE].load();
  122.         jjAnimations[jjAnimSets[ANIM::AMMO] + 59] = jjAnimations[jjAnimSets[ANIM::SONCSHIP] + 0];
  123.        
  124.         jjObjectPresets[OBJECT::THING].determineCurAnim(ANIM::SONCSHIP, 0);
  125.         jjObjectPresets[OBJECT::THING].determineCurFrame();
  126.         jjObjectPresets[OBJECT::THING].behavior = RedeemerPickup();
  127.         jjObjectPresets[OBJECT::THING].scriptedCollisions = true;
  128.        
  129.         generateRedeemerPickupSprite(jjAnimSets[ANIM::CUSTOM[1]], array<uint> = {0});
  130.        
  131.         jjSampleLoad(SOUND::P2_POEP, "FTURROCK.wav");
  132.         jjSampleLoad(SOUND::P2_PTOEI, "EXPSTD3.wav");
  133.         jjSampleLoad(SOUND::P2_SPLOUT, "redeemer_flight.wav");
  134.         jjSampleLoad(SOUND::COMMON_SPRING1, "S3K_87.wav");
  135.                 jjSampleLoad(SOUND::SPRING_SPRING1, "S3K_87.wav");
  136.  
  137.         jjObjectPresets[OBJECT::BOLLPLATFORM].direction = SPRITE::FLIPH;
  138.         jjObjectPresets[OBJECT::FRUITPLATFORM].direction = SPRITE::FLIPH;      
  139.         jjObjectPresets[OBJECT::PINKPLATFORM].direction = SPRITE::FLIPHV;
  140.         jjObjectPresets[OBJECT::PINKPLATFORM].bulletHandling = HANDLING::DESTROYBULLET;
  141.         jjANIMFRAME@ frame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::PINKPLAT].firstAnim + 0].firstFrame];
  142.         frame.hotSpotY = -12;
  143.  
  144.         setAmmoPresets(OBJECT::BOUNCERAMMO15);
  145.         setAmmoPresets(OBJECT::TOASTERAMMO15);
  146.         setAmmoPresets(OBJECT::RFAMMO15);
  147.  
  148.         jjObjectPresets[OBJECT::PRETZEL].behavior = WarpOrb();
  149.         jjObjectPresets[OBJECT::PRETZEL].lightType = LIGHT::LASER;
  150.         jjObjectPresets[OBJECT::PRETZEL].light = 10;
  151.         jjObjectPresets[OBJECT::PRETZEL].scriptedCollisions = true;
  152.        
  153.         jjANIMFRAME@ spikebollFrame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::SPIKEBOLL].firstAnim].firstFrame];
  154.         jjPIXELMAP(0, 0, 32, 32, 4).save(spikebollFrame);
  155.         spikebollFrame.hotSpotX = -spikebollFrame.width/2;
  156.         spikebollFrame.hotSpotY = -spikebollFrame.height + 14;
  157.        
  158.         for (int x = 0; x < jjLayerWidth[1]; x++) {
  159.                 for (int y = 0; y < jjLayerHeight[1]; y++) {
  160.                         uint16 myTile = jjTileGet(1, x, y);
  161.                         if (myTile < 1350) {
  162.                                 jjTileSet(1, x, y, myTile + 1350);
  163.                         }
  164.                 }
  165.         }
  166.        
  167.         jjANIMFRAME@ CircularGlow = jjAnimFrames[jjObjectPresets[OBJECT::ORANGE].curFrame];
  168.         jjPIXELMAP circle(32, 32);
  169.         for (uint x = 0; x < circle.width; ++x)
  170.                 for (uint y = 0; y < circle.height; ++y)
  171.                         circle[x,y] = (sqrt(pow(16-int(x), 2) + pow(16-int(y), 2)) < 16) ? 234 : 0;
  172.         circle.save(CircularGlow);
  173.         CircularGlow.hotSpotX = -CircularGlow.width/2;
  174.         CircularGlow.hotSpotY = -CircularGlow.height/2;
  175.        
  176.         jjWeapons[WEAPON::TNT].allowed = true;
  177. }
  178.  
  179. jjANIMSET@ customSpringSprite;
  180. array<int> fastCustomSpringSpeeds(jjLocalPlayerCount);
  181. bool generateCustomSpringSprites(jjANIMSET@ anim, const array<uint> &in colors) {
  182.         int length = colors.length();
  183.         bool success = (@customSpringSprite = anim).allocate(array<uint>(length * 3, 5)) !is null;
  184.         if (success) {
  185.                 uint srcSet = jjAnimSets[ANIM::SPRING];
  186.                 for (int i = 0; i < length; i++) {
  187.                         uint color = colors[i];
  188.                         uint destAnimOffset = anim + i * 3;
  189.                         for (int j = 0; j < 3; j++) {
  190.                                 uint srcAnim = jjAnimations[srcSet + j];
  191.                                 uint destAnim = jjAnimations[destAnimOffset + j];
  192.                                 for (int k = 0; k < 5; k++) {
  193.                                         jjPIXELMAP image(jjAnimFrames[destAnim + k] = jjAnimFrames[srcAnim + k]);
  194.                                         int width = image.width;
  195.                                         int height = image.height;
  196.                                         for (int l = 0; l < height; l++) {
  197.                                                 for (int m = 0; m < width; m++) {
  198.                                                         int pixel = image[m, l];
  199.                                                         if (pixel >= 32 && pixel < 40)
  200.                                                                 image[m, l] = color + (pixel & 7);
  201.                                                 }
  202.                                         }
  203.                                         if (!image.save(jjAnimFrames[destAnim + k]))
  204.                                                 return false;
  205.                                 }
  206.                         }
  207.                 }
  208.         }
  209.         return success;
  210. }
  211. void initializeCustomSpring(jjOBJ@ obj) {
  212.         int anim = obj.curAnim;
  213.         obj.behave(obj.behavior = BEHAVIOR::SPRING, false);
  214.         if (obj.curAnim != anim) {
  215.                 obj.curAnim = anim + 2;
  216.                 obj.determineCurFrame();
  217.         }
  218.         obj.draw();
  219. }
  220.  
  221. void turnIntoCustomSpring(jjOBJ@ obj, uint color, float power, bool horizontal) {
  222.         if (horizontal) {
  223.                 obj.xSpeed = power;
  224.                 obj.ySpeed = 0.f;
  225.         } else {
  226.                 obj.xSpeed = 0.f;
  227.                 obj.ySpeed = -power;
  228.                 if (obj.state == STATE::START && obj.creatorType == CREATOR::LEVEL) {
  229.                         int x = int(obj.xPos) >> 5;
  230.                         int y = int(obj.yPos) >> 5;
  231.                         if (jjParameterGet(x, y, 0, 1) != 0) {
  232.                                 jjParameterSet(x, y, 0, 1, 0);
  233.                                 obj.yPos -= 4.f;
  234.                                 obj.ySpeed = power;
  235.                         }
  236.                 }
  237.         }
  238.         obj.behavior = initializeCustomSpring;
  239.         obj.curAnim = customSpringSprite + color * 3 + (horizontal ? 1 : 0);
  240.         obj.energy = obj.frameID = obj.freeze = obj.justHit = obj.light = obj.points = 0;
  241.         obj.isBlastable = obj.isTarget = obj.scriptedCollisions = obj.triggersTNT = false;
  242.         obj.deactivates = obj.isFreezable = true;
  243.         obj.bulletHandling = HANDLING::IGNOREBULLET;
  244.         obj.playerHandling = HANDLING::SPECIAL;
  245.         obj.lightType = LIGHT::NORMAL;
  246.         obj.determineCurFrame();
  247. }
  248.  
  249.  
  250. class Meteor : jjBEHAVIORINTERFACE {
  251.         void onBehave(jjOBJ@ obj) {
  252.                 obj.behave(BEHAVIOR::BULLET, obj.state == STATE::EXPLODE? true:false);
  253.                 jjPLAYER@ creator = jjPlayers[obj.creatorID];
  254.                
  255.                 if (obj.state != STATE::EXPLODE) {
  256.                         if (obj.counter == 1 && creator.isLocal) {
  257.                                 jjSample(creator.xPos, creator.yPos, SOUND::ORANGE_BOEMR, 42, obj.eventID == OBJECT::BOUNCERBULLET? 22000 : 20000);
  258.                                 obj.var[2] = 0;
  259.                                
  260.                         }
  261.                         obj.age += obj.direction == 0? 10 : 10 * obj.direction;
  262.                        
  263.                         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);
  264.                        
  265.                         jjPARTICLE@ smoke = jjAddParticle(PARTICLE::SMOKE);
  266.                         if (smoke !is null) {
  267.                                 smoke.xPos = smoke.xPos;
  268.                                 smoke.yPos = smoke.yPos;
  269.                         }
  270.                        
  271.                         if (obj.eventID == OBJECT::BOUNCERBULLETPU && obj.var[4] == 0) {
  272.                                 jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[22], 0, 0, -obj.age, 1, 1, SPRITE::TRANSLUCENTSINGLEHUE, 40);
  273.                                 jjPARTICLE@ cinders = jjAddParticle(PARTICLE::FIRE);
  274.                                 if (cinders !is null) {
  275.                                         cinders.xPos = int(obj.xPos - 8) + jjRandom()%17;
  276.                                         cinders.yPos = int(obj.yPos - 8) + jjRandom()%17;
  277.                                 }
  278.                         }
  279.                        
  280.                         if (obj.yPos > jjWaterLevel) {
  281.                                 obj.var[4] = 1;
  282.                                 obj.xSpeed = obj.xSpeed * 0.875;
  283.                                 obj.ySpeed = obj.ySpeed * 0.875;
  284.                         }
  285.                
  286.                         switch (obj.direction) {
  287.                                 case 1: obj.xSpeed -= obj.eventID == OBJECT::BOUNCERBULLET? 0.1:0.15; obj.ySpeed += obj.eventID == OBJECT::BOUNCERBULLET? 0.15:0.2; break;
  288.                                 case -1: obj.xSpeed += obj.eventID == OBJECT::BOUNCERBULLET? 0.1:0.15; obj.ySpeed += obj.eventID == OBJECT::BOUNCERBULLET? 0.15:0.2; break;
  289.                         }
  290.                        
  291.                         if (obj.xSpeed == 0) obj.ySpeed += 0.4;
  292.                         if (obj.ySpeed > 8) obj.ySpeed = 8;
  293.                        
  294.                 } else {
  295.                         obj.age = 0;
  296.                         if (obj.var[2] == 0) {
  297.                                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BENZIN1, 0, 0);
  298.                                 obj.var[2] = 1;
  299.                                
  300.                                 for (int i = -1; i <= 1; i+= 2) {
  301.                                         Rock temp;
  302.                                                 jjOBJ@ rock = jjObjects[jjAddObject(OBJECT::SHARD, int(obj.xPos + (i * 12)), int(obj.yPos - 8), obj.creatorID, CREATOR::PLAYER, jjVOIDFUNCOBJ(temp.onBehave))];
  303.                                                 rock.determineCurAnim(obj.eventID == OBJECT::BOUNCERBULLETPU? ANIM::CUSTOM[22] : ANIM::FONT, obj.eventID == OBJECT::BOUNCERBULLETPU? 0:14);
  304.                                                 rock.playerHandling = HANDLING::PLAYERBULLET;
  305.                                                 rock.var[3] = 2;
  306.                                                 rock.var[4] = obj.var[4];
  307.                                                 rock.var[5] = obj.eventID == OBJECT::BOUNCERBULLETPU? 0:1;
  308.                                                 rock.var[6] = obj.eventID == OBJECT::BOUNCERBULLETPU? 8:0;
  309.                                                 rock.special = obj.eventID == OBJECT::BOUNCERBULLETPU? 40:72;
  310.                                                 rock.animSpeed = 2;
  311.                                                 rock.direction = i;
  312.                                                 rock.xSpeed = 6 * i;
  313.                                                 rock.ySpeed = -3;
  314.                                                 rock.state = STATE::FLY;
  315.                                                 rock.lightType = LIGHT::POINT;
  316.                                                 rock.light = 10;
  317.                                                 rock.counterEnd = jjObjectPresets[OBJECT::BOUNCERBULLET].counterEnd;
  318.                                                 rock.killAnim = jjObjectPresets[OBJECT::BOUNCERBULLET].killAnim;
  319.                                 }
  320.                         }
  321.                 }
  322.         }
  323. }
  324.  
  325. class Rock : jjBEHAVIORINTERFACE {
  326.         void onBehave(jjOBJ@ obj) {
  327.                 obj.behave(BEHAVIOR::BULLET, obj.state == STATE::EXPLODE? true:false);
  328.                
  329.                 if (obj.state == STATE::FLY) {
  330.                         obj.age += obj.direction == 0? 10 : 10 * obj.direction;
  331.                         jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[22], obj.var[5] == 0? 0:1, 0, -obj.age, 0.5, 0.5, SPRITE::SINGLEHUE, obj.special);
  332.                        
  333.                         switch (obj.direction) {
  334.                                 case 1: obj.xSpeed -= 0.05; obj.ySpeed += 0.1; break;
  335.                                 case -1: obj.xSpeed += 0.05; obj.ySpeed += 0.1; break;
  336.                         }
  337.                        
  338.                         if (obj.yPos > jjWaterLevel) {
  339.                                 obj.var[4] = 1;
  340.                                 obj.xSpeed = obj.xSpeed * 0.875;
  341.                                 obj.ySpeed = obj.ySpeed * 0.875;
  342.                         }
  343.                        
  344.                         jjPARTICLE@ smoke = jjAddParticle(PARTICLE::SMOKE);
  345.                         if (smoke !is null && obj.var[5] == 2) {
  346.                                 smoke.xPos = obj.xPos;
  347.                                 smoke.yPos = obj.yPos;
  348.                         }
  349.                        
  350.                 }
  351.                
  352.         }
  353. }
  354.  
  355. class Redeemer : jjBEHAVIORINTERFACE {
  356.         void onBehave(jjOBJ@ obj) {
  357.                 //var[0] - angle
  358.                 //var[1] - angular speed
  359.                 //var[2] - has the redeemer exploded
  360.                 //special - value of jjGameTicks at the time the last packet was sent/received 
  361.                
  362.                 int angle;
  363.                 float speed = jjObjectPresets[OBJECT::TNT].xSpeed;
  364.                
  365.                 if (obj.creatorType != CREATOR::PLAYER)
  366.                         obj.delete();
  367.                 jjPLAYER@ creator = jjPlayers[obj.creatorID];
  368.                
  369.                 switch (obj.state) {
  370.                         case STATE::START:
  371.                                 obj.curAnim = jjObjectPresets[obj.eventID].curAnim;
  372.                                 jjSamplePriority(SOUND::P2_POEP);
  373.                                 obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
  374.                                 obj.ySpeed = speed * -jjSin(obj.var[0]);
  375.                                 obj.xSpeed = speed * jjCos(obj.var[0]);
  376.                                 obj.direction = obj.xSpeed < 0.f ? -1 : 1;
  377.                                 obj.var[2] = 0;
  378.                                 if (creator.isLocal) controllingRedeemer = true;
  379.                                 obj.state = STATE::FLY;
  380.                         break;
  381.                                
  382.                         case STATE::FLY:
  383.                                 redeemerCamera(creator, obj);
  384.                                 jjDrawRotatedSprite(obj.xPos, obj.yPos, (creator.isLocal || jjGameMode != GAME::CTF)? ANIM::SONCSHIP : ANIM::SONCSHIP, (creator.isLocal || jjGameMode != GAME::CTF)? 0 : redeemerColor(creator), obj.curFrame, obj.var[0], 2, 2, SPRITE::NORMAL);
  385.                                 if (creator.isLocal) {
  386.                                         redeemerAngle = obj.var[0];
  387.                                         if (!jjLowDetail && (obj.counter % 131 == 36 || obj.counter == 1) && controllingRedeemer) jjSamplePriority(SOUND::P2_SPLOUT);
  388.                                         float dx = creator.xPos - obj.xPos, dy = creator.yPos - obj.yPos;
  389.                                                 if (dx * dx + dy * dy < 420 * 420) warning = true;
  390.                                                 else warning = false;
  391.                                         if (creator.health == 0) {
  392.                                                 pretendUnfreezeCamera();
  393.                                                 controllingRedeemer = false;
  394.                                         } else {
  395.                                                 if (controllingRedeemer) {
  396.                                                         creator.xSpeed = 0;
  397.                                                         if (creator.keyRight && !jjMaskedPixel(int(creator.xPos-16), int(creator.yPos))) creator.xPos -= 0.368f;
  398.                                                         if (creator.keyLeft && !jjMaskedPixel(int(creator.xPos+16), int(creator.yPos))) creator.xPos += 0.368f;
  399.                                                 }
  400.                                                 if (obj.counter >= 21) {
  401.                                                         if (controllingRedeemer) {
  402.                                                                 if (creator.keyRight || (obj.direction < 0 ? creator.keyUp : creator.keyDown))
  403.                                                                         obj.var[1] = -16;
  404.                                                                 else if (creator.keyLeft || (obj.direction < 0 ? creator.keyDown : creator.keyUp))
  405.                                                                         obj.var[1] = 16;
  406.                                                                 else
  407.                                                                         obj.var[1] = 0;
  408.                                                         }
  409.                                                         else obj.var[1] = 0;
  410.                                                 }
  411.                                                 if (obj.counter >= 127)
  412.                                                         obj.counter = 35;
  413.                                                 else if (obj.counter >= 35 && creator.keyFire && controllingRedeemer)
  414.                                                         obj.state = STATE::EXPLODE;
  415.                                         }
  416.                                         xPos = int(obj.xPos);
  417.                                         yPos = int(obj.yPos);
  418.                                 }
  419.                                 if (obj.yPos >= jjLayerHeight[4]*32 || obj.yPos <= 0 || obj.xPos <= 0 || obj.xPos >= jjLayerWidth[4]*32) obj.state = STATE::EXPLODE;
  420.                                 obj.var[0] = obj.var[0] + obj.var[1];
  421.                                 obj.ySpeed = speed * -jjSin(obj.var[0]);
  422.                                 obj.xSpeed = speed * jjCos(obj.var[0]);
  423.                                 if (obj.counter % 3 == 0 && !jjLowDetail) spawnFireTrail(obj);
  424.                         break;
  425.                        
  426.                         case STATE::EXPLODE:
  427.                                 if (obj.var[2] == 0) {
  428.                                         RedeemerExplosion temp;
  429.                                         jjOBJ@ explosion = jjObjects[jjAddObject(OBJECT::BULLET, obj.xPos, obj.yPos, obj.creatorID, CREATOR::PLAYER, jjVOIDFUNCOBJ(temp.onBehave))];
  430.                                         jjSamplePriority(SOUND::P2_PTOEI);
  431.                                         obj.var[2] = 1;
  432.                                         explosion.var[2] = 1;
  433.                                 } else {
  434.                                         jjDrawResizedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 5, obj.curFrame + 5, 8, 8, SPRITE::NORMAL, 0, 3, 3);
  435.                                 }
  436.                                 pretendUnfreezeCamera();
  437.                                 if (creator.isLocal) controllingRedeemer = false;
  438.                         break;
  439.                 }
  440.                
  441.                 int previousState = obj.state;
  442.                 obj.behave(BEHAVIOR::BULLET, false);
  443.                 if (!creator.isLocal) {
  444.                         if (obj.special + 128 > jjGameTicks)
  445.                                 obj.state = previousState;
  446.                 } else if (obj.special + 4 <= jjGameTicks) {
  447.                         jjSTREAM packet;
  448.                         packet.push(int8(obj.creatorID));
  449.                         packet.push(obj.state == STATE::EXPLODE);
  450.                         packet.push(obj.xPos);
  451.                         packet.push(obj.yPos);
  452.                         if (obj.state != STATE::EXPLODE) {
  453.                                 packet.push(int16(obj.var[0]));
  454.                                 packet.push(int16(obj.var[1]));
  455.                         }
  456.                         jjSendPacket(packet);
  457.                         obj.special = jjGameTicks;
  458.                 }
  459.         }
  460. }
  461.  
  462. void spawnFireTrail(jjOBJ@ obj) {
  463.         jjOBJ@ trail = jjObjects[jjAddObject(OBJECT::EXPLOSION, int(obj.xPos - jjCos(obj.var[0])), int(obj.yPos - jjSin(obj.var[0])))];
  464.         trail.determineCurAnim(ANIM::AMMO, 3);
  465.         trail.lightType = LIGHT::POINT;
  466.         trail.playerHandling = HANDLING::PARTICLE;
  467.         trail.bulletHandling = HANDLING::IGNOREBULLET;
  468.         trail.isBlastable = false;
  469. }
  470.  
  471. void redeemerCamera(jjPLAYER@ creator, jjOBJ@ obj) {
  472.         float dx = jjLayerWidth[4] - obj.xPos, dy = jjLayerHeight[4] - obj.yPos;
  473.        
  474.         if (jjLayerWidth[4] - dx <= 400) cameraX = 400;
  475.         else cameraX = int(obj.xPos);
  476.        
  477.         if (jjLayerHeight[4] - dy <= 400) cameraY = 400;
  478.         else cameraY = int(obj.yPos);
  479.        
  480.         if (controllingRedeemer) {
  481.                 pretendFreezeCamera(int(obj.xPos) >= 8528? 8528 : cameraX, int(obj.yPos) >= 4080? 4080 : cameraY, true);
  482.        
  483.                 for (int i = 0; i < 32; i++) {
  484.                         jjPLAYER@ play = jjPlayers[i];
  485.                         float reticleScale = jjSin(jjGameTicks*5);
  486.                         float reticleColor = jjSin(jjGameTicks*10)*2;
  487.                         float pdx = play.xPos - obj.xPos, pdy = play.yPos - obj.yPos;
  488.                         if ((pdx * pdx + pdy * pdy < 320 * 320) && (jjGameMode != GAME::CTF || jjFriendlyFire || play.team != creator.team) && !play.isLocal)
  489.                                 jjDrawResizedSprite(play.xPos, play.yPos, ANIM::SONCSHIP, 7, 0, reticleScale + 2, reticleScale + 2, SPRITE::SINGLECOLOR, int(reticleColor + 18), 1);
  490.                                                        
  491.                 }
  492.         }
  493. }
  494.  
  495. class RedeemerPickup : jjBEHAVIORINTERFACE {
  496.         float getY(const ::jjOBJ@ obj) const {
  497.                 int arg = (((obj.objectID << 3) + ::jjGameTicks) << (obj.yPos > ::jjWaterLevel ? 1 : 4)) + (int(obj.xPos) << 4);
  498.                 return obj.yPos + ::jjSin(arg) * 4.f;
  499.         }
  500.        
  501.         void onBehave(jjOBJ@ obj) {
  502.                 obj.behave(BEHAVIOR::PICKUP, false);
  503.                 jjDrawResizedSprite(int(obj.xPos) - (6 * obj.direction), getY(obj), ANIM::CUSTOM[1], 0, obj.curFrame, 2 * obj.direction, 2, ((obj.xPos < 4464 && jjLocalPlayers[0].team == TEAM::BLUE) || (obj.xPos > 4464 && jjLocalPlayers[0].team == TEAM::RED))? SPRITE::NORMAL : SPRITE::NORMAL);
  504.         }
  505.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
  506.                 if (play.ammo[WEAPON::TNT] < jjWeapons[WEAPON::TNT].maximum) {
  507.                         if (play.isLocal) {
  508.                                 play.ammo[WEAPON::TNT] = play.ammo[WEAPON::TNT] + 1;
  509.                                 play.currWeapon = WEAPON::TNT;
  510.                                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_LOADSPAZ, 0, 0);
  511.                                 if (play.charCurr == CHAR::JAZZ) {
  512.                                         jjSample(play.xPos, play.yPos, SOUND::JAZZSOUNDS_JUMMY, 0, 0);
  513.                                 }
  514.                                 if (play.charCurr == CHAR::SPAZ) {
  515.                                         jjSample(play.xPos, play.yPos, SOUND::SPAZSOUNDS_HAPPY, 0, 0);
  516.                                 }
  517.                                 if (play.charCurr == CHAR::LORI) {
  518.                                         jjSample(play.xPos, play.yPos, SOUND::LORISOUNDS_WEHOO, 0, 0);
  519.                                 }
  520.                         }
  521.                         obj.behavior = BEHAVIOR::EXPLOSION2;
  522.                         obj.scriptedCollisions = false;
  523.                         obj.frameID = 0;
  524.                 }
  525.                 return true;
  526.         }
  527. }
  528.  
  529. class RedeemerExplosion : jjBEHAVIORINTERFACE {
  530.         void onBehave(jjOBJ@ obj) {
  531.                 //var[0] - obj.counter
  532.                 //var[2] - has the redeemer exploded
  533.                 //var[4] - blast radius
  534.                 //var[5] - damage
  535.                 //var[8] - has the player been hit by the explosion
  536.        
  537.                 obj.playerHandling = HANDLING::PARTICLE;
  538.                 obj.bulletHandling = HANDLING::IGNOREBULLET;
  539.                
  540.                 obj.lightType = obj.var[2] == 1? LIGHT::RING2 : LIGHT::NONE;
  541.                
  542.                 jjPLAYER@ creator = jjPlayers[obj.creatorID];
  543.                 jjPLAYER@ play = jjLocalPlayers[0];
  544.                
  545.                 if (obj.var[2] == 1) {
  546.                         obj.var[0] = obj.var[0] + 1;
  547.                         obj.light += 2;
  548.                         obj.var[4] = obj.light * 5;
  549.                        
  550.                         if (obj.var[4] >= 460) obj.var[5] = 1;
  551.                         else if (obj.var[4] >= 360 && obj.var[4] < 460) obj.var[5] = 2;
  552.                         else if (obj.var[4] < 360) obj.var[5] = 7;
  553.                        
  554.                         for (int i = 1; i < jjObjectCount; i++) {
  555.                                 jjOBJ@ target = jjObjects[i];
  556.                                 float dx = target.xPos - obj.xPos, dy = target.yPos - obj.yPos;
  557.                                 if (isDestructibleItem(target) && target.var[4] == 0 && creator.isLocal) {
  558.                                         if (dx * dx + dy * dy < obj.var[4] * obj.var[4]) {
  559.                                                 if (target.eventID != OBJECT::TNT) creator.objectHit(target, -1, HANDLING::SPECIAL);
  560.                                                 else target.state = STATE::EXPLODE;
  561.                                                 target.var[4] = 1;
  562.                                                 if (target.eventID == OBJECT::WATERSHIELD) play.shieldTime = 40*70;
  563.                                         }
  564.                                         else target.var[4] = 0;
  565.                                 }
  566.                                 else if (target.behavior == BEHAVIOR::PICKUP && target.state == STATE::FLOAT) {
  567.                                         if (dx * dx + dy * dy < obj.var[4] * obj.var[4])
  568.                                                 target.state = STATE::FLOATFALL;
  569.                                 }
  570.                         }
  571.                        
  572.                         float pdx = play.xPos - obj.xPos, pdy = play.yPos - obj.yPos;
  573.                         if (pdx * pdx + pdy * pdy < 1600 * 1600) {
  574.                                 uint random = jjRandom();
  575.                                 int magnitude = (2 << (random & 3)) - 1;
  576.                                 int halfMagnitude = magnitude >> 1;
  577.                                
  578.                                 if ((jjGameTicks & 1 == 0 && !jjTriggers[19]) || (jjGameTicks & 1 == 0 && jjTriggers[19] && jjMaskedHLine(int(play.xPos) - 12, 24, int(play.yPos) + 21)) && !controllingRedeemer)
  579.                                         pretendFreezeCamera(play.cameraX + (random & magnitude) - halfMagnitude, play.cameraY + (random >> 8 & magnitude) - halfMagnitude, false);
  580.                                 else
  581.                                         pretendUnfreezeCamera();
  582.                         }
  583.                 }
  584.                
  585.                 if (jjIsServer && gameIsActive()) {
  586.                         for (int i = 0; i < 32; i++) {
  587.                                 jjPLAYER@ player = jjPlayers[i];
  588.                                 if (
  589.                                         player.isActive && player.isInGame && player.health > 0 && gameIsActive()
  590.                                                 && ((jjGameMode != GAME::CTF && jjGameCustom != GAME::PEST && jjGameCustom != GAME::RT) || jjFriendlyFire || player.team != creator.team || player is creator)
  591.                                 ) {
  592.                                         float dx = player.xPos - obj.xPos, dy = player.yPos - obj.yPos;
  593.                                         if (dx * dx + dy * dy < obj.var[4] * obj.var[4]) {
  594.                                                 if (obj.var[8] & 1 << i == 0) {
  595.                                                         player.hurt(obj.var[5], true, creator);
  596.                                                         obj.var[8] = obj.var[8] | 1 << i;
  597.                                                 }
  598.                                         }
  599.                                 }
  600.                         }
  601.                 }
  602.                
  603.                 if (obj.var[0] == 1) {
  604.                         jjSample(obj.xPos, obj.yPos, SOUND::BILSBOSS_FIRE, 0, 0);
  605.                         for (int i = -8; i <= 8; i+=8) {
  606.                                 for (int j = -8; j <= 8; j+=8) {
  607.                                         if (i != 0 || j != 0) {
  608.                                                 Fireworks temp;
  609.                                                 int id = jjAddObject(OBJECT::ELECTROBULLET, obj.xPos, obj.yPos, obj.creatorID, CREATOR::PLAYER, jjVOIDFUNCOBJ(temp.onBehave));
  610.                                                 if (id != 0) {
  611.                                                         jjOBJ@ flares = jjObjects[id];
  612.                                                         flares.xSpeed = j*2;
  613.                                                         flares.ySpeed = i*2;
  614.                                                 }
  615.                                         }
  616.                                 }
  617.                         }
  618.                 }
  619.                 else if (obj.var[0] == 70) {
  620.                         obj.var[0] = 0;
  621.                         obj.var[2] = 0;
  622.                         obj.var[4] = 0;
  623.                         if (!controllingRedeemer) pretendUnfreezeCamera();
  624.                         obj.delete();
  625.                 }
  626.         }
  627. }
  628.  
  629. class Fireworks : jjBEHAVIORINTERFACE {
  630.         void onBehave(jjOBJ@ obj) {
  631.                 obj.behave(BEHAVIOR::ELECTROBULLET, false);
  632.                 obj.counterEnd = 210;
  633.                 if (obj.ySpeed < 10 && obj.xSpeed != 0) obj.ySpeed += 0.5;
  634.                 obj.playerHandling = HANDLING::PARTICLE;
  635.                 obj.bulletHandling = HANDLING::IGNOREBULLET;
  636.                 if (obj.state == STATE::FLY) obj.particlePixelExplosion(1);
  637.         }
  638. }
  639.  
  640. class WarpOrb : jjBEHAVIORINTERFACE {
  641.         void onBehave(jjOBJ@ obj) {
  642.                 if (obj.state == STATE::START) {
  643.                         obj.xPos = obj.xOrg += 16;
  644.                         obj.yPos = obj.yOrg += 8;      
  645.                 }
  646.                 obj.behave(BEHAVIOR::PICKUP, false);
  647.                 if (obj.state == STATE::FLOATFALL) obj.state = STATE::FLOAT;
  648.         }
  649.         void onDraw(jjOBJ@ obj) {                      
  650.                 int frame = obj.objectID * 8 + jjGameTicks;
  651.                 frame = (frame + int(obj.xPos) + int(obj.yPos) * 256)*16;
  652.                
  653.                 jjDrawTile(obj.xPos- 16, (obj.yPos + jjSin(frame)*4) - 18, 8 + TILE::ANIMATED);
  654.                 jjDrawSprite(obj.xPos, obj.yPos + jjSin(frame)*4, ANIM::SPIKEBOLL, 0, 0, 0, SPRITE::TRANSLUCENT, 255);
  655.                 jjDrawSprite(obj.xPos, obj.yPos + jjSin(frame)*4, ANIM::SPIKEBOLL, 0, 0, 0, SPRITE::BLEND_COLOR, 255);
  656.                 jjDrawSprite(obj.xPos, obj.yPos + jjSin(frame)*4, ANIM::SPIKEBOLL, 0, 0, 0, SPRITE::TRANSLUCENT, 255);
  657.                                                
  658.                 //jjDrawString(obj.xPos + 48, obj.yPos, "" + VeryCoolMathWizFunction(jjGameTicks));
  659.         }
  660.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  661.                 player.warpToID(0);
  662.                 jjSample(player.xPos, player.yPos, SOUND::COMMON_PICKUP1, 63, 17500);
  663.                 obj.frameID = 0;
  664.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  665.                 return true;
  666.         }
  667. }
  668.  
  669. void onReceive(jjSTREAM &in packet, int clientID) {
  670.         int8 playerID;
  671.         bool explosion;
  672.         float xPos, yPos;
  673.         int16 angle, angleSpeed;
  674.         jjSTREAM packetBackup;
  675.         if (jjIsServer)
  676.                 packetBackup = packet;
  677.         if (packet.pop(playerID) && playerID >= 0 && playerID < 32 &&
  678.                 packet.pop(explosion) && packet.pop(xPos) && packet.pop(yPos) &&
  679.                 (explosion || packet.pop(angle) && packet.pop(angleSpeed))
  680.         ) {
  681.                 const jjPLAYER@ player = jjPlayers[playerID];
  682.                 if (!jjIsServer || player.isActive && player.isInGame && player.clientID == clientID ) {
  683.                         jjOBJ@ redeemer;
  684.                         for (int i = 0; i < jjObjectCount; i++) {
  685.                                 jjOBJ@ obj = jjObjects[i];
  686.                                 if (obj.isActive && obj.eventID == OBJECT::TNT && obj.creatorType == CREATOR::PLAYER && obj.creatorID == uint(playerID)) {
  687.                                         @redeemer = obj;
  688.                                         break;
  689.                                 }
  690.                         }
  691.                         if (redeemer is null && jjGameTicks < 140) {
  692.                                 int id = jjAddObject(OBJECT::TNT, xPos, yPos, playerID, CREATOR::PLAYER);
  693.                                 if (id > 0)
  694.                                         @redeemer = jjObjects[id];
  695.                         }
  696.                         if (redeemer !is null) {
  697.                                 if (jjIsServer)
  698.                                         jjSendPacket(packetBackup, -clientID);
  699.                                 if (explosion)
  700.                                         redeemer.state = STATE::EXPLODE;
  701.                                 redeemer.xPos = xPos;
  702.                                 redeemer.yPos = yPos;
  703.                                 redeemer.var[0] = angle;
  704.                                 redeemer.var[1] = angleSpeed;
  705.                                 redeemer.special = jjGameTicks;
  706.                         }
  707.                 }
  708.         }
  709. }
  710.  
  711. bool onDrawAmmo(jjPLAYER@ play, jjCANVAS@ canvas) {
  712.         if (controllingRedeemer) {
  713.                 canvas.drawString(
  714.                         jjSubscreenWidth - 480,
  715.                         jjSubscreenHeight - 450,
  716.                         "Redeemer!",
  717.                         STRING::MEDIUM,
  718.                         STRING::NORMAL
  719.                 );
  720.                 //canvas.drawString(
  721.                         //jjSubscreenWidth - 496,
  722.                         //jjSubscreenHeight - 422,
  723.                         //"Redeemer pos " + int(xPos / 32) + "," + int(yPos / 32),
  724.                         //STRING::SMALL,
  725.                         //STRING::NORMAL
  726.                 //);
  727.                 canvas.drawString(
  728.                         jjSubscreenWidth - 624,
  729.                         jjSubscreenHeight - 406,
  730.                         "|Use the |||movement keys |||||to rotate the missile",
  731.                         STRING::SMALL,
  732.                         STRING::NORMAL
  733.                 );
  734.                 canvas.drawString(
  735.                         jjSubscreenWidth - 625,
  736.                         jjSubscreenHeight - 390,
  737.                         "|Press |||FIRE |||||to detonate the missile in mid-air",
  738.                         STRING::SMALL,
  739.                         STRING::NORMAL
  740.                 );
  741.                 canvas.drawRectangle(
  742.                         jjSubscreenWidth - 750,
  743.                         jjSubscreenHeight - 240,
  744.                         140,
  745.                         140,
  746.                         87,
  747.                         SPRITE::TRANSLUCENT
  748.                 );
  749.                 canvas.drawString(
  750.                         jjSubscreenWidth - 708,
  751.                         jjSubscreenHeight - 108,
  752.                         "Radar",
  753.                         STRING::SMALL,
  754.                         STRING::PALSHIFT,
  755.                         16
  756.                 );
  757.                 canvas.drawRotatedSprite(
  758.                         jjSubscreenWidth - 680,
  759.                         jjSubscreenHeight - 170,
  760.                         ANIM::CUSTOM[1],
  761.                         0,
  762.                         jjGameTicks >> 2,
  763.                         redeemerAngle,
  764.                         1,
  765.                         1,
  766.                         SPRITE::SINGLEHUE,
  767.                         80
  768.                 );
  769.                
  770.                 for (int i = 0; i < 32; i++) {
  771.                         jjPLAYER@ player = jjPlayers[i];
  772.                        
  773.                         uint8 teamColor;
  774.                         switch (player.team) {
  775.                                 case TEAM::BLUE: teamColor = 34; break;
  776.                                 case TEAM::RED: teamColor = 24; break;
  777.                                 case TEAM::GREEN: teamColor = 18; break;
  778.                                 case TEAM::YELLOW: teamColor = 40; break;
  779.                                 default: teamColor = 24; break;
  780.                         }
  781.                        
  782.                         int radarOffsetX = int(xPos - player.xPos) / 35;
  783.                         int radarOffsetY = int(yPos - player.yPos) / 35;
  784.                        
  785.                         if (radarOffsetX < 70 && radarOffsetX > -63 && radarOffsetY < 70 && radarOffsetY > -63 && player.xPos > 0 && player.yPos > 0 && !play.isSpectating && !play.isOut && !play.isConnecting) {
  786.                                 if (player.flag != 0 && !player.isLocal) {
  787.                                         canvas.drawResizedSprite(
  788.                                                 ((jjSubscreenWidth - 678) - radarOffsetX),
  789.                                                 ((jjSubscreenHeight - 168) - radarOffsetY),
  790.                                                 ANIM::FLAG,
  791.                                                 3,
  792.                                                 jjGameTicks >> 2,
  793.                                                 radarOffsetX > 0? 0.4:-0.4,
  794.                                                 0.4,
  795.                                                 SPRITE::SINGLECOLOR,
  796.                                                 player.team == TEAM::BLUE? 24 : 34
  797.                                         );
  798.                                         canvas.drawRectangle(
  799.                                                 ((jjSubscreenWidth - 680) - radarOffsetX),
  800.                                                 ((jjSubscreenHeight - 170) - radarOffsetY),
  801.                                                 6,
  802.                                                 6,
  803.                                                 teamColor,
  804.                                                 SPRITE::NORMAL
  805.                                         );
  806.                                 }
  807.                                 else if (player.flag == 0) {
  808.                                         canvas.drawRectangle(
  809.                                                 ((jjSubscreenWidth - 680) - radarOffsetX),
  810.                                                 ((jjSubscreenHeight - 170) - radarOffsetY),
  811.                                                 player.isLocal? 8:6,
  812.                                                 player.isLocal? 8:6,
  813.                                                 player.isLocal? 64 : jjGameMode == GAME::CTF? teamColor : 24,
  814.                                                 SPRITE::NORMAL
  815.                                         );
  816.                                 }
  817.                         }
  818.                        
  819.                         for (int j = 1; j < jjObjectCount; j++) {
  820.                                 if (jjObjects[j].eventID == OBJECT::TNT) {
  821.                                        
  822.                                         uint8 redeemerColor;
  823.                                         switch (jjPlayers[jjObjects[j].creatorID].team) {
  824.                                                 case TEAM::BLUE: redeemerColor = 34; break;
  825.                                                 case TEAM::RED: redeemerColor = 24; break;
  826.                                                 case TEAM::GREEN: redeemerColor = 18; break;
  827.                                                 case TEAM::YELLOW: redeemerColor = 40; break;
  828.                                                 default: redeemerColor = 88; break;
  829.                                         }
  830.                                        
  831.                                         int radarMissileOffsetX = int(xPos - jjObjects[j].xPos) / 35;
  832.                                         int radarMissileOffsetY = int(yPos - jjObjects[j].yPos) / 35;
  833.                                        
  834.                                         if (!jjPlayers[jjObjects[j].creatorID].isLocal && radarMissileOffsetX < 70 && radarMissileOffsetX > -63 && radarMissileOffsetY < 70 && radarMissileOffsetY > -63) {
  835.                                                 canvas.drawRotatedSprite(
  836.                                                         ((jjSubscreenWidth - 680) - radarMissileOffsetX),
  837.                                                         ((jjSubscreenHeight - 170) - radarMissileOffsetY),
  838.                                                         ANIM::CUSTOM[1],
  839.                                                         0,
  840.                                                         jjGameTicks >> 2,
  841.                                                         jjObjects[j].var[0],
  842.                                                         0.5,
  843.                                                         0.5,
  844.                                                         SPRITE::SINGLECOLOR,
  845.                                                         jjGameMode == GAME::CTF? redeemerColor : 90
  846.                                                 );
  847.                                         }
  848.                                 }
  849.                         }
  850.                 }
  851.                
  852.                 if (warning) {
  853.                         canvas.drawString(
  854.                                 jjSubscreenWidth - 550,
  855.                                 jjSubscreenHeight - 374,
  856.                                 "||WARNING: YOU ARE IN RANGE!",
  857.                                 STRING::SMALL,
  858.                                 STRING::NORMAL
  859.                         );
  860.                 }
  861.                
  862.                 if (!jjLowDetail && jjGameTicks % 140 >= 7) {
  863.                         canvas.drawString(
  864.                                 jjSubscreenWidth - 132,
  865.                                 jjSubscreenHeight - 46,
  866.                                 "REC",
  867.                                 STRING::MEDIUM,
  868.                                 STRING::NORMAL
  869.                         );
  870.                         canvas.drawSprite(
  871.                                 jjSubscreenWidth - 146,
  872.                                 jjSubscreenHeight - 45,
  873.                                 ANIM::VINE,
  874.                                 0,
  875.                                 0,
  876.                                 0,
  877.                                 SPRITE::NORMAL
  878.                         );
  879.                 }
  880.         }
  881.         else if (!controllingRedeemer && play.currWeapon == WEAPON::TNT) {
  882.                 canvas.drawString(
  883.                         jjSubscreenWidth - 80,
  884.                         jjSubscreenHeight - 14,
  885.                         "x" + play.ammo[WEAPON::TNT],
  886.                         STRING::MEDIUM,
  887.                         STRING::NORMAL
  888.                 );
  889.                 canvas.drawResizedSprite(
  890.                         jjSubscreenWidth - 110,
  891.                         jjSubscreenHeight - 14,
  892.                         ANIM::CUSTOM[1],
  893.                         0,
  894.                         jjGameTicks >> 2,
  895.                         2,
  896.                         2,
  897.                         SPRITE::NORMAL
  898.                 );
  899.         }
  900.        
  901.         return controllingRedeemer || play.currWeapon == WEAPON::TNT;
  902. }
  903.  
  904. jjANIMSET@ redeemerPickupSprite;
  905. bool generateRedeemerPickupSprite(jjANIMSET@ anim, const array<uint> &in colors) {
  906.         int length = colors.length();
  907.         bool success = (@redeemerPickupSprite = anim).allocate(array<uint>(length, 8)) !is null;
  908.         if (success) {
  909.                 uint srcSet = jjAnimSets[ANIM::SONCSHIP];
  910.                 for (int i = 0; i < length; i++) {
  911.                         uint color = colors[i];
  912.                         uint destAnimOffset = anim + i;
  913.                         uint srcAnim = jjAnimations[srcSet + 0];
  914.                         uint destAnim = jjAnimations[destAnimOffset + 0];
  915.                         for (int k = 0; k < 8; k++) {
  916.                                 jjPIXELMAP image(jjAnimFrames[destAnim + k] = jjAnimFrames[srcAnim + k]);
  917.                                 int width = image.width;
  918.                                 int height = image.height;
  919.                                 for (int l = 0; l < height; l++) {
  920.                                         for (int m = 0; m < width; m++) {
  921.                                                 int pixel = image[m, l];
  922.                                                 if (pixel >= 40 && pixel < 48)
  923.                                                         image[m, l] = color;
  924.                                         }
  925.                                 }
  926.                         if (!image.save(jjAnimFrames[destAnim + k]))
  927.                                 return false;
  928.                         }
  929.                 }
  930.         }
  931.         return success;
  932. }
  933.  
  934. void onMain() {
  935.         array<jjLAYER@> layers = jjLayerOrderGet();
  936.        
  937.         layers[8].xOffset -=1;
  938.         layers[9].xOffset -=0.2f;
  939.         if (layers[8].xOffset % (layers[8].widthReal*32) == 0) layers[8].xOffset = 0;
  940.         if (layers[9].xOffset % (layers[9].widthReal*32) == 0) layers[9].xOffset = 0;
  941.         layers[8].yOffset += jjSin(jjGameTicks*10)/5;
  942.        
  943.         for (int i = 1; i < jjObjectCount; i++) {
  944.                 jjOBJ@ obj = jjObjects[i];
  945.                 if (obj.isActive && obj.eventID == OBJECT::COPTER && obj.state == STATE::FLY) {
  946.                         obj.counter = 0;
  947.                         if (obj.var[4] == 0)
  948.                                 obj.state = STATE::DONE;
  949.                 }
  950.         }
  951. }
  952.  
  953. void onPlayer(jjPLAYER@ play) {
  954.         processFrozenCamera(play);
  955.  
  956.         if (controllingRedeemer) play.currWeapon = WEAPON::BLASTER;
  957.        
  958.         for (uint i = 0; i < oneWays.length(); i++) {
  959.                 jjEventSet(oneWays[i].x, oneWays[i].y, AREA::ONEWAY);
  960.         }
  961.         oneWays.resize(0);
  962.         int px = int(play.xPos), py = int(play.yPos);
  963.         bool masked;
  964.         for (int i = -11 + int(play.ySpeed); i <= 14; i++) {
  965.                 if (masked = jjMaskedHLine(px - 14, 28, py + i))
  966.                         break;
  967.         }
  968.         if (!masked) {
  969.                 for (int i = 8; i <= 16; i += 8) {
  970.                         for (int j = 12; j <= 20; j += 8) {
  971.                                 int x = (px + play.direction * j) >>> 5, y = (py + i) >>> 5;
  972.                                 if (x >= 0 && y >= 0 && x < jjLayerWidth[4] && y < jjLayerHeight[4] && jjEventGet(x, y) == AREA::ONEWAY) {
  973.                                         vector2i point;
  974.                                         jjEventSet(point.x = x, point.y = y, 0);
  975.                                         oneWays.insertLast(point);
  976.                                 }
  977.                         }
  978.                 }
  979.         }
  980. }
  981.  
  982. void onLevelReload() {
  983.         MLLE::Palette.apply();
  984. }
  985.  
  986. void onLevelBegin() {
  987.         for (int i = 1; i < 255; i++) {
  988.                 jjOBJ@ preset = jjObjectPresets[i];
  989.                 if (preset.playerHandling == HANDLING::PICKUP) {
  990.                         preset.behavior = CannotBeShotDown(preset.behavior);
  991.                 }
  992.         }
  993. }
  994.  
  995. class CannotBeShotDown : jjBEHAVIORINTERFACE {
  996.         CannotBeShotDown(const jjBEHAVIOR &in behavior) {
  997.                 originalBehavior = behavior;
  998.         }
  999.         void onBehave(jjOBJ@ obj) {
  1000.                 obj.behave(originalBehavior);
  1001.                 if (obj.state == STATE::FLOATFALL)
  1002.                         obj.state = STATE::FLOAT;
  1003.         }
  1004.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  1005.                 if (bullet is null) {
  1006.                         obj.behavior = originalBehavior;
  1007.                         if (player.objectHit(obj, force, obj.playerHandling))
  1008.                                 return true;
  1009.                         obj.behavior = this;
  1010.                 }
  1011.                 return false;
  1012.         }
  1013.         private jjBEHAVIOR originalBehavior;
  1014. }
  1015.  
  1016. class SpringMushroom : jjBEHAVIORINTERFACE {
  1017.         void onBehave(jjOBJ@ obj) {
  1018.                 obj.behave(BEHAVIOR::SPRING, false);
  1019.                 jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos - 16, obj.curFrame, obj.eventID != OBJECT::HORREDSPRING? 0: obj.direction == 0? 0:512, 2, 2, SPRITE::NORMAL);
  1020.                 if (obj.state == STATE::SPRING && !jjLowDetail) {
  1021.                         jjPARTICLE@ spores = jjAddParticle(PARTICLE::FIRE);
  1022.                         if (spores !is null) {
  1023.                                 spores.xPos = int(obj.xPos - 28) + (jjRandom()%28)*2;
  1024.                                 spores.yPos = int(obj.yPos - 14) + jjRandom()%14;
  1025.                                 if (jjRandom()%2 > 0)
  1026.                                         spores.ySpeed = -1;
  1027.                                 else spores.ySpeed = -0.6;
  1028.                                 spores.fire.color = 64;
  1029.                                 spores.fire.colorStop = 72;
  1030.                         }
  1031.                 }
  1032.         }
  1033. }