Downloads containing ab20ctf05.j2as

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

File preview

  1. #include "MLLE-Include-1.4.asc"
  2. const bool MLLESetupSuccessful = MLLE::Setup();
  3. #pragma require "xlmbeach1.j2t"
  4. #pragma require "Lomat.j2t"
  5. #pragma require "ab20ctf05-MLLE-Data-2.j2l"
  6. #pragma require "ab20ctf05-MLLE-Data-1.j2l"
  7. #pragma require "ab20ctf05.j2l"
  8. #pragma require "rain9.wav"
  9. #pragma require "lightning2.wav"
  10. #pragma require "splash01.wav"
  11. #pragma require "SEroller.asc"
  12. #pragma require "SEfirework.asc"
  13. #include "SEroller.asc"
  14. #include "SEfirework.asc"
  15. #include "limitedoxygen.asc"
  16.  
  17. se::DefaultWeaponHook weaponHook;
  18. bool layersInitialized = false;
  19.  
  20. /*******************************************************************/
  21. class Level {
  22.         private int lightningDelay = 90;
  23.         private int waterHeight = 80*32;
  24.         private float lightningOffset = 0;
  25.         private bool lightning = false;
  26.         private int sample = 0;
  27.        
  28.         private jjPAL standard, palette;
  29.         private int hue, sat, light;
  30.        
  31.         void darkenLayer(jjLAYER@ layer, float factor) {
  32.                 array<int> tileIDs, uniqueTileIDs;
  33.                 for (int i = 0; i < layer.height; i++) {
  34.                         for (int j = 0; j < layer.width; j++) {
  35.                                 int tileID = layer.tileGet(j, i);
  36.                                 if (tileID != 0)
  37.                                         tileIDs.insertLast(tileID);
  38.                         }
  39.                 }
  40.                 int prev = 0;
  41.                 tileIDs.sortAsc();
  42.                 for (uint i = 0; i < tileIDs.length(); i++) {
  43.                         if (tileIDs[i] != prev)
  44.                                 uniqueTileIDs.insertLast(prev = tileIDs[i]);
  45.                 }
  46.                 uint firstNewTile = jjTileCount;
  47.                 jjTilesFromTileset(jjTilesetFileName, 1, uniqueTileIDs.length());
  48.                 array<uint8> mapping(256);
  49.                 for (uint i = 0; i < uniqueTileIDs.length(); i++) {
  50.                         jjPIXELMAP tile(uniqueTileIDs[i]);
  51.                         for (int j = 0; j < 32; j++) {
  52.                                 for (int k = 0; k < 32; k++) {
  53.                                         uint8 pixel = tile[k, j];
  54.                                         if (pixel != 0) {
  55.                                                 if (mapping[pixel] == 0) {
  56.                                                         jjPALCOLOR color = jjPalette.color[pixel];
  57.                                                         color.red = uint8(color.red / factor);
  58.                                                         color.green = uint8(color.green / factor);
  59.                                                         color.blue = uint8(color.blue / factor);
  60.                                                         mapping[pixel] = jjPalette.findNearestColor(color);
  61.                                                 }
  62.                                                 tile[k, j] = mapping[pixel];
  63.                                         }
  64.                                 }
  65.                         }
  66.                         tile.save(firstNewTile + i);
  67.                 }
  68.                 layer.generateSettableTileArea();
  69.                 for (int i = 0; i < layer.height; i++) {
  70.                         for (int j = 0; j < layer.widthReal; j++) {
  71.                                 int tileID = layer.tileGet(j, i);
  72.                                 if (tileID != 0)
  73.                                         layer.tileSet(j, i, firstNewTile + uniqueTileIDs.find(tileID));
  74.                         }
  75.                 }
  76.         }
  77.        
  78.         void doStuffWithLayers() {
  79.                 auto proposedLayerOrder = jjLayerOrderGet();
  80.                 for (int i = 2; i < 6; i++) {
  81.                         jjLAYER@ layer = jjLAYER(jjLayers[1]);
  82.                         layer.xSpeed = (0.09/i)*1.85;
  83.                         layer.ySpeed = (0.05/i)*1.85;
  84.                         layer.xOffset = jjRandom() & 127;
  85.                         layer.yOffset = jjLayers[1].yOffset + i*24;
  86.                         proposedLayerOrder.insertAt(proposedLayerOrder.length - 3, layer);
  87.                 }
  88.                 jjLayerOrderSet(proposedLayerOrder);
  89.                
  90.                 proposedLayerOrder[proposedLayerOrder.length - 2].hasTiles = false;
  91.                 jjTileType[822] = 1;
  92.         }
  93.  
  94.         void handleInvisibleBarriers(jjPLAYER@ play) {
  95.                 const int bounds = 16;
  96.        
  97.                 if (play.xPos > ((jjLayerWidth[4]*32) - bounds) || play.xPos < bounds) {
  98.                         play.xPos = play.xPos < (bounds+1)? bounds : (jjLayerWidth[4]*32) - bounds;
  99.                         play.xSpeed = 0;
  100.                         play.specialMove = 0;
  101.                 }
  102.        
  103.                 if (jjEventGet(int(play.xPos/32), int(play.yPos/32)) == AREA::PATH) {
  104.                         int direction = jjParameterGet(int(play.xPos/32), int(play.yPos/32), 6, 3);
  105.                        
  106.                         if (direction > 0) {
  107.                                 play.xSpeed = -2;
  108.                         } else {
  109.                                 play.xSpeed = 2;
  110.                         }
  111.                 }
  112.         }
  113.        
  114.         void handleVerticalRain() {
  115.                 jjIsSnowing = !jjLowDetail;
  116.        
  117.                 for (int i = 0; i < 1024; i++) {
  118.                         jjPARTICLE@ particle = jjParticles[i];
  119.                         if (particle.type == PARTICLE::RAIN) {
  120.                                 particle.xSpeed = 0;
  121.                                 particle.ySpeed = jjLocalPlayers[0].ySpeed < 0? 10 : int(10 + jjLocalPlayers[0].ySpeed);
  122.                                
  123.                                 if (jjMaskedVLine(int(particle.xPos), int(particle.yPos - 1000), 992) || jjMaskedHLine(int(particle.xPos), 2, int(particle.yPos - 8))) {
  124.                                         particle.type = PARTICLE::INACTIVE;
  125.                                 }
  126.                         }
  127.                 }
  128.         }
  129.        
  130.         void loadSamples() {
  131.                 jjSampleLoad(SOUND::WIND_WIND2A, "rain9.wav");
  132.                 jjSampleLoad(SOUND::BILSBOSS_THUNDER, "lightning2.wav");
  133.                 jjSampleLoad(SOUND::COMMON_WATER, "splash01.wav");
  134.         }
  135.        
  136.         void makeRainLookRealistic() {
  137.                 jjANIMATION@ animBubble = jjAnimations[jjAnimSets[ANIM::COMMON] + 1];
  138.                 for (uint i = 0; i < animBubble.frameCount; ++i) {
  139.                         jjANIMFRAME@ frame = jjAnimFrames[animBubble + i];
  140.                         jjPIXELMAP sprite(frame);
  141.                         for (uint x = 0; x < sprite.width; ++x)
  142.                                 for (uint y = 0; y < sprite.height; ++y)
  143.                                 if (sprite[x,y] != 0) sprite[x,y] += 115;
  144.                         sprite.save(frame);
  145.                 }
  146.                
  147.                 jjPIXELMAP rain(32,32);
  148.                 for (uint x = 0; x < rain.width; ++x) {
  149.                         for (uint y = 0; y < rain.height; ++y) {
  150.                                 if (x == 16) {
  151.                                         if (y <= 24) rain[x,y] = 75;
  152.                                         else rain[x,y] = 74;
  153.                                 } else {
  154.                                         rain[x,y] = 0;
  155.                                 }
  156.                         }
  157.                 }
  158.                
  159.                 jjANIMATION@ animRain = jjAnimations[jjAnimSets[ANIM::COMMON].firstAnim + 2];
  160.                 for (uint frameID = 0; frameID < animRain.frameCount; ++frameID) {
  161.                         jjANIMFRAME@ frame = jjAnimFrames[animRain.firstFrame + frameID];
  162.                         rain.save(frame);
  163.                         frame.hotSpotX = -frame.width/2;
  164.                         frame.hotSpotY = -frame.height;
  165.                 }
  166.         }
  167.        
  168.         void processLevelEffects(jjPLAYER@ play) {
  169.                 play.lightType = LIGHT::NONE;
  170.                 if (!jjLowDetail) {
  171.                         if (play.yPos > int(jjWaterLevel + 64)) {
  172.                                 play.lighting = 80 - int((play.yPos - jjWaterLevel)/24);
  173.                         } else {
  174.                                 play.lighting = 80;
  175.                         }
  176.                         if (jjGameTicks > 7) {
  177.                                 sample = jjSampleLooped(play.xPos, play.yPos > jjWaterLevel? jjWaterLevel : play.yPos, SOUND::WIND_WIND2A, sample, 36, 0);
  178.                         }
  179.                 } else {
  180.                         play.lighting = 100;
  181.                 }
  182.        
  183.                 array<jjLAYER@> layers = jjLayerOrderGet();
  184.                 int lightningLayer = layers.length - 2;
  185.                 layers[lightningLayer].xOffset = lightningOffset;
  186.                 if (!jjLowDetail) {
  187.                         if (lightning && lightningDelay > 0) lightningDelay--;
  188.                        
  189.                         if (jjGameTicks % 1000 >= 940 && jjGameTicks % 1000 <= 979) {
  190.                                 if (jjGameTicks % 5 == 0) {
  191.                                         layers[lightningLayer].hasTiles = true;
  192.                                         if (play.yPos < jjWaterLevel + 600) play.lighting = play.yPos < jjWaterLevel + 64? 130:115;
  193.                                 }
  194.                                 else {
  195.                                         layers[lightningLayer].hasTiles = false;
  196.                                         if (play.yPos < jjWaterLevel + 600) play.lighting = play.yPos < jjWaterLevel + 64? 105:85;
  197.                                 }
  198.                        
  199.                                 if (jjGameTicks % 18 == 0) lightningOffset = jjRandom()%800;
  200.                         }
  201.                        
  202.                         if (jjGameTicks % 1000 >= 980) {
  203.                                 lightning = true;
  204.                                 lightningDelay = 90;
  205.                         }
  206.                        
  207.                         if (lightning && lightningDelay == 0) {
  208.                                 for (int i = 0; i < 2; i++) {
  209.                                         jjSample(play.xPos, play.yPos < jjWaterLevel? play.yPos : jjWaterLevel, SOUND::BILSBOSS_THUNDER, 31 + jjRandom()%32, 0);
  210.                                 }
  211.                                 lightning = false;
  212.                         }
  213.                 }
  214.         }
  215.        
  216.         void relocateCTFBases() {
  217.                 for (int i = 1; i < jjObjectCount; i++) {
  218.                         if (jjObjects[i].eventID == OBJECT::CTFBASE || jjObjects[i].behavior == BEHAVIOR::FLAG) jjObjects[i].yOrg += 16;
  219.                 }
  220.         }
  221.  
  222.         void relocateObjects() {
  223.                 for (int i = 1; i < jjObjectCount; i++) {
  224.                         jjOBJ@ obj = jjObjects[i];
  225.                         if (obj.eventID == OBJECT::TOASTERPOWERUP) {
  226.                                 obj.xPos = obj.xOrg + (obj.xOrg > int(jjLayerWidth[4]/2)? 12:-12);
  227.                                 obj.direction = obj.xOrg > int(jjLayerWidth[4]/2)? 1:-1;
  228.                         }
  229.                 }
  230.         }
  231.        
  232.         void removeSpritePaletteReferences() {
  233.                 array<int> mapping(256);
  234.                 for (int i = 1; i < 96; i++) {
  235.                         jjPALCOLOR color = jjPalette.color[i];
  236.                         int best = 0x40000;
  237.                         for (int j = 96; j < 256; j++) {
  238.                                 jjPALCOLOR match = jjPalette.color[j];
  239.                                 int red = int(match.red) - color.red;
  240.                                 int green = int(match.green) - color.green;
  241.                                 int blue = int(match.blue) - color.blue;
  242.                                 int dist = red * red + green * green + blue * blue;
  243.                                 if (dist < best) {
  244.                                         best = dist;
  245.                                         mapping[i] = j;
  246.                                 }
  247.                         }
  248.                 }
  249.                 for (int i = 96; i < 256; i++) {
  250.                         mapping[i] = i;
  251.                 }
  252.                 for (uint i = 1; i < jjTileCount; i++) {
  253.                         jjPIXELMAP tile(i);
  254.                         for (int j = 0; j < 32; j++) {
  255.                                 for (int k = 0; k < 32; k++) {
  256.                                         tile[k, j] = mapping[tile[k, j]];
  257.                                 }              
  258.                         }
  259.                         tile.save(i, true);
  260.                 }
  261.         }
  262.                
  263.         void setLayerSettings() {
  264.                 array<jjLAYER@> layers = jjLayerOrderGet();
  265.                 layers[0].hasTiles = jjLowDetail || jjColorDepth == 8? true:false;
  266.                 layers[0].yOffset = layers[8].yOffset = waterHeight - jjWaterLevel;
  267.                 layers[6].hasTiles = layers[7].hasTiles = jjColorDepth == 16? true:false;
  268.                 jjWaterLayer = jjLowDetail || jjColorDepth == 8? 0:2;
  269.         }
  270.        
  271.         void setLevelPalette() {
  272.                 standard.load("Diam2.j2t");
  273.                 jjPalette.copyFrom(16, 40, 16, standard, 1);
  274.                 jjPalette.copyFrom(59, 37, 59, standard, 1);
  275.                
  276.                 palette.load("ICJungS.j2t");
  277.                 for (int n = 96; n <= 254; n++) {
  278.                         hue = jjPalette.color[n].getHue();
  279.                         sat = jjPalette.color[n].getSat();
  280.                         light = jjPalette.color[n].getLight();
  281.                        
  282.                         if (n < 176 || n > 207) palette.color[n].setHSL(hue, sat / 4, int(light * 2));
  283.                 }
  284.                
  285.                 jjPalette.copyFrom(96, 80, 96, palette, 1);
  286.                 jjPalette.copyFrom(208, 46, 208, palette, 1);
  287.                 jjPalette.gradient(128,137,94, 17,29,41, 176, 32);
  288.                
  289.                 jjPalette.apply();
  290.         }
  291.        
  292.         void setCustomObjects() {
  293.                 RollerEdit roller;
  294.                 FireworkEdit firework;
  295.  
  296.                 roller.loadAnims(jjAnimSets[ANIM::CUSTOM[0]]);
  297.                 roller.loadSamples(array<SOUND::Sample> = {SOUND::ORANGE_SWEEP2L});
  298.                 roller.setAsWeapon(3, weaponHook);
  299.                 firework.loadAnims(jjAnimSets[ANIM::CUSTOM[1]]);
  300.                 firework.loadSamples(array<SOUND::Sample> = {SOUND::ORANGE_BOEML, SOUND::ORANGE_BOEMR});
  301.                 firework.setAsWeapon(4, weaponHook);
  302.  
  303.                 generateCustomSpringSprites(jjAnimSets[ANIM::CUSTOM[2]], array<uint> = {40, 16, 88});
  304.                 turnIntoCustomSpring(jjObjectPresets[OBJECT::FROZENSPRING], 0, 19.f, false);
  305.                 turnIntoCustomSpring(jjObjectPresets[OBJECT::HORREDSPRING], 1, 22.f, false);
  306.                 turnIntoCustomSpring(jjObjectPresets[OBJECT::HORGREENSPRING], 2, 28.f, false);
  307.                
  308.                 jjObjectPresets[OBJECT::HORREDSPRING].causesRicochet = jjObjectPresets[OBJECT::HORGREENSPRING].causesRicochet = false;
  309.                
  310.                 for (int i = 1; i < 255; i++) {
  311.                         if (jjObjectPresets[i].playerHandling == HANDLING::PICKUP) {
  312.                                 jjObjectPresets[i].behavior = CannotBeShotDown(jjObjectPresets[i].behavior);
  313.                         }
  314.                 }
  315.         }
  316.  
  317.         void setWaterProperties() {
  318.                 jjSetWaterLevel(waterHeight,true);
  319.                 jjWaterLighting = WATERLIGHT::GLOBAL;
  320.                 jjSetWaterGradient(160,200,150, 10,20,5);
  321.         }
  322.        
  323.         void setSkyProperties() {
  324.                 jjUseLayer8Speeds = true;
  325.                 jjTexturedBGTexture = TEXTURE::PSYCH;
  326.                 jjTexturedBGFadePositionY = 0.42;
  327.         }      
  328.        
  329.         void warpPlayersInSP() {
  330.                 if (jjGameMode == GAME::COOP || jjGameMode == GAME::SP) {
  331.                         for (int i = 0; i < 4; i++) {
  332.                                 if (jjLocalPlayers[i].yOrg < 32) jjLocalPlayers[i].warpToID(255, true);
  333.                         }
  334.                 }
  335.         }
  336. }
  337.  
  338. /*******************************************************************/
  339. class RollerEdit : se::RollerWeapon {
  340.         protected void behaveCommon(::jjOBJ@ obj, int mask, float verticalTolerance, float maxSpeed) const override {
  341.                 switch (obj.state) {
  342.                         case STATE::START:
  343.                                 if (obj.creatorType == CREATOR::PLAYER && ::jjPlayers[obj.creatorID].isLocal)
  344.                                         ::jjSample(obj.xPos, obj.yPos, obj.yPos < jjWaterLevel? getSample() : SOUND::COMMON_SWISH6);
  345.                                 if (obj.xSpeed == 0.f) {
  346.                                         if (obj.var[7] != 0)
  347.                                                 obj.direction = obj.var[7] < 0 ? -1 : 1;
  348.                                         else if (::jjGameConnection != GAME::LOCAL)
  349.                                                 obj.direction = 1;
  350.                                         else if (obj.creatorType == CREATOR::PLAYER)
  351.                                                 obj.direction = ::jjPlayers[obj.creatorID].direction;
  352.                                         else if (obj.creatorType == CREATOR::OBJECT)
  353.                                                 obj.direction = ::jjObjects[obj.creatorID].direction;
  354.                                 }
  355.                                 obj.xAcc = ::jjObjectPresets[obj.eventID].xAcc;
  356.                                 if (obj.direction < 0)
  357.                                         obj.xAcc = -obj.xAcc;
  358.                                 obj.yAcc = ::jjObjectPresets[obj.eventID].yAcc;
  359.                                 obj.xSpeed += obj.var[7] / 1e5f;
  360.                                 obj.state = STATE::ROTATE;
  361.                         case STATE::ROTATE:
  362.                                 if (move(obj, mask, verticalTolerance, maxSpeed) || obj.counter++ > int(obj.counterEnd))
  363.                                         obj.state = STATE::EXPLODE;
  364.                                 if (::jjGameTicks % 3 == 0) {
  365.                                         obj.frameID++;
  366.                                         if (obj.frameID >= int(::jjAnimations[obj.curAnim].frameCount))
  367.                                                 obj.frameID = 0;
  368.                                 }
  369.                                 obj.determineCurFrame();
  370.                                 obj.draw();
  371.                                 break;
  372.                         case STATE::EXPLODE:
  373.                                 obj.curAnim = obj.killAnim;
  374.                                 if (obj.curAnim == 0) {
  375.                                         obj.delete();
  376.                                 } else {
  377.                                         if (obj.playerHandling != HANDLING::EXPLOSION) {
  378.                                                 obj.frameID = obj.freeze = 0;
  379.                                                 obj.isTarget = obj.triggersTNT = false;
  380.                                                 obj.playerHandling = HANDLING::EXPLOSION;
  381.                                         }
  382.                                         if (::jjGameTicks & 3 == 0)
  383.                                                 obj.frameID++;
  384.                                         if (obj.frameID < int(::jjAnimations[obj.curAnim].frameCount)) {
  385.                                                 obj.determineCurFrame();
  386.                                                 obj.draw();
  387.                                         } else {
  388.                                                 obj.delete();
  389.                                         }
  390.                                 }
  391.                                 break;
  392.                         case STATE::KILL:
  393.                         case STATE::DEACTIVATE:
  394.                                 obj.delete();
  395.                                 break;
  396.                 }
  397.         }
  398. }
  399.  
  400. class FireworkEdit : se::FireworkWeapon {
  401.         protected void behave(::jjOBJ@ obj) const override {
  402.                 switch (obj.state) {
  403.                         case STATE::START:
  404.                                 if (obj.creatorType == CREATOR::PLAYER && ::jjPlayers[obj.creatorID].isLocal)
  405.                                         ::jjSample(obj.xPos, obj.yPos, obj.yPos > jjWaterLevel ? SOUND::AMMO_MISSILE : getSamples()[0], obj.yPos > jjWaterLevel? 32:0, 0);
  406.                                 obj.counterEnd = ::jjObjectPresets[obj.eventID].counterEnd;
  407.                                 obj.yAcc = -0.25f;
  408.                                 obj.xSpeed += obj.var[7] / 2e5f;
  409.                                 obj.state = STATE::ROCKETFLY;
  410.                         case STATE::ROCKETFLY:
  411.                                 if (obj.xAcc * obj.xSpeed >= 0.f)
  412.                                         obj.xSpeed = obj.xAcc = 0.f;
  413.                                 else
  414.                                         obj.xPos += obj.xSpeed += obj.xAcc;
  415.                                 if (obj.ySpeed < -9.f) {
  416.                                         obj.yAcc = 0.f;
  417.                                         obj.ySpeed = -9.f;
  418.                                 }
  419.                                 obj.yPos += obj.ySpeed += obj.yAcc;
  420.                                 if (::jjMaskedPixel(int(obj.xPos), int(obj.yPos)) && ::jjEventAtLastMaskedPixel != AREA::ONEWAY) {
  421.                                         obj.xPos -= obj.xSpeed;
  422.                                         obj.yPos -= obj.ySpeed;
  423.                                         obj.state = STATE::EXTRA;
  424.                                 } else if (obj.counter++ > int(obj.counterEnd)) {
  425.                                         obj.state = STATE::EXTRA;
  426.                                 }
  427.                                 obj.draw();
  428.                                 break;
  429.                         case STATE::EXTRA:
  430.                         case STATE::EXPLODE:
  431.                                 ::jjSample(obj.xPos, obj.yPos, obj.yPos > jjWaterLevel ? SOUND::AMMO_BOEM1 : getSamples()[1]);
  432.                                 for (int i = 0; i < 6; i++) {
  433.                                         ::jjSample(obj.xPos + ::jjSin((i << 10) / 6) * 320.f, obj.yPos + ::jjCos((i << 10) / 6) * 320.f, obj.yPos > jjWaterLevel ? SOUND::AMMO_BOEM1 : getSamples()[1]);
  434.                                 }
  435.                                 {
  436.                                         array<int> colors = {34, 81, 24, 50};
  437.                                         int particles = 12 + (obj.var[6] >> 1 & 4);
  438.                                         for (int i = 0; i < particles; i++) {
  439.                                                 int id = ::jjAddObject(obj.eventID, obj.xPos, obj.yPos, obj.creatorID, obj.creatorType, @::jjVOIDFUNCOBJ(behaveParticle));
  440.                                                 if (id > 0) {
  441.                                                         ::jjOBJ@ other = @::jjObjects[id];
  442.                                                         other.animSpeed >>= 1;
  443.                                                         other.counterEnd -= 10;
  444.                                                         other.curAnim = obj.killAnim;
  445.                                                         other.determineCurFrame();
  446.                                                         other.var[1] = colors[obj.var[6] >> 2 & 2 | i & 1];
  447.                                                         other.xSpeed = ::jjSin((i << 10) / particles) * 6.f;
  448.                                                         other.ySpeed = ::jjCos((i << 10) / particles) * 6.f;
  449.                                                         other.xAcc = other.xSpeed / -64.f;
  450.                                                         other.yAcc = other.ySpeed / -64.f;
  451.                                                         if (obj.state == STATE::EXPLODE)
  452.                                                                 other.playerHandling = HANDLING::PARTICLE;
  453.                                                 }
  454.                                         }
  455.                                 }
  456.                                 obj.draw();
  457.                         case STATE::KILL:
  458.                         case STATE::DEACTIVATE:
  459.                                 obj.delete();
  460.  
  461.  
  462.                                 break;
  463.                 }
  464.         }
  465. }
  466.        
  467. jjANIMSET@ customSpringSprite;
  468. array<int> fastCustomSpringSpeeds(jjLocalPlayerCount);
  469. bool generateCustomSpringSprites(jjANIMSET@ anim, const array<uint> &in colors) {
  470.         int length = colors.length();
  471.         bool success = (@customSpringSprite = anim).allocate(array<uint>(length * 3, 5)) !is null;
  472.         if (success) {
  473.                 uint srcSet = jjAnimSets[ANIM::SPRING];
  474.                 for (int i = 0; i < length; i++) {
  475.                         uint color = colors[i];
  476.                         uint destAnimOffset = anim + i * 3;
  477.                         for (int j = 0; j < 3; j++) {
  478.                                 uint srcAnim = jjAnimations[srcSet + j];
  479.                                 uint destAnim = jjAnimations[destAnimOffset + j];
  480.                                 for (int k = 0; k < 5; k++) {
  481.                                         jjPIXELMAP image(jjAnimFrames[destAnim + k] = jjAnimFrames[srcAnim + k]);
  482.                                         int width = image.width;
  483.                                         int height = image.height;
  484.                                         for (int l = 0; l < height; l++) {
  485.                                                 for (int m = 0; m < width; m++) {
  486.                                                         int pixel = image[m, l];
  487.                                                         if (pixel >= 32 && pixel < 40)
  488.                                                                 image[m, l] = color + (pixel & 7);
  489.                                                 }
  490.                                         }
  491.                                         if (!image.save(jjAnimFrames[destAnim + k]))
  492.                                                 return false;
  493.                                 }
  494.                         }
  495.                 }
  496.         }
  497.         return success;
  498. }
  499.  
  500. void initializeCustomSpring(jjOBJ@ obj) {
  501.         int anim = obj.curAnim;
  502.         obj.behave(obj.behavior = BEHAVIOR::SPRING, false);
  503.         if (obj.curAnim != anim) {
  504.                 obj.curAnim = anim + 2;
  505.                 obj.determineCurFrame();
  506.         }
  507.         obj.draw();
  508. }
  509.  
  510. void turnIntoCustomSpring(jjOBJ@ obj, uint color, float power, bool horizontal) {
  511.         if (horizontal) {
  512.                 obj.xSpeed = power;
  513.                 obj.ySpeed = 0.f;
  514.         } else {
  515.                 obj.xSpeed = 0.f;
  516.                 obj.ySpeed = -power;
  517.                 if (obj.state == STATE::START && obj.creatorType == CREATOR::LEVEL) {
  518.                         int x = int(obj.xPos) >> 5;
  519.                         int y = int(obj.yPos) >> 5;
  520.                         if (jjParameterGet(x, y, 0, 1) != 0) {
  521.                                 jjParameterSet(x, y, 0, 1, 0);
  522.                                 obj.yPos -= 4.f;
  523.                                 obj.ySpeed = power;
  524.                         }
  525.                 }
  526.         }
  527.         obj.behavior = initializeCustomSpring;
  528.         obj.curAnim = customSpringSprite + color * 3 + (horizontal ? 1 : 0);
  529.         obj.energy = obj.frameID = obj.freeze = obj.justHit = obj.light = obj.points = 0;
  530.         obj.isBlastable = obj.isTarget = obj.scriptedCollisions = obj.triggersTNT = false;
  531.         obj.deactivates = obj.isFreezable = true;
  532.         obj.bulletHandling = HANDLING::IGNOREBULLET;
  533.         obj.playerHandling = HANDLING::SPECIAL;
  534.         obj.lightType = LIGHT::NORMAL;
  535.         obj.determineCurFrame();
  536. }
  537.  
  538. class CannotBeShotDown : jjBEHAVIORINTERFACE {
  539.         jjBEHAVIOR originalBehavior;
  540.         CannotBeShotDown(jjBEHAVIOR behavior) {
  541.                 originalBehavior = behavior;
  542.         }
  543.         void onBehave(jjOBJ@ obj) override {
  544.                 obj.behave(originalBehavior);
  545.                 if (obj.state == STATE::FLOATFALL)
  546.                         obj.state = STATE::FLOAT;
  547.                 if (obj.eventID == OBJECT::FULLENERGY)
  548.                         obj.xPos = obj.xOrg - 16;
  549.         }
  550.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  551.                 obj.behavior = originalBehavior;
  552.                 if (bullet is null)
  553.                         player.objectHit(obj, force, obj.playerHandling);
  554.                 else
  555.                         bullet.objectHit(obj, obj.playerHandling);
  556.                 obj.behavior = CannotBeShotDown(obj.behavior);
  557.                 return true;
  558.         }
  559. }
  560.  
  561. /*******************************************************************/
  562. Level level;
  563.  
  564. void onLevelLoad() {
  565.         level.loadSamples();
  566.         level.makeRainLookRealistic();
  567.         level.removeSpritePaletteReferences();
  568.         level.setCustomObjects();
  569.         level.setWaterProperties();
  570.         level.setSkyProperties();
  571. }
  572.  
  573. void onLevelBegin() {
  574.         level.doStuffWithLayers();
  575.         level.relocateCTFBases();
  576.         level.setLevelPalette();
  577.         level.warpPlayersInSP();
  578.         water::maxOxygen = 1800;
  579.        
  580.         if (!layersInitialized) {
  581.                 level.darkenLayer(jjLayers[6], 1.65);
  582.                 level.darkenLayer(jjLayers[7], 3.25);
  583.                        
  584.                 layersInitialized = true;
  585.         }
  586. }
  587.  
  588. void onLevelReload() {
  589.         onLevelLoad();
  590.         onLevelBegin();
  591. }
  592.  
  593. void onPlayer(jjPLAYER@ play) {
  594.         weaponHook.processPlayer(play);
  595.         water::limitedOxygen(play);
  596.         level.handleInvisibleBarriers(play);
  597.         level.processLevelEffects(play);
  598. }
  599.  
  600. void onPlayerInput(jjPLAYER@ play) {
  601.         weaponHook.processPlayerInput(play);
  602. }
  603.  
  604. void onMain() {
  605.         weaponHook.processMain();
  606.         level.handleVerticalRain();
  607.         level.relocateObjects();
  608.         level.setLayerSettings();
  609. }
  610.  
  611. void onReceive(jjSTREAM &in packet, int clientID) {
  612.         weaponHook.processPacket(packet, clientID);
  613. }
  614.  
  615. bool onDrawAmmo(jjPLAYER@ play, jjCANVAS@ canvas) {
  616.         return weaponHook.drawAmmo(play, canvas);
  617. }
  618.  
  619. bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ canvas) {
  620.         water::drawOxygenTimer(play, canvas);
  621.         return false;  
  622. }