Downloads containing SwitchTestV2.j2as

Downloads
Name Author Game Mode Rating
JJ2+ Only: Mighty Switch Test! 2!Featured Download Violet CLM Test 8.5 Download file

File preview

  1. #pragma require "SwitchTestV2.j2a"
  2. #pragma offer "STV2-Switch.wav"
  3. #pragma offer "STV2-Dash.wav"
  4. #pragma offer "STV2-Build.wav"
  5. #pragma offer "STV2-Ghost.wav"
  6. #pragma offer "STV2-Fall.wav"
  7. #pragma offer "SUCCESSF.WAV"
  8.  
  9. int WarpTargetID = 0;
  10.  
  11. int AssignWarpTargetID(int assign) {
  12.         WarpTargetID = assign;
  13.         @SwitchBlocks = @Incidents[assign].SwitchBlocks;
  14.         return assign;
  15. }
  16.  
  17. const uint LEVELWIDTH = jjLayerWidth[4];
  18. const uint LEVELHEIGHT = jjLayerHeight[4];
  19. const uint16 FIRSTWALLTILE = 1;
  20. const uint16 LASTCAVETILE = 268;
  21. jjPLAYER@ Player = jjLocalPlayers[0];
  22.  
  23. jjTEXTAPPEARANCE ColoredBounce(STRING::BOUNCE);
  24. array<jjPAL@> Palettes;
  25.  
  26. const int UNFINISHEDINCIDENTTIME = 70 * 10000;
  27.  
  28. string StringifyTime(int timeInTicks, int par = 0) {
  29.         string prefix = "$";
  30.         if (par != 0) {
  31.                 const int timeDifference = timeInTicks / 70 - par;
  32.                 if (timeDifference < 0)
  33.                         prefix = "'";
  34.                 else if (timeDifference < 3)
  35.                         prefix = "&";
  36.                 else if (timeDifference < 6)
  37.                         prefix = "%";
  38.         }
  39.         return prefix + formatInt(timeInTicks / (70*60), "0", 2) + ":" + formatInt((timeInTicks % (70*60)) / 70, "0", 2) + "." + formatInt(((timeInTicks % (70*60)) % 70) * 100 / 70, "0", 2);
  40. }
  41.  
  42. class Point {
  43.         int x; int y;
  44.         Point(){}
  45.         Point(int xx, int yy) {
  46.                 x = xx;// * 32.f + 16;
  47.                 y = yy;// * 32.f + 16;
  48.         }
  49. }
  50. class PerIncident {
  51.         array<int> ammoCount(4);
  52.         bool initiallyBlue;
  53.         CHAR::Char character;
  54.         Point warpTargetLocation;
  55.         string incidentNumberAsString;
  56.         int recordNumberOfDeaths = 10000; //arbitarily large
  57.         int recordTimeElapsed = UNFINISHEDINCIDENTTIME;
  58.         string recordTimeElapsedAsString = "$|||||||--:--";
  59.         int parTime;
  60.         int LeftP, LeftT = -1, RightP, RightT, TopP, TopT, BottomP, BottomT, WidthP, HeightP, CenterXP, CenterYP, LeftOuterBoundP, TopOuterBoundP, RightOuterBoundP;
  61.         array<SwitchBlock> SwitchBlocks;
  62.         array<Point> AmmoPickupLocations;
  63.         PerIncident(){}
  64.         void stringifyRecordTime() {
  65.                 if (recordTimeElapsed != UNFINISHEDINCIDENTTIME)
  66.                         recordTimeElapsedAsString = StringifyTime(recordTimeElapsed, parTime);
  67.         }
  68.         void setup(int x, int y) {
  69.                 warpTargetLocation = Point(x * 32, y * 32);
  70.                 const int params = jjParameterGet(x, y, 8, 12);
  71.                 ///@Event 240=Warp Target                     |+|Area      |Warp   |Target |WarpID:8|Character:{Any,Jazz,Spaz,Lori}2|Color:{Yellow,Blue}1|Par:5
  72.                 switch (params & 3) {
  73.                         case 0:
  74.                                 character = CHAR::Char(-1);
  75.                                 break;
  76.                         case 1:
  77.                                 character = CHAR::JAZZ;
  78.                                 break;
  79.                         case 2:
  80.                                 character = CHAR::SPAZ;
  81.                                 break;
  82.                         case 3:
  83.                                 character = AllowLori ? CHAR::LORI : CHAR::JAZZ;
  84.                                 break;
  85.                 }
  86.                 initiallyBlue = params & 4 == 4;
  87.                 parTime = (params >> 3) & 31;
  88.                 //jjDebug(incidentNumberAsString + ": " + parTime);
  89.                 ///@Event 14=Starting Ammo                     |+|Area      |Start   |Ammo |Ammo:6|Weapon:{Switcher,Dasher,Builder,Ghoster}2
  90.                 int otherX = x + 1;
  91.                 while (true) {
  92.                         if (jjEventGet(otherX, y) != 14) {
  93.                                 if (otherX == x + 1) //none specified
  94.                                         ammoCount[0] = 63; //infinite
  95.                                 break;
  96.                         } else {
  97.                                 ammoCount[jjParameterGet(otherX, y, 6, 2)] = jjParameterGet(otherX, y, 0, 6);
  98.                                 ++otherX;
  99.                         }
  100.                 }
  101.         }
  102. }
  103. array<PerIncident> Incidents;
  104. int NumberOfDeathsThisIncident;
  105. int TimeStartedThisIncident;
  106. string TimeElapsedStringThisIncident = "";
  107. bool DeathsRecordSet = false;
  108. bool TimeElapsedRecordSet = false;
  109. uint LastIncidentCompleted = 0;
  110. int CurrentMenuOption = 0;
  111. int PreviousMenuOption = 0;
  112. uint8 ThumbnailFadeTimer = 0;
  113. bool ViewingLevelSelect = false;
  114. bool PressedMenuKeyLastTick = false;
  115. uint8 PaletteID = 0;
  116.  
  117. array<string> IncidentNames;
  118.  
  119. class SwitchBlock {
  120.         Point location;
  121.         uint8 triggerID;
  122.         //uint16 tileOff;
  123.         //uint16 tileOn;
  124.         uint16 originalTileID;
  125.         void doSwitch() const {
  126.                 jjTileSet(4, location.x, location.y, jjTileGet(4, location.x, location.y) ^ 1);
  127.         }
  128.         SwitchBlock(){}
  129.         SwitchBlock(int xx, int yy, uint16 tileID, uint16 newTriggerID = CurrentTriggerID) {
  130.                 location = Point(xx, yy);
  131.                 //tileOff = tileID;
  132.                 //tileOn = tileOff ^ 1;
  133.                 originalTileID = tileID;
  134.                 if (tileID >= 36 && tileID <= 39) {
  135.                         triggerID = ((tileID - 36) >> 1) ^ 1;
  136.                 } else {
  137.                         triggerID = newTriggerID;
  138.                         //jjTileSet(4, xx, yy, 37 + (triggerID << 1));
  139.                 }
  140.         }
  141. }
  142. array<SwitchBlock>@ SwitchBlocks;
  143.  
  144. uint8 tileIDIsWallTile(uint16 tileID) {
  145.         if (tileID == FIRSTWALLTILE) return 3;
  146.         else if (tileID == LASTCAVETILE) return 1;
  147.         else if (tileID == 0 || tileID == 105) return 0;
  148.         return 2;
  149. }
  150. void applyOutlinesToTile(uint8 t, uint h, uint v, uint8 l) {
  151.         uint16 newTileID = FIRSTWALLTILE;
  152.         if (tilesAreWalls[h][v-1] < t)
  153.                 newTileID += 1; //top
  154.         if (tilesAreWalls[h+1][v] < t)
  155.                 newTileID += 2; //right
  156.         if (tilesAreWalls[h][v+1] < t)
  157.                 newTileID += 4; //bottom
  158.         if (tilesAreWalls[h-1][v] < t)
  159.                 newTileID += 8; //left
  160.         if (tilesAreWalls[h+1][v] >= t && tilesAreWalls[h][v-1] >= t && tilesAreWalls[h+1][v-1] < t)
  161.                 newTileID += 16; //top right
  162.         if (tilesAreWalls[h+1][v] >= t && tilesAreWalls[h][v+1] >= t && tilesAreWalls[h+1][v+1] < t)
  163.                 newTileID += 32; //bottom right
  164.         if (tilesAreWalls[h-1][v] >= t && tilesAreWalls[h][v+1] >= t && tilesAreWalls[h-1][v+1] < t)
  165.                 newTileID += 64; //bottom left
  166.         if (tilesAreWalls[h-1][v] >= t && tilesAreWalls[h][v-1] >= t && tilesAreWalls[h-1][v-1] < t)
  167.                 newTileID += 128; //top left
  168.         if (t == 1)
  169.                 newTileID = LASTCAVETILE - (newTileID - FIRSTWALLTILE); //cave
  170.        
  171.         jjTileSet(l, h-1, v-1, newTileID);
  172. }
  173. array<array<uint8>> tilesAreWalls(LEVELWIDTH + 2, array<uint8>(LEVELHEIGHT + 2, 0));
  174. void onLevelLoad() {
  175.         jjGenerateSettableTileArea(4, 0, 0, LEVELWIDTH, LEVELHEIGHT);
  176.         { //redo walls
  177.                 for (uint h = 0; h < LEVELWIDTH; ++h)
  178.                         tilesAreWalls[h][0] = tilesAreWalls[h][LEVELHEIGHT] = 2; //wall
  179.                 for (uint v = 0; v < LEVELHEIGHT; ++v)
  180.                         tilesAreWalls[0][v] = tilesAreWalls[LEVELWIDTH][v] = 2;
  181.                 for (uint h = 1; h <= LEVELWIDTH; ++h)
  182.                         for (uint v = 1; v <= LEVELHEIGHT; ++v)
  183.                                 tilesAreWalls[h][v] = tileIDIsWallTile(jjTileGet(4, h-1, v-1));
  184.         }
  185.        
  186.         {
  187.                 jjTexturedBGTexture = TEXTURE::NORMAL;
  188.                 jjSetFadeColors(255,255,255);
  189.                 ColoredBounce.pipe = STRING::SPECIALSIGN;
  190.         }
  191.        
  192.         {
  193.                 //jjSampleLoad(SOUND::COMMON_HARP1, "SUCCESSF.WAV");
  194.                 const array<string> SampleFilenames = {"STV2-Switch.wav", "STV2-Dash.wav", "STV2-Build.wav", "STV2-Ghost.wav", "STV2-Fall.wav"};
  195.                 for (uint i = 0; i < SampleFilenames.length; ++i)
  196.                         jjSampleLoad(SOUND::Sample(SOUND::FROG_FROG + i), SampleFilenames[i]);
  197.         }
  198.        
  199.         {
  200.                 jjAnimSets[ANIM::CUSTOM[0]].allocate(array<uint> = {3});
  201.                 jjPIXELMAP verticalGradient(2, 80);
  202.                 for (uint i = 0; i < verticalGradient.height; ++i)
  203.                         verticalGradient[0,i] = i*256/160;
  204.                 verticalGradient.save(jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[0]]]]);
  205.                 jjPIXELMAP asteroid(0, 0, 64, 64, 5);
  206.                 AsteroidCurFrame = jjAnimations[jjAnimSets[ANIM::CUSTOM[0]]]+1;
  207.                 jjANIMFRAME@ asteroidFrame = jjAnimFrames[AsteroidCurFrame];
  208.                 asteroid.save(asteroidFrame);
  209.                 asteroidFrame.hotSpotX = -asteroidFrame.width / 2;
  210.                 asteroidFrame.hotSpotY = -asteroidFrame.height / 2;
  211.                 jjPIXELMAP ghostcord(0, 64, 32, 96, 5);
  212.                 GhostCordCurFrame = jjAnimations[jjAnimSets[ANIM::CUSTOM[0]]]+2;
  213.                 jjANIMFRAME@ ghostCordFrame = jjAnimFrames[GhostCordCurFrame];
  214.                 ghostcord.save(ghostCordFrame);
  215.                 ghostCordFrame.hotSpotX = -ghostCordFrame.width / 2;
  216.                 ghostCordFrame.hotSpotY = 0;
  217.                
  218.                 jjAnimSets[ANIM::CUSTOM[1]].load(0, "SwitchTestV2.j2a");
  219.                 jjAnimSets[ANIM::CUSTOM[2]].load(1, "SwitchTestV2.j2a");
  220.                 for (int i = 4; i <= 8; ++i)
  221.                         jjAnimFrames[jjAnimations[jjAnimSets[ANIM::FONT]] + i] = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[1]]] + i]; //par stars
  222.                 //jjAnimations[jjAnimSets[ANIM::FONT] + 2] = jjAnimations[jjAnimSets[ANIM::CUSTOM[1]]]; //large font
  223.         }
  224.        
  225.         {
  226.                 string incidentNameList;
  227.                 for (uint i = 0; i < 10; ++i)
  228.                         incidentNameList += jjHelpStrings[i];
  229.                 IncidentNames = incidentNameList.split('|');
  230.         }
  231.        
  232.         {
  233.                 for (int x = LEVELWIDTH - 1; x >= 0; --x)
  234.                         for (int y = LEVELHEIGHT - 1; y >= 0; --y) {
  235.                                 const uint8 eventID = jjEventGet(x, y);
  236. ///@Event 242=Corner|+|Area|Corner||WarpID:8
  237.                                 if (eventID == AREA::WARPTARGET || eventID == AREA::AREAID) {
  238.                                         const uint8 warpID = jjParameterGet(x, y, 0, 8);
  239.                                         if (warpID >= Incidents.length)
  240.                                                 Incidents.resize(warpID + 1);
  241.                                         PerIncident@ incident = Incidents[warpID];
  242.                                         if (eventID == AREA::WARPTARGET) {
  243.                                                 incident.incidentNumberAsString = formatInt(warpID + 1, "0", 2);
  244.                                                 incident.setup(x,y);
  245.                                         } else {
  246.                                                 //int LeftP, LeftT, RightP = -1, RightT, TopP, TopT, BottomP, BottomT;
  247.                                                 if (incident.LeftT == -1) { //first corner found
  248.                                                         incident.LeftT = x;
  249.                                                         incident.TopT = y;
  250.                                                 } else {
  251.                                                         if (x >= incident.LeftT)
  252.                                                                 incident.RightT = x;
  253.                                                         else {
  254.                                                                 incident.RightT = incident.LeftT;
  255.                                                                 incident.LeftT = x;
  256.                                                         }
  257.                                                         if (y >= incident.TopT)
  258.                                                                 incident.BottomT = y;
  259.                                                         else {
  260.                                                                 incident.BottomT = incident.TopT;
  261.                                                                 incident.TopT = y;
  262.                                                         }
  263.                                                         incident.RightT += 1;
  264.                                                         incident.BottomT += 1;
  265.                                                         incident.LeftP = incident.LeftT << 5;
  266.                                                         incident.RightP = incident.RightT << 5;
  267.                                                         incident.TopP = incident.TopT << 5;
  268.                                                         incident.BottomP = incident.BottomT << 5;
  269.                                                         incident.WidthP = incident.RightP - incident.LeftP;
  270.                                                         incident.HeightP = incident.BottomP - incident.TopP;
  271.                                                         incident.CenterXP = incident.LeftP + incident.WidthP / 2;
  272.                                                         incident.CenterYP = incident.TopP + incident.HeightP / 2;
  273.                                                         incident.LeftOuterBoundP = incident.LeftP - 12*32;
  274.                                                         incident.RightOuterBoundP = incident.RightP + 12*32;
  275.                                                         incident.TopOuterBoundP = incident.TopP - 8*32;
  276.                                                         //jjDebug(warpID + ": " + incident.WidthP + ", " + incident.HeightP);
  277.                                                         for (int h = incident.LeftT; h <= incident.RightT; ++h)
  278.                                                                 for (int v = incident.TopT; v <= incident.BottomT; ++v) {
  279.                                                                         const uint16 tileID = jjTileGet(4, h-1, v-1);
  280.                                                                         if (tileID >= 36 && tileID <= 39) //switch block!
  281.                                                                                 incident.SwitchBlocks.insertLast(SwitchBlock(h-1, v-1, tileID));
  282.                                                                         if (jjEventGet(h-1, v-1) == OBJECT::FASTFIRE)
  283.                                                                                 incident.AmmoPickupLocations.insertLast(Point(h-1,v-1));
  284.                                                                 }
  285.                                                 }
  286.                                         }
  287.                                 }
  288.                         }
  289.                 @SwitchBlocks = @Incidents[WarpTargetID >= 0 ? WarpTargetID : (Incidents.length + WarpTargetID)].SwitchBlocks;
  290.         }
  291.        
  292.         {
  293.                 jjAnimSets[ANIM::CUSTOM[3]].allocate(array<uint>={Incidents.length});
  294.                 uint thumbnailFrameID = jjAnimations[jjAnimSets[ANIM::CUSTOM[3]]];
  295.                 const array<uint8> switchBlockThumbnailColors = {116, 113, 133, 129,  0,0,0, 120};
  296.                 for (uint i = 0; i < Incidents.length; ++i) {
  297.                         const PerIncident@ incident = Incidents[i];
  298.                         jjPIXELMAP thumbnail(incident.RightT - incident.LeftT + 1, incident.BottomT - incident.TopT + 1);
  299.                         uint xP = 0;
  300.                         for (int x = incident.LeftT; x <= incident.RightT; ++x, ++xP) {
  301.                                 const auto@ column = tilesAreWalls[x+1];
  302.                                 uint yP = 0;
  303.                                 for (int y = incident.TopT; y <= incident.BottomT; ++y, ++yP)
  304.                                         switch (column[y+1]) {
  305.                                                 case 1:
  306.                                                         thumbnail[xP,yP] = 120;
  307.                                                         break;
  308.                                                 case 2: {
  309.                                                         const uint16 shortenedTileID = uint(jjTileGet(4,x,y) - 36);
  310.                                                         if (shortenedTileID < 8)
  311.                                                                 thumbnail[xP,yP] = switchBlockThumbnailColors[shortenedTileID];
  312.                                                         break; }
  313.                                                 case 3:
  314.                                                         thumbnail[xP,yP] = 115;
  315.                                                         break;
  316.                                         }
  317.                         }
  318.                         jjANIMFRAME@ thumbframe = jjAnimFrames[thumbnailFrameID++];
  319.                         thumbnail.save(thumbframe);
  320.                         thumbframe.hotSpotX = -thumbnail.width / 2;
  321.                         thumbframe.hotSpotY = -thumbnail.height / 2;
  322.                 }
  323.         }
  324.        
  325.         {
  326.                 for (uint h = 1; h <= LEVELWIDTH; ++h)
  327.                         for (uint v = 1; v <= LEVELHEIGHT; ++v) {
  328.                                 const uint8 t = tilesAreWalls[h][v];
  329.                                 if (t == 1 || t == 3) {
  330.                                         applyOutlinesToTile(t,h,v,4);
  331.                                         if (t == 3) {
  332.                                                 const auto top = tilesAreWalls[h][v-1], bottom = tilesAreWalls[h][v+1], left = tilesAreWalls[h-1][v], right = tilesAreWalls[h+1][v];
  333.                                                 if (((top == 1 || bottom == 1) && !(left == 3 && right == 3)) || ((left == 1 || right == 1) && !(top == 3 && bottom == 3))) {
  334.                                                         jjGenerateSettableTileArea(5,h-1,v-1,1,1);
  335.                                                         applyOutlinesToTile(1,h,v,5);
  336.                                                 }
  337.                                         }
  338.                                 }
  339.                         }
  340.                 tilesAreWalls.resize(0);
  341.         }
  342.        
  343.         {
  344.                 for (uint i = 0; i < 4; ++i) {
  345.                         jjPAL DarkerPalette = jjBackupPalette;
  346.                         DarkerPalette.fill(0,0,0, 176, 48, i / 4.0);
  347.                         Palettes.insertLast(@DarkerPalette);
  348.                 }
  349.                 jjSTREAM settings("SwitchTestV2-Settings.asdat");
  350.                 if (!settings.isEmpty()) {
  351.                         settings.pop(PaletteID);
  352.                         if (PaletteID != 0)
  353.                                 TogglePalette();
  354.                 }
  355.         }
  356.        
  357.         {
  358.         //jjHelpStrings[10] = "switchtestv2someotherfilename.asdat";
  359.                 jjSTREAM records(jjHelpStrings[10]);
  360.                 uint numberOfRecords = records.getSize() / 8; //will be 0 if the file doesn't exist yet
  361.                 if (numberOfRecords > Incidents.length) //improperly formatted file somehow
  362.                         numberOfRecords = Incidents.length;
  363.                 bool everyIncidentHasBeenBeatenSoFar = true;
  364.                 for (uint i = 0; i < numberOfRecords; ++i) {
  365.                         records.pop(Incidents[i].recordNumberOfDeaths);
  366.                         records.pop(Incidents[i].recordTimeElapsed);
  367.                         if (Incidents[i].recordTimeElapsed != 70 * 10000) {
  368.                                 if (everyIncidentHasBeenBeatenSoFar)
  369.                                         LastIncidentCompleted = i + 1;
  370.                         } else
  371.                                 everyIncidentHasBeenBeatenSoFar = false;
  372.                         Incidents[i].stringifyRecordTime();
  373.                         //jjDebug(i + ": " + Incidents[i].recordTimeElapsedAsString);
  374.                 }
  375.                 if (LastIncidentCompleted >= Incidents.length)
  376.                         LastIncidentCompleted = Incidents.length - 1;
  377.         }
  378.        
  379.         {
  380.                 jjWeapons[1].replenishes = false;
  381.                 for (int i = 1; i <= 9; ++i)
  382.                         jjWeapons[i].defaultSample = false;
  383.                 jjObjectPresets[OBJECT::BLASTERBULLET].behavior = Switcher;
  384.                 jjObjectPresets[OBJECT::BOUNCERBULLET].behavior = Dasher;
  385.                 jjObjectPresets[OBJECT::BOUNCERBULLET].xSpeed = 32;
  386.                 jjObjectPresets[OBJECT::BOUNCERBULLET].ySpeed = 0;
  387.                 jjObjectPresets[OBJECT::BOUNCERBULLET].xAcc = 4;
  388.                 jjObjectPresets[OBJECT::BOUNCERBULLET].yAcc = 0;
  389.                 jjObjectPresets[OBJECT::ICEBULLET].behavior = Builder;
  390.                 jjObjectPresets[OBJECT::ICEBULLET].xSpeed = 1;
  391.                 jjObjectPresets[OBJECT::ICEBULLET].freeze = 0;
  392.                 jjObjectPresets[OBJECT::ICEBULLET].lightType = LIGHT::NONE;
  393.                 jjObjectPresets[OBJECT::ICEBULLET].curFrame = jjAnimations[jjAnimSets[ANIM::CUSTOM[2]] + 2] + 8;
  394.                 jjObjectPresets[OBJECT::SEEKERBULLET].behavior = Ghoster;
  395.                 jjObjectPresets[OBJECT::MORPH].behavior = MorphBeam;
  396.                 jjObjectPresets[OBJECT::MORPH].playerHandling = HANDLING::SPECIALDONE;
  397.                 jjObjectPresets[OBJECT::FASTFEET].behavior = Recolor;
  398.                 jjObjectPresets[OBJECT::FASTFEET].playerHandling = HANDLING::SPECIALDONE;
  399.                 jjObjectPresets[OBJECT::AIRBOARD].behavior = Airboard();
  400.                 jjObjectPresets[OBJECT::AIRBOARD].scriptedCollisions = true;
  401.                 jjObjectPresets[OBJECT::FASTFIRE].behavior = Ammo();
  402.                 jjObjectPresets[OBJECT::FASTFIRE].scriptedCollisions = true;
  403.                 jjObjectPresets[OBJECT::BUBBLESHIELDBULLET].behavior = function(obj) { obj.delete(); };
  404.         }
  405.        
  406.         for (uint transition = 0; transition < 4; ++transition) {
  407.                 jjPIXELMAP new(39 - transition);
  408.                 const jjPIXELMAP old(39 - (transition ^ 1));
  409.                 for (uint frame = 0; frame < 16; ++frame) {
  410.                         const uint corner = (frame + 1) * 4;
  411.                         for (uint x = 0; x < 32; ++x)
  412.                                 for (uint y = 0; y < 32; ++y)
  413.                                         if (x + y < corner)
  414.                                                 new[x,y] = old[x,y];
  415.                         new.save(jjTileCount + (transition << 4) + frame);
  416.                 }
  417.         }
  418. }
  419. void onLevelBegin() {
  420.         Player.spriteMode = SPRITE::INVISIBLE;
  421.         if (WarpTargetID < 0) //debug
  422.                 AssignWarpTargetID(Incidents.length + WarpTargetID); //e..g -1 is last, -2 is next but last
  423.         if (jjGameMode == GAME::CTF)
  424.                 jjChat("/noctfcolors on");
  425.         if (jjGameConnection != GAME::LOCAL && jjIsServer)
  426.                 jjChat("/nosplitscreeners on");
  427.         for (int i = 1; i <= 4; ++i) {
  428.                 jjWeapons[i].allowed = true;
  429.                 jjWeapons[i].style = WEAPON::MISSILE;
  430.         }
  431.         changeIncident(Player);
  432. }
  433.  
  434. int LastResolutionHeight;
  435. int ResolutionCameraOffsetX, ResolutionCameraOffsetY;
  436. int AsteroidCurFrame, GhostCordCurFrame;
  437. const float ASTEROIDXOFFSET = 200;
  438. class Asteroid {
  439.         float xPos, yPos;
  440.         float xSpeed, ySpeed;
  441.         int angle;
  442.         int height;
  443.         int multiplier;
  444.         int scaleCmp;
  445.         Asteroid() {
  446.                 initialize();
  447.         }
  448.         int opCmp(const Asteroid& in other) {
  449.                 return scaleCmp - other.scaleCmp;
  450.         }
  451.         void initialize() {
  452.                 xPos = jjRandom() & 800;
  453.                 yPos = 0;
  454.                 height = 480 + (jjRandom() % 280);
  455.                 xSpeed = ySpeed * multiplier / 3; //previous values
  456.                 ySpeed = ((jjRandom() & 31) + 4) / 17.f;
  457.                 angle = jjRandom();
  458.                 multiplier = ((jjRandom() & 1) << 1) - 1;
  459.         }
  460.         void draw() {
  461.                 const int counter = int(yPos += ySpeed);
  462.                 const float scale = 1.5 + multiplier * ((counter - 256) / 256.f);
  463.                 scaleCmp = int(scale * 10000);
  464.                 jjDrawRotatedSpriteFromCurFrame(Player.cameraX + (xPos -= xSpeed), Player.cameraY + 700 - jjSin(counter) * height, AsteroidCurFrame, angle += multiplier, scale, scale, SPRITE::BRIGHTNESS, int8(scale * 66), (scale > 1.5) ? 6 : 7, 4, Player.playerID);
  465.                 if (counter >= 520)
  466.                         initialize();
  467.         }
  468. }
  469. array<Asteroid> Asteroids(5);
  470. void onMain() {
  471.         if (jjGameConnection != GAME::LOCAL && (Player.health <= 0 || !Player.isInGame)) {
  472.                 GameBegun = false;
  473.                 Dying = false;
  474.                 GameOver = false;
  475.                 CurrentMenuOption = 0;
  476.                 ViewingLevelSelect = false;
  477.         }
  478.        
  479.         if (LastResolutionHeight != jjResolutionHeight) {
  480.                 ResolutionCameraOffsetX = (jjResolutionWidth - 320) / -2;
  481.                 ResolutionCameraOffsetY = (jjResolutionHeight - 200) / -2;
  482.                 if (jjGameConnection == GAME::LOCAL) { //in local games, having a partially empty layer 8 will normally leave trails, but by imposing a maxresolution SLIGHTLY smaller than the real resolution, this can be avoided
  483.                         LastResolutionHeight = jjResolutionHeight;
  484.                         jjChat("/maxresolution " + jjResolutionWidth + "x" + (LastResolutionHeight - 1));
  485.                         for (int i = 0; i < 8; ++i) jjAlert("");
  486.                 }
  487.         }
  488.        
  489.         if (!jjLowDetail) {
  490.                 Asteroids.sortDesc();
  491.                 for (int i = Asteroids.length; --i >= 0;)
  492.                         Asteroids[i].draw();
  493.         }
  494.        
  495.         const bool pressingF2 = jjKey[0x71] && !jjKey[0x11];
  496.         if (pressingF2 != PressingF2LastTick) {
  497.                 PressingF2LastTick = pressingF2;
  498.                 if (pressingF2) {
  499.                         PaletteID = (PaletteID + 1) & 3;
  500.                         TogglePalette();
  501.                         jjSTREAM settings;
  502.                         settings.push(PaletteID);
  503.                         settings.save("SwitchTestV2-Settings.asdat");
  504.                 }
  505.         }
  506.        
  507.         for (int otherPlayerID = 0; otherPlayerID < 32; ++otherPlayerID) {
  508.                 const jjPLAYER@ otherPlayer = jjPlayers[otherPlayerID];
  509.                 if (otherPlayer.isInGame && otherPlayer.spriteMode == SPRITE::INVISIBLE) {
  510.                         const bool flashing = otherPlayer.blink != 0 && ((jjRenderFrame >> 2) & 1) == 0;
  511.                         if (!flashing || (Dying && KnockedIntoScreenCounter != 0)) {
  512.                                 if (!Dying || otherPlayer !is Player) {
  513.                                         if (AirboardCounter < AIRBOARDHALFWAYTIME || otherPlayer !is Player) { //normal drawing case
  514.                                                 jjDrawSpriteFromCurFrame(otherPlayer.xPos, otherPlayer.yPos, otherPlayer.curFrame, otherPlayer.direction, SPRITE::PLAYER, otherPlayerID);
  515.                                                 //jjDrawRectangle(otherPlayer.xPos - 20, otherPlayer.yPos, 40, 1, 24);
  516.                                                 //jjDrawRectangle(otherPlayer.xPos, otherPlayer.yPos - 20, 1, 40, 24);
  517.                                         } else {
  518.                                                 const auto scale = (AIRBOARDHALFWAYTIME - (AirboardCounter - AIRBOARDHALFWAYTIME)) / 50.f + 1;
  519.                                                 Player.light = 12 + int(scale * 2);
  520.                                                 jjDrawResizedSpriteFromCurFrame(otherPlayer.xPos, otherPlayer.yPos, otherPlayer.curFrame, scale,scale, SPRITE::PLAYER, otherPlayerID);
  521.                                         }
  522.                                 } else {
  523.                                         if (SweptAwayIntoSpaceCounter > 20) {
  524.                                                 const float scale = SweptAwayIntoSpaceCounter / 160.f;
  525.                                                 jjANIMATION@ animation = jjAnimations[LastPlayerCurAnim];
  526.                                                 jjDrawResizedSpriteFromCurFrame(otherPlayer.xPos, otherPlayer.yPos, animation.firstFrame + ((jjGameTicks >> 2) % animation.frameCount), scale * otherPlayer.direction, scale, SPRITE::PLAYER, otherPlayerID, 6);
  527.                                         } else if (KnockedIntoScreenCounter != 0) {
  528.                                                 const jjANIMATION@ FallAnim = jjAnimations[jjAnimSets[otherPlayer.setID] + RABBIT::FALL];
  529.                                                 float scale = KnockedIntoScreenCounter;
  530.                                                 if (scale > 3.0f)
  531.                                                         scale = 3.0f;
  532.                                                 jjDrawResizedSprite(otherPlayer.xPos, otherPlayer.yPos, ANIM::AMMO, 5, int(scale * 2), scale, scale, SPRITE::SINGLEHUE, 95, 1);
  533.                                                 if (!flashing)
  534.                                                         jjDrawRotatedSpriteFromCurFrame(otherPlayer.xPos, otherPlayer.yPos + KnockedIntoScreenCounter * 2, FallAnim.firstFrame + ((jjGameTicks >> 3) % FallAnim.frameCount), int(KnockedIntoScreenCounter * 4 * otherPlayer.direction), scale, scale, SPRITE::PLAYER, otherPlayerID, 1);
  535.                                         }
  536.                                 }
  537.                         }
  538.                 }
  539.         }
  540.  
  541.         for (uint triggerID = 0; triggerID < 2; ++triggerID) {
  542.                 if (TriggerTimers[triggerID] == 15) { //15 and 16 look just the same
  543.                         TriggerTimers[triggerID] = -1;
  544.                         jjTileType[38 - (triggerID << 1)] = 0; //normal
  545.                         jjTileType[39 - (triggerID << 1)] = 0; //
  546.                 } else if (TriggerTimers[triggerID] != -1) {
  547.                         if (TriggerTimers[triggerID] == 0) {
  548.                                 jjTileType[38 - (triggerID << 1)] = 3; //invisible
  549.                                 jjTileType[39 - (triggerID << 1)] = 3; //
  550.                         }
  551.                         const uint tileID = jjTileCount + ((triggerID << 5) | TriggerTimers[triggerID]);
  552.                         for (int i = SwitchBlocks.length - 1; i >= 0; --i) {
  553.                                 const SwitchBlock@ block = SwitchBlocks[i];
  554.                                 if (block.triggerID == triggerID)
  555.                                         jjDrawTile(block.location.x << 5, block.location.y << 5, tileID + ((jjTileGet(4, block.location.x, block.location.y) & 1) << 4), TILE::ALLQUADRANTS, 5);
  556.                         }
  557.                         ++TriggerTimers[triggerID];
  558.                 }
  559.         }
  560. }
  561.  
  562. void onDrawLayer8(jjPLAYER@ play, jjCANVAS@ screen) {
  563.         jjUseLayer8Speeds = true;
  564.        
  565.         if (jjLayerHasTiles[2] = jjKey[0x76]) //F7
  566.                 jjLayerYOffset[2] = jjSin(jjGameTicks << 3) * 4;
  567.        
  568.         //for (uint i = 0; i < 16; ++i)
  569.         //      for (uint j = 0; j < 4; ++j)
  570.         //              screen.drawTile(i * 32, j * 32, jjTileCount + j * 16 + i);
  571. }
  572. const int glowOffset = 320-78-(jjGameConnection==GAME::LOCAL?1:2);
  573. void onDrawLayer6(jjPLAYER@ play, jjCANVAS@ screen) {
  574.         jjUseLayer8Speeds = false;
  575.         screen.drawResizedSprite(ResolutionCameraOffsetX, ResolutionCameraOffsetY + glowOffset, ANIM::CUSTOM[0], 0, 0, jjResolutionWidth, 1.f, SPRITE::ALPHAMAP, 207);
  576.         //screen.drawString(ResolutionCameraOffsetX + 50, ResolutionCameraOffsetY + 50, "Hello World", STRING::MEDIUM, STRING::NORMAL, 0, SPRITE::RESIZED, 128);
  577. }
  578.  
  579. void onDrawLayer4(jjPLAYER@, jjCANVAS@ screen) {
  580.         const Point@ point = Incidents[WarpTargetID].warpTargetLocation;
  581.         int xPos = point.x - 32;
  582.         int yPos = point.y - 64;
  583.         //draw staticky screen
  584.         screen.drawRectangle(xPos, yPos, 96, 64, 70);
  585.         if (!jjLowDetail)
  586.                 screen.drawRectangle(xPos += 2, yPos += 2, 92, 60, 68, SPRITE::BLEND_DISSOLVE, 192);
  587.         screen.drawString(xPos, yPos + 14, Incidents[WarpTargetID].incidentNumberAsString, jjAnimations[jjAnimSets[ANIM::CUSTOM[1]]]);//, STRING::NORMAL, 0, SPRITE::PALSHIFT);
  588.         if (!jjLowDetail) {
  589.                 screen.drawRectangle(xPos, yPos, 92, 60, 66, SPRITE::BLEND_DISSOLVE, 128);
  590.                 screen.drawRectangle(xPos, yPos, 92, 60, 64, SPRITE::BLEND_DISSOLVE, 64);
  591.         }
  592.         //screen.drawString(xPos - 32, yPos + 80, IncidentNames[WarpTargetID], STRING::MEDIUM);
  593.         const uint xTile = point.x / 32 - 1, yTile = point.y / 32 - 2;
  594.         uint16 tileID;
  595.         for (uint x = 0; x < 3; ++x)
  596.                 for (uint y = 0; y < 2; ++y)
  597.                         if ((tileID = jjTileGet(4, xTile+x, yTile+y)) != 43) //blocks spawned by Builder in front of the screen
  598.                                 screen.drawTile((xTile + x) << 5, (yTile + y) << 5, tileID);
  599. }
  600.  
  601. string ColorTimeString(const string &in time, const string &in color, bool includeNoStarPrefix) {
  602.         if (includeNoStarPrefix || time[0] != '$'[0])
  603.                 return time.substr(0,1) + color + time.substr(1);
  604.         return color + time.substr(1);
  605. }
  606.  
  607. bool GameBegun = WarpTargetID != 0;
  608. bool GameOver = false;
  609. bool onDrawAmmo(jjPLAYER@ play, jjCANVAS@ screen) {
  610.         if (!play.isLocal || GameOver)
  611.                 return true;
  612.         if (!GameBegun) {
  613.                 if (!ViewingLevelSelect) {
  614.                         if (/*jjGameConnection != GAME::LOCAL || */LastIncidentCompleted == 0) {
  615.                                 if ((jjRenderFrame & 8) == 0)
  616.                                         screen.drawString(0x8000, jjResolutionHeight - 100, "Press Fire", STRING::MEDIUM, STRING::SPIN);
  617.                         } else {
  618.                                 array<string> Options = {"#New Game", "#Continue", "#Incident Select"};
  619.                                 if (jjGameConnection != GAME::LOCAL)
  620.                                         Options.removeAt(2);
  621.                                 if (LastIncidentCompleted == Incidents.length-1)
  622.                                         Options.removeAt(1);
  623.                                 for (uint i = 0; i < Options.length; ++i)
  624.                                         screen.drawString(0x8000, jjResolutionHeight - 250 + i*50, Options[i], STRING::MEDIUM, int(i) == CurrentMenuOption ? STRING::SPIN : STRING::NORMAL);
  625.                         }
  626.                         screen.drawString(0x10000 + 10, 12, "1|F2 for brightness", STRING::SMALL);
  627.                 } else {
  628.                         screen.drawString(0x8000, 40, "Incident Select", STRING::LARGE);
  629.                         if (jjSubscreenWidth >= 800) {
  630.                                 if (ThumbnailFadeTimer != 0)
  631.                                         screen.drawResizedSprite(80, jjSubscreenHeight>>1, ANIM::CUSTOM[3], 0, PreviousMenuOption, 5,5, SPRITE::BLEND_DISSOLVE, ThumbnailFadeTimer);
  632.                                 screen.drawResizedSprite(80, jjSubscreenHeight>>1, ANIM::CUSTOM[3], 0, CurrentMenuOption, 5,5, ThumbnailFadeTimer == 0 ? SPRITE::BLEND_NORMAL : SPRITE::BLEND_DISSOLVE, 255 - ThumbnailFadeTimer);
  633.                                 if (ThumbnailFadeTimer != 0)
  634.                                         ThumbnailFadeTimer -= 8;
  635.                         }
  636.                         array<int> xPos = {0x10000 + (jjSubscreenWidth - 120), 180, 450};
  637.                         for (uint j = 0; j < 3; ++j)
  638.                                 xPos[j] += (jjSubscreenWidth - 640) / 2 * (j == 0 ? -1 : 1);
  639.                         int yPos = 120;
  640.                         const uint maxIncidentsDisplayedAtOnce = (jjSubscreenHeight - 170) / 30;
  641.                         int firstIncidentToList = CurrentMenuOption - (maxIncidentsDisplayedAtOnce / 2);
  642.                         if (firstIncidentToList < 0) firstIncidentToList = 0;
  643.                         else {
  644.                                 const int theMax = int(Incidents.length) - maxIncidentsDisplayedAtOnce - 1;
  645.                                 if (firstIncidentToList > theMax) firstIncidentToList = theMax;
  646.                         }
  647.                         for (uint i = 0; i < maxIncidentsDisplayedAtOnce; ++i) {
  648.                                 const uint incidentNumber = i + firstIncidentToList;
  649.                                 const auto@ incident = Incidents[incidentNumber];
  650.                                 const array<string> strings = {"|||" + incident.incidentNumberAsString, "#" + IncidentNames[incidentNumber], /*incidentNumber < LastIncidentCompleted ? */ColorTimeString(incident.recordTimeElapsedAsString, incident.recordNumberOfDeaths == 0 ? "||||" : "", true)/* : ""*/};
  651.                                 for (uint j = 0; j < 3; ++j)
  652.                                         screen.drawString(xPos[j], yPos, strings[j], STRING::MEDIUM, (j == 1 && int(incidentNumber) == CurrentMenuOption) ? STRING::SPIN : /*incidentNumber > LastIncidentCompleted ? STRING::DARK :*/ STRING::NORMAL);
  653.                                 yPos += 30;
  654.                         }
  655.                        
  656.                         const int totalsYStart = jjSubscreenHeight - 60;
  657.                         screen.drawSprite(5, totalsYStart, ANIM::CUSTOM[1], 0, 5, 1, SPRITE::PALSHIFT, 230);
  658.                         const uint MaxStars = (Incidents.length - 1)*3;
  659.                         screen.drawString(26, totalsYStart+10, (TotalStars < MaxStars ? ":  " : "||||:  ") + TotalStars + "/" + MaxStars, STRING::MEDIUM);
  660.                         screen.drawSpriteFromCurFrame(17, totalsYStart+33, jjObjectPresets[OBJECT::STOPWATCH].curFrame + 7 - (CurrentMenuOption & 7));
  661.                         screen.drawString(26, totalsYStart+30, TotalTime, STRING::MEDIUM);
  662.                        
  663.                         if (!UsedIngameF11AtLeastOnce && jjGameConnection == GAME::LOCAL)
  664.                                 screen.drawString(0x10000 + 10, 12, "1|Hold F11  ingame to return here.", STRING::SMALL);
  665.                         return true;
  666.                 }
  667.                 const int titleYPos = jjResolutionHeight / 5;
  668.                 screen.drawString(0x8000, titleYPos, "Mighty Switch Test!", STRING::LARGE, STRING::NORMAL, 0, SPRITE::SINGLEHUE, 112);
  669.                 screen.drawString(0x8110, titleYPos + 14, "2!", STRING::LARGE, STRING::NORMAL, 0, SPRITE::SINGLEHUE, 128);
  670.         } else if (AirboardCounter != 0) {
  671.                 if (AirboardCounter >= AIRBOARDHALFWAYTIME) { //finished
  672.                         screen.drawString(0x8000, 70, !Cheater ? ("Incident " + Incidents[WarpTargetID].incidentNumberAsString + " Complete!") : ("Cheated in Incident " + Incidents[WarpTargetID].incidentNumberAsString), STRING::LARGE, STRING::BOUNCE);
  673.                        
  674.                         const bool wideScreen = jjResolutionWidth > 640;
  675.                         const int ColumnZeroXPos = 50; //labels
  676.                         const int ColumnOneXPos = wideScreen ? 200 : 180; //new
  677.                         const int ColumnTwoXPos = wideScreen ? 380 : 330; //record
  678.                         const int ColumnThreeXPos = wideScreen ? 610 : 520; //par
  679.                         int YPos = 160;
  680.                         if (!Cheater) {
  681.                                 screen.drawString(ColumnOneXPos, YPos, "New:", STRING::MEDIUM);
  682.                                 screen.drawString(ColumnTwoXPos, YPos, "Record:", STRING::MEDIUM);
  683.                                 screen.drawString(ColumnThreeXPos, YPos, "Par:", STRING::MEDIUM);
  684.                         }
  685.                        
  686.                         YPos += 40;
  687.                        
  688.                         if (!Cheater) {
  689.                                 screen.drawString(ColumnZeroXPos, YPos, "Time:", STRING::MEDIUM);
  690.                                 screen.drawString(ColumnOneXPos, YPos, ColorTimeString(TimeElapsedStringThisIncident, TimeElapsedRecordSet ? "|||" : "", false), STRING::MEDIUM);
  691.                         }
  692.                        
  693.                         if (Cheater)
  694.                                 {}
  695.                         else if (TimeElapsedRecordSet)
  696.                                 screen.drawString(ColumnTwoXPos, YPos, "#|||||~New  Record!", STRING::MEDIUM, STRING::SPIN);
  697.                         else
  698.                                 screen.drawString(ColumnTwoXPos, YPos, ColorTimeString(Incidents[WarpTargetID].recordTimeElapsedAsString, "", false), STRING::MEDIUM);
  699.                                
  700.                         if (!Cheater)
  701.                                 screen.drawString(ColumnThreeXPos, YPos, "0:" + formatInt(Incidents[WarpTargetID].parTime, "0", 2), STRING::MEDIUM);
  702.                        
  703.                         YPos += 40;
  704.                        
  705.                         screen.drawString(ColumnZeroXPos, YPos, "Deaths:", STRING::MEDIUM);
  706.                         screen.drawString(ColumnOneXPos, YPos, (DeathsRecordSet ? NumberOfDeathsThisIncident == 0 ? "||||" : "|||" : "") + NumberOfDeathsThisIncident, STRING::MEDIUM);
  707.                         if (Cheater)
  708.                                 {}
  709.                         else if (DeathsRecordSet)
  710.                                 screen.drawString(ColumnTwoXPos, YPos, (NumberOfDeathsThisIncident == 0 ? "#|" : "#") + "|||||~New  Record!", STRING::MEDIUM, STRING::SPIN);
  711.                         else
  712.                                 screen.drawString(ColumnTwoXPos, YPos, "" + Incidents[WarpTargetID].recordNumberOfDeaths, STRING::MEDIUM);
  713.                        
  714.                         if (AirboardCounter == AIRBOARDHALFWAYTIME) {
  715.                                 screen.drawString(0x8000, jjResolutionHeight - 100, "|F|I|R|E|: |Continue       |B|A|C|K|S|P|A|C|E|:|||||| Retry", STRING::MEDIUM, ColoredBounce);
  716.                                 screen.drawString(0x8000, jjResolutionHeight - 70, "|F|1|1|: ||Incident Select", STRING::MEDIUM, ColoredBounce);
  717.                         }
  718.                 } else { //started
  719.                         //eh
  720.                 }
  721.         } else {
  722.                 for (uint i = 1; i <= 4; ++i) {
  723.                         const jjANIMATION@ curAnim = jjAnimations[jjAnimSets[ANIM::CUSTOM[2]] + i - 1];
  724.                         if (curAnim.frameCount > 0) {
  725.                                 const uint curFrame = curAnim.firstFrame + ((jjGameTicks >> 2) % curAnim.frameCount);
  726.                                 const int x = jjSubscreenWidth - 120 + i * 25;
  727.                                 const SPRITE::Mode spriteMode = i == play.currWeapon ? SPRITE::SINGLEHUE : SPRITE::TRANSLUCENT;
  728.                                 const uint8 spriteParam = 128 - (CurrentTriggerID << 4);
  729.                                 int ammoCount = play.ammo[i];
  730.                                 if (i == play.currWeapon && ammoCount != 0)
  731.                                         screen.drawSprite(x - 8, jjSubscreenHeight - 17, ANIM::FONT, 1, (ammoCount == 63) ? 95 : (16 + ammoCount), 1, SPRITE::SINGLEHUE, spriteParam);
  732.                                 if (ammoCount == 63)
  733.                                         ammoCount = 1;
  734.                                 while (ammoCount != 0)
  735.                                         screen.drawSpriteFromCurFrame(x, jjSubscreenHeight - 3 - ammoCount-- * 25, curFrame, 1, spriteMode, spriteParam);
  736.                         }
  737.                 }
  738.                
  739.                 if (NumberOfDeathsThisIncident >= 10)// && !Dying)
  740.                         screen.drawString(0x10000 + 10, 12, "1|F7 for hints", STRING::SMALL);
  741.                 if (TimeSpentPressingF11 != 0) {
  742.                         screen.drawString(0x10000 + 10, 34, "1|Hold F11 for Incident Select.", STRING::SMALL);
  743.                         screen.drawRectangle(jjSubscreenWidth - 160, 50, TimeSpentPressingF11, 8, 16 + (125 - TimeSpentPressingF11) / 20);
  744.                 }
  745.         }
  746.         return true;
  747. }
  748. bool onDrawScore(jjPLAYER@ play, jjCANVAS@ screen) {
  749.         return true;//!GameBegun;
  750. }
  751. bool onDrawHealth(jjPLAYER@ play, jjCANVAS@ screen) {
  752.         return true;
  753. }
  754. bool onDrawLives(jjPLAYER@ play, jjCANVAS@ screen) {
  755.         return true;
  756. }
  757. bool onDrawShieldTimer(jjPLAYER@ play, jjCANVAS@ screen) { //this doesn't currently exist, but it's a plausible name for such a hook if one is ever defined, so this might be helpful someday
  758.         return true;
  759. }
  760.  
  761. const uint AIRBOARDTOTALTIME = 560;
  762. const uint AIRBOARDHALFWAYTIME = 280; //AIRBOARDTOTALTIME / 2;
  763. uint SpendFramesCheckingIfPlayerIsStuck = 0;
  764. uint SweptAwayIntoSpaceCounter = 0;
  765. uint SweptAwayIntoSpaceMaximumLength = 160;
  766. float KnockedIntoScreenCounter = 0;
  767. bool Dying = false;
  768. uint AirboardCounter = 0, AirboardFlyawayCounter = 0;
  769. bool WarpingToNextIncidentOnline = false;
  770. uint LastPlayerCurAnim;
  771. float LastPlayerXSpeed, LastPlayerYSpeed;
  772. int CurrentTriggerID = 0;
  773. bool RetryingLastIncident = false;
  774. uint TimeSpentPressingF11 = 0;
  775. bool UsedIngameF11AtLeastOnce = false;
  776. bool PressingF2LastTick = false;
  777.  
  778. void immobilize(jjPLAYER@ play) {
  779.         play.keyFire = play.keyJump = play.keySelect = play.keyRun = play.keyLeft = play.keyRight = play.keyUp = play.keyDown = false;
  780.         play.xSpeed = play.xAcc = play.ySpeed = play.yAcc = 0;
  781.         play.idle = 0;
  782. }
  783. void ReturnToLevelSelect() {
  784.         GameBegun = false;
  785.         ViewingLevelSelect = true;
  786.         CalculateTotalStars();
  787.         Dying = false;
  788.         RetryingLastIncident = false;
  789.         TimeSpentPressingF11 = 0;
  790.         AirboardCounter = 0;
  791.         Player.lighting = 100;
  792.         Player.fly = FLIGHT::NONE;
  793.         Player.shieldTime = -70;
  794.         CurrentMenuOption = WarpTargetID;
  795. }
  796. uint TotalStars;
  797. string TotalTime;
  798. void CalculateTotalStars() {
  799.         TotalStars = 0;
  800.         uint totalTimeInt = 0;
  801.         uint hours = 0;
  802.         for (uint i = 0; i < Incidents.length - 1; ++i) {
  803.                 const PerIncident@ incident = Incidents[i];
  804.                 const int timeDifference = incident.recordTimeElapsed / 70 - incident.parTime;
  805.                 if (timeDifference < 0)
  806.                         TotalStars += 3;
  807.                 else if (timeDifference < 3)
  808.                         TotalStars += 2;
  809.                 else if (timeDifference < 6)
  810.                         TotalStars += 1;
  811.                 if (incident.recordTimeElapsed != UNFINISHEDINCIDENTTIME)
  812.                         totalTimeInt += incident.recordTimeElapsed;
  813.                 else
  814.                         hours += 1;
  815.         }
  816.         hours += totalTimeInt / (70*60*60);
  817.         totalTimeInt %= (70*60*60);
  818.         TotalTime = ":  " + hours + ":" + StringifyTime(totalTimeInt).substr(1);
  819. }
  820. void Die() {
  821.         if (!Dying) {
  822.                 Player.cameraFreeze(Player.cameraX, Player.cameraY, false, true);
  823.                 NumberOfDeathsThisIncident += 1;
  824.                 TimeSinceLastMorph = 140;
  825.                 Dying = true;
  826.                 Cheater = false;
  827.                 Player.keyFire = false;
  828.                 Player.specialMove = 0;
  829.         }
  830. }
  831. void SweepIntoSpace(bool quick) {
  832.         if (!quick) {
  833.                 SweptAwayIntoSpaceCounter = SweptAwayIntoSpaceMaximumLength;
  834.                 if (SweptAwayIntoSpaceMaximumLength > 70)
  835.                         SweptAwayIntoSpaceMaximumLength -= 2; //shorter each time, to limit player boredom
  836.                 jjSample(Player.xPos, Player.yPos, SOUND::FROG_FROG4);//, 63, 22050);
  837.         } else
  838.                 SweptAwayIntoSpaceCounter = 45;
  839.         LastPlayerCurAnim = Player.curAnim;
  840.         LastPlayerXSpeed = Player.xSpeed / 8;
  841.         LastPlayerYSpeed = Player.ySpeed / 8;
  842.         Die();
  843. }
  844. void safeWarp(jjPLAYER@ play, uint8 warpID) {
  845.         play.warpToID(warpID, true);
  846.         play.xPos = (int(play.xPos) & ~31) + 16;
  847.         play.yPos = (int(play.yPos) & ~31) + 12;
  848. }
  849. void changeIncident(jjPLAYER@ play, bool warp = true) {
  850.         if (warp)
  851.                 safeWarp(play, WarpTargetID);
  852.         for (int i = SwitchBlocks.length - 1; i >= 0; --i) {
  853.                 const SwitchBlock@ block = SwitchBlocks[i];
  854.                 const uint16 originalTileID = block.originalTileID;
  855.                 jjTileSet(4, block.location.x, block.location.y, originalTileID);
  856.                 if (!(originalTileID >= 36 && originalTileID <= 39)) //only a temporary block
  857.                         SwitchBlocks.removeAt(i);
  858.         }
  859.         for (uint i = jjObjectCount; --i != 0; )
  860.                 if (jjObjects[i].behavior == Builder)
  861.                         jjDeleteObject(i);
  862.         TriggerTimers[0] = TriggerTimers[1] = -1;
  863.         for (uint tileID = 36; tileID <= 39; ++tileID)
  864.                 jjTileType[tileID] = 0;
  865.         Triggers[0] = Triggers[1] = false;
  866.         const auto@ incident = Incidents[WarpTargetID];
  867.         if (incident.character != -1) {
  868.                 const bool airboard = play.fly == FLIGHT::AIRBOARD;
  869.                 play.morphTo(incident.character, false);
  870.                 if (airboard) play.fly = FLIGHT::AIRBOARD;
  871.         }
  872.         Player.helicopter = 0;
  873.         play.currWeapon = WEAPON::GUN9;
  874.         for (uint i = 1; i <= incident.ammoCount.length; ++i) {
  875.                 play.ammo[i] = incident.ammoCount[i - 1];
  876.                 jjWeapons[i].infinite = play.ammo[i] == 63;
  877.                 if (play.ammo[i] != 0 && i < play.currWeapon)
  878.                                 play.currWeapon = i;
  879.         }
  880.         for (uint i = 0; i < incident.AmmoPickupLocations.length; ++i) {
  881.                 const Point@ point = incident.AmmoPickupLocations[i];
  882.                 jjParameterSet(point.x, point.y, 2,1,0);
  883.         }
  884.         GhostTriggerID = CurrentTriggerID = incident.initiallyBlue ? 1 : 0;
  885.         SetFur();
  886.         play.alreadyDoubleJumped = false;
  887.         if (play.fly == FLIGHT::FLYCARROT)
  888.                 play.fly = FLIGHT::NONE;
  889.         Dying = false;
  890.         play.xOrg = play.xPos;
  891.         play.yOrg = play.yPos;
  892.         GhostX = GhostY = 0;
  893.         TimeStartedThisIncident = jjGameTicks;
  894.         TimeSinceLastMorph = 140;
  895.        
  896.         play.cameraFreeze(incident.warpTargetLocation.x + 16, incident.warpTargetLocation.y + 13, true, true);
  897.         if (warp)
  898.                 play.cameraUnfreeze(true);
  899. }
  900. void beginIncident() {
  901.         if (Player.currWeapon != WEAPON::GUN9)
  902.                 jjSamplePriority(SOUND::Sample(SOUND::FROG_FROG + Player.currWeapon - WEAPON::BLASTER));
  903.         //jjDebug(IncidentNames[WarpTargetID]);
  904.         TimeStartedThisIncident = jjGameTicks;
  905.         NumberOfDeathsThisIncident = 0;
  906.         Cheater = false;
  907. }
  908.  
  909. const uint NormalFur = jjLocalPlayers[0].fur;
  910. void SetFur() {
  911.         const uint8 furColor = 128 - (CurrentTriggerID << 4); //0x28 - (CurrentTriggerID << 3);
  912.         //if (jjGameMode == GAME::CTF)f
  913.         //      Player.fur = furColor | (furColor << 8) | (furColor << 16) | (furColor << 24);
  914.         //else
  915.         switch (Player.charCurr) {
  916.                 case CHAR::JAZZ:
  917.                         Player.fur = (NormalFur & 0x000000FF) | (furColor << 8) | (furColor << 16) | (furColor << 24);
  918.                         break;
  919.                 case CHAR::SPAZ:
  920.                         Player.fur = (NormalFur & 0xFF00FF00) | (furColor << 0) | (furColor << 16);
  921.                         break;
  922.         }
  923. }
  924. int warpInsideTile(int xTile, int yTile) {
  925.         const uint16 tileID = jjTileGet(4, xTile, yTile);
  926.         if (tileID == 0 || tileID == 105) //space
  927.                 return -2;
  928.         if (tileID == 37 || tileID == 39) //special block came into solidity
  929.                 return 1;
  930.         //if (tileID == 105) //border for purposes of Dasher gun
  931.         //      return 3;
  932.         if (jjMaskedPixel(xTile << 5, yTile << 5)) //wallclimbing
  933.                 return 2;
  934.         if (tileID == uint(38 - (CurrentTriggerID << 1))) //special block that isn't solid
  935.                 return -1;
  936.         return 0;
  937. }
  938. int warpInsidePixel(float x, float y) {
  939.         return warpInsideTile(int(x) >> 5, int(y) >> 5);
  940. }
  941.  
  942. void onPlayer(jjPLAYER@ play) {
  943.         if (GameOver) {
  944.                 if (jjGameConnection == GAME::LOCAL) {
  945.                         play.xSpeed = 1;
  946.                         play.ySpeed = 0;
  947.                         play.direction = 1;
  948.                 }
  949.                 play.keyFire = false;
  950.                 play.fly = FLIGHT::AIRBOARD;
  951.                 play.shieldType = SHIELD::BUBBLE;
  952.                 play.shieldTime = 70 * 99;
  953.         } else if (!GameBegun) {
  954.                 const bool canChangeSelection = ViewingLevelSelect || LastIncidentCompleted != 0;
  955.                 const bool fire = play.keyFire || play.keySelect || jjKey[0xD];
  956.                 const bool up = canChangeSelection && play.keyUp;
  957.                 const bool down = canChangeSelection && play.keyDown;
  958.                 bool pageUp = false, pageDown = false, end = false, home = false;
  959.                 if (ViewingLevelSelect) {
  960.                         pageUp = jjKey[33];
  961.                         pageDown = jjKey[34];
  962.                         end = jjKey[35];
  963.                         home = jjKey[36];
  964.                 }
  965.                
  966.                 if (!PressedMenuKeyLastTick) {
  967.                         const bool alreadyWon = LastIncidentCompleted == Incidents.length-1;
  968.                         int theMax;
  969.                         if (ViewingLevelSelect)
  970.                                 theMax = Incidents.length - 1;//(LastIncidentCompleted + (alreadyWon ? 0 : 1));
  971.                         else {
  972.                                 theMax = 3;
  973.                                 if (alreadyWon) theMax -= 1;
  974.                                 if (jjGameConnection != GAME::LOCAL) theMax -= 1;
  975.                         }
  976.                         bool incidentChanged = false;
  977.                         const auto menuOptionBeforeChange = CurrentMenuOption;
  978.                         if (up) {
  979.                                 incidentChanged = true;
  980.                                 if ((CurrentMenuOption -= 1) < 0)
  981.                                         CurrentMenuOption = theMax - 1;
  982.                         } else if (down) {
  983.                                 incidentChanged = true;
  984.                                 if ((CurrentMenuOption += 1) >= theMax)
  985.                                         CurrentMenuOption = 0;
  986.                         } else if (pageUp) {
  987.                                 incidentChanged = true;
  988.                                 if ((CurrentMenuOption -= (jjSubscreenHeight - 170) / 30) < 0)
  989.                                         CurrentMenuOption = 0;
  990.                         } else if (pageDown) {
  991.                                 incidentChanged = true;
  992.                                 if ((CurrentMenuOption += (jjSubscreenHeight - 170) / 30) >= theMax)
  993.                                         CurrentMenuOption = theMax - 1;
  994.                         } else if (home) {
  995.                                 incidentChanged = true;
  996.                                 CurrentMenuOption = 0;
  997.                         } else if (end) {
  998.                                 incidentChanged = true;
  999.                                 CurrentMenuOption = theMax - 1;
  1000.                         } else if (fire) {
  1001.                                 if (!ViewingLevelSelect) {
  1002.                                         if (alreadyWon && CurrentMenuOption == 1) CurrentMenuOption = 2;
  1003.                                         switch (CurrentMenuOption) {
  1004.                                         case 0:
  1005.                                                 GameBegun = true;
  1006.                                                 play.cameraUnfreeze(false);
  1007.                                                 if (jjGameConnection != GAME::LOCAL)
  1008.                                                         LastIncidentCompleted = AssignWarpTargetID(0);
  1009.                                                 changeIncident(play);
  1010.                                                 return;
  1011.                                         case 1:
  1012.                                                 CurrentMenuOption = LastIncidentCompleted;
  1013.                                                 break;
  1014.                                         case 2:
  1015.                                                 ViewingLevelSelect = true;
  1016.                                                 CalculateTotalStars();
  1017.                                                 CurrentMenuOption = 0;
  1018.                                                 PressedMenuKeyLastTick = true;
  1019.                                                 return;
  1020.                                         }
  1021.                                 } else {
  1022.                                 }
  1023.                                
  1024.                                 ThumbnailFadeTimer = 0;
  1025.                                 //WarpingToNextIncidentOnline = true;
  1026.                                 AssignWarpTargetID(CurrentMenuOption);
  1027.                                 //play.warpToID(AssignWarpTargetID(CurrentMenuOption));
  1028.                                 changeIncident(play);
  1029.                                 jjSamplePriority(SOUND::COMMON_SHLDOF3);
  1030.                                 play.shieldTime = 1;
  1031.                                 play.shieldType = SHIELD::BUBBLE;
  1032.                                 GameBegun = true;
  1033.                                 beginIncident();
  1034.                                 return;
  1035.                         }
  1036.                        
  1037.                         if (ViewingLevelSelect && incidentChanged) {
  1038.                                 PreviousMenuOption = menuOptionBeforeChange;
  1039.                                 ThumbnailFadeTimer = 248;
  1040.                         }
  1041.                 }
  1042.                
  1043.                 immobilize(play);
  1044.                 play.cameraFreeze(0, 160, false, true);
  1045.                
  1046.                 PressedMenuKeyLastTick = fire || up || down || pageUp || pageDown || home || end;
  1047.         } else {
  1048.                 if (!Dying) {
  1049.                         if (WarpingToNextIncidentOnline) {
  1050.                                 if (play.warpID == 0) {
  1051.                                         WarpingToNextIncidentOnline = false;
  1052.                                         //play.cameraUnfreeze(false);
  1053.                                         beginIncident();
  1054.                                         play.xOrg = play.xPos;
  1055.                                         play.yOrg = play.yPos;
  1056.                                 }
  1057.                         } else if (play.fly != FLIGHT::AIRBOARD) {
  1058.                                 //normal gameplay
  1059.                                 const int shouldWarpTile = warpInsidePixel(play.xPos, play.yPos);
  1060.                                 if (shouldWarpTile > 0) { //inside solid tile
  1061.                                         {//if (shouldWarpTile == 1 && play.fly == 0) {
  1062.                                                 jjSample(play.xPos, play.yPos, SOUND::COMMON_SMASH);
  1063.                                                 jjSample(play.xPos, play.yPos, SOUND::SPAZSOUNDS_AUTSCH1);
  1064.                                                 KnockedIntoScreenCounter = 1.0;
  1065.                                         }
  1066.                                         Die();
  1067.                                 } else if (shouldWarpTile == -2) { //empty space
  1068.                                         SweepIntoSpace(false);
  1069.                                 } else { //normal tile
  1070.                                         TimeSinceLastMorph += 1;
  1071.                                         if (SpendFramesCheckingIfPlayerIsStuck > 0) { //copied over from SwitchTestV.j2as
  1072.                                                 --SpendFramesCheckingIfPlayerIsStuck;
  1073.                                                 if (warpInsidePixel(play.xPos, play.yPos) <= 0) {
  1074.                                                         const int truncatedX = int(play.xPos) & 31;
  1075.                                                         if (truncatedX < 12) {
  1076.                                                                 const int shouldWarp = warpInsidePixel(play.xPos - 32, play.yPos);
  1077.                                                                 if (shouldWarp >= 1)
  1078.                                                                         play.xPos += (12 - truncatedX); //extract
  1079.                                                                 //else if (shouldWarp == -1)
  1080.                                                                 //      play.xPos -= 32; //squeeze into tube
  1081.                                                         } else if (truncatedX > 20) {
  1082.                                                                 const int shouldWarp = warpInsidePixel(play.xPos + 32, play.yPos);
  1083.                                                                 if (shouldWarp >= 1)
  1084.                                                                         play.xPos -= (truncatedX - 20);
  1085.                                                                 //else if (shouldWarp == -1)
  1086.                                                                 //      play.xPos += 32;
  1087.                                                         }
  1088.                                                         const int truncatedY = int(play.yPos) & 31;
  1089.                                                         if (truncatedY > 12) {
  1090.                                                                 int shouldWarp = warpInsidePixel(play.xPos, play.yPos + 32);
  1091.                                                                 if (shouldWarp <= 0) {
  1092.                                                                         if (truncatedX < 12)
  1093.                                                                                 shouldWarp = warpInsidePixel(play.xPos - 32, play.yPos + 32);
  1094.                                                                         else if (truncatedX > 20)
  1095.                                                                                 shouldWarp = warpInsidePixel(play.xPos + 32, play.yPos + 32);
  1096.                                                                 }
  1097.                                                                 if (shouldWarp >= 1)
  1098.                                                                         play.yPos -= (truncatedY - 12);
  1099.                                                                 //else if (shouldWarp == -1)
  1100.                                                                 //      play.yPos += 32;
  1101.                                                         } else if (truncatedY < 4 ) {
  1102.                                                                 if (warpInsidePixel(play.xPos, play.yPos - 32) >= 1)
  1103.                                                                         play.yPos += (4 - truncatedY);
  1104.                                                         }
  1105.                                                 }
  1106.                                         }
  1107.                                         if (play.ySpeed < LastPlayerYSpeed && LastPlayerYSpeed < 0.f && jjMaskedHLine(int(play.xPos) - 13, 26, int(play.yPos) + 25)) { //walljump
  1108.                                                 play.ySpeed = 1;
  1109.                                                 if (play.keyDown)
  1110.                                                         play.buttstomp = 1;
  1111.                                                 play.keyJump = false; //probably not needed
  1112.                                         }
  1113.                                         LastPlayerYSpeed = play.ySpeed;
  1114.                                        
  1115.                                         if (play.currWeapon == WEAPON::SEEKER && play.ammo[WEAPON::CURRENT] != 0 && !Dying) { //ghost
  1116.                                                 float ghostDrawX, ghostDrawY;
  1117.                                                 if (GhostX == 0 && GhostY == 0) {
  1118.                                                         ghostDrawX = play.xOrg;
  1119.                                                         ghostDrawY = play.yOrg;
  1120.                                                 } else {
  1121.                                                         ghostDrawX = play.xPos + GhostX;
  1122.                                                         ghostDrawY = play.yPos + GhostY;
  1123.                                                         jjDrawRotatedSpriteFromCurFrame(play.xPos, play.yPos, GhostCordCurFrame, GhostAngle, 1, GhostScale, SPRITE::TRANSLUCENT,0, 4);
  1124.                                                 }
  1125.                                                 jjDrawSpriteFromCurFrame(ghostDrawX, ghostDrawY, play.curFrame, play.direction, SPRITE::/*TRANSLUCENT*/SINGLEHUE,72, 4);
  1126.                                                 //if (play.blink == 0 || ((jjRenderFrame >> 2) & 1) == 1)
  1127.                                                         //jjDrawSpriteFromCurFrame(play.xPos, play.yPos, play.curFrame, play.direction, SPRITE::PLAYER, play.playerID, 3);
  1128.                                         }
  1129.                                        
  1130.                                         if (jjGameConnection == GAME::LOCAL) {
  1131.                                                 if (jjKey[0x7A]) {
  1132.                                                         if (TimeSpentPressingF11++ == 125) {
  1133.                                                                 UsedIngameF11AtLeastOnce = true;
  1134.                                                                 ReturnToLevelSelect();
  1135.                                                         }
  1136.                                                 } else
  1137.                                                         TimeSpentPressingF11 = 0;
  1138.                                         }
  1139.                                 }
  1140.                         } else { //on airboard
  1141.                                 if (play.shieldTime <= 0) { //just starting
  1142.                                         AirboardCounter = AIRBOARDTOTALTIME;
  1143.                                         AirboardFlyawayCounter = 0;
  1144.                                         play.xOrg = play.xPos;
  1145.                                         play.yOrg = play.yPos;
  1146.                                         play.cameraFreeze(play.cameraX, play.cameraY, false, true);
  1147.                                         play.lighting = 0;
  1148.                                         play.shieldType = SHIELD::BUBBLE;
  1149.                                        
  1150.                                         switch (play.charCurr) {
  1151.                                                 case CHAR::JAZZ:
  1152.                                                         jjSamplePriority(SOUND::ENDTUNEJAZZ_TUNE);
  1153.                                                         break;
  1154.                                                 case CHAR::SPAZ:
  1155.                                                         jjSamplePriority(SOUND::ENDTUNESPAZ_TUNE);
  1156.                                                         break;
  1157.                                                 case CHAR::LORI:
  1158.                                                         jjSamplePriority(SOUND::ENDTUNELORI_CAKE);
  1159.                                                         break;
  1160.                                         }
  1161.                                 } else if (AirboardCounter == AIRBOARDHALFWAYTIME) { //halfway
  1162.                                         if (jjKey[0x7A]) {
  1163.                                                 ReturnToLevelSelect();
  1164.                                                 Player.light = 13;
  1165.                                                 return;
  1166.                                         } else {
  1167.                                                 RetryingLastIncident = jjKey[0x08]; //backspace
  1168.                                                 if (RetryingLastIncident || play.keyFire) {
  1169.                                                         AirboardCounter -= 1;
  1170.                                                         Player.light = 13;
  1171.                                                 }
  1172.                                         }
  1173.                                 } else if (--AirboardCounter == AIRBOARDHALFWAYTIME-2) {
  1174.                                         if (!RetryingLastIncident)
  1175.                                                 AssignWarpTargetID(WarpTargetID + 1); //advancement
  1176.                                         else
  1177.                                                 RetryingLastIncident = false;
  1178.                                         changeIncident(play);
  1179.                                         play.cameraFreeze(play.xPos, play.yPos, true, true);
  1180.                                         play.lighting = 100;
  1181.                                 }
  1182.                                 ++AirboardFlyawayCounter;
  1183.                                 if (jjIsTSF && AirboardCounter == 80)
  1184.                                         play.shieldType = SHIELD::NONE; //TSF has sound count bug
  1185.                                 if (AirboardCounter == 0) { //end
  1186.                                         play.fly = FLIGHT::NONE; //lose airboard
  1187.                                         {
  1188.                                                 jjOBJ@ shard = jjObjects[jjAddObject(OBJECT::SHARD, play.xPos, play.yPos, 0, CREATOR::OBJECT, BEHAVIOR::AIRBOARDFALL)]; //dispose of airboard
  1189.                                                 shard.determineCurAnim(ANIM::PICKUPS, 36);
  1190.                                                 shard.xSpeed = 3.5;
  1191.                                         }
  1192.                                         play.shieldTime = 1; //dispose of shield
  1193.                                         play.xPos = play.xOrg;
  1194.                                         play.yPos = play.yOrg;
  1195.                                         //play.xOrg = 0;
  1196.                                         //play.yOrg = 0;
  1197.                                         play.cameraUnfreeze(true);
  1198.                                         beginIncident();
  1199.                                 } else {
  1200.                                         if (AirboardCounter >= AIRBOARDHALFWAYTIME) { //leave old place
  1201.                                                 const auto angle = jjGameTicks << 3;
  1202.                                                 const auto distance = AirboardFlyawayCounter * 1.5;
  1203.                                                 play.xPos = play.xOrg + jjSin(angle) * distance;
  1204.                                                 play.yPos = play.yOrg + jjCos(angle) * distance;
  1205.                                         } else if (AirboardCounter < AIRBOARDHALFWAYTIME) { //approach new place
  1206.                                                 play.xPos = play.xOrg - AirboardCounter * 1.75;
  1207.                                         }
  1208.                                         play.shieldTime = 70 * 99;
  1209.                                         play.direction = 1; //right
  1210.                                         immobilize(play);
  1211.                                 }
  1212.                         }
  1213.                 } else { //dying==true
  1214.                         if (SweptAwayIntoSpaceCounter != 0) {
  1215.                                 SweptAwayIntoSpaceCounter -= 1;
  1216.                                 if (SweptAwayIntoSpaceCounter == 20) {
  1217.                                         jjObjects[jjAddObject(OBJECT::EXPLOSION, play.xPos, play.yPos)].determineCurAnim(ANIM::AMMO, 82);
  1218.                                 } else if (SweptAwayIntoSpaceCounter > 20) {
  1219.                                         play.xPos += LastPlayerXSpeed;
  1220.                                         play.yPos += LastPlayerYSpeed;
  1221.                                 }
  1222.                         } else if (KnockedIntoScreenCounter != 0) {
  1223.                                 if ((KnockedIntoScreenCounter += 0.2) >= 24.f)
  1224.                                         KnockedIntoScreenCounter = 0;
  1225.                         } else {
  1226.                                 changeIncident(play);
  1227.                                 play.cameraUnfreeze(true);
  1228.                                 play.blink = 70;
  1229.                         }
  1230.                         immobilize(play);
  1231.                 }
  1232.                
  1233.                 //if (true) {
  1234.                         const PerIncident@ incident = Incidents[WarpTargetID];
  1235.                         const bool safeToUnfreeze = !Dying && play.fly != FLIGHT::AIRBOARD && !WarpingToNextIncidentOnline;
  1236.                         if (jjSubscreenWidth >= incident.WidthP)
  1237.                                 play.cameraFreeze(incident.CenterXP, safeToUnfreeze && jjSubscreenHeight < incident.HeightP, true, true);
  1238.                         if (jjSubscreenHeight >= incident.HeightP)
  1239.                                 play.cameraFreeze(safeToUnfreeze && jjSubscreenWidth < incident.WidthP, incident.CenterYP, true, true);
  1240.                         else if (safeToUnfreeze && jjSubscreenWidth < incident.WidthP)
  1241.                                 play.cameraUnfreeze(true);
  1242.                 //}
  1243.         }
  1244. }
  1245.  
  1246. bool LoadedSuccessfulSound = false;
  1247. void Next() {
  1248.         if (uint(WarpTargetID) < Incidents.length - 1) {
  1249.                 const int TimeElapsedThisIncident = jjGameTicks - TimeStartedThisIncident;
  1250.                 TimeElapsedStringThisIncident = StringifyTime(TimeElapsedThisIncident, Incidents[WarpTargetID].parTime);
  1251.                 if (!Cheater) {
  1252.                         if (TimeElapsedRecordSet = (Incidents[WarpTargetID].recordTimeElapsed > TimeElapsedThisIncident)) {
  1253.                                 Incidents[WarpTargetID].recordTimeElapsed = TimeElapsedThisIncident;
  1254.                                 Incidents[WarpTargetID].stringifyRecordTime();
  1255.                         }
  1256.                         if (DeathsRecordSet = (Incidents[WarpTargetID].recordNumberOfDeaths > NumberOfDeathsThisIncident)) {
  1257.                                 Incidents[WarpTargetID].recordNumberOfDeaths = NumberOfDeathsThisIncident;
  1258.                         }
  1259.                         if (TimeElapsedRecordSet || DeathsRecordSet) { //at least one record set
  1260.                                 jjSTREAM records;
  1261.                                 for (uint i = 0; i < Incidents.length; ++i) {
  1262.                                         records.push(Incidents[i].recordNumberOfDeaths);
  1263.                                         records.push(Incidents[i].recordTimeElapsed);
  1264.                                 }
  1265.                                 records.save(jjHelpStrings[10]);
  1266.                         }
  1267.                 } else
  1268.                         TimeElapsedRecordSet = DeathsRecordSet = false;
  1269.                        
  1270.                 SpendFramesCheckingIfPlayerIsStuck = 0;
  1271.                 SweptAwayIntoSpaceCounter = 0;
  1272.                 KnockedIntoScreenCounter = 0;
  1273.                 Dying = false;
  1274.                
  1275.                 if (jjGameConnection == GAME::LOCAL)
  1276.                         Player.fly = FLIGHT::AIRBOARD;
  1277.                 else {
  1278.                         if (!LoadedSuccessfulSound) {
  1279.                                 jjSampleLoad(SOUND::COMMON_TELPORT1, "SUCCESSF.WAV");
  1280.                                 LoadedSuccessfulSound = true;
  1281.                         }
  1282.                         WarpingToNextIncidentOnline = true;
  1283.                         Player.warpToID(AssignWarpTargetID(WarpTargetID + 1));
  1284.                         LastIncidentCompleted = WarpTargetID;
  1285.                         changeIncident(Player, false);
  1286.                 }
  1287.         } else if (!GameOver) {
  1288.                 GameOver = true;
  1289.                 if (jjGameConnection == GAME::LOCAL)
  1290.                         jjNxt();
  1291.                 else
  1292.                         Player.cameraUnfreeze();
  1293.         }
  1294. }
  1295.  
  1296. bool Cheater = false;
  1297. bool onCheat(string &in cheat) {
  1298.         if (Player.fly == FLIGHT::AIRBOARD || Dying || GameOver || !GameBegun)
  1299.                 return true;
  1300.         if (cheat == "jjnxt" || cheat == "jjnext" || cheat == "jjshield") {
  1301.                 Cheater = true;
  1302.                 Next();
  1303.         } else if (cheat == "jjfly") {
  1304.                 Cheater = true;
  1305.                 Player.fly = (Player.fly == FLIGHT::NONE) ? FLIGHT::FLYCARROT : FLIGHT::NONE;
  1306.         } else if (cheat == "jjmorph") {
  1307.                 Cheater = true;
  1308.                 Player.morph(true, true);
  1309.                 SetFur();
  1310.         } else if (cheat == "jjk") {
  1311.                 Die();
  1312.         } else if (cheat == "jjammo" || cheat == "jjguns" || cheat == "jjgod") {
  1313.                 for (int i = 1; i <= 4; ++i)
  1314.                         if (!jjWeapons[i].infinite)
  1315.                                 Player.ammo[i] = 9;
  1316.                 Cheater = true;
  1317.         }
  1318.         return !(cheat == "jjt" || cheat == "jjq");
  1319. }
  1320.  
  1321. array<bool> Triggers(2, false);
  1322. array<int> TriggerTimers(2, -1);
  1323. void setTriggerTo(uint8 triggerID, bool value) {
  1324.         if (Triggers[triggerID] != value) {
  1325.                 Triggers[triggerID] = value;
  1326.                 for (int i = SwitchBlocks.length - 1; i >= 0; --i) {
  1327.                         const SwitchBlock@ block = SwitchBlocks[i];
  1328.                         if (block.triggerID == triggerID)
  1329.                                 block.doSwitch();
  1330.                 }
  1331.                 TriggerTimers[triggerID] = 0;
  1332.         }
  1333. }
  1334. void PlayerJustRelocated() {
  1335.         switch (warpInsidePixel(Player.xPos, Player.yPos)) {
  1336.                 case -2:
  1337.                 case 3:
  1338.                         SweepIntoSpace(true);
  1339.                         break;
  1340.                 case 1:
  1341.                 case 2:
  1342.                         KnockedIntoScreenCounter = 2.0;
  1343.                         jjSample(Player.xPos, Player.yPos, SOUND::COMMON_SMASH);
  1344.                         Die();
  1345.                         break;
  1346.                 default:
  1347.                         SpendFramesCheckingIfPlayerIsStuck = 2;
  1348.                         break;
  1349.         }
  1350. }
  1351. void Switcher(jjOBJ@ obj) {
  1352.         if (jjPlayers[obj.creator].isLocal) {
  1353.                 setTriggerTo(CurrentTriggerID, !Triggers[CurrentTriggerID]);
  1354.                 SpendFramesCheckingIfPlayerIsStuck = 2;
  1355.                 jjSamplePriority(SOUND::COMMON_CANSPS);
  1356.         }
  1357.         obj.delete();
  1358. }
  1359. void Dasher(jjOBJ@ obj) {
  1360.         if (jjPlayers[obj.creator].isLocal) {
  1361.                 array<int> morphTiles;
  1362.                 if (TimeSinceLastMorph > 140) {
  1363.                         for (int i = jjObjectCount; --i != 0;) {
  1364.                                 const jjOBJ@ beam = jjObjects[i];
  1365.                                 if (beam.behavior == MorphBeam) {
  1366.                                         morphTiles.insertLast(beam.var[0]);
  1367.                                         morphTiles.insertLast(beam.var[1]);
  1368.                                 }
  1369.                         }
  1370.                 }
  1371.                 if (obj.ySpeed == -32)
  1372.                         Player.yPos = (int(Player.yPos) & ~31) + 12;
  1373.                 else
  1374.                         Player.xPos = (int(Player.xPos) & ~31) + 16 + Player.direction * 4;
  1375.                 const PerIncident@ incident = Incidents[WarpTargetID];
  1376.                 while (true) {
  1377.                         int targetX = int(Player.xPos + obj.xSpeed);
  1378.                         int targetY = int(Player.yPos + obj.ySpeed);
  1379.                         if (targetX < incident.LeftOuterBoundP || targetX >= incident.RightOuterBoundP || targetY < incident.TopOuterBoundP)
  1380.                                 break;
  1381.                         const int shouldWarp = warpInsidePixel(targetX, targetY);
  1382.                         if (shouldWarp > 0) //wall
  1383.                                 break;
  1384.                         float sparkX = Player.xPos, sparkY = Player.yPos;
  1385.                         for (int i = 0; i < 8; ++i, sparkX += obj.xAcc, sparkY += obj.yAcc) {
  1386.                                 for (int j = -1; j <= 1; j += 2) {
  1387.                                         jjPARTICLE@ spark = jjAddParticle(PARTICLE::SPARK);
  1388.                                         if (spark !is null) {
  1389.                                                 spark.xPos = sparkX;
  1390.                                                 spark.yPos = sparkY;
  1391.                                                 spark.xSpeed = obj.yAcc * j / 6;
  1392.                                                 spark.ySpeed = obj.xAcc * j / 6;
  1393.                                                 spark.spark.color = CurrentTriggerID == 0 ? 40 : 32;
  1394.                                                 spark.spark.colorStop = spark.spark.color + 8;
  1395.                                         }
  1396.                                 }
  1397.                                 jjAddObject(OBJECT::EXPLOSION, sparkX, sparkY, 0,CREATOR::OBJECT, function(expl) { expl.playerHandling = HANDLING::EXPLOSION; expl.lightType = LIGHT::POINT2; if (++expl.age == 10) expl.delete(); });
  1398.                         }
  1399.                         Player.xPos = targetX;
  1400.                         Player.yPos = targetY;
  1401.                         if (shouldWarp == -1) { //unsolid block of the right color
  1402.                                 targetX >>= 5;
  1403.                                 targetY >>= 5;
  1404.                                 jjTileSet(4, targetX, targetY, jjTileGet(4, targetX, targetY) + 1);
  1405.                         } else {
  1406.                                 const int currTile = getCurrTileValue(targetX, targetY);
  1407.                                 for (uint i = 0; i < morphTiles.length; ++i)
  1408.                                         if (currTile == morphTiles[i]) {
  1409.                                                 morphTiles.resize(0);
  1410.                                                 Morph();
  1411.                                         }
  1412.                         }
  1413.                 }
  1414.                 PlayerJustRelocated();
  1415.                 jjSamplePriority(SOUND::COMMON_TELPORT2);
  1416.         }
  1417.         obj.delete();
  1418. }
  1419. void Builder(jjOBJ@ obj) {
  1420.         if (jjPlayers[obj.creator].isLocal) {
  1421.                 if (obj.state == STATE::START) {
  1422.                         if (obj.ySpeed < 0)
  1423.                                 obj.xPos = (int(Player.xPos) & ~31) | 16;
  1424.                         else
  1425.                                 obj.yPos = (int(Player.yPos) & ~31) | 16;
  1426.                         obj.state = STATE::FLY;
  1427.                         obj.var[0] = 40 - (CurrentTriggerID << 3);
  1428.                         obj.var[1] = 39 - (CurrentTriggerID << 1);
  1429.                         obj.var[2] = CurrentTriggerID;
  1430.                 }
  1431.                 obj.xPos += obj.xSpeed;
  1432.                 obj.yPos += obj.ySpeed;
  1433.                 const int xNext = int(obj.xPos + obj.xSpeed * 16), yNext = int(obj.yPos + obj.ySpeed * 16);
  1434.                 if (jjMaskedPixel(xNext, yNext) && jjTileGet(4, xNext>>5, yNext>>5) != uint(obj.var[1] ^ 2)) {
  1435.                         while (jjMaskedPixel(int(obj.xPos), int(obj.yPos))) { //in case a block sprung up around this while it was in flight
  1436.                                 obj.xPos -= obj.xSpeed * 32;
  1437.                                 obj.yPos -= obj.ySpeed * 32;
  1438.                         }
  1439.                         const PerIncident@ incident = Incidents[WarpTargetID];
  1440.                         if (!(obj.xPos < incident.LeftOuterBoundP || obj.xPos >= incident.RightOuterBoundP || obj.yPos < incident.TopOuterBoundP)) {
  1441.                                 const uint x = int(obj.xPos) >> 5, y = int(obj.yPos) >> 5;
  1442.                                 const uint16 oldTileID = jjTileGet(4, x,y);
  1443.                                 jjTileSet(4, x,y, obj.var[1]);
  1444.                                 if (!(oldTileID >= 36 && oldTileID <= 39))
  1445.                                         SwitchBlocks.insertLast(SwitchBlock(x,y, oldTileID, obj.var[2]));
  1446.                                 SpendFramesCheckingIfPlayerIsStuck = 2;
  1447.                                 jjSamplePriority(SOUND::COMMON_PLOPKORK);
  1448.                         }
  1449.                         obj.delete();
  1450.                 } else
  1451.                         jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.age -= obj.direction, 1,1, SPRITE::SINGLEHUE, obj.var[0]);//, 3);
  1452.         } else
  1453.                 obj.delete();
  1454. }
  1455. int GhostX = 0, GhostY = 0, GhostAngle;
  1456. float GhostScale;
  1457. int GhostTriggerID;
  1458. void Ghoster(jjOBJ@ obj) {
  1459.         if (jjPlayers[obj.creator].isLocal) {
  1460.                 if (GhostX == 0 && GhostY == 0) {
  1461.                         const int potentialGhostX = (int(Player.xOrg) & ~31) - (int(Player.xPos) & ~31);
  1462.                         const int potentialGhostY = (int(Player.yOrg) & ~31) - (int(Player.yPos) & ~31);
  1463.                         //const float maxGhostDistance = 96;
  1464.                         //if (abs(potentialGhostX) <= maxGhostDistance && abs(potentialGhostY) <= maxGhostDistance) {
  1465.                                 GhostX = potentialGhostX;
  1466.                                 GhostY = potentialGhostY;
  1467.                         //} else {
  1468.                         //      jjSamplePriority(SOUND::COMMON_HORN1);
  1469.                         //      return;
  1470.                         //}
  1471.                 }
  1472.                 //else
  1473.                 {
  1474.                         int targetX = int(Player.xPos), targetY = int(Player.yPos);
  1475.                         if (warpInsidePixel(targetX, targetY) == -1) { //unsolid block of the right color
  1476.                                 targetX >>= 5;
  1477.                                 targetY >>= 5;
  1478.                                 jjTileSet(4, targetX, targetY, jjTileGet(4, targetX, targetY) + 1);
  1479.                         }
  1480.                 }
  1481.                
  1482.                 Player.xPos += GhostX; //offsetPosition?
  1483.                 Player.yPos += GhostY;
  1484.                 GhostX = -GhostX;
  1485.                 GhostY = -GhostY;
  1486.                 if (GhostTriggerID != CurrentTriggerID) {
  1487.                         CurrentTriggerID ^= 1;
  1488.                         GhostTriggerID ^= 1;
  1489.                         SetFur();
  1490.                 }
  1491.                 PlayerJustRelocated();
  1492.                
  1493.                 GhostAngle = int(atan2(-GhostX, GhostY) * -512.0 * 0.318309886142228);
  1494.                 GhostScale = sqrt(GhostX*GhostX+GhostY*GhostY) / 96.f;
  1495.                 jjSamplePriority(SOUND::COMMON_STEAM);
  1496.         }
  1497.         obj.delete();
  1498. }
  1499.  
  1500. const bool AllowLori = false; //jjIsTSF;
  1501. uint TimeSinceLastMorph = 140;
  1502. int getCurrTileValue(int x, int y) {
  1503.         return (x >> 5) + (y >> 5 << 16);
  1504. }
  1505. int getCharID(CHAR::Char curr) {
  1506.         if (curr == CHAR::JAZZ)
  1507.                 return 0;
  1508.         if (curr == CHAR::SPAZ)
  1509.                 return 1;
  1510.         return 2;
  1511. }
  1512. ///@Event 71=Morph Beam                     |+|Morph      |Morph   | |Direction:{Up,Down}1
  1513. void MorphBeam(jjOBJ@ obj) {
  1514.         if (obj.state == STATE::START) {
  1515.                 obj.state = STATE::WAKE;
  1516.                 obj.direction = jjParameterGet(int(obj.xPos / 32), int(obj.yPos / 32), 0, 1);
  1517.                 obj.xPos = obj.xOrg = int(obj.xPos) & ~31;
  1518.                 obj.yPos = obj.yOrg = int(obj.yPos) & ~31;
  1519.                 obj.var[0] = getCurrTileValue(int(obj.xPos), int(obj.yPos));
  1520.                 if (obj.direction == 0)  { //up
  1521.                         obj.direction = -1;
  1522.                         obj.yPos += 31;
  1523.                 } else { //down
  1524.                 }
  1525.                 obj.var[1] = getCurrTileValue(int(obj.xPos), int(obj.yPos + 32 * obj.direction));
  1526.         } else if (obj.state == STATE::DEACTIVATE) {
  1527.                 obj.deactivate();
  1528.         } else {
  1529.                 const bool canMorph = TimeSinceLastMorph > 140 && Player.fly != FLIGHT::AIRBOARD && !Dying && !GameOver;
  1530.                 if (canMorph) {
  1531.                         jjPARTICLE@ part = jjAddParticle(PARTICLE::STAR);
  1532.                         if (part !is null) {
  1533.                                 part.xPos = obj.xPos + (jjRandom() & 31);
  1534.                                 part.yPos = obj.yPos;
  1535.                                 part.xSpeed = 0;
  1536.                                 part.ySpeed = (jjRandom() & 3) * obj.direction;
  1537.                                 part.star.color = ((3 - getCharID(Player.charCurr)) << 3) + (jjRandom() & 7);
  1538.                                 part.star.angularSpeed = (jjRandom() & 31);
  1539.                                 part.star.frame = canMorph ? 0 : 1;
  1540.                         }
  1541.                         jjDrawRectangle(obj.xPos, obj.yPos, 32, 64 * obj.direction, 15, SPRITE::BLEND_NORMAL, uint8(abs(jjSin(jjRenderFrame << 4) * 64)));
  1542.                         if (Player.currTile == obj.var[0] || Player.currTile == obj.var[1])
  1543.                                 Morph();
  1544.                 }
  1545.         }
  1546. }
  1547. void Morph() {
  1548.         if (jjIsTSF)
  1549.                 jjCharacters[CHAR::LORI].morphBoxCycle = AllowLori;
  1550.         Player.morph(true, true);
  1551.         SetFur();
  1552.         Player.alreadyDoubleJumped = false;
  1553.         TimeSinceLastMorph = 0;
  1554.         //jjSample(Player.xPos, Player.yPos, SOUND::COMMON_GLASS2);
  1555.         jjSample(Player.xPos, Player.yPos, SOUND::COMMON_HOLYFLUT);
  1556.         for (uint i = 0; i < 1024; ++i)
  1557.                 if (jjParticles[i].type == PARTICLE::STAR)
  1558.                         jjParticles[i].type = PARTICLE::INACTIVE;
  1559. }
  1560.  
  1561. ///@Event 79=Recolor |+|Morph |Recolor   | |Color:{Yellow,Blue}1
  1562. void Recolor(jjOBJ@ obj) {
  1563.         if (obj.state == STATE::START) {
  1564.                 obj.state = STATE::WAKE;
  1565.                 obj.var[0] = getCurrTileValue(int(obj.xPos), int(obj.yPos));
  1566.                 obj.var[2] = jjParameterGet(int(obj.xPos / 32), int(obj.yPos / 32), 0, 1);
  1567.                 obj.var[3] = 82 + obj.var[2] * 80;
  1568.                 obj.xPos = int(obj.xPos) & ~31;
  1569.                 obj.yPos = int(obj.yPos) & ~31;
  1570.         } else if (obj.state == STATE::DEACTIVATE) {
  1571.                 obj.deactivate();
  1572.         } else {
  1573.                 jjDrawTile(obj.xPos, obj.yPos, obj.var[3] + ((jjGameTicks >> 1) & 7));
  1574.                 if (Player.currTile == obj.var[0] && CurrentTriggerID != obj.var[2] && Player.fly != FLIGHT::AIRBOARD && !Dying && !GameOver) {
  1575.                         CurrentTriggerID ^= 1;
  1576.                         Player.morphTo(Player.charCurr, true);
  1577.                         SetFur();
  1578.                         jjSample(Player.xPos, Player.yPos, SOUND::COMMON_SHLDOF3);
  1579.                 }
  1580.         }
  1581. }
  1582.  
  1583. class Airboard : jjBEHAVIORINTERFACE {
  1584.         void onBehave(jjOBJ@ obj) { obj.behave(BEHAVIOR::AIRBOARD); }
  1585.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  1586.                 if (bullet is null) {
  1587.                         if (!Dying || !player.isLocal) {
  1588.                                 if (player.isLocal) {
  1589.                                         if (Player.fly != FLIGHT::AIRBOARD && !GameOver)
  1590.                                                 Next();
  1591.                                         else
  1592.                                                 return true;
  1593.                                 }
  1594.                                 obj.frameID = 0;
  1595.                                 obj.behavior = BEHAVIOR::EXPLOSION2;
  1596.                                 obj.playerHandling = HANDLING::EXPLOSION;
  1597.                         }
  1598.                 }
  1599.                 return true;
  1600.         }
  1601. }
  1602.  
  1603. ///@Event 61=Ammo |+|Ammo |Ammo   |Pickup |Weapon:{Switcher,Dasher,Builder,Ghoster}2
  1604. class Ammo : jjBEHAVIORINTERFACE {
  1605.         void onBehave(jjOBJ@ obj) {
  1606.                 if (obj.state == STATE::START) {
  1607.                         obj.var[0] = jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0, 2) + 1;
  1608.                         obj.determineCurAnim(ANIM::CUSTOM[2], obj.var[0] - 1);
  1609.                         obj.state = STATE::FLOAT;
  1610.                 } else if (obj.state == STATE::DEACTIVATE)
  1611.                         obj.deactivate();
  1612.                 else {
  1613.                         obj.playerHandling = jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 2, 1) == 0 ? HANDLING::PICKUP : HANDLING::PARTICLE;
  1614.                         const jjANIMATION@ anim = jjAnimations[obj.curAnim];
  1615.                         if (obj.playerHandling == HANDLING::PICKUP)
  1616.                                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos + jjSin((obj.objectID*8+jjGameTicks+int(obj.xPos+obj.yPos*256))*16) * 4, anim.firstFrame + ((jjGameTicks >> 2) % anim.frameCount), 1, SPRITE::SINGLEHUE,  128 - (CurrentTriggerID << 4));
  1617.                 }
  1618.         }
  1619.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  1620.                 if (bullet is null) {
  1621.                         if (!Dying && player.isLocal && Player.fly != FLIGHT::AIRBOARD && !GameOver) {
  1622.                                 jjParameterSet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 2, 1, 1);
  1623.                                 const int ammoType = obj.var[0];
  1624.                                 const int previousAmmoAmount = player.ammo[ammoType];
  1625.                                 if ((previousAmmoAmount == 0 && jjAutoWeaponChange) || (player.ammo[WEAPON::CURRENT] == 0))
  1626.                                         player.currWeapon = ammoType;
  1627.                                 player.ammo[ammoType] = previousAmmoAmount + 1;
  1628.                                 jjSample(obj.xPos, obj.yPos, SOUND::Sample(SOUND::FROG_FROG + ammoType - 1));
  1629.                                 jjObjects[jjAddObject(OBJECT::EXPLOSION, obj.xPos, obj.yPos)].curAnim = obj.killAnim;
  1630.                         }
  1631.                 }
  1632.                 return true;
  1633.         }
  1634. }
  1635.  
  1636. void TogglePalette() {
  1637.         Palettes[PaletteID].apply();
  1638.         const uint8 fadeColorIntensity = 255 * (4-PaletteID) / 4;
  1639.         jjSetFadeColors(fadeColorIntensity,fadeColorIntensity,fadeColorIntensity);
  1640. }