Downloads containing Jazz-otJ.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Jazz of the JungleFeatured Download Violet CLM Single player 8.9 Download file

File preview

  1. const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
  2. #include "MLLE-Include-1.5.asc" ///@MLLE-Generated
  3. #pragma require "Aztec2.j2t" ///@MLLE-Generated
  4. #pragma require "Amazont.j2t" ///@MLLE-Generated
  5. #pragma require "Jazz-otJ.j2as" ///@MLLE-Generated
  6.  
  7. #include "Jazz1Enemies v05.asc"
  8. #pragma require "FishFace.j2a"
  9. #pragma require "JillYEAH.wav"
  10. #include "ArtificialArrow.asc"
  11.  
  12. class PlayerX {
  13.         PlayerX(){}
  14.         bool Fish = false;
  15.         bool Immobile = false;
  16.         int NoJumping = 0;
  17.         float WaterLevel = jjLayerHeight[4] * 32;
  18.         int Apples = 2;
  19. }
  20. array<PlayerX> PlayersX(jjLocalPlayerCount);
  21.  
  22. const int KeyCurFrame = jjObjectPresets[147].curFrame;
  23. void onLevelLoad() {
  24.         ///@Event 142=Bouncing Ball                          |-|Enemy      |Bounce|Ball
  25.         Jazz1::MakeEnemy(142, Jazz1::Enemies::Battleships_BounceSpike).SetUsesJJ2StyleDeathAnimation(true).SetPointsFormula(Jazz1::PointsFormula::MultiplyByFive).SetDeathSound(SOUND::COMMON_METALHIT);
  26.         ///@Event 143=Flying Devil                        |-|Enemy      |Devil|Fly
  27.         Recolor(Jazz1::MakeEnemy(143, Jazz1::Enemies::Holidaius_Devil).SetUsesJJ2StyleDeathAnimation(true).SetPointsFormula(Jazz1::PointsFormula::MultiplyByFive).SetDeathSound(SOUND::BUBBA_BUBBAEXPLO).GetFirstFrame(), 2, array<uint8> = {40,24, 56,24});
  28.         ///@Event 144=Snake                        |-|Enemy      |Snake
  29.         Recolor(Jazz1::MakeEnemy(144, Jazz1::Enemies::Jungrock_JetSnake).SetUsesJJ2StyleDeathAnimation(true).SetPointsFormula(Jazz1::PointsFormula::MultiplyByFive).GetFirstFrame(), 3, array<uint8> = {56,79});
  30.         ///@Event 145=Slug                        |-|Enemy      |Slug
  31.         Recolor(Jazz1::MakeEnemy(145, Jazz1::Enemies::Sluggion_Sluggi, false, 2, Resize::Method::Scale2x).SetUsesJJ2StyleDeathAnimation(true).SetPointsFormula(Jazz1::PointsFormula::MultiplyByFive).SetWalkingEnemyCliffReaction(Jazz1::CliffReaction::TurnAround).SetSpeed(0.4).SetDeathSound(SOUND::HATTER_SPLIN).GetFirstFrame(), 3, array<uint8> = {16,72, 56,72, 40,75, 24,32});
  32.        
  33.         DeactivateEnemyWrappers(array<uint8>={142,143,144,145});
  34.        
  35.         //if (jjAnimSets[ANIM::PLUS_SCENERY] == 0)
  36.         //      jjAnimSets[ANIM::PLUS_SCENERY].load();
  37.         ///@Event 237=Beehive                        |-|Enemy      |Hive
  38.         jjObjectPresets[OBJECT::BEEBOY].behavior = OhBeehive;
  39.        
  40.         ///@Event 253=Pacman Ghost                        |-|Enemy      |Pacman|Ghost|Bird:c1
  41.         jjObjectPresets[OBJECT::PACMANGHOST].behavior = PacmanGhost();
  42.         jjObjectPresets[OBJECT::PACMANGHOST].scriptedCollisions = true;
  43.         jjObjectPresets[OBJECT::PACMANGHOST].playerHandling = HANDLING::SPECIAL;
  44.         jjObjectPresets[OBJECT::PACMANGHOST].bulletHandling = HANDLING::DETECTBULLET;
  45.        
  46.         jjObjectPresets[OBJECT::BLASTERBULLET].determineCurAnim(ANIM::AMMO, 49);
  47.         jjObjectPresets[OBJECT::BLASTERBULLET].special = 0;
  48.         jjObjectPresets[OBJECT::BLASTERBULLET].behavior = WeaponOne;
  49.         jjWeapons[WEAPON::BLASTER].defaultSample = false;
  50.         jjWeapons[WEAPON::BLASTER].replenishes = false;
  51.         jjObjectPresets[OBJECT::BOUNCERBULLET].behavior = SpikeBall;
  52.         jjObjectPresets[OBJECT::BOUNCERBULLET].determineCurAnim(ANIM::ROBOT, 0);
  53.         jjObjectPresets[OBJECT::BOUNCERBULLET].special = 0;
  54.         jjObjectPresets[OBJECT::BOUNCERBULLET].determineCurFrame();
  55.         jjObjectPresets[OBJECT::BOUNCERBULLET].var[0] = 26;
  56.         jjObjectPresets[OBJECT::BOUNCERBULLET].counterEnd /= 2;
  57.         jjObjectPresets[OBJECT::BOUNCERBULLET].animSpeed = 3;
  58.         jjWeapons[WEAPON::BOUNCER].defaultSample = false;
  59.         jjWeapons[WEAPON::BLASTER].style = WEAPON::CAPPED;
  60.         jjWeapons[WEAPON::BOUNCER].style = WEAPON::CAPPED;
  61.        
  62.         Recolor(jjAnimations[jjAnimSets[ANIM::AMMO] + 66], 9, array<uint8>={64, 40});
  63.         Recolor(jjAnimations[jjAnimSets[ANIM::AMMO] + 49], 10, array<uint8>={16,24, 24,88});
  64.        
  65.         if (jjAnimSets[ANIM::FISH] == 0)
  66.                 jjAnimSets[ANIM::FISH].load();
  67.         jjAnimSets[ANIM::CUSTOM[0]].load(0, "FishFace.j2a");
  68.         for (uint i = 0; i < 2; ++i) {
  69.                 jjANIMATION@ fishAnim = jjAnimations[jjAnimSets[ANIM::DOG] + i] = jjAnimations[jjAnimSets[ANIM::FISH] + i];
  70.                 for (uint j = 0; j < fishAnim.frameCount; ++j) {
  71.                         jjANIMFRAME@ fishFrame = jjAnimFrames[fishAnim + j];
  72.                         fishFrame.coldSpotY = -fishFrame.height;
  73.                 }
  74.         }
  75.         jjObjectPresets[OBJECT::DOGGYDOGG].determineCurFrame();
  76.         jjObjectPresets[OBJECT::DOGGYDOGG].behavior = function(o) { if (o.state == STATE::KILL) o.deactivate(); o.behave(BEHAVIOR::DOGGYDOGG); };
  77.        
  78.         ///@Event 146=Morph Head                        |-|Morph      |Morph|Head|Character:{Rabbit,Frog,Bird,Fish}2|notcheckpoint:c1
  79.         jjObjectPresets[146].behavior = MorphHead();
  80.         jjObjectPresets[146].playerHandling = HANDLING::SPECIAL;
  81.         jjObjectPresets[146].scriptedCollisions = true;
  82.        
  83.         for (int i = 0; i < 3; ++i) {
  84.                 jjPIXELMAP key(611 - i * 90);
  85.                 jjANIMFRAME@ frame = jjAnimFrames[KeyCurFrame - i];
  86.                 key.save(frame);
  87.                 frame.hotSpotX = frame.hotSpotY = -16;
  88.         }
  89.         ///@Event 147=Key|_|Trigger      |Key||Type:{Key,Gem}1
  90.         jjObjectPresets[147].behavior = Key();
  91.         jjObjectPresets[147].scriptedCollisions = true;
  92.        
  93.         if (jjAnimSets[ANIM::DESTSCEN] == 0)
  94.                 jjAnimSets[ANIM::DESTSCEN].load();
  95.         ///@Event 154=Lock|_|Trigger      |Lock||Type:{Key,Gem,Right,Left}2|Height-1:2
  96.         jjObjectPresets[154].behavior = Lock();
  97.         jjObjectPresets[154].scriptedCollisions = true;
  98.        
  99.         jjAnimSets[ANIM::FENCER].allocate(array<uint>={0,0});;
  100.         jjAnimations[jjAnimSets[ANIM::FENCER] + 0] = jjAnimations[jjAnimSets[ANIM::FROG] + 0];
  101.         jjAnimations[jjAnimSets[ANIM::FENCER] + 1] = jjAnimations[jjAnimSets[ANIM::FROG] + 2];
  102.         ///@Event 155=Frog|-|Enemy      |Frog
  103.         jjObjectPresets[155].behavior = BEHAVIOR::FENCER;
  104.         jjObjectPresets[155].energy = 1;
  105.         jjObjectPresets[155].playerHandling = HANDLING::ENEMY;
  106.         jjObjectPresets[155].bulletHandling = HANDLING::HURTBYBULLET;
  107.         jjObjectPresets[155].points = jjObjectPresets[OBJECT::FENCER].points;
  108.         jjObjectPresets[155].determineCurAnim(ANIM::FENCER, 1);
  109.         jjObjectPresets[155].determineCurFrame();
  110.         jjObjectPresets[155].behavior = FrogFencer;
  111.        
  112.         jjObjectPresets[OBJECT::APPLE].behavior = Apple();
  113.         jjObjectPresets[OBJECT::APPLE].scriptedCollisions = true;
  114.        
  115.         jjObjectPresets[OBJECT::CARROT].behavior =
  116.         jjObjectPresets[OBJECT::FASTFIRE].behavior =
  117.         jjObjectPresets[OBJECT::REDGEM].behavior =
  118.         jjObjectPresets[OBJECT::GREENGEM].behavior =
  119.         jjObjectPresets[OBJECT::BLUEGEM].behavior =
  120.         BEHAVIOR::INACTIVE;
  121.        
  122.         jjCharacters[CHAR::JAZZ].groundJump = GROUND::CROUCH;
  123.         jjCharacters[CHAR::SPAZ].groundJump = GROUND::CROUCH;
  124.         if (jjIsTSF)
  125.                 jjCharacters[CHAR::LORI].groundJump = GROUND::CROUCH;
  126.         jjCharacters[CHAR::BIRD2].canRun = true;
  127.         jjCharacters[CHAR::SPAZ].airJump = AIR::HELICOPTER;
  128.        
  129.         GenerateWaterLayer();
  130.         jjUseLayer8Speeds = true;
  131.         jjTexturedBGStyle = TEXTURE::WARPHORIZON;
  132.         jjTexturedBGUsed = true;
  133.         jjTexturedBGTexture = TEXTURE::RANEFORUSV;
  134.         jjSetFadeColors(166);
  135.        
  136.         {
  137.                 jjPIXELMAP vine(0,0,32,136);
  138.                 for (uint x = 0; x < 32; ++x)
  139.                         for (uint y = 0; y < 136; ++y)
  140.                                 if (vine[x,y] == 0)
  141.                                         vine[x,y] = 128;
  142.                 jjANIMFRAME@ vineFrame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::VINE]] + 1];
  143.                 vine.save(vineFrame);
  144.                 vineFrame.hotSpotX = -16;
  145.         }
  146.        
  147.         ///@Event 34=Ammo                        |-|Ammo      |Ammo||Spike:c1
  148.         jjObjectPresets[OBJECT::BOUNCERAMMO3].behavior = Ammo();
  149.         jjObjectPresets[OBJECT::BOUNCERAMMO3].scriptedCollisions = true;
  150.        
  151.         jjSampleLoad(SOUND::COMMON_COIN, "JillYEAH.wav");
  152.        
  153.         onLevelReload();
  154.        
  155.         for (int x = jjLayerWidth[3]; --x >= 0;)
  156.                 for (int y = jjLayerHeight[3]; --y >= 0;) {
  157.                         const uint8 ev = jjEventGet(x,y);
  158.                         if (ev == AREA::WARPTARGET) {
  159.                                 const uint targetID = jjParameterGet(x,y,0,8);
  160.                                 if (targetID >= Targets.length)
  161.                                         Targets.resize(targetID + 1);
  162.                                 Targets[targetID] = array<int> = {x * 32 + 16, y * 32 + 16};
  163.                                 //jjEventSet(x,y, 0);
  164.                         }
  165.         }
  166.        
  167.         if (jjDifficulty > 0)
  168.                 jjHelpStrings[5] = jjHelpStrings[5] + "@@You can hold TAB at any time@for a hint to your next goal.";
  169. }
  170. //enemies:
  171. ///@Event 100=
  172. ///@Event 102=
  173. ///@Event 103=
  174. ///@Event 104=
  175. ///@Event 105=
  176. ///@Event 106=
  177. ///@Event 107=
  178. ///@Event 108=
  179. ///@Event 109=
  180. ///@Event 110=
  181. ///@Event 113=
  182. ///@Event 115=
  183. ///@Event 117=
  184. ///@Event 118=
  185. ///@Event 120=
  186. ///@Event 123=
  187. ///@Event 124=
  188. ///@Event 125=
  189. ///@Event 126=
  190. ///@Event 127=
  191. ///@Event 152=
  192. ///@Event 183=
  193. ///@Event 184=
  194. ///@Event 190=
  195. ///@Event 191=
  196. ///@Event 197=
  197. ///@Event 225=
  198. ///@Event 236=
  199. ///@Event 237=
  200. ///@Event 248=
  201. ///@Event 249=
  202. ///@Event 250=
  203. ///@Event 252=
  204.  
  205. void Recolor(uint frameID, uint frameCount, array<uint8> gradients) {
  206.         while (frameCount-- != 0) {
  207.                 jjANIMFRAME@ frame = jjAnimFrames[frameID++];
  208.                 jjPIXELMAP image(frame);
  209.                
  210.                 for (uint x = 0; x < frame.width; ++x)
  211.                         for (uint y = 0; y < frame.height; ++y)
  212.                                 for (uint g = 0; g < gradients.length; g += 2)
  213.                                         if (image[x,y] & ~7 == gradients[g]) {
  214.                                                 const uint8 replacement = gradients[g+1];
  215.                                                 //if (replacement < 112)
  216.                                                         image[x,y] = (image[x,y] & 7) + replacement;
  217.                                                 //else
  218.                                                 //      image[x,y] = ((image[x,y] & 7) << 1) + replacement;
  219.                                                 break;
  220.                                         }
  221.                
  222.                 image.save(frame);
  223.         }
  224. }
  225.  
  226. class DeactivateEnemyWrapper : jjBEHAVIORINTERFACE {
  227.         Jazz1::Enemy@ enemy;
  228.         DeactivateEnemyWrapper(Jazz1::Enemy@ e) { @enemy = @e; }
  229.         void onBehave(jjOBJ@ obj) {
  230.                 enemy.onBehave(obj);
  231.                 if (obj.state == STATE::KILL && obj.creatorType == CREATOR::LEVEL) {
  232.                         //jjEventSet(int(obj.xOrg)/32, int(obj.yOrg)/32, obj.eventID); //shouldn't be needed
  233.                         jjParameterSet(int(obj.xOrg)/32, int(obj.yOrg)/32, -1, 1, 0); //deactivate
  234.                 }
  235.         }
  236.         void onDraw(jjOBJ@ obj) { enemy.onDraw(obj); }
  237.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) { return enemy.onObjectHit(obj, bullet, player, force); }
  238. }
  239. void DeactivateEnemyWrappers(array<uint8> objectIDs) {
  240.         for (uint i = 0; i < objectIDs.length; ++i)
  241.                 jjObjectPresets[objectIDs[i]].behavior = DeactivateEnemyWrapper(cast<Jazz1::Enemy@>(cast<jjBEHAVIORINTERFACE>(jjObjectPresets[objectIDs[i]].behavior)));
  242. }
  243.  
  244. void OhBeehive(jjOBJ@ obj) {
  245.         if (obj.creatorType == CREATOR::LEVEL) {
  246.                 if (obj.state == STATE::START) {
  247.                         obj.determineCurAnim(ANIM::PLUS_SCENERY, 3);
  248.                         obj.state = STATE::STILL;
  249.                         obj.counterEnd = 0;
  250.                         obj.energy = 5;
  251.                 } else {
  252.                         if (obj.state == STATE::KILL)
  253.                                 jjParameterSet(int(obj.xOrg)/32, int(obj.yOrg)/32, -1, 1, 0); //deactivate
  254.                         obj.behave(BEHAVIOR::FISH);
  255.                         if (obj.counterEnd != 0) {
  256.                                 if (obj.counterEnd == 255) {
  257.                                         jjOBJ@ lastBee = jjObjects[obj.special];
  258.                                         if (lastBee.behavior != OhBeehive || lastBee.creatorID != uint(obj.objectID) || lastBee.state != STATE::IDLE) {
  259.                                                 obj.counterEnd = 0; //will be changed to 1 momentarily
  260.                                                 obj.special = jjAddObject(obj.eventID, obj.xPos, obj.yPos, obj.objectID, CREATOR::OBJECT);
  261.                                         } else { //already got one buzzing around
  262.                                                 obj.counterEnd = 220; //check again soon
  263.                                         }
  264.                                 }
  265.                                 ++obj.counterEnd;
  266.                         } else if (obj.findNearestPlayer(200*200) >= 0)
  267.                                 obj.counterEnd = 192;
  268.                 }
  269.         } else {
  270.                 if (obj.state == STATE::ATTACK) {
  271.                         jjDifficulty -= 1;
  272.                         obj.counter = 70; //never stop
  273.                         obj.behave(BEHAVIOR::BEE);
  274.                         jjDifficulty += 1;
  275.                 } else
  276.                         obj.behave(BEHAVIOR::BEE);
  277.         }
  278. }
  279.  
  280. bool everyOtherBirdShot = false;
  281. void WeaponOne(jjOBJ@ obj) {
  282.         const jjPLAYER@ player = jjPlayers[obj.creatorID];
  283.         if (player.charCurr == CHAR::BIRD2) {
  284.                 everyOtherBirdShot = !everyOtherBirdShot;
  285.                 if (everyOtherBirdShot) {
  286.                         obj.delete();
  287.                         return;
  288.                 }
  289.                 obj.special = obj.determineCurAnim(ANIM::AMMO, 66);
  290.                 obj.behavior = BirdShot;
  291.                 obj.animSpeed = 2;
  292.                 obj.var[3] = 5; //not blaster
  293.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_FLAP);
  294.         } else if (PlayersX[player.localPlayerID].Fish) {
  295.                 obj.determineCurAnim(ANIM::AMMO, 30);
  296.                 obj.behavior = BEHAVIOR::BULLET;
  297.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_SPLUT);
  298.         } else {
  299.                 obj.var[6] = 16; //fireball
  300.                 obj.state = STATE::FLY;
  301.                 obj.determineCurFrame();
  302.                 obj.behavior = Knife;
  303.         }
  304.         obj.behave();
  305. }
  306.  
  307. void BirdShot(jjOBJ@ obj) {
  308.         obj.behave(BEHAVIOR::BULLET);
  309. }
  310. bool KnifeMask(float x, float y) {
  311.         return jjMaskedPixel(int(x), int(y)) && (jjEventAtLastMaskedPixel == 0 || jjEventAtLastMaskedPixel > AREA::HOOK);
  312. }
  313. void Knife(jjOBJ@ obj) {
  314.         if (obj.counter++ < 7) {
  315.                 obj.xSpeed += obj.xAcc;
  316.                 const float newX = obj.xPos + obj.xSpeed + obj.var[7]/65536.0;
  317.                 obj.ySpeed += obj.yAcc;
  318.                 const float newY = obj.yPos + obj.ySpeed;
  319.                 if (KnifeMask(newX, newY)) {
  320.                         obj.counter = 50; //return
  321.                 } else {
  322.                         obj.xPos = newX;
  323.                         obj.yPos = newY;
  324.                 }
  325.         } else {
  326.                 jjPLAYER@ play = jjPlayers[obj.creatorID];
  327.                 const float dx = play.xPos - obj.xPos;
  328.                 const float dy = play.yPos - obj.yPos;
  329.                 if ((abs(dx) < 18 && abs(dy) < 18) || (obj.state == STATE::DEACTIVATE && !jjDeactivatingBecauseOfDeath)) {
  330.                         jjSamplePriority(SOUND::COMMON_PICKUPW1);
  331.                         obj.delete();
  332.                         if (play.ammo[WEAPON::BLASTER] <= AMMOBUFFER)
  333.                                 play.currWeapon = WEAPON::BLASTER;
  334.                         play.ammo[WEAPON::BLASTER] = play.ammo[WEAPON::BLASTER] + 1;
  335.                         return;
  336.                 } else {
  337.                         const float targetX = (dx > 0) ? 3 : -3;
  338.                         const float targetY = (dy > 0) ? 2 : -2;
  339.                         if (obj.xSpeed < targetX) obj.xSpeed += 0.1;
  340.                         else if (obj.xSpeed > targetX) obj.xSpeed -= 0.1;
  341.                         if (obj.ySpeed < targetY) obj.ySpeed += 0.1;
  342.                         else if (obj.ySpeed > targetY) obj.ySpeed -= 0.1;
  343.                         if (!KnifeMask(obj.xPos + obj.xSpeed, obj.yPos))
  344.                                 obj.xPos += obj.xSpeed;
  345.                         else
  346.                                 obj.xSpeed = 0;
  347.                         if (!KnifeMask(obj.xPos, obj.yPos + obj.ySpeed))
  348.                                 obj.yPos += obj.ySpeed;
  349.                         else
  350.                                 obj.ySpeed = 0;
  351.                 }
  352.         }
  353.         if (obj.counter > 3) {
  354.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame);
  355.                 if (++obj.counterEnd >= 5) {
  356.                         obj.counterEnd = 0;
  357.                         obj.frameID += 1;
  358.                         obj.determineCurFrame();
  359.                 }
  360.         }
  361. }
  362.  
  363. array<jjMASKMAP@> FullMasks = {jjMASKMAP(false), jjMASKMAP(true)};
  364. void SetFishMask(bool setTo) {
  365.         FullMasks[setTo ? 1 : 0].save(1040, false);
  366. }
  367.  
  368. void NotSwimming(jjOBJ@) {
  369.         jjSetWaterLevel(jjLayerHeight[4] * 32, true);
  370.         SetFishMask(false);
  371. }
  372. void onLevelReload() {
  373.         MLLE::Palette.apply();
  374.         jjAddObject(OBJECT::BEES,0,0,0,CREATOR::OBJECT, NotSwimming);
  375.         for (int x = jjLayerWidth[3]; --x >= 0;)
  376.                 for (int y = jjLayerHeight[3]; --y >= 0;) {
  377.                         const uint8 ev = jjEventGet(x,y);
  378.                         if (ev == 34) //ammo
  379.                                 jjParameterSet(x,y,1,1,0);
  380.         }
  381. }
  382.  
  383. array<array<int>> Targets(0);
  384. uint CurrentTargetID = 0;
  385. bool SetTarget(uint nu) {
  386.         if (nu > CurrentTargetID) {
  387.                 CurrentTargetID = nu;
  388.                 return true;
  389.         }
  390.         return false;
  391. }
  392. void onFunction5(jjPLAYER@, uint8 nu) {
  393.         SetTarget(nu);
  394. }
  395.  
  396. const int AMMOBUFFER = 10;
  397. void onPlayer(jjPLAYER@ play) {
  398.         play.lives = 5;
  399.         if (play.health <= 0)
  400.                 PlayersX[play.localPlayerID].Apples = 2;
  401.         play.fastfire = 8;
  402.         if (PlayersX[play.localPlayerID].Fish) {
  403.                 play.spriteMode = SPRITE::INVISIBLE;
  404.                 play.currWeapon = WEAPON::BLASTER;
  405.                 jjWeapons[WEAPON::BLASTER].infinite = true;
  406.                 play.keySelect = false;
  407.                 play.jumpStrength = -5;
  408.                 SetFishMask(true);
  409.                 jjSetWaterLevel(PlayersX[play.localPlayerID].WaterLevel, true);
  410.                 if (play.yPos <= jjWaterLevel) {
  411.                         play.keyDown = play.keyFire = false;
  412.                         if (play.ySpeed > 0)
  413.                                 play.keyJump = false;
  414.                 }
  415.                 float ySpeed = play.ySpeed;
  416.                 if (ySpeed < -2.f) ySpeed = -2.f;
  417.                 else if (ySpeed > 2.f) ySpeed = 2.f;
  418.                 jjDrawRotatedSprite(play.xPos, play.yPos, ANIM::FISH, 1, jjGameTicks / 6, int(ySpeed * -64) * play.direction, play.direction, 1, SPRITE::PLAYER, play.playerID);
  419.         } else {
  420.                 play.spriteMode = SPRITE::PLAYER;
  421.                 NotSwimming(null);
  422.                 if (play.charCurr == CHAR::FROG) {
  423.                         play.jumpStrength = play.keyJump ? -18 : -7;
  424.                         play.keyJump = true;
  425.                         play.keyFire = false;
  426.                         if (PlayersX[play.localPlayerID].Immobile)
  427.                                 play.keyLeft = play.keyRight = false;
  428.                 } else if (play.charCurr == CHAR::BIRD2) {
  429.                         play.currWeapon = WEAPON::BLASTER;
  430.                         play.keySelect = false;
  431.                         jjWeapons[WEAPON::BLASTER].infinite = true;
  432.                         if (PlayersX[play.localPlayerID].Immobile) {
  433.                                 play.keyUp = play.keyDown = play.keyJump = play.keyFire = play.keyRun = false; //ONLY left/right allowed
  434.                                 play.ySpeed = -1.5;
  435.                                 play.invincibility = -200;
  436.                         }
  437.                 } else {
  438.                         if (play.keySelect || play.ammo[WEAPON::CURRENT] <= AMMOBUFFER)
  439.                                 play.keyFire = false;
  440.                         if (play.keyDown && !jjMaskedPixel(int(play.xPos), int(play.yPos) + 21) && (jjEventGet(int(play.xPos) >> 5, int(play.yPos) >> 5) != AREA::VINE))
  441.                                 play.keyDown = false;
  442.                         if (PlayersX[play.localPlayerID].NoJumping > jjGameTicks)
  443.                                 play.keyJump = false;
  444.                         jjWeapons[WEAPON::BLASTER].infinite = false;
  445.                         play.jumpStrength = -10;
  446.                 }
  447.         }
  448.        
  449.         if (CurrentTargetID < Targets.length)
  450.                 if (jjDifficulty <= 0 || jjKey[9])
  451.                         ArtificialArrow::DrawArrow(Targets[CurrentTargetID], play, SPRITE::SINGLEHUE, 72);
  452. }
  453.  
  454.  
  455. class PacmanGhost : jjBEHAVIORINTERFACE {
  456.         void onBehave(jjOBJ@ obj) {
  457.                 if (obj.state == STATE::START) {
  458.                         obj.state = STATE::FLY;
  459.                         obj.yPos -= 32;
  460.                         if (jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0, 1) == 1) {
  461.                                 obj.scriptedCollisions = false;
  462.                                 obj.playerHandling = HANDLING::ENEMY;
  463.                                 obj.bulletHandling = HANDLING::HURTBYBULLET;
  464.                                 obj.determineCurAnim(ANIM::BIRD, 9);
  465.                         }
  466.                 } else if (obj.state == STATE::FLY) {
  467.                         obj.pathMovement();
  468.                         if (++obj.counterEnd >= 6) {
  469.                                 obj.counterEnd = 0;
  470.                                 obj.frameID += 1;
  471.                                 obj.determineCurFrame();
  472.                         }
  473.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.scriptedCollisions ? obj.direction * -2 - 1 : obj.direction, obj.justHit == 0 ? SPRITE::NORMAL : SPRITE::SINGLECOLOR, 15);
  474.                 } else if (obj.state == STATE::KILL) {
  475.                         obj.deactivate();
  476.                 } else { //freeze, deactivate
  477.                         obj.behave(BEHAVIOR::SPARK);
  478.                 }
  479.         }
  480.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int) {
  481.                 if (bullet !is null) {
  482.                         if (bullet.behavior == BirdShot) {
  483.                                 obj.bulletHandling = HANDLING::HURTBYBULLET;
  484.                                 obj.scriptedCollisions = false;
  485.                                 bullet.objectHit(obj, HANDLING::ENEMY);
  486.                         }
  487.                 } else
  488.                         play.objectHit(obj, 0, HANDLING::ENEMY);
  489.                 return true;
  490.         }
  491. }
  492.  
  493. void SpikeBall(jjOBJ@ obj) {
  494.         if (obj.state == STATE::DEACTIVATE || obj.state == STATE::EXPLODE || obj.state == STATE::KILL) {
  495.                 jjPLAYER@ owner = jjPlayers[obj.creatorID];
  496.                 owner.ammo[WEAPON::BOUNCER] = owner.ammo[WEAPON::BOUNCER] + 1;
  497.                 obj.behavior = BEHAVIOR::BOUNCERBULLETPU;
  498.         }
  499.         obj.behave(BEHAVIOR::BOUNCERBULLETPU);
  500. }
  501.  
  502. int getLivesAnimID(int character, bool swimming) {
  503.         if (!swimming) switch (character) {
  504.                 case CHAR::JAZZ:
  505.                         return jjAnimSets[ANIM::FACES] + 3;
  506.                 case CHAR::SPAZ:
  507.                         return jjAnimSets[ANIM::FACES] + (jjIsTSF ? 5 : 4);
  508.                 case CHAR::LORI:
  509.                         return jjAnimSets[ANIM::FACES] + 4;
  510.                 case CHAR::FROG:
  511.                         return jjAnimSets[ANIM::FACES] + 2;
  512.                 case CHAR::BIRD:
  513.                 case CHAR::BIRD2:
  514.                         return jjAnimSets[ANIM::FACES] + 0;
  515.         }
  516.         return jjAnimSets[ANIM::CUSTOM[0]];
  517. }
  518.  
  519. bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {
  520.         const jjANIMATION@ anim = jjAnimations[getLivesAnimID(player.charCurr, PlayersX[player.localPlayerID].Fish)];
  521.         canvas.drawSpriteFromCurFrame(0, jjSubscreenHeight, anim.firstFrame + (jjGameTicks / 6) % anim.frameCount, 1, SPRITE::PLAYER, player.playerID);
  522.         int x = 20;
  523.         for (int keyType = 0; keyType < 2; ++keyType) {
  524.                 for (uint i = 0; i < Keys[keyType]; ++i)
  525.                         canvas.drawSpriteFromCurFrame(x += 30, jjSubscreenHeight - 20, KeyCurFrame - keyType);
  526.         }
  527.         jjLayerXOffset[2] =
  528.         jjLayerXOffset[2] + 2.5;
  529.         return true;
  530. }
  531.  
  532. void onFunction0(jjPLAYER@ player, uint8 level) {
  533.         if (PlayersX[player.localPlayerID].Fish) {
  534.                 int target;
  535.                 if (level == 0) target = int(player.yPos) & ~31;
  536.                 else target = level * 32;
  537.                 PlayersX[player.localPlayerID].WaterLevel = target;
  538.         } else if (player.charCurr != CHAR::FROG) {
  539.                 if (player.health > 0)
  540.                         player.hurt(7, true);
  541.         }
  542. }
  543.  
  544. void onFunction1(jjPLAYER@ player, bool canMove) {
  545.         if (player.charCurr == CHAR::FROG)
  546.                 PlayersX[player.localPlayerID].Immobile = !canMove;
  547. }
  548.  
  549. const array<CHAR::Char> paramToChar = {jjLocalPlayers[0].charOrig, CHAR::FROG, CHAR::BIRD2, CHAR::JAZZ};
  550. class MorphHead : jjBEHAVIORINTERFACE {
  551.         void onBehave(jjOBJ@ obj) {
  552.                 if (obj.state == STATE::START) {
  553.                         obj.var[0] = jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0, 2); //{Rabbit,Frog,Bird,Fish}
  554.                         obj.var[1] = paramToChar[obj.var[0]];
  555.                         obj.var[2] = jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 2, 1);
  556.                         obj.curAnim = getLivesAnimID(obj.var[1], obj.var[0] == 3);
  557.                         obj.state = STATE::FLOAT;
  558.                         obj.special = 0;
  559.                         obj.curFrame = jjAnimations[jjAnimSets[ANIM::AMMO] + 5] + 6;
  560.                 } else if (obj.state == STATE::DEACTIVATE) {
  561.                         obj.deactivate();
  562.                 } else {
  563.                         {
  564.                                 const int playerID = obj.findNearestPlayer(150 * 150);
  565.                                 if (playerID >= 0)
  566.                                         obj.direction = (jjPlayers[playerID].xPos >= obj.xPos) ? 1 : -1;
  567.                         }
  568.                         const jjANIMATION@ anim = jjAnimations[obj.curAnim];
  569.                         jjDrawSpriteFromCurFrame(obj.xPos - 14 * obj.direction, obj.yPos + 16, anim.firstFrame + (jjGameTicks / 6) % anim.frameCount, obj.direction);
  570.                         if (obj.special != 0 && (jjGameTicks & 7) == 0) {
  571.                                 obj.frameID = 100;
  572.                                 obj.state = STATE::ACTION;
  573.                                 obj.behave(BEHAVIOR::CHECKPOINT, false);
  574.                                 for (int i = 0; i < jjLocalPlayerCount; ++i) {
  575.                                         jjPLAYER@ player = jjLocalPlayers[i];
  576.                                         player.xOrg = obj.xPos; //don't use checkpoints' curframe hotspot code, because lives icons have weird hotspots
  577.                                         player.yOrg = obj.yPos;
  578.                                 }
  579.                                 obj.curFrame = jjObjectPresets[obj.eventID].curFrame;
  580.                                 obj.special = 0;
  581.                                 obj.var[0] = jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0, 2);
  582.                                
  583.                                 for (int x = jjLayerWidth[3]; --x >= 0;)
  584.                                         for (int y = jjLayerHeight[3]; --y >= 0;) {
  585.                                                 const uint8 ev = jjEventGet(x,y);
  586.                                                 if (ev == 34 && jjParameterGet(x,y,1,1) == 1) //ammo
  587.                                                         jjEventSet(x,y, 0);
  588.                                 }
  589.                         }
  590.                 }
  591.         }
  592.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int) {
  593.                 if (bullet is null) {
  594.                         if (obj.var[0] != 3) {
  595.                                 if (player.charCurr != obj.var[1] || PlayersX[player.localPlayerID].Fish) {
  596.                                         player.morphTo(CHAR::Char(obj.var[1]), true);
  597.                                         PlayersX[player.localPlayerID].Fish = false;
  598.                                         PlayersX[player.localPlayerID].NoJumping = jjGameTicks + 3;
  599.                                         if (player.charCurr == CHAR::BIRD2)
  600.                                                 SetTarget(6);
  601.                                         else if (player.charCurr == CHAR::FROG)
  602.                                                 SetTarget(11);
  603.                                         else if (player.charCurr == jjLocalPlayers[0].charOrig && player.ammo[WEAPON::BLASTER] > 0)
  604.                                                 SetTarget(8);
  605.                                 } else return true;
  606.                         } else {
  607.                                 if (!PlayersX[player.localPlayerID].Fish) {
  608.                                         player.morphTo(CHAR::JAZZ, true); //I guess
  609.                                         PlayersX[player.localPlayerID].Fish = true;
  610.                                         player.xPos = obj.xPos;
  611.                                         SetTarget(14);
  612.                                 } else return true;
  613.                         }
  614.                         jjSample(obj.xPos, obj.yPos, SOUND::COMMON_HOLYFLUT);
  615.                         obj.special = player.playerID + CREATOR::PLAYER; //to trigger checkpoint code
  616.                         for (uint objectID = jjObjectCount; --objectID != 0;) { //delete bullets from this player, for better checkpoint oldAmmo numbers
  617.                                 jjOBJ@ bb = jjObjects[objectID];
  618.                                 if (bb.behavior == Knife && bb.creator == obj.special) {
  619.                                         player.ammo[WEAPON::BLASTER] =
  620.                                         player.ammo[WEAPON::BLASTER] + 1;
  621.                                         bb.delete();
  622.                                 } else if (bb.behavior == BEHAVIOR::BOUNCERBULLETPU && bb.creator == obj.special) {
  623.                                         player.ammo[WEAPON::BOUNCER] =
  624.                                         player.ammo[WEAPON::BOUNCER] + 1;
  625.                                         bb.delete();
  626.                                 }
  627.                         }
  628.                         if (obj.var[2] == 1) //nocheckpoint
  629.                                 obj.special = 0;
  630.                 }
  631.                 return true;
  632.         }
  633. }
  634.  
  635. array<uint> Keys(2, 0);
  636. class Key : jjBEHAVIORINTERFACE {
  637.         void onBehave(jjOBJ@ obj) {
  638.                 if (obj.state == STATE::START) {
  639.                         obj.special = jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0, 1);
  640.                         obj.curAnim -= obj.special;
  641.                 }
  642.                 if (obj.xSpeed != 0) {
  643.                         obj.xSpeed = 0;
  644.                         if (obj.ySpeed == 0) obj.ySpeed = 0.5;
  645.                 }
  646.                 obj.ySpeed = abs(obj.ySpeed);
  647.                 if (obj.ySpeed > 3) obj.ySpeed = 3;
  648.                 obj.behave(BEHAVIOR::PICKUP, false);
  649.                 jjDrawSpriteFromCurFrame(obj.xPos, (obj.ySpeed != 0.f) ? obj.yPos : (obj.yPos + jjSin((obj.objectID*8+jjGameTicks+int(obj.xPos+obj.yPos*256))*16) * 4), obj.curFrame, obj.direction, SPRITE::NORMAL,0, 2);
  650.         }
  651.         bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ player, int) {
  652.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  653.                 obj.playerHandling = HANDLING::EXPLOSION;
  654.                 Keys[obj.special] += 1;
  655.                 jjEventSet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0);
  656.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_COIN);
  657.                 if (obj.special == 0) { //key
  658.                         if (player.charCurr != CHAR::FROG)
  659.                                 SetTarget(9);
  660.                         else
  661.                                 SetTarget(12);
  662.                 } else //gem
  663.                         SetTarget(3);
  664.                 return true;
  665.         }
  666. }
  667.  
  668.  
  669. class Lock : jjBEHAVIORINTERFACE {
  670.         void onBehave(jjOBJ@ obj) {
  671.                 if (obj.state == STATE::START) {
  672.                         obj.var[0] = jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0, 2);
  673.                         obj.var[1] = jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 2, 2);
  674.                         obj.curAnim = KeyCurFrame - obj.var[0];
  675.                         if (obj.var[0] == 3) obj.curAnim += 1;
  676.                         obj.curFrame = jjAnimations[jjAnimSets[ANIM::DESTSCEN]] + 2;
  677.                         obj.yPos += obj.var[1] * 16;
  678.                         obj.state = STATE::WAIT;
  679.                 } else if (obj.state == STATE::DEACTIVATE) {
  680.                         obj.deactivate();
  681.                 } else {
  682.                         jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curAnim, 256, 1, obj.var[0] != 2 ? 1 : -1, obj.var[0] <= 1 ? SPRITE::SINGLECOLOR : SPRITE::NORMAL, 0, 2);
  683.                 }
  684.         }
  685.         bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ player, int) {
  686.                 if (obj.var[0] <= 1) { //key/gem
  687.                         if (Keys[obj.var[0]] <= 0) {
  688.                                 if (obj.var[2] < jjGameTicks ) {
  689.                                         obj.var[2] = jjGameTicks + 210;
  690.                                         jjAnimations[jjObjectPresets[OBJECT::SILVERCOIN].curAnim].frameCount = 1;
  691.                                         jjAnimations[jjObjectPresets[OBJECT::SILVERCOIN].curAnim].firstFrame = KeyCurFrame - obj.var[0];
  692.                                         player.testForCoins(1);
  693.                                         SetTarget(2);
  694.                                 }
  695.                                 return true;
  696.                         }
  697.                         Keys[obj.var[0]] -= 1;
  698.                 } else { //right/left
  699.                         if ((obj.var[0] == 2) == (player.xPos >= obj.xPos))
  700.                                 return true;
  701.                 }
  702.                 const int xTile = int(obj.xOrg) >> 5, yTile = int(obj.yOrg) >> 5;
  703.                 jjGenerateSettableTileArea(4, xTile,yTile, 1, obj.var[1] + 1);
  704.                 jjEventSet(xTile,yTile, 0);
  705.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_DAMPED1);
  706.                 for (int y = 0; y <= obj.var[1]; ++y) {
  707.                         const uint16 tileID = jjTileGet(4,xTile,yTile+y);
  708.                         //jjAddParticleTileExplosion(xTile,yTile+y,tileID,false);
  709.                         for (int q = 0; q < 4; ++q) {
  710.                                 jjPARTICLE@ part = jjAddParticle(PARTICLE::TILE);
  711.                                 if (part !is null) {
  712.                                         part.xPos = xTile * 32 + (q & 1) * 16;
  713.                                         part.yPos = (yTile + y) * 32 + (q & 2) * 8;
  714.                                         part.ySpeed = y - obj.var[1] + ((q & 2) / 4.f) - 1;
  715.                                         part.xSpeed = ((q & 1) * 2 - 1) / 2.f * (abs(part.ySpeed) + 1) / 1.75;
  716.                                         part.tile.tileID = tileID;
  717.                                         part.tile.quadrant = TILE::Quadrant(q);
  718.                                 }
  719.                         }
  720.                         jjTileSet(4,xTile,yTile+y,0);
  721.                 }
  722.                 obj.delete();
  723.                 return true;
  724.         }
  725. }
  726.  
  727. class Apple : jjBEHAVIORINTERFACE {
  728.         void onBehave(jjOBJ@ obj) {
  729.                 obj.behave(BEHAVIOR::PICKUP);
  730.         }
  731.         bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ player, int) {
  732.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  733.                 obj.playerHandling = HANDLING::EXPLOSION;
  734.                 jjSample(obj.xPos, obj.yPos, SOUND::Sample(SOUND::COMMON_EAT1 + (jjRandom() & 3)));
  735.                 if (player.health < jjMaxHealth) {
  736.                         PlayerX@ playerX = PlayersX[player.localPlayerID];
  737.                         if (playerX.Apples < 10) {
  738.                                 playerX.Apples += 1;
  739.                         } else {
  740.                                 playerX.Apples = 2;
  741.                                 player.health += 1;
  742.                         }
  743.                 }
  744.                 if (jjDifficulty > 2)
  745.                         jjEventSet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0);
  746.                 return true;
  747.         }
  748. }
  749.  
  750. const int HeartCurFrame = jjAnimations[jjAnimSets[ANIM::PICKUPS] + 41];
  751. bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
  752.         canvas.drawRectangle(jjSubscreenWidth - 51, 5, 44, 12, 30);
  753.         if (player.health > 0 && (player.health > 1 || jjRenderFrame & 3 < 2)) {
  754.                 canvas.drawSpriteFromCurFrame(jjSubscreenWidth - 65, 11, HeartCurFrame);
  755.                 canvas.drawRectangle(jjSubscreenWidth - 50, 6, (player.health - 1) * 10 + PlayersX[player.localPlayerID].Apples, 10, 24);
  756.         }
  757.         return true;
  758. }
  759.  
  760. void GenerateWaterLayer() { //one-time
  761.         jjLAYER newLayer(jjLayerWidth[3], jjLayerHeight[3]);
  762.         newLayer.xSpeed = newLayer.ySpeed = 1;
  763.         dictionary createdTiles;
  764.         auto newTileID = jjTileCount;
  765.         for (int x = jjLayerWidth[3]; --x >= 0;)
  766.                 for (int y = jjLayerHeight[3]; --y >= 0;) {
  767.                         const uint16 waterTileID = jjTileGet(3,x,y);
  768.                         if (waterTileID >= 1365 && waterTileID <= 1427) {
  769.                                 const uint16 wallTileID = jjTileGet(4,x,y);
  770.                                 if (wallTileID != 0) { //would this ever come up? well, no harm in it
  771.                                         const string combo = waterTileID + "_" + wallTileID;
  772.                                         uint16 comboTileID;
  773.                                         if (!createdTiles.get(combo, comboTileID)) {
  774.                                                 comboTileID = newTileID++;
  775.                                                 jjPIXELMAP water(waterTileID), wall(wallTileID);
  776.                                                 for (uint xx = 0; xx < 32; ++xx)
  777.                                                         for (uint yy = 0; yy < 32; ++yy)
  778.                                                                 if (water[xx,yy] != 0)
  779.                                                                         wall[xx,yy] = 0;
  780.                                                 createdTiles.set(combo, comboTileID);
  781.                                                 wall.save(comboTileID);
  782.                                         }
  783.                                         newLayer.generateSettableTileArea(x,y,1,1); //not the most efficient but we're probably not dealing with big enough numbers for it to be a problem
  784.                                         newLayer.tileSet(x,y,comboTileID);
  785.                                 }
  786.                         }
  787.                 }
  788.         auto layers = jjLayerOrderGet();
  789.         layers.insertAt(layers.findByRef(jjLayers[3]), newLayer);
  790.         jjLayerOrderSet(layers);
  791. }
  792.  
  793. void FrogFencer(jjOBJ@ obj) {
  794.         if (obj.state == STATE::KILL) {
  795.                 obj.deactivate();
  796.         } else if (obj.state == STATE::JUMP) {
  797.                 if (obj.ySpeed < 0) {
  798.                         if (jjMaskedPixel(int(obj.xPos), int(obj.yPos))) //hit ceiling
  799.                                 obj.ySpeed = 0;
  800.                 }
  801.                 if (jjMaskedVLine(int(obj.xPos + obj.xSpeed), int(obj.yPos) - 10, 15)) { //hit wall
  802.                         obj.xPos -= obj.xSpeed;
  803.                 }
  804.         } else if (obj.state == STATE::STILL) {
  805.                 if (obj.counter & 7 == 3 && obj.frameID == 7) {
  806.                         if ((obj.var[5] = (obj.var[5] ^ 1)) == 1)
  807.                                 jjSample(obj.xPos, obj.yPos, SOUND::FROG_FROG);
  808.                 }
  809.         } else if (obj.state == STATE::START) {
  810.                 obj.yPos -= 10;
  811.         }
  812.         obj.behave(BEHAVIOR::FENCER, false);
  813.         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos + 5, obj.curFrame, obj.direction);
  814. }
  815.  
  816. class Ammo : jjBEHAVIORINTERFACE {
  817.         void onBehave(jjOBJ@ obj) {
  818.                 if (obj.state == STATE::START) {
  819.                         if (jjParameterGet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 0, 1) == 0) {
  820.                                 obj.determineCurAnim(ANIM::AMMO, 49);
  821.                                 obj.var[3] =
  822.                                 obj.var[3] - 1;
  823.                         } else obj.determineCurAnim(ANIM::ROBOT, 0);
  824.                 }
  825.                 obj.behave(BEHAVIOR::PICKUP);
  826.         }
  827.         bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ play, int) {
  828.                 WEAPON::Weapon gunID = WEAPON::Weapon(obj.var[3] + 1);
  829.                 for (int i = 0; i < jjLocalPlayerCount; ++i) {
  830.                         jjPLAYER@ player = jjLocalPlayers[i];
  831.                         if (player.ammo[gunID] <= 0) {
  832.                                 player.ammo[gunID] = AMMOBUFFER;
  833.                                 player.currWeapon = gunID;
  834.                         }
  835.                         player.ammo[gunID] =
  836.                         player.ammo[gunID] + 1;
  837.                 }
  838.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  839.                 obj.playerHandling = HANDLING::EXPLOSION;
  840.                 jjParameterSet(int(obj.xOrg) >> 5, int(obj.yOrg) >> 5, 1, 1, 1);
  841.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PICKUPW1);
  842.                 if (gunID == WEAPON::BLASTER)
  843.                         SetTarget((play.charCurr == CHAR::BIRD2) ? 7 : 8);
  844.                 else
  845.                         SetTarget(17);
  846.                 return true;
  847.         }
  848. }
  849. bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
  850.         int x = jjSubscreenWidth - 24;
  851.         int y = jjSubscreenHeight - 20;
  852.         for (int weaponID = 1; weaponID <= 2; ++weaponID) {
  853.                 const jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::BLASTERBULLET + weaponID - 1].curAnim];
  854.                 const int ammo = player.ammo[weaponID] - AMMOBUFFER;
  855.                 for (int b = 0; b < ammo; ++b)
  856.                         canvas.drawSpriteFromCurFrame(x, y - b * 24, anim.firstFrame + (jjGameTicks / 6 + b) % anim.frameCount, 1, (uint(weaponID) == player.currWeapon && player.charCurr != CHAR::BIRD2 && !PlayersX[player.localPlayerID].Fish) ? SPRITE::NORMAL : SPRITE::BLEND_NORMAL, 96);
  857.                 x -= 24;
  858.         }
  859.         return true;
  860. }
  861.  
  862. bool onCheat(string &in cheat) {
  863.         if (cheat == "jjammo" || cheat == "jjguns" || cheat == "jjgod") {
  864.                 for (int i = 0; i < jjLocalPlayerCount; ++i)
  865.                         for (int w = 1; w <= 2; ++w) {
  866.                                 if (jjLocalPlayers[i].ammo[w] == 0)
  867.                                         jjLocalPlayers[i].ammo[w] = AMMOBUFFER;
  868.                                 jjLocalPlayers[i].ammo[w] =
  869.                                         jjLocalPlayers[i].ammo[w] + 1;
  870.                         }
  871.                 jjAlert(cheat, false, STRING::MEDIUM);
  872.                 return true;
  873.         } else if (cheat == "jjmorph") {
  874.                 return true;
  875.         }
  876.         return false;
  877. }
  878.  
  879. void onFunction3() {
  880.         for (int x = jjLayerWidth[3]; --x >= 0;)
  881.                 for (int y = jjLayerHeight[3]; --y >= 0;) {
  882.                         const uint8 ev = jjEventGet(x,y);
  883.                         if (ev == 146) //morph head
  884.                                 jjEventSet(x,y, 0);
  885.                         else if (ev == 255) {
  886.                                 jjEventSet(x,y, jjParameterGet(x,y,0,8));
  887.                         }
  888.         }
  889. }
  890.  
  891. void onFunction4(jjPLAYER@ play) {
  892.         MLLE::GetLayer("River").hasTiles = false;
  893.         MLLE::GetLayer("Far Trees").hasTiles = false;
  894.         MLLE::GetLayer("Farther Trees").hasTiles = false;
  895.         MLLE::GetLayer("Owls").hasTiles = false;
  896.         jjTexturedBGUsed = false;
  897.         jjMusicStop();
  898.         PlayersX[play.localPlayerID].Immobile = true;
  899.         SetTarget(22);
  900. }