Downloads containing xlmskyward.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Skyward ShowdownFeatured Download PurpleJazz Capture the flag 9.1 Download file

File preview

  1. #pragma require "FTURROCK.wav"
  2. #pragma require "EXPSTD3.wav"
  3. #pragma require "redeemer_flight.wav"
  4. #pragma require "wind1.wav"
  5. #pragma require "SHIPHUMS.wav"
  6. #pragma require "xlmskywardex.j2l"
  7. #pragma require "xlmskywardex2.j2l"
  8.  
  9. bool gameIsActive() {
  10.         return jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME;
  11. }
  12.  
  13. bool CTFMode() {
  14.         return (jjGameMode == GAME::CTF && jjGameCustom == GAME::NOCUSTOM) || jjGameCustom == GAME::DCTF;
  15. }
  16.  
  17. bool otherMode() {
  18.         return jjGameMode != GAME::CTF || jjGameCustom == GAME::TB || jjGameCustom == GAME::TLRS || jjGameCustom == GAME::DOM;
  19. }
  20.  
  21. bool muteAmbientWind() {
  22.         return (jjLocalPlayers[0].xPos < 22*32 && jjLocalPlayers[0].yPos < 46*32) || (jjLocalPlayers[0].xPos > 258*32 && jjLocalPlayers[0].yPos < 46*32);
  23. }
  24.  
  25. bool isWindSound(jjOBJ@ sound) {
  26.         return sound.xOrg >= 277*32;
  27. }
  28.  
  29. bool isShipSound(jjOBJ@ sound) {
  30.         return sound.xOrg <= 2*32;
  31. }
  32.  
  33. bool isDestructibleItem(jjOBJ@ target) {
  34.         return target.eventID == OBJECT::TNT || target.eventID == OBJECT::BOUNCERPOWERUP || target.eventID == OBJECT::RFPOWERUP || target.eventID == OBJECT::TOASTERPOWERUP || target.eventID == OBJECT::GUN8POWERUP || target.eventID == OBJECT::LIGHTNINGSHIELD;
  35. }
  36.  
  37. const array<uint16> invisibleTiles = {
  38.         75,
  39.         102, 103,
  40.         112, 113, 116, 117, 118, 119,
  41.         122, 123, 126, 127, 128, 129,
  42.         132, 133,
  43.         142, 143,
  44.         152, 153
  45. };
  46.  
  47. bool controllingRedeemer, warning = false;
  48. int xPos = 0, yPos = 0, cameraX = 0, cameraY = 0, redeemerAngle = 0, CTFArrowTimer = 0;
  49. uint delay = 0;
  50.  
  51. int redeemerColor(jjPLAYER@ creator) {
  52.         switch(creator.team) {
  53.                 case TEAM::BLUE: return 0;
  54.                 case TEAM::RED: return 1;
  55.                 case TEAM::GREEN: return 2;
  56.                 case TEAM::YELLOW: return 3;
  57.         }
  58.         return 0;
  59. }
  60.  
  61. uint8 getTeamColor(TEAM::Color team) {
  62.         switch (team) {
  63.                 case TEAM::BLUE: return 32;
  64.                 case TEAM::RED: return 24;
  65.         }
  66.         return 32;
  67. }
  68.  
  69. const array<TEAM::Color> teams = {TEAM::BLUE, TEAM::RED, TEAM::GREEN, TEAM::YELLOW};
  70. const float PI = 3.1415927f;
  71.  
  72. void onLevelLoad() {
  73.         jjSampleLoad(SOUND::P2_POEP, "FTURROCK.wav");
  74.         jjSampleLoad(SOUND::P2_PTOEI, "EXPSTD3.wav");
  75.         jjSampleLoad(SOUND::P2_SPLOUT, "redeemer_flight.wav");
  76.         jjSampleLoad(SOUND::WIND_WIND2A, "wind1.wav");
  77.         jjSampleLoad(SOUND::SCIENCE_PLOPKAOS, "SHIPHUMS.wav");
  78.  
  79.         jjAnimSets[ANIM::SONCSHIP].load();
  80.         jjAnimSets[ANIM::VINE].load();
  81.         jjAnimations[jjAnimSets[ANIM::AMMO] + 59] = jjAnimations[jjAnimSets[ANIM::SONCSHIP] + 0];
  82.        
  83.         jjTexturedBGFadePositionY = 0.1;
  84.         jjUseLayer8Speeds = true;
  85.        
  86.         jjObjectPresets[OBJECT::LIGHTNINGSHIELD].behavior =
  87.         jjObjectPresets[OBJECT::RFPOWERUP].behavior = OscillatingMonitor();
  88.        
  89.         jjObjectPresets[OBJECT::AMBIENTSOUND].behavior = AmbientSound();
  90.        
  91.         jjObjectPresets[OBJECT::TNT].determineCurFrame();
  92.         jjObjectPresets[OBJECT::TNT].behavior = Redeemer();
  93.         jjObjectPresets[OBJECT::TNT].xSpeed = jjObjectPresets[OBJECT::BLASTERBULLET].xSpeed * 1.33;
  94.         jjObjectPresets[OBJECT::TNT].counterEnd = 255;
  95.         jjObjectPresets[OBJECT::TNT].killAnim = jjObjectPresets[OBJECT::SEEKERBULLET].killAnim;
  96.        
  97.         jjObjectPresets[OBJECT::TNTAMMO3].behavior = RedeemerPickup();
  98.         jjObjectPresets[OBJECT::TNTAMMO3].determineCurFrame();
  99.         jjObjectPresets[OBJECT::TNTAMMO3].scriptedCollisions = true;
  100.        
  101.         jjWeapons[WEAPON::TNT].maximum = 1;
  102.         jjWeapons[WEAPON::GUN8].comesFromGunCrates = true;
  103.        
  104.         jjLayerXOffset[6] = 128;
  105.        
  106.         array<jjLAYER@> extraLayers = jjLayersFromLevel("xlmskywardex.j2l", array<uint> = {1,2,3,5,6,7}, 0);
  107.         array<jjLAYER@> extraLayers2 = jjLayersFromLevel("xlmskywardex2.j2l", array<uint> = {1,2,3,4,5,6,7}, 0);
  108.         jjLayerOrderSet(array<jjLAYER@> = {extraLayers[0], extraLayers[1], extraLayers2[4], extraLayers2[5], extraLayers2[6], jjLayers[3], jjLayers[1], jjLayers[4], extraLayers2[0], extraLayers2[1], extraLayers2[2], extraLayers2[3], jjLayers[2], jjLayers[5], extraLayers[2], jjLayers[6], jjLayers[7], extraLayers[3], extraLayers[4], extraLayers[5], jjLayers[8]});
  109.        
  110.         extraLayers[1].spriteMode = SPRITE::TRANSLUCENTCOLOR;
  111.         extraLayers[1].spriteParam = 86;
  112.        
  113.         extraLayers[2].xOffset = 100;
  114.         extraLayers[2].yOffset = 360;
  115.         extraLayers[3].xOffset = 80;
  116.         extraLayers[3].yOffset = 120;
  117.         extraLayers[4].xOffset = 160;
  118.         extraLayers[4].yOffset = 140;
  119.         extraLayers[5].xOffset = 320;
  120.         extraLayers[5].yOffset = 160;
  121.        
  122.         extraLayers2[4].xOffset = 200;
  123.         extraLayers2[4].yOffset = -1700;
  124.         extraLayers2[5].xOffset = 300;
  125.         extraLayers2[5].yOffset = -1600;
  126.         extraLayers2[6].xOffset = 400;
  127.         extraLayers2[6].yOffset = -1500;
  128.        
  129.         for (uint16 i = 0; i < invisibleTiles.length(); i++) {
  130.                 jjTileType[invisibleTiles[i]] = 3; //invisible
  131.         }
  132.         jjTileType[1176] = 5;
  133.        
  134.         generateColoredRedeemerSprites(jjAnimSets[ANIM::CUSTOM[0]], array<uint> = {32, 24, 16, 40});
  135.         generateRedeemerPickupSprite(jjAnimSets[ANIM::CUSTOM[1]], array<uint> = {0});
  136. }
  137.  
  138. jjANIMSET@ coloredRedeemerSprite;
  139. bool generateColoredRedeemerSprites(jjANIMSET@ anim, const array<uint> &in colors) {
  140.         int length = colors.length();
  141.         bool success = (@coloredRedeemerSprite = anim).allocate(array<uint>(length * 4, 8)) !is null;
  142.         if (success) {
  143.                 uint srcSet = jjAnimSets[ANIM::SONCSHIP];
  144.                 for (int i = 0; i < length; i++) {
  145.                         uint color = colors[i];
  146.                         uint destAnimOffset = anim + i;
  147.                         for (int j = 0; j < 4; j++) {
  148.                                 uint srcAnim = jjAnimations[srcSet + 0];
  149.                                 uint destAnim = jjAnimations[destAnimOffset + j];
  150.                                 for (int k = 0; k < 8; k++) {
  151.                                         jjPIXELMAP image(jjAnimFrames[destAnim + k] = jjAnimFrames[srcAnim + k]);
  152.                                         int width = image.width;
  153.                                         int height = image.height;
  154.                                         for (int l = 0; l < height; l++) {
  155.                                                 for (int m = 0; m < width; m++) {
  156.                                                         int pixel = image[m, l];
  157.                                                         if (pixel >= 88 && pixel < 96)
  158.                                                                 image[m, l] = color + (pixel & 7);
  159.                                                 }
  160.                                         }
  161.                                         if (!image.save(jjAnimFrames[destAnim + k]))
  162.                                                 return false;
  163.                                 }
  164.                         }
  165.                 }
  166.         }
  167.         return success;
  168. }
  169.  
  170. jjANIMSET@ redeemerPickupSprite;
  171. bool generateRedeemerPickupSprite(jjANIMSET@ anim, const array<uint> &in colors) {
  172.         int length = colors.length();
  173.         bool success = (@redeemerPickupSprite = anim).allocate(array<uint>(length, 8)) !is null;
  174.         if (success) {
  175.                 uint srcSet = jjAnimSets[ANIM::SONCSHIP];
  176.                 for (int i = 0; i < length; i++) {
  177.                         uint color = colors[i];
  178.                         uint destAnimOffset = anim + i;
  179.                         uint srcAnim = jjAnimations[srcSet + 0];
  180.                         uint destAnim = jjAnimations[destAnimOffset + 0];
  181.                         for (int k = 0; k < 8; k++) {
  182.                                 jjPIXELMAP image(jjAnimFrames[destAnim + k] = jjAnimFrames[srcAnim + k]);
  183.                                 int width = image.width;
  184.                                 int height = image.height;
  185.                                 for (int l = 0; l < height; l++) {
  186.                                         for (int m = 0; m < width; m++) {
  187.                                                 int pixel = image[m, l];
  188.                                                 if (pixel >= 40 && pixel < 48)
  189.                                                         image[m, l] = color;
  190.                                         }
  191.                                 }
  192.                         if (!image.save(jjAnimFrames[destAnim + k]))
  193.                                 return false;
  194.                         }
  195.                 }
  196.         }
  197.         return success;
  198. }
  199.  
  200. class OscillatingMonitor : jjBEHAVIORINTERFACE {
  201.         void onBehave(jjOBJ@ obj) {
  202.                 obj.behave(BEHAVIOR::MONITOR);
  203.                 obj.xPos = obj.xOrg - 4;
  204.                 obj.yPos = (obj.yOrg + 2) - jjLayerYOffset[2];
  205.                 obj.direction = -1;
  206.         }
  207. }
  208.  
  209. class AmbientSound : jjBEHAVIORINTERFACE {
  210.         void onBehave(jjOBJ@ sound) {
  211.                 sound.behave(jjLowDetail && jjGameTicks >= 2? BEHAVIOR::INACTIVE : BEHAVIOR::AMBIENTSOUND, false);
  212.                 if (jjLocalPlayers[0].health > 0) delay++;
  213.                 else delay = 0;
  214.                
  215.                 if (jjTriggers[19] && isWindSound(sound) && delay >= 35) {
  216.                         loopSound(sound);
  217.                 }
  218.                 if (!jjTriggers[19] && isShipSound(sound) && delay >= 35) {
  219.                         loopSound(sound);
  220.                 }
  221.        
  222.                 if ((muteAmbientWind() && isWindSound(sound)) || jjLocalPlayers[0].health == 0) {
  223.                         jjTriggers[19] = false;
  224.                         sound.xPos = sound.xOrg;
  225.                         sound.yPos = sound.yOrg;
  226.                 }
  227.         }
  228. }
  229.  
  230. void loopSound(jjOBJ@ sound) {
  231.         sound.xPos = jjLocalPlayers[0].xPos;
  232.         sound.yPos = jjLocalPlayers[0].yPos;
  233. }
  234.  
  235. class Redeemer : jjBEHAVIORINTERFACE {
  236.         void onBehave(jjOBJ@ obj) {
  237.                 //var[0] - angle
  238.                 //var[1] - angular speed
  239.                 //var[2] - has the redeemer exploded
  240.                 //special - value of jjGameTicks at the time the last packet was sent/received 
  241.                
  242.                 int angle;
  243.                 float speed = jjObjectPresets[OBJECT::TNT].xSpeed;
  244.                
  245.                 if (obj.creatorType != CREATOR::PLAYER)
  246.                         obj.delete();
  247.                 jjPLAYER@ creator = jjPlayers[obj.creatorID];
  248.                
  249.                 switch (obj.state) {
  250.                         case STATE::START:
  251.                                 obj.curAnim = jjObjectPresets[obj.eventID].curAnim;
  252.                                 jjSamplePriority(SOUND::P2_POEP);
  253.                                 obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
  254.                                 obj.ySpeed = speed * -jjSin(obj.var[0]);
  255.                                 obj.xSpeed = speed * jjCos(obj.var[0]);
  256.                                 obj.direction = obj.xSpeed < 0.f ? -1 : 1;
  257.                                 obj.var[2] = 0;
  258.                                 if (creator.isLocal) controllingRedeemer = true;
  259.                                 obj.state = STATE::FLY;
  260.                         break;
  261.                                
  262.                         case STATE::FLY:
  263.                                 redeemerCamera(creator, obj);
  264.                                 jjDrawRotatedSprite(obj.xPos, obj.yPos, (creator.isLocal || jjGameMode != GAME::CTF)? ANIM::SONCSHIP : ANIM::CUSTOM[0], (creator.isLocal || jjGameMode != GAME::CTF)? 0 : redeemerColor(creator), obj.curFrame, obj.var[0], 2, 2, SPRITE::NORMAL);
  265.                                 if (creator.isLocal) {
  266.                                         redeemerAngle = obj.var[0];
  267.                                         if (!jjLowDetail && (obj.counter % 131 == 36 || obj.counter == 1) && controllingRedeemer) jjSamplePriority(SOUND::P2_SPLOUT);
  268.                                         float dx = creator.xPos - obj.xPos, dy = creator.yPos - obj.yPos;
  269.                                                 if (dx * dx + dy * dy < 420 * 420) warning = true;
  270.                                                 else warning = false;
  271.                                         if (creator.health == 0) {
  272.                                                 creator.cameraUnfreeze(true);
  273.                                                 controllingRedeemer = false;
  274.                                         } else {
  275.                                                 if (controllingRedeemer) creator.xSpeed = 0;
  276.                                                 if (obj.counter >= 21) {
  277.                                                         if (controllingRedeemer) {
  278.                                                                 if (creator.keyRight || (obj.direction < 0 ? creator.keyUp : creator.keyDown))
  279.                                                                         obj.var[1] = -16;
  280.                                                                 else if (creator.keyLeft || (obj.direction < 0 ? creator.keyDown : creator.keyUp))
  281.                                                                         obj.var[1] = 16;
  282.                                                                 else
  283.                                                                         obj.var[1] = 0;
  284.                                                         }
  285.                                                         else obj.var[1] = 0;
  286.                                                 }
  287.                                                 if (obj.counter >= 127)
  288.                                                         obj.counter = 35;
  289.                                                 else if (obj.counter >= 35 && creator.keyFire && controllingRedeemer)
  290.                                                         obj.state = STATE::EXPLODE;
  291.                                         }
  292.                                         xPos = int(obj.xPos);
  293.                                         yPos = int(obj.yPos);
  294.                                 }
  295.                                 if (obj.yPos >= jjLayerHeight[4]*32) obj.state = STATE::EXPLODE;
  296.                                 obj.var[0] = obj.var[0] + obj.var[1];
  297.                                 obj.ySpeed = speed * -jjSin(obj.var[0]);
  298.                                 obj.xSpeed = speed * jjCos(obj.var[0]);
  299.                                 if (obj.counter % 3 == 0 && !jjLowDetail) spawnFireTrail(obj);
  300.                         break;
  301.                        
  302.                         case STATE::EXPLODE:
  303.                                 if (obj.var[2] == 0) {
  304.                                         RedeemerExplosion temp;
  305.                                         jjOBJ@ explosion = jjObjects[jjAddObject(OBJECT::BULLET, obj.xPos, obj.yPos, obj.creatorID, CREATOR::PLAYER, jjVOIDFUNCOBJ(temp.onBehave))];
  306.                                         jjSamplePriority(SOUND::P2_PTOEI);
  307.                                         obj.var[2] = 1;
  308.                                         explosion.var[2] = 1;
  309.                                 } else {
  310.                                         jjDrawResizedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 5, obj.curFrame + 5, 8, 8, SPRITE::NORMAL, 0, 2, 2);
  311.                                 }
  312.                                 creator.cameraUnfreeze(true);
  313.                                 if (creator.isLocal) controllingRedeemer = false;
  314.                         break;
  315.                 }
  316.                
  317.                 int previousState = obj.state;
  318.                 obj.behave(BEHAVIOR::BULLET, false);
  319.                 if (!creator.isLocal) {
  320.                         if (obj.special + 128 > jjGameTicks)
  321.                                 obj.state = previousState;
  322.                 } else if (obj.special + 4 <= jjGameTicks) {
  323.                         jjSTREAM packet;
  324.                         packet.push(int8(obj.creatorID));
  325.                         packet.push(obj.state == STATE::EXPLODE);
  326.                         packet.push(obj.xPos);
  327.                         packet.push(obj.yPos);
  328.                         if (obj.state != STATE::EXPLODE) {
  329.                                 packet.push(int16(obj.var[0]));
  330.                                 packet.push(int16(obj.var[1]));
  331.                         }
  332.                         jjSendPacket(packet);
  333.                         obj.special = jjGameTicks;
  334.                 }
  335.         }
  336. }
  337.  
  338. void spawnFireTrail(jjOBJ@ obj) {
  339.         jjOBJ@ trail = jjObjects[jjAddObject(OBJECT::EXPLOSION, int(obj.xPos - jjCos(obj.var[0])), int(obj.yPos - jjSin(obj.var[0])))];
  340.         trail.determineCurAnim(ANIM::AMMO, 3);
  341.         trail.lightType = LIGHT::POINT;
  342.         trail.playerHandling = HANDLING::PARTICLE;
  343.         trail.bulletHandling = HANDLING::IGNOREBULLET;
  344.         trail.isBlastable = false;
  345. }
  346.  
  347. void redeemerCamera(jjPLAYER@ creator, jjOBJ@ obj) {
  348.         float dx = jjLayerWidth[4] - obj.xPos, dy = jjLayerHeight[4] - obj.yPos;
  349.        
  350.         if (jjLayerWidth[4] - dx <= 400) cameraX = 400;
  351.         else cameraX = int(obj.xPos);
  352.        
  353.         if (jjLayerHeight[4] - dy <= 400) cameraY = 400;
  354.         else cameraY = int(obj.yPos);
  355.        
  356.         if (controllingRedeemer) {
  357.                 creator.cameraFreeze(int(obj.xPos) >= 8528? 8528 : cameraX, int(obj.yPos) >= 4080? 4080 : cameraY, true, true);
  358.        
  359.                 for (int i = 0; i < 32; i++) {
  360.                         jjPLAYER@ play = jjPlayers[i];
  361.                         float reticleScale = jjSin(jjGameTicks*5);
  362.                         float reticleColor = jjSin(jjGameTicks*10)*2;
  363.                         float pdx = play.xPos - obj.xPos, pdy = play.yPos - obj.yPos;
  364.                         if ((pdx * pdx + pdy * pdy < 320 * 320) && (jjGameMode != GAME::CTF || jjFriendlyFire || play.team != creator.team) && !play.isLocal)
  365.                                 jjDrawResizedSprite(play.xPos, play.yPos, ANIM::SONCSHIP, 7, 0, reticleScale + 2, reticleScale + 2, SPRITE::SINGLECOLOR, int(reticleColor + 18), 1);
  366.                                                        
  367.                 }
  368.         }
  369. }
  370.  
  371. class RedeemerPickup : jjBEHAVIORINTERFACE {
  372.         void onBehave(jjOBJ@ obj) {
  373.                 obj.behave(BEHAVIOR::PICKUP, false);
  374.                 obj.direction = obj.xPos > 4464? -1:1;
  375.                 if (obj.isActive) jjDrawResizedSprite(int(obj.xPos) - (6 * obj.direction), int(obj.yPos) + jjSin(jjGameTicks*10)*6, ANIM::CUSTOM[1], 0, obj.curFrame, 2 * obj.direction, 2, ((obj.xPos < 4464 && jjLocalPlayers[0].team == TEAM::BLUE) || (obj.xPos > 4464 && jjLocalPlayers[0].team == TEAM::RED))? SPRITE::NORMAL : SPRITE::TRANSLUCENTSINGLEHUE, jjLocalPlayers[0].team == TEAM::BLUE? 24:32);
  376.         }
  377.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
  378.                 if (play.ammo[WEAPON::TNT] < jjWeapons[WEAPON::TNT].maximum) {
  379.                         if ((obj.xPos < 4464 && play.team == TEAM::BLUE) || (obj.xPos > 4464 && play.team == TEAM::RED) || otherMode()) {
  380.                                 if (play.isLocal) {
  381.                                         play.ammo[WEAPON::TNT] = play.ammo[WEAPON::TNT] + 1;
  382.                                         if (jjAutoWeaponChange) play.currWeapon = WEAPON::TNT;
  383.                                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_LOADSPAZ, 0, 0);
  384.                                 }
  385.                                 obj.behavior = BEHAVIOR::EXPLOSION2;
  386.                                 obj.scriptedCollisions = false;
  387.                                 obj.frameID = 0;
  388.                         }
  389.                 }
  390.                 return true;
  391.         }
  392. }
  393.  
  394. class RedeemerExplosion : jjBEHAVIORINTERFACE {
  395.         void onBehave(jjOBJ@ obj) {
  396.                 //var[0] - obj.counter
  397.                 //var[2] - has the redeemer exploded
  398.                 //var[4] - blast radius
  399.                 //var[5] - damage
  400.                 //var[8] - has the player been hit by the explosion
  401.        
  402.                 obj.playerHandling = HANDLING::PARTICLE;
  403.                 obj.bulletHandling = HANDLING::IGNOREBULLET;
  404.                
  405.                 obj.lightType = obj.var[2] == 1? LIGHT::RING2 : LIGHT::NONE;
  406.                
  407.                 jjPLAYER@ creator = jjPlayers[obj.creatorID];
  408.                 jjPLAYER@ play = jjLocalPlayers[0];
  409.                
  410.                 if (obj.var[2] == 1) {
  411.                         obj.var[0] = obj.var[0] + 1;
  412.                         obj.light += 2;
  413.                         obj.var[4] = obj.light * 5;
  414.                        
  415.                         if (obj.var[4] >= 460) obj.var[5] = 1;
  416.                         else if (obj.var[4] >= 360 && obj.var[4] < 460) obj.var[5] = 2;
  417.                         else if (obj.var[4] < 360) obj.var[5] = 3;
  418.                        
  419.                         for (int i = 1; i < jjObjectCount; i++) {
  420.                                 jjOBJ@ target = jjObjects[i];
  421.                                 float dx = target.xPos - obj.xPos, dy = target.yPos - obj.yPos;
  422.                                 if (isDestructibleItem(target) && target.var[4] == 0) {
  423.                                         if (dx * dx + dy * dy < obj.var[4] * obj.var[4]) {
  424.                                                 if (target.eventID != OBJECT::TNT) creator.objectHit(target, -1, HANDLING::SPECIAL);
  425.                                                 else target.state = STATE::EXPLODE;
  426.                                                 target.var[4] = 1;
  427.                                         }
  428.                                         else target.var[4] = 0;
  429.                                 }
  430.                                 else if (target.behavior == BEHAVIOR::PICKUP && target.state == STATE::FLOAT) {
  431.                                         if (dx * dx + dy * dy < obj.var[4] * obj.var[4])
  432.                                                 target.state = STATE::FLOATFALL;
  433.                                 }
  434.                         }
  435.                        
  436.                         float pdx = play.xPos - obj.xPos, pdy = play.yPos - obj.yPos;
  437.                         if (pdx * pdx + pdy * pdy < 1600 * 1600) {
  438.                                 uint random = jjRandom();
  439.                                 int magnitude = (2 << (random & 3)) - 1;
  440.                                 int halfMagnitude = magnitude >> 1;
  441.                                
  442.                                 if ((jjGameTicks & 1 == 0 && !jjTriggers[19]) || (jjGameTicks & 1 == 0 && jjTriggers[19] && jjMaskedHLine(int(play.xPos) - 12, 24, int(play.yPos) + 21)) && !controllingRedeemer)
  443.                                         play.cameraFreeze(play.cameraX + (random & magnitude) - halfMagnitude, play.cameraY + (random >> 8 & magnitude) - halfMagnitude, false, true);
  444.                                 else
  445.                                         play.cameraUnfreeze();
  446.                         }
  447.                 }
  448.                
  449.                 if (jjIsServer && gameIsActive()) {
  450.                         for (int i = 0; i < 32; i++) {
  451.                                 jjPLAYER@ player = jjPlayers[i];
  452.                                 if (
  453.                                         player.isActive && player.isInGame && player.health > 0 &&
  454.                                                 (jjGameMode != GAME::CTF || jjFriendlyFire || player.team != creator.team || player is creator)
  455.                                 ) {
  456.                                         float dx = player.xPos - obj.xPos, dy = player.yPos - obj.yPos;
  457.                                         if (dx * dx + dy * dy < obj.var[4] * obj.var[4]) {
  458.                                                 if (obj.var[8] & 1 << i == 0) {
  459.                                                         player.hurt(obj.var[5], true, creator);
  460.                                                         obj.var[8] = obj.var[8] | 1 << i;
  461.                                                 }
  462.                                         }
  463.                                 }
  464.                         }
  465.                 }
  466.                
  467.                 if (obj.var[0] == 1) {
  468.                         jjSample(obj.xPos, obj.yPos, SOUND::BILSBOSS_FIRE, 0, 0);
  469.                         for (int i = -8; i <= 8; i+=8) {
  470.                                 for (int j = -8; j <= 8; j+=8) {
  471.                                         if (i != 0 || j != 0) {
  472.                                                 Fireworks temp;
  473.                                                 int id = jjAddObject(OBJECT::ELECTROBULLET, obj.xPos, obj.yPos, obj.creatorID, CREATOR::PLAYER, jjVOIDFUNCOBJ(temp.onBehave));
  474.                                                 if (id != 0) {
  475.                                                         jjOBJ@ flares = jjObjects[id];
  476.                                                         flares.xSpeed = j*2;
  477.                                                         flares.ySpeed = i*2;
  478.                                                 }
  479.                                         }
  480.                                 }
  481.                         }
  482.                 }
  483.                 else if (obj.var[0] == 70) {
  484.                         obj.var[0] = 0;
  485.                         obj.var[2] = 0;
  486.                         obj.var[4] = 0;
  487.                         if (!controllingRedeemer) play.cameraUnfreeze();
  488.                         obj.delete();
  489.                 }
  490.         }
  491. }
  492.  
  493. class Fireworks : jjBEHAVIORINTERFACE {
  494.         void onBehave(jjOBJ@ obj) {
  495.                 obj.behave(BEHAVIOR::ELECTROBULLET, false);
  496.                 obj.counterEnd = 210;
  497.                 if (obj.ySpeed < 10 && obj.xSpeed != 0) obj.ySpeed += 0.5;
  498.                 obj.playerHandling = HANDLING::PARTICLE;
  499.                 obj.bulletHandling = HANDLING::IGNOREBULLET;
  500.                 if (obj.state == STATE::FLY) obj.particlePixelExplosion(1);
  501.         }
  502. }
  503.  
  504. void onReceive(jjSTREAM &in packet, int clientID) {
  505.         int8 playerID;
  506.         bool explosion;
  507.         float xPos, yPos;
  508.         int16 angle, angleSpeed;
  509.         jjSTREAM packetBackup;
  510.         if (jjIsServer)
  511.                 packetBackup = packet;
  512.         if (packet.pop(playerID) && playerID >= 0 && playerID < 32 &&
  513.                 packet.pop(explosion) && packet.pop(xPos) && packet.pop(yPos) &&
  514.                 (explosion || packet.pop(angle) && packet.pop(angleSpeed))
  515.         ) {
  516.                 const jjPLAYER@ player = jjPlayers[playerID];
  517.                 if (!jjIsServer || player.isActive && player.isInGame && player.clientID == clientID ) {
  518.                         jjOBJ@ redeemer;
  519.                         for (int i = 0; i < jjObjectCount; i++) {
  520.                                 jjOBJ@ obj = jjObjects[i];
  521.                                 if (obj.isActive && obj.eventID == OBJECT::TNT && obj.creatorType == CREATOR::PLAYER && obj.creatorID == uint(playerID)) {
  522.                                         @redeemer = obj;
  523.                                         break;
  524.                                 }
  525.                         }
  526.                         if (redeemer is null && jjGameTicks < 140) {
  527.                                 int id = jjAddObject(OBJECT::TNT, xPos, yPos, playerID, CREATOR::PLAYER);
  528.                                 if (id > 0)
  529.                                         @redeemer = jjObjects[id];
  530.                         }
  531.                         if (redeemer !is null) {
  532.                                 if (jjIsServer)
  533.                                         jjSendPacket(packetBackup, -clientID);
  534.                                 if (explosion)
  535.                                         redeemer.state = STATE::EXPLODE;
  536.                                 redeemer.xPos = xPos;
  537.                                 redeemer.yPos = yPos;
  538.                                 redeemer.var[0] = angle;
  539.                                 redeemer.var[1] = angleSpeed;
  540.                                 redeemer.special = jjGameTicks;
  541.                         }
  542.                 }
  543.         }
  544. }
  545.  
  546. void onPlayer(jjPLAYER@ play) {
  547.         if (play.shieldTime > 15*70) play.shieldTime = 15*70;
  548.        
  549.         play.lightType = LIGHT::NONE;
  550.         jjEnforceLighting = LIGHT::COMPLETE;
  551.        
  552.         if (controllingRedeemer) play.currWeapon = WEAPON::BLASTER;
  553.        
  554.         if (play.fly == FLIGHT::AIRBOARD && play.timerState == TIMER::STOPPED) play.timerStart(20*70);
  555.         if (play.fly == FLIGHT::NONE) play.timerStop();
  556.         if (play.timerState == TIMER::STARTED && play.timerTime <= 3*70 && play.timerTime > 0 && play.timerTime % 70 == 0) jjSamplePriority(SOUND::COMMON_NOCOIN);
  557. }
  558.  
  559. void onPlayerTimerEnd(jjPLAYER@ play) { play.fly = FLIGHT::NONE; }
  560.  
  561. void onMain() {
  562.         if (!jjLowDetail) {
  563.                 jjLayerYOffset[1] = jjCos(jjGameTicks*8)*4;
  564.                 jjLayerYOffset[2] = jjCos(jjGameTicks*8)*4;
  565.         } else {
  566.                 jjLayerYOffset[1] =
  567.                 jjLayerYOffset[2] = 0;
  568.         }
  569.        
  570.         array<jjLAYER@> layers = jjLayerOrderGet();
  571.         layers[0].hasTiles = layers[1].hasTiles = controllingRedeemer;
  572.         layers[8].xOffset = jjSin(jjGameTicks*48)*8;
  573.         layers[9].xOffset = -jjSin(jjGameTicks*48)*8;
  574.         layers[10].yOffset = jjCos(jjGameTicks*48)*8;
  575.         layers[11].yOffset = -jjCos(jjGameTicks*48)*8;
  576.        
  577.         if (controllingRedeemer && CTFMode()) {
  578.                 if (CTFArrowTimer > jjGameTicks + 280) CTFArrowTimer = jjGameTicks;
  579.                 if (CTFArrowTimer < jjGameTicks) {
  580.                         if (CTFArrowTimer + 64 >= jjGameTicks) {
  581.                                 for (int i = 0; i < 32; i++) {
  582.                                         jjPLAYER@ target = jjPlayers[i];
  583.                                         if (target.flag != 0 && target.team != jjLocalPlayers[0].team) {
  584.                                                 int angle = int(atan2(target.yPos - yPos, target.xPos - xPos) * (512 / PI));
  585.                                                 const float scale = 64.f / (112.f - jjSin((jjGameTicks - CTFArrowTimer) << 3) * 64.f);
  586.                                                 jjDrawRotatedSprite(xPos + 32 * jjCos(angle), yPos + 32 * jjSin(angle), ANIM::FLAG, 0, 0, 970 - angle, scale, scale, SPRITE::PALSHIFT, getTeamColor(target.team) - 32, 1);
  587.                                         }
  588.                                 }
  589.                         } else {
  590.                                 CTFArrowTimer = jjGameTicks + 210;
  591.                         }
  592.                 }
  593.         }
  594. }
  595.  
  596. bool onDrawPlayerTimer(jjPLAYER@ play, jjCANVAS@ canvas) {
  597.         if (play.fly == FLIGHT::AIRBOARD) {
  598.         canvas.drawString(
  599.                         jjSubscreenWidth - 75,
  600.                 jjSubscreenHeight - 284,
  601.                 "" + (play.timerTime > 0? (play.timerTime + 70) / 70 : 0),
  602.                 STRING::LARGE,
  603.                 STRING::PALSHIFT,
  604.                 play.timerTime > 3*70?
  605.                 24 :
  606.                 jjGameTicks % 28 > 14?
  607.                 -40 :
  608.                 -24
  609.                 );
  610.                
  611.                 canvas.drawSprite(
  612.                         jjSubscreenWidth - 92,
  613.                         jjSubscreenHeight - 280,
  614.                         ANIM::PICKUPS,
  615.                         36,
  616.                         jjGameTicks / 6 % 8
  617.                 );
  618.         }
  619.         return true;
  620. }
  621.  
  622. bool onDrawAmmo(jjPLAYER@ play, jjCANVAS@ canvas) {
  623.         if (controllingRedeemer) {
  624.                 canvas.drawString(
  625.                         jjSubscreenWidth - 480,
  626.                         jjSubscreenHeight - 450,
  627.                         "Redeemer!",
  628.                         STRING::MEDIUM,
  629.                         STRING::NORMAL
  630.                 );
  631.                 canvas.drawString(
  632.                         jjSubscreenWidth - 496,
  633.                         jjSubscreenHeight - 422,
  634.                         "Redeemer pos " + int(xPos / 32) + "," + int(yPos / 32),
  635.                         STRING::SMALL,
  636.                         STRING::NORMAL
  637.                 );
  638.                 canvas.drawString(
  639.                         jjSubscreenWidth - 624,
  640.                         jjSubscreenHeight - 406,
  641.                         "|Use the |||movement keys |||||to rotate the missile",
  642.                         STRING::SMALL,
  643.                         STRING::NORMAL
  644.                 );
  645.                 canvas.drawString(
  646.                         jjSubscreenWidth - 625,
  647.                         jjSubscreenHeight - 390,
  648.                         "|Press |||FIRE |||||to detonate the missile in mid-air",
  649.                         STRING::SMALL,
  650.                         STRING::NORMAL
  651.                 );
  652.                 canvas.drawRectangle(
  653.                         jjSubscreenWidth - 750,
  654.                         jjSubscreenHeight - 240,
  655.                         140,
  656.                         140,
  657.                         87,
  658.                         SPRITE::TRANSLUCENT
  659.                 );
  660.                 canvas.drawString(
  661.                         jjSubscreenWidth - 708,
  662.                         jjSubscreenHeight - 108,
  663.                         "Radar",
  664.                         STRING::SMALL,
  665.                         STRING::PALSHIFT,
  666.                         16
  667.                 );
  668.                 canvas.drawRotatedSprite(
  669.                         jjSubscreenWidth - 680,
  670.                         jjSubscreenHeight - 170,
  671.                         ANIM::CUSTOM[1],
  672.                         0,
  673.                         jjGameTicks >> 2,
  674.                         redeemerAngle,
  675.                         1,
  676.                         1,
  677.                         SPRITE::SINGLEHUE,
  678.                         80
  679.                 );
  680.                
  681.                 for (int i = 0; i < 32; i++) {
  682.                         jjPLAYER@ player = jjPlayers[i];
  683.                        
  684.                         uint8 teamColor;
  685.                         switch (player.team) {
  686.                                 case TEAM::BLUE: teamColor = 34; break;
  687.                                 case TEAM::RED: teamColor = 24; break;
  688.                                 case TEAM::GREEN: teamColor = 18; break;
  689.                                 case TEAM::YELLOW: teamColor = 40; break;
  690.                                 default: teamColor = 24; break;
  691.                         }
  692.                        
  693.                         int radarOffsetX = int(xPos - player.xPos) / 35;
  694.                         int radarOffsetY = int(yPos - player.yPos) / 35;
  695.                        
  696.                         if (radarOffsetX < 70 && radarOffsetX > -63 && radarOffsetY < 70 && radarOffsetY > -63 && player.xPos > 0 && player.yPos > 0 && !play.isSpectating && !play.isOut && !play.isConnecting) {
  697.                                 if (player.flag != 0 && !player.isLocal) {
  698.                                         canvas.drawResizedSprite(
  699.                                                 ((jjSubscreenWidth - 678) - radarOffsetX),
  700.                                                 ((jjSubscreenHeight - 168) - radarOffsetY),
  701.                                                 ANIM::FLAG,
  702.                                                 3,
  703.                                                 jjGameTicks >> 2,
  704.                                                 radarOffsetX > 0? 0.4:-0.4,
  705.                                                 0.4,
  706.                                                 SPRITE::SINGLECOLOR,
  707.                                                 player.team == TEAM::BLUE? 24 : 34
  708.                                         );
  709.                                         canvas.drawRectangle(
  710.                                                 ((jjSubscreenWidth - 680) - radarOffsetX),
  711.                                                 ((jjSubscreenHeight - 170) - radarOffsetY),
  712.                                                 6,
  713.                                                 6,
  714.                                                 teamColor,
  715.                                                 SPRITE::NORMAL
  716.                                         );
  717.                                 }
  718.                                 else if (player.flag == 0) {
  719.                                         canvas.drawRectangle(
  720.                                                 ((jjSubscreenWidth - 680) - radarOffsetX),
  721.                                                 ((jjSubscreenHeight - 170) - radarOffsetY),
  722.                                                 player.isLocal? 8:6,
  723.                                                 player.isLocal? 8:6,
  724.                                                 player.isLocal? 64 : jjGameMode == GAME::CTF? teamColor : 24,
  725.                                                 SPRITE::NORMAL
  726.                                         );
  727.                                 }
  728.                         }
  729.                        
  730.                         for (int j = 1; j < jjObjectCount; j++) {
  731.                                 if (jjObjects[j].eventID == OBJECT::TNT) {
  732.                                        
  733.                                         uint8 redeemerColor;
  734.                                         switch (jjPlayers[jjObjects[j].creatorID].team) {
  735.                                                 case TEAM::BLUE: redeemerColor = 34; break;
  736.                                                 case TEAM::RED: redeemerColor = 24; break;
  737.                                                 case TEAM::GREEN: redeemerColor = 18; break;
  738.                                                 case TEAM::YELLOW: redeemerColor = 40; break;
  739.                                                 default: redeemerColor = 88; break;
  740.                                         }
  741.                                        
  742.                                         int radarMissileOffsetX = int(xPos - jjObjects[j].xPos) / 35;
  743.                                         int radarMissileOffsetY = int(yPos - jjObjects[j].yPos) / 35;
  744.                                        
  745.                                         if (!jjPlayers[jjObjects[j].creatorID].isLocal && radarMissileOffsetX < 70 && radarMissileOffsetX > -63 && radarMissileOffsetY < 70 && radarMissileOffsetY > -63) {
  746.                                                 canvas.drawRotatedSprite(
  747.                                                         ((jjSubscreenWidth - 680) - radarMissileOffsetX),
  748.                                                         ((jjSubscreenHeight - 170) - radarMissileOffsetY),
  749.                                                         ANIM::CUSTOM[1],
  750.                                                         0,
  751.                                                         jjGameTicks >> 2,
  752.                                                         jjObjects[j].var[0],
  753.                                                         0.5,
  754.                                                         0.5,
  755.                                                         SPRITE::SINGLECOLOR,
  756.                                                         jjGameMode == GAME::CTF? redeemerColor : 90
  757.                                                 );
  758.                                         }
  759.                                 }
  760.                         }
  761.                 }
  762.                
  763.                 if (warning) {
  764.                         canvas.drawString(
  765.                                 jjSubscreenWidth - 550,
  766.                                 jjSubscreenHeight - 374,
  767.                                 "||WARNING: YOU ARE IN RANGE!",
  768.                                 STRING::SMALL,
  769.                                 STRING::NORMAL
  770.                         );
  771.                 }
  772.                
  773.                 if (!jjLowDetail && jjGameTicks % 140 >= 7) {
  774.                         canvas.drawString(
  775.                                 jjSubscreenWidth - 132,
  776.                                 jjSubscreenHeight - 46,
  777.                                 "REC",
  778.                                 STRING::MEDIUM,
  779.                                 STRING::NORMAL
  780.                         );
  781.                         canvas.drawSprite(
  782.                                 jjSubscreenWidth - 146,
  783.                                 jjSubscreenHeight - 45,
  784.                                 ANIM::VINE,
  785.                                 0,
  786.                                 0,
  787.                                 0,
  788.                                 SPRITE::NORMAL
  789.                         );
  790.                 }
  791.         }
  792.         else if (!controllingRedeemer && play.currWeapon == WEAPON::TNT) {
  793.                 canvas.drawString(
  794.                         jjSubscreenWidth - 80,
  795.                         jjSubscreenHeight - 14,
  796.                         "x" + play.ammo[WEAPON::TNT],
  797.                         STRING::MEDIUM,
  798.                         STRING::NORMAL
  799.                 );
  800.                 canvas.drawResizedSprite(
  801.                         jjSubscreenWidth - 110,
  802.                         jjSubscreenHeight - 14,
  803.                         ANIM::CUSTOM[1],
  804.                         0,
  805.                         jjGameTicks >> 2,
  806.                         2,
  807.                         2,
  808.                         SPRITE::NORMAL
  809.                 );
  810.         }
  811.        
  812.         return controllingRedeemer || play.currWeapon == WEAPON::TNT;
  813. }
  814.  
  815. void onFunction0(jjPLAYER@ play) {
  816.         play.showText("@@@@Skyward Showdown@Created by PurpleJazz of XLM (2009-2017)@Tileset by BlurredD@np: Kevin Riepl - Skyscraper [2:01]");
  817. }