Downloads containing ab23btl08.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Anniversary Bash 23 levels Jazz2Online Multiple N/A Download file

File preview

  1. const bool MLLESetupSuccessful = MLLE::Setup(array<MLLEWeaponApply@> = {null, null, BubbleGun::Weapon(), null, null, null, WeaponVMega::GravityWell::Weapon(), null, null}); ///@MLLE-Generated
  2. #include "MLLE-Include-1.5w.asc" ///@MLLE-Generated
  3. #pragma require "ab23btl08-MLLE-Data-4.j2l" ///@MLLE-Generated
  4. #pragma require "ab23btl08-MLLE-Data-3.j2l" ///@MLLE-Generated
  5. #pragma require "ab23btl08-MLLE-Data-2.j2l" ///@MLLE-Generated
  6. #pragma require "ab23btl08-MLLE-Data-1.j2l" ///@MLLE-Generated
  7. #pragma require "ab23btl08.j2l" ///@MLLE-Generated
  8. #include "WeaponVMega7.asc" ///@MLLE-Generated
  9. #pragma require "WeaponVMega7.asc" ///@MLLE-Generated
  10. #include "BubbleGun.asc" ///@MLLE-Generated
  11. #pragma require "BubbleGun.asc" ///@MLLE-Generated
  12. #pragma require "spelunky_ignite.wav"
  13. #pragma require "Loonejjjjxdddd3.pal"
  14.  
  15. array<bool> canJump(jjLocalPlayerCount, true);
  16. array<bool> canJumpTwo(jjLocalPlayerCount, true);
  17.  
  18. jjPAL PsychPal;
  19.  
  20. bool palCycle = true;
  21. const string filename = "malice.asdat";
  22.  
  23. void onLevelLoad() {
  24.         PsychPal.load("Loonejjjjxdddd3.pal");
  25.         PsychPal.color[255].red = PsychPal.color[255].green = PsychPal.color[255].blue = 0;
  26.         PsychPal.apply();
  27.        
  28.         jjObjectPresets[OBJECT::BRIDGE].behavior = MyBridge;
  29.        
  30.         jjLayers[1].spriteMode = SPRITE::BLEND_NORMAL;
  31.         jjLayers[1].spriteParam = 255;
  32.        
  33.         jjTexturedBGFadePositionY = 0.30;
  34.        
  35.         jjSampleLoad(SOUND::P2_POEP, "spelunky_ignite.wav");
  36.        
  37.         jjTexturedBGTexture = TEXTURE::PSYCH;
  38.         jjUseLayer8Speeds = true;
  39.        
  40.         jjWaterLayer = 28;
  41.         jjSetWaterGradient(0,22,44, 0,44,22);
  42.         jjWaterLighting = (WATERLIGHT::GLOBAL);
  43.        
  44.         generateCustomSpringSprites(jjAnimSets[ANIM::CUSTOM[9]], array<uint> = {40, 16, 64, 88});
  45.     turnIntoCustomSpring(jjObjectPresets[OBJECT::FROZENSPRING], 0, 19.75f, false);
  46.     turnIntoCustomSpring(jjObjectPresets[OBJECT::HORREDSPRING], 1, 26.55f, false);
  47.     turnIntoCustomSpring(jjObjectPresets[OBJECT::HORGREENSPRING], 2, 17.75f, false);
  48.     turnIntoCustomSpring(jjObjectPresets[OBJECT::HORBLUESPRING], 3, 33.3f, false);
  49.        
  50.         jjObjectPresets[OBJECT::HORREDSPRING].causesRicochet = jjObjectPresets[OBJECT::HORBLUESPRING].causesRicochet = jjObjectPresets[OBJECT::HORGREENSPRING].causesRicochet = false;
  51.  
  52.     jjObjectPresets[OBJECT::GUNCRATE].behavior = ColoredCrate();
  53.         jjObjectPresets[OBJECT::SHARD].behavior = ColoredCrate();
  54.        
  55.         // jjDelayGeneratedCrateOrigins = true;
  56.        
  57.         jjWeapons[WEAPON::GUN8].spread = SPREAD::NORMAL;
  58.         jjObjectPresets[OBJECT::FIREBALLBULLETPU].var[6] = 8 + 16;
  59.        
  60.         jjObjectPresets[OBJECT::BOUNCERPOWERUP].direction = -1;
  61.         jjObjectPresets[OBJECT::GUN9POWERUP].direction = -1;
  62.         jjObjectPresets[OBJECT::TOASTERPOWERUP].direction = 0;
  63.        
  64.         jjObjectPresets[OBJECT::CARROT].direction = SPRITE::FLIPV;
  65.         jjObjectPresets[OBJECT::ICEPOWERUP].direction = SPRITE::FLIPV;
  66.         // jjObjectPresets[OBJECT::ICEAMMO15].direction = SPRITE::FLIPH;
  67.         jjObjectPresets[OBJECT::SMALLTREE].direction = SPRITE::FLIPV;
  68.        
  69.         jjObjectPresets[OBJECT::SWINGINGVINE].behavior = SyncedVine;
  70.        
  71.         jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::BRIDGE] + 2];
  72.         for (uint j = 0; j < anim.frameCount; j++) {
  73.                 jjANIMFRAME@ frame = jjAnimFrames[anim + j];
  74.                 jjPIXELMAP sprite(frame);
  75.                 for (uint x = 0; x < sprite.width; ++x) {
  76.                         for (uint y = 0; y < sprite.height; ++y) {
  77.                                 if (sprite[x,y] >= 16 && sprite[x,y] <= 23) sprite[x,y] = 128 + (sprite[x,y]&7)*2;
  78.                         }
  79.                 }
  80.                 sprite.save(frame);
  81.         }
  82.        
  83.        
  84.         jjSTREAM savedSettings(filename);
  85.         while (!savedSettings.isEmpty()) {
  86.                 savedSettings.pop(palCycle);
  87.         }
  88.        
  89. }
  90.  
  91. void onLevelBegin() {
  92.         for (int i = 1; i < jjObjectCount; i++) {
  93.                 if (jjObjects[i].eventID == OBJECT::BRIDGE){
  94.                         jjObjects[i].yOrg += 7;
  95.                         jjObjects[i].yPos += 7;
  96.                 }
  97.         }
  98.         for (int x = 0; x < jjLayerWidth[4]; x++) {
  99.         for (int y = 0; y < jjLayerHeight[4]; y++) {
  100.             if ((jjEventGet(x, y) == OBJECT::GENERATOR && jjParameterGet(x, y, 0, 8) == 255) || jjEventGet(x, y) == 255) {
  101.                 jjEventSet(x, y, AREA::ONEWAY);
  102.             }
  103.         }
  104.     }
  105.        
  106.                
  107. /*      jjObjectPresets[OBJECT::TOASTERBULLETPU].lightType = LIGHT::BRIGHT;
  108.         jjObjectPresets[OBJECT::TOASTERBULLETPU].light = 8;
  109.        
  110.         jjObjectPresets[OBJECT::FIREBALLBULLETPU].lightType = LIGHT::BRIGHT;
  111.         jjObjectPresets[OBJECT::FIREBALLBULLETPU].light = 8;
  112.        
  113.         jjObjectPresets[OBJECT::RFBULLET].lightType = LIGHT::BRIGHT;
  114.         jjObjectPresets[OBJECT::RFBULLET].light = 8;
  115.        
  116.         jjObjectPresets[OBJECT::BOUNCERBULLETPU].lightType = LIGHT::BRIGHT;
  117.         jjObjectPresets[OBJECT::BOUNCERBULLETPU].light = 8;
  118.        
  119.         jjObjectPresets[OBJECT::ICEBULLETPU].lightType = LIGHT::BRIGHT;
  120.         jjObjectPresets[OBJECT::ICEBULLETPU].light = 8;
  121.        
  122.         jjObjectPresets[OBJECT::ELECTROBULLETPU].lightType = LIGHT::BRIGHT;
  123.         jjObjectPresets[OBJECT::ELECTROBULLETPU].light = 8;
  124.        
  125.         jjObjectPresets[OBJECT::SEEKERBULLETPU].lightType = LIGHT::BRIGHT;
  126.         jjObjectPresets[OBJECT::SEEKERBULLETPU].light = 8; */
  127. }
  128.  
  129. void onMain() {
  130.         jjWeapons[WEAPON::GUN8].comesFromGunCrates = true;
  131.     jjWeapons[WEAPON::GUN9].comesFromGunCrates = true;
  132.        
  133.         for (int i = 1; i < jjObjectCount; i++) {
  134.         jjOBJ@ obj = jjObjects[i];
  135.         if (obj.eventID == OBJECT::ICEBULLETPU && obj.xSpeed > -0.5 && obj.xSpeed < 0.5) {
  136.             obj.xSpeed = 0;
  137.         }
  138.                 if (jjObjects[i].eventID == OBJECT::CHESHIRE1){
  139.                         jjObjects[i].yPos = jjObjects[i].yOrg - 15;
  140.                         if (jjObjects[i].xPos>118*32 && jjObjects[i].yPos>48*32 && jjObjects[i].xPos<122*32 && jjObjects[i].yPos<52) {
  141.                         jjObjectPresets[OBJECT::CHESHIRE1].direction = SPRITE::FLIPHV;
  142.                        
  143.                         }
  144.                 }
  145.                 if (jjObjects[i].eventID == OBJECT::BOUNCERPOWERUP) {
  146.                         jjObjects[i].yPos = 58.5*32;
  147.                         jjObjects[i].ySpeed = 0;
  148.                 }
  149.                 if (jjObjects[i].eventID == OBJECT::BLASTERPOWERUP) {
  150.                         jjObjects[i].yPos = 109.68*32;
  151.                         jjObjects[i].ySpeed = 0;
  152.                 }
  153.                 if (obj.eventID == OBJECT::TOASTERPOWERUP){
  154.                         obj.xPos = obj.xOrg + 16;
  155.                 }
  156.     }
  157.        
  158.         if (jjGameTicks % 4 == 0 && palCycle) {
  159.         for (int i = 128; i <= 143; i++) {
  160.             jjPalette.color[i].setHSL(PsychPal.color[i].getHue() + jjGameTicks / 6, PsychPal.color[i].getSat(), PsychPal.color[i].getLight());
  161.         }
  162.         jjPalette.apply();
  163.     }
  164.        
  165.         if (jjGameTicks == 210)
  166.                 jjConsole("|You can use the following command to pause the color changing foliage:||!palette");
  167.  
  168.  
  169. }
  170.  
  171. void onPlayer(jjPLAYER@ player){
  172.         player.lightType = LIGHT::NONE;
  173.        
  174.         handleFastCustomSpringSpeeds(player);
  175.        
  176.         if (player.yPos >= 0) {
  177.                 //Wherein "skill 3" means "MP Only"
  178.                 int skill = jjParameterGet(uint16(player.xPos/32), uint16(player.yPos/32), -4, 2);     
  179.                 if (skill == 2) {
  180.                         if (jjLayers[1].spriteParam > 96) {
  181.                                 if (jjLayers[1].spriteParam == 255) jjSamplePriority(SOUND::P2_POEP);
  182.                                 jjLayers[1].spriteParam = jjLayers[1].spriteParam - 10;
  183.                         }
  184.                 }
  185.                 else if (jjLayers[1].spriteParam != 255) {
  186.                         jjLayers[1].spriteParam = jjLayers[1].spriteParam + 10;
  187.                 }
  188.         }
  189.        
  190.         for (int i = 1; i < jjObjectCount; i++) {
  191.         if (jjObjects[i].isActive && jjObjects[i].eventID == OBJECT::COPTER && jjObjects[i].state == STATE::FLY) {
  192.             //Only set the counter if it's available to take
  193.             jjObjects[i].counter = 0;
  194.             if (jjObjects[i].var[4] == 0)
  195.                 jjObjects[i].state = STATE::DONE;
  196.         }
  197.         }
  198.        
  199.         if (!canJump[player.localPlayerID]) {
  200.         player.keyJump = player.keyDown = player.keyFire = player.keyLeft = player.keyRight = false;
  201.     }
  202.        
  203.         if (!canJumpTwo[player.localPlayerID]) {
  204.         player.keyJump = player.keyDown = player.keyFire = false;
  205.     }
  206.        
  207.         if (player.health == 0 || jjGameTicks == 1){
  208.           canJump[player.localPlayerID] = true;
  209.           canJumpTwo[player.localPlayerID] = true;
  210.          // player.yOrg += 32;
  211.         }
  212. }
  213.  
  214.  
  215. jjANIMSET@ customSpringSprite;
  216. array<int> fastCustomSpringSpeeds(jjLocalPlayerCount);
  217. bool generateCustomSpringSprites(jjANIMSET@ anim, const array<uint> &in colors) {
  218.     int length = colors.length();
  219.     bool success = (@customSpringSprite = anim).allocate(array<uint>(length * 3, 5)) !is null;
  220.     if (success) {
  221.         uint srcSet = jjAnimSets[ANIM::SPRING];
  222.         for (int i = 0; i < length; i++) {
  223.             uint color = colors[i];
  224.             uint destAnimOffset = anim + i * 3;
  225.             for (int j = 0; j < 3; j++) {
  226.                 uint srcAnim = jjAnimations[srcSet + j];
  227.                 uint destAnim = jjAnimations[destAnimOffset + j];
  228.                 for (int k = 0; k < 5; k++) {
  229.                     jjPIXELMAP image(jjAnimFrames[destAnim + k] = jjAnimFrames[srcAnim + k]);
  230.                     int width = image.width;
  231.                     int height = image.height;
  232.                     for (int l = 0; l < height; l++) {
  233.                         for (int m = 0; m < width; m++) {
  234.                             int pixel = image[m, l];
  235.                             if (pixel >= 32 && pixel < 40)
  236.                                 image[m, l] = color + (pixel & 7);
  237.                         }
  238.                     }
  239.                     if (!image.save(jjAnimFrames[destAnim + k]))
  240.                         return false;
  241.                 }
  242.             }
  243.         }
  244.     }
  245.     return success;
  246. }
  247. void initializeCustomSpring(jjOBJ@ obj) {
  248.     int anim = obj.curAnim;
  249.     obj.behave(obj.behavior = BEHAVIOR::SPRING, false);
  250.     if (obj.curAnim != anim) {
  251.         obj.curAnim = anim + 2;
  252.         obj.determineCurFrame();
  253.     }
  254.     obj.draw();
  255. }
  256.  
  257. void turnIntoCustomSpring(jjOBJ@ obj, uint color, float power, bool horizontal) {
  258.     if (horizontal) {
  259.         obj.xSpeed = power;
  260.         obj.ySpeed = 0.f;
  261.     } else {
  262.         obj.xSpeed = 0.f;
  263.         obj.ySpeed = -power;
  264.         if (obj.state == STATE::START && obj.creatorType == CREATOR::LEVEL) {
  265.             int x = int(obj.xPos) >> 5;
  266.             int y = int(obj.yPos) >> 5;
  267.             if (jjParameterGet(x, y, 0, 1) != 0) {
  268.                 jjParameterSet(x, y, 0, 1, 0);
  269.                 obj.yPos -= 4.f;
  270.                 obj.ySpeed = power;
  271.             }
  272.         }
  273.     }
  274.     obj.behavior = initializeCustomSpring;
  275.     obj.curAnim = customSpringSprite + color * 3 + (horizontal ? 1 : 0);
  276.     obj.energy = obj.frameID = obj.freeze = obj.justHit = obj.light = obj.points = 0;
  277.     obj.isBlastable = obj.isTarget = obj.scriptedCollisions = obj.triggersTNT = false;
  278.     obj.deactivates = obj.isFreezable = true;
  279.     obj.bulletHandling = HANDLING::IGNOREBULLET;
  280.     obj.playerHandling = HANDLING::SPECIAL;
  281.     obj.lightType = LIGHT::NORMAL;
  282.     obj.determineCurFrame();
  283. }
  284.  
  285. void handleFastCustomSpringSpeeds(jjPLAYER@ play) {
  286.         if (play.ySpeed < -32.f) {
  287.                 fastCustomSpringSpeeds[play.localPlayerID] = int(ceil((play.ySpeed + 32.f) / -0.125f));
  288.         } else if (fastCustomSpringSpeeds[play.localPlayerID] != 0) {
  289.                 if (play.ySpeed < -31.f) {
  290.                         fastCustomSpringSpeeds[play.localPlayerID]--;
  291.                         play.ySpeed = -32.f;
  292.                 } else {
  293.                         fastCustomSpringSpeeds[play.localPlayerID] = 0;
  294.                 }
  295.         }
  296. }
  297.  
  298. void onDrawLayer7(jjPLAYER@ play, jjCANVAS@ screen) {
  299.         jjSetWaterLevel((play.cameraY - jjLayers[15].getYPosition(play)) + 320, true);
  300. }
  301. void onDrawLayer4(jjPLAYER@ play, jjCANVAS@ screen) {
  302.   jjSetWaterLevel(16000, true);
  303. }
  304.  
  305. class ColoredCrate : jjBEHAVIORINTERFACE {
  306.     int color;
  307.     void onBehave(jjOBJ@ obj) {
  308.         obj.behave(obj.eventID == OBJECT::GUNCRATE? BEHAVIOR::CRATE : BEHAVIOR::SHARD);
  309.        
  310.         if (obj.state == STATE::KILL) {
  311.             obj.delete();
  312.         }
  313.     }
  314.     void onDraw(jjOBJ@ obj) {
  315.         color = jjParameterGet(int(obj.xOrg/32), int(obj.yOrg/32)+ (jjEventGet(int(obj.xOrg/32), int(obj.yOrg/32)) == 255? 0:1), 0, 8);
  316.         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, color == 0? SPRITE::NORMAL : SPRITE::SINGLEHUE, color);
  317.                
  318.     }
  319. }
  320. void onFunction3(jjPLAYER@ play, bool preventJump) {
  321.     canJump[play.localPlayerID] = preventJump;
  322. }
  323.  
  324. void onFunction4(jjPLAYER@ play, bool preventJump) {
  325.     canJumpTwo[play.localPlayerID] = preventJump;
  326. }
  327.  
  328. bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
  329.         return MLLE::WeaponHook.drawAmmo(player, canvas);
  330. }
  331.  
  332. /* array<string> Areas = {
  333.     "a", "b", "ยง2Loon is so awesome, so is PJ for helping me with this script!", "d", "e", "f", "g", "h"
  334. };
  335. string currArea = Areas[0];
  336.  
  337. void onFunction0(jjPLAYER@ play, int8 id) {
  338.     currArea = Areas[id];
  339. }
  340.  
  341. bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ canvas) {
  342.     canvas.drawString(jjSubscreenWidth - 16 - (currArea.length()*8), 64, currArea, STRING::SMALL, STRING::NORMAL);
  343.     return false;
  344. } */
  345.  
  346. //violet's witchcraft bridges
  347.  
  348. enum BridgeVariables { PhysicalWidth, MaximumSagDistance, VisualWidth, FirstObjectIDOfPlatformForSplitscreenPlayers, Angle = FirstObjectIDOfPlatformForSplitscreenPlayers + 3 };
  349. void MyBridge(jjOBJ@ obj) {
  350. //first, check collision with bridge
  351.  
  352.         if (obj.state==STATE::START) {
  353.                 obj.state=STATE::STILL;
  354.  
  355.                 const uint xTile = uint(obj.xOrg) >> 5, yTile = uint(obj.yOrg) >> 5;
  356.                 obj.var[BridgeVariables::PhysicalWidth] = 32 * jjParameterGet(xTile,yTile, 0,4);
  357.                 obj.curAnim = jjAnimSets[ANIM::BRIDGE].firstAnim + (jjParameterGet(xTile,yTile, 4,3) % 7); //"Type" parameter... % 7 because there are only seven bridge types for some reason.
  358.  
  359.                 int toughness = jjParameterGet(xTile,yTile, 7,4);
  360.                 if (toughness == 0) toughness = 4; //default toughness of 4, to avoid dividing by zero
  361.                 obj.var[BridgeVariables::MaximumSagDistance] = obj.var[BridgeVariables::PhysicalWidth] / toughness;
  362.                
  363.                 //int heightInTiles = jjParameterGet(xTile,yTile, 11,-5);
  364.                 int heightInTiles = jjParameterGet(xTile, yTile-1, 0, -8);
  365.                 obj.var[BridgeVariables::Angle] = int(atan2(heightInTiles, obj.var[BridgeVariables::PhysicalWidth] / 32) * 162.974662f);
  366.                 obj.xAcc = jjCos(obj.var[BridgeVariables::Angle]);
  367.                 obj.yAcc = jjSin(obj.var[BridgeVariables::Angle]);
  368.                
  369.                 { //determine how wide the bridge is, in drawn pixels (will always be >= how wide it is in mask pixels)
  370.                         int frameID = 0;
  371.                         int bridge_len = 0;
  372.                         const int numberOfFramesUsedByAnimation = jjAnimations[obj.curAnim].frameCount;
  373.                         const uint firstBridgeFrameID = jjAnimations[obj.curAnim].firstFrame;
  374.                         while (true) {
  375.                                 if ((bridge_len += jjAnimFrames[firstBridgeFrameID + frameID].width) >= obj.var[BridgeVariables::PhysicalWidth])
  376.                                         break;
  377.                                        
  378.                                 if (++frameID >= numberOfFramesUsedByAnimation)
  379.                                         frameID = 0;
  380.                         }
  381.                         obj.var[BridgeVariables::VisualWidth] = bridge_len;
  382.                 }
  383.  
  384.                 obj.xOrg -= 16; //start at left edge of tile, not center
  385.                 obj.yOrg -= 6; //worth noting that bridges are never deactivated, so we don't need to worry about where this gets moved to at all
  386.                
  387.                 for (int i = 1; i < jjLocalPlayerCount; ++i) { //this portion has no native JJ2 counterpart, because the API for platforms is still pretty limited
  388.                         const int platformObjectID = jjAddObject(OBJECT::BRIDGE, 0,0, obj.objectID,CREATOR::OBJECT, BEHAVIOR::BEES);
  389.                         jjOBJ@ platform = jjObjects[platformObjectID];
  390.                         platform.deactivates = false;
  391.                         platform.curFrame = jjAnimations[obj.curAnim].firstFrame;
  392.                         obj.var[BridgeVariables::FirstObjectIDOfPlatformForSplitscreenPlayers - 1 + i] = platformObjectID;
  393.                 }
  394.         }
  395.        
  396.         obj.clearPlatform();
  397.         for (int i = 1; i < jjLocalPlayerCount; ++i)
  398.                 jjObjects[obj.var[BridgeVariables::FirstObjectIDOfPlatformForSplitscreenPlayers - 1 + i]].clearPlatform();
  399.  
  400.         array<int> pressXPosition;
  401.         array<jjPLAYER@> pressPlayer;
  402.         for (int playerID = 0; playerID < 32; ++playerID) {
  403.                 jjPLAYER@ play = jjPlayers[playerID];
  404.                 if (play.isActive && play.shieldType != SHIELD::LASER && jjObjects[play.platform].eventID != OBJECT::BURGER) { //all active players are valid, even if isLocal is false
  405.                         const int tx = int(play.xPos-obj.xOrg);
  406.                         const int ty = int(play.yPos-obj.yOrg - obj.yAcc / obj.xAcc * tx);
  407.  
  408.                         if ((tx >= 0) && (tx <= obj.var[BridgeVariables::PhysicalWidth]) && //player is within bridge area (horizontal)
  409.                                 (ty > -32) && (ty < obj.var[BridgeVariables::MaximumSagDistance]) && //(and vertical) //-32 was -24
  410.                                 (play.ySpeed > -1.f)) //not jumping, using a spring, etc.
  411.                         {
  412.                                 pressXPosition.insertLast(tx);
  413.                                 pressPlayer.insertLast(play);
  414.                         }
  415.                 }
  416.         }
  417.        
  418.         float max, amp, leftamp, rightamp;
  419.         int     leftmostPressedX, rightmostPressedX;
  420.  
  421.         if (pressPlayer.length != 0) {
  422.                 if (pressPlayer.length > 1) {
  423.                         leftmostPressedX=12312312;
  424.                         rightmostPressedX=0;
  425.                         uint t = 0;
  426.                         do {
  427.                                 const int pressedX = pressXPosition[t];
  428.                                 if (pressedX < leftmostPressedX)
  429.                                         leftmostPressedX = pressedX;
  430.                                 if (pressedX > rightmostPressedX)
  431.                                         rightmostPressedX = pressedX;
  432.                         } while (++t < pressPlayer.length);
  433.  
  434.                         leftamp =  obj.var[BridgeVariables::MaximumSagDistance]*jjSin((512* leftmostPressedX)/obj.var[BridgeVariables::PhysicalWidth]);
  435.                         rightamp = obj.var[BridgeVariables::MaximumSagDistance]*jjSin((512*rightmostPressedX)/obj.var[BridgeVariables::PhysicalWidth]);
  436.                 }
  437.                
  438.                 uint t = 0;
  439.                 uint numberOfLocalPlayersNeedingPlatforms = 0;
  440.                 do {
  441.                         const auto pressedPosition = pressXPosition[t];
  442.                         if (pressPlayer.length == 1)
  443.                                 max = obj.var[BridgeVariables::MaximumSagDistance] * jjSin((512 * pressedPosition) / obj.var[BridgeVariables::PhysicalWidth]); //same formula as side amps above, but for single player bridges
  444.                         else if ((pressedPosition>leftmostPressedX) && (pressedPosition<rightmostPressedX))
  445.                                 max = leftamp+(rightamp-leftamp)*(pressedPosition-leftmostPressedX)/(rightmostPressedX-leftmostPressedX);
  446.                         else
  447.                                 max = obj.var[BridgeVariables::MaximumSagDistance]*jjSin((512 * pressedPosition)/obj.var[BridgeVariables::PhysicalWidth]);
  448.  
  449.                         jjPLAYER@ play = pressPlayer[t];
  450.                         play.yPos = obj.yOrg + obj.yAcc / obj.xAcc * pressedPosition + max - 24;
  451.                         if (play.isLocal) {
  452.                                 jjOBJ@ platform = (numberOfLocalPlayersNeedingPlatforms == 0) ? obj : jjObjects[obj.var[BridgeVariables::FirstObjectIDOfPlatformForSplitscreenPlayers - 1 + numberOfLocalPlayersNeedingPlatforms]];
  453.                                 platform.bePlatform(
  454.                                         platform.xPos = play.xPos,
  455.                                         platform.yPos = play.yPos + 24
  456.                                 );
  457.                                 //platform.draw();
  458.                                 if (play.buttstomp < 120)
  459.                                         play.buttstomp = 120;
  460.                                 numberOfLocalPlayersNeedingPlatforms += 1;
  461.                         }
  462.                 } while (++t < pressPlayer.length);
  463.         }
  464.  
  465.         //draw
  466.         float bridge_len_x = 0, bridge_len_y = 0;
  467.         int frameID = 0;
  468.         const int numberOfFramesUsedByAnimation = jjAnimations[obj.curAnim].frameCount;
  469.         while (true) {  //cooba - change specifically for mlfingers.j2l conditions
  470.                 obj.curFrame = jjAnimations[obj.curAnim].firstFrame + frameID;
  471.                 const jjANIMFRAME@ frame = jjAnimFrames[obj.curFrame];
  472.                
  473.                 float plankOffset = 0; //"straight bridge, or terugveren"
  474.                 if (pressPlayer.length == 1) {
  475.                         const auto pressedPosition = pressXPosition[0];
  476.                         plankOffset = ((bridge_len_x<pressedPosition) ?
  477.                                 (max*jjSin(int(256*bridge_len_x)/pressedPosition)) : //left
  478.                                 (max*jjCos(int(256*(bridge_len_x-pressedPosition))/(obj.var[BridgeVariables::VisualWidth]-pressedPosition) ))
  479.                         );
  480.                 } else if (pressPlayer.length > 1) {
  481.                         if (bridge_len_x < leftmostPressedX)
  482.                                 plankOffset = (leftamp*jjSin(int(256*bridge_len_x)/leftmostPressedX));
  483.                         else if (bridge_len_x > rightmostPressedX)
  484.                                 plankOffset = (rightamp*jjCos(int(256*(bridge_len_x-rightmostPressedX))/(obj.var[BridgeVariables::VisualWidth]-rightmostPressedX) ));
  485.                         else
  486.                                 plankOffset = leftamp+(rightamp-leftamp)*(bridge_len_x-leftmostPressedX)/(rightmostPressedX-leftmostPressedX);
  487.                 }
  488.                 jjDrawRotatedSpriteFromCurFrame(
  489.                         obj.xOrg + bridge_len_x - frame.hotSpotX,
  490.                         obj.yOrg + bridge_len_y + plankOffset,
  491.                         obj.curFrame,
  492.                         -obj.var[BridgeVariables::Angle],
  493.                         1,
  494.                         1,
  495.                         jjLocalPlayers[0].shieldType == SHIELD::LASER? SPRITE::TRANSLUCENTPALSHIFT : SPRITE::NORMAL,
  496.                         96
  497.                 );
  498.  
  499.                 if (int(bridge_len_x += obj.xAcc * frame.width) >= obj.var[BridgeVariables::PhysicalWidth])
  500.                         break;
  501.                 bridge_len_y += obj.yAcc * frame.width;
  502.                        
  503.                 if (++frameID >= numberOfFramesUsedByAnimation)
  504.                         frameID = 0;
  505.         }
  506. }
  507.  
  508. const uint NumberOfBitsDevotedToSyncParameter = 2; //this should correspond to the number of bits you assign to the Sync parameter in your JCS.ini (or MLLE equivalent) entry for Swinging Vine. So for example if you give it a parameter Sync:2, this variable should ALSO equal 2.
  509.  
  510.  
  511. void SyncedVine(jjOBJ@ obj) {
  512.   obj.var[1] = 128; //vine length--set this in whatever way appeals to you, but a constant 128 is the value that normal swinging vines use.
  513.  
  514.   if (lastSwingingVineLUTLength != obj.var[1]) { //need to generate LUT (LookUp Table) by doing the same math swinging vine objects do
  515.     lastSwingingVineLUTLength = obj.var[1];
  516.     PossibleVineVariableConfigurations = array<array<int>> = {{obj.var[1] * 256, 0}};
  517.     while (true) {
  518.       const array<int>@ oldConfiguration = @PossibleVineVariableConfigurations[PossibleVineVariableConfigurations.length-1];
  519.       array<int> newConfiguration(2);
  520.       newConfiguration[1] = oldConfiguration[1] + ((oldConfiguration[0] > 0) ? -32 : 32);
  521.       newConfiguration[0] = oldConfiguration[0] + newConfiguration[1];
  522.       if (newConfiguration[1] == 0 && newConfiguration[0] == obj.var[1] * 256) //gone full circle
  523.         break;
  524.       PossibleVineVariableConfigurations.insertLast(newConfiguration);
  525.     }
  526.   }
  527.  
  528.   const array<int>@ syncedConfiguration = PossibleVineVariableConfigurations[(jjGameTicks + (jjParameterGet(uint(obj.xOrg) >> 5, uint(obj.yOrg) >> 5, 0, NumberOfBitsDevotedToSyncParameter) * (PossibleVineVariableConfigurations.length / (1 << NumberOfBitsDevotedToSyncParameter)))) % PossibleVineVariableConfigurations.length];
  529.   for (uint i = 0; i < 2; ++i)
  530.     obj.var[2 + i] = syncedConfiguration[i];
  531.    
  532.   //clean up:
  533.   obj.state = STATE::ACTION;
  534.   obj.behavior = BEHAVIOR::SWINGINGVINE;
  535.   obj.behave();
  536. }
  537. int lastSwingingVineLUTLength = -1;
  538. array<array<int>> PossibleVineVariableConfigurations;
  539.  
  540. bool onLocalChat(string &in text, CHAT::Type type) {
  541.         if (jjRegexMatch(text, "!palette", true)) {
  542.                 if (palCycle) {
  543.                         jjConsole("Palette cycling disabled!");
  544.                         palCycle = false;
  545.                         jjSTREAM stream;
  546.                         stream.push(palCycle);
  547.                         stream.save(filename);
  548.                 }
  549.                 else {
  550.                         jjConsole("Palette cycling enabled!");
  551.                         palCycle = true;
  552.                         jjSTREAM stream;
  553.                         stream.push(palCycle);
  554.                         stream.save(filename);
  555.                 }
  556.         return true;
  557.         }
  558.         else return false;
  559. }