Downloads containing mo4a_1-1_revisited1.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Mystery of the Four... chandie Single player 6.6 Download file

File preview

  1. const bool MLLESetupSuccessful = MLLE::Setup(array<MLLEWeaponApply@> = {null, null, DefaultWeapons::Blaster(), DefaultWeapons::Blaster(), WeaponVMega::Backfire::Weapon(), null, DefaultWeapons::Blaster(), null, DefaultWeapons::Blaster()}); ///@MLLE-Generated
  2. #include "MLLE-Include-1.5w.asc" ///@MLLE-Generated
  3. #pragma require "mo4a_1-1_revisited1.j2l" ///@MLLE-Generated
  4. #include "WeaponVMega5.asc" ///@MLLE-Generated
  5. #pragma require "WeaponVMega5.asc" ///@MLLE-Generated
  6. #include "MLLE-DefaultWeapons.asc" ///@MLLE-Generated
  7. #pragma require "MLLE-DefaultWeapons.asc" ///@MLLE-Generated
  8. #include "Jazz1Enemies v05.asc"
  9. #include "Resize v11.asc"
  10. #include "TrueColor v13.asc"
  11. #include "HH18savegems.asc"
  12.  
  13. bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
  14.     canvas.drawSprite(20, 585, ANIM::PICKUPS, 84, jjGameTicks>>2, -1, SPRITE::PALSHIFT, -8);
  15.     canvas.drawString(30, 585, formatInt(player.coins%4, "1") + "/3", STRING::SMALL, STRING::NORMAL);
  16.     return false;
  17. }
  18.  
  19. bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {return true;}
  20.  
  21. void onLevelLoad()  {
  22.         gem::restorePlayerGems();
  23.         jjLevelName = ("@@@@@@@@@Central Area");
  24.         jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckpointWrapper;
  25.         jjObjectPresets[OBJECT::SAVEPOST].deactivates = false;
  26.  
  27.         jjObjectPresets[OBJECT::GRASSPLATFORM].scriptedCollisions = true;
  28.         jjObjectPresets[OBJECT::GRASSPLATFORM].behavior = SpikeBump();
  29.         jjObjectPresets[OBJECT::GRASSPLATFORM].deactivates = false;
  30.  
  31.         jjObjectPresets[OBJECT::SILVERCOIN].behavior = PlatinCoin();
  32.         jjObjectPresets[OBJECT::SILVERCOIN].scriptedCollisions = true;
  33.  
  34.         jjObjectPresets[OBJECT::AIRBOARD].behavior = Key();
  35.         jjObjectPresets[OBJECT::AIRBOARD].scriptedCollisions = true;
  36.  
  37.         jjObjectPresets[OBJECT::FREEZEENEMIES].behavior = Key2();
  38.         jjObjectPresets[OBJECT::FREEZEENEMIES].scriptedCollisions = true;
  39.  
  40.         jjObjectPresets[OBJECT::MOTH].behavior = Magic;
  41.  
  42.         jjObjectPresets[OBJECT::STOPWATCH].behavior = Key3();
  43.         jjObjectPresets[OBJECT::STOPWATCH].scriptedCollisions = true;
  44.  
  45.         jjObjectPresets[OBJECT::RFAMMO15].behavior = Backfiregun();
  46.         jjObjectPresets[OBJECT::RFAMMO15].scriptedCollisions = true;
  47.         jjObjectPresets[OBJECT::RFAMMO15].playerHandling = HANDLING::SPECIAL;
  48.  
  49.         jjObjectPresets[OBJECT::RFAMMO3].deactivates = false;
  50.  
  51.         jjObjectPresets[OBJECT::FASTFEET].behavior = CountlessJumps();
  52.         jjObjectPresets[OBJECT::FASTFEET].scriptedCollisions = true;
  53.  
  54.         jjObjectPresets[OBJECT::EVA].behavior = TimeMachine;
  55.         jjObjectPresets[OBJECT::EVA].determineCurAnim(ANIM::FLAG, 1);
  56.         jjObjectPresets[OBJECT::EVA].putOnGround(false);
  57.         jjObjectPresets[OBJECT::EVA].scriptedCollisions = true;
  58.         jjANIMATION@ tmAnim = jjAnimations[jjObjectPresets[OBJECT::EVA].curAnim];
  59.         for (uint i = 0; i < tmAnim.frameCount; ++i)
  60.                 jjAnimFrames[tmAnim.firstFrame + i].coldSpotY = -44;
  61.         for (uint i = 0; i < tmAnim.frameCount; ++i)
  62.                 jjAnimFrames[tmAnim.firstFrame + i].hotSpotY = -64;
  63.         for (uint i = 0; i < tmAnim.frameCount; ++i)
  64.                 jjAnimFrames[tmAnim.firstFrame + i].hotSpotX = -54;
  65.  
  66.         jjObjectPresets[OBJECT::FLYCARROT].determineCurAnim(ANIM::PLUS_WARP, 0);
  67.         jjObjectPresets[OBJECT::FLYCARROT].behavior = Bonus;
  68.         jjObjectPresets[OBJECT::FLYCARROT].scriptedCollisions = true;
  69.         jjANIMATION@ BAnim = jjAnimations[jjObjectPresets[OBJECT::FLYCARROT].curAnim];
  70.         for (uint i = 0; i < BAnim.frameCount; ++i)
  71.                 jjAnimFrames[BAnim.firstFrame + i].hotSpotY = -85;
  72.         for (uint i = 0; i < BAnim.frameCount; ++i)
  73.                 jjAnimFrames[BAnim.firstFrame + i].hotSpotX = 20;
  74.  
  75.         jjObjectPresets[OBJECT::INVINCIBILITY].determineCurAnim(ANIM::PLUS_WARP, 1);
  76.         jjObjectPresets[OBJECT::INVINCIBILITY].behavior = Bonuseye;
  77.         jjObjectPresets[OBJECT::INVINCIBILITY].scriptedCollisions = true;
  78.         jjObjectPresets[OBJECT::INVINCIBILITY].bulletHandling = HANDLING::IGNOREBULLET;
  79.         jjObjectPresets[OBJECT::INVINCIBILITY].playerHandling = HANDLING::PLAYERBULLET;
  80.         jjANIMATION@ BeAnim = jjAnimations[jjObjectPresets[OBJECT::INVINCIBILITY].curAnim];
  81.  
  82.         for (uint i = 0; i < BeAnim.frameCount; ++i)
  83.                 jjAnimFrames[BeAnim.firstFrame + i].hotSpotY = 16;
  84.         for (uint i = 0; i < BeAnim.frameCount; ++i)
  85.                 jjAnimFrames[BeAnim.firstFrame + i].hotSpotX = 26;
  86.  
  87.         Jazz1::MakeEnemy(OBJECT::DRAGONFLY, Jazz1::Enemies::Jungrock_RedBuzzer).SetUsesJJ2StyleDeathAnimation(true);
  88.         Jazz1::MakeEnemy(OBJECT::LIZARD, Jazz1::Enemies::Holidaius_SnowMonkey).SetUsesJJ2StyleDeathAnimation(true);
  89.         Jazz1::MakeEnemy(OBJECT::HATTER, Jazz1::Enemies::Marbelara_Schwarzenguard, true).SetUsesJJ2StyleDeathAnimation(true).SetBulletFireSound(SOUND::INTRO_SHOT1).SetBulletExplosionSound(SOUND::COMMON_GUNSM1);
  90.  
  91.         jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::SPIKEBOLL3D].curAnim];
  92.         anim.frameCount = 1;
  93.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  94.         jjPIXELMAP pump(0, 0*32, 3*32, 3*32, 4);
  95.         pump.save(frame);
  96.         frame.hotSpotY = -47;
  97.         frame.hotSpotX = -47;
  98.  
  99. }
  100.  
  101. class PlatinCoin : jjBEHAVIORINTERFACE {
  102.  
  103.         void onBehave(jjOBJ@ obj) {
  104.                 obj.behave(BEHAVIOR::PICKUP, false);
  105.                 ++obj.counter;
  106.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  107.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -8);
  108. }
  109.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  110.                 p.coins += 1;
  111.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_COIN, 1000);
  112.  
  113.                 return true;
  114.         }
  115. }
  116.  
  117. void Bonuseye(jjOBJ@ obj){
  118.         obj.direction = -1;
  119.         obj.behave(BEHAVIOR::PICKUP, false);
  120.         obj.draw();
  121. }
  122. void Bonus(jjOBJ@ obj){
  123.         obj.putOnGround();
  124.         obj.direction = -1;
  125.         obj.behave(BEHAVIOR::PICKUP, false);
  126.         obj.draw();
  127. }
  128.  
  129.  
  130. int spring = 0;
  131. int CountlessDoubleJumps = 0;
  132.  
  133. void TimeMachine(jjOBJ@ obj){
  134.         obj.behave(BEHAVIOR::EVA, false);
  135.                         jjDrawResizedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, 1, SPRITE::PALSHIFT, 8);
  136.  
  137. }
  138.  
  139. class Backfiregun : jjBEHAVIORINTERFACE {
  140.  
  141.         void onBehave(jjOBJ@ obj) {
  142.                 if(p.ammo[WEAPON::RF] > 1)
  143.                 {obj.delete();}
  144.                 obj.behave(BEHAVIOR::MONITOR); 
  145.         }
  146.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
  147.                 if(bullet !is null && play !is null)
  148.                         {obj.behave(BEHAVIOR::EXPLOSION2);
  149.                         jjAddObject(OBJECT::RFAMMO3, 163*32, 42*32);
  150.                         jjAddObject(OBJECT::RFAMMO3, 162*32, 42*32);                   
  151.                         jjAddObject(OBJECT::RFAMMO3, 163*32, 43*32);
  152.                         jjAddObject(OBJECT::RFAMMO3, 162*32, 43*32);
  153.                         jjAddObject(OBJECT::RFAMMO3, 188*32, 49*32);
  154.                         jjAddObject(OBJECT::RFAMMO3, 187*32, 50*32);
  155.                         jjAddObject(OBJECT::RFAMMO3, 188*32, 51*32);
  156.                         jjAddObject(OBJECT::RFAMMO3, 187*32, 52*32);
  157.                         jjAddObject(OBJECT::RFAMMO3, 188*32, 53*32);
  158.                         jjSample(p.xPos, p.yPos, SOUND::COMMON_HARP1, 1000);}
  159.                 return true;
  160.         }
  161.        
  162. }
  163.  
  164. void Magic(jjOBJ@ obj) {
  165.         obj.behave(BEHAVIOR::MOTH,false);
  166.         if(jjTriggers[1] == true){
  167.                 jjPARTICLE@ particle = jjAddParticle(PARTICLE::FLOWER);
  168.                 particle.xPos = obj.xPos;
  169.                 particle.yPos = obj.yPos;
  170.         }
  171. }
  172. void onFunction0(jjPLAYER@ p) {
  173.                 {jjNxt("mo4a_1-4_save", false, true);
  174.                 gem::saveGemData();}
  175. }
  176.  
  177. void onFunction1(jjPLAYER@ p) {
  178.         p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@Sacred Woods", STRING::MEDIUM);
  179.         jjEnabledASFunctions[1] = false;}
  180.  
  181. void onFunction2(jjPLAYER@ p) {
  182.         p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@Totem Hills", STRING::MEDIUM);
  183.         jjEnabledASFunctions[2] = false;}
  184.  
  185. void onFunction3(jjPLAYER@ p) {
  186.         p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@Temple of Sun", STRING::MEDIUM);
  187.         jjEnabledASFunctions[3] = false;}
  188.  
  189. bool buyammo = false;
  190. bool startrush = false;
  191. bool readytorush = false;
  192.  
  193. void onPlayer(jjPLAYER@ p) {
  194.         gem::trackPlayerGems(p);
  195.         gem::upgradeHealth(p);
  196.  
  197.         if(p.coins == 0)
  198.         {p.coins += 3;}
  199.  
  200.         if (p.idle > 5 && p.xPos >180*32 && (p.keyLeft || p.keyRight || p.keyJump || p.keyFire))
  201.                 {spike = false;}
  202.  
  203.         if(p.food == 100 && jjKey[0x52] == false && startrush == false)
  204.                 {p.showText("@@@@@@@@@@@@@@@@Press 'R' when you need to use Sugar Rush!", STRING::MEDIUM);
  205.                 p.startSugarRush(0);
  206.                 startrush = true;
  207.                 readytorush = true;}
  208.  
  209.         if(readytorush == true)
  210.                 {p.food = 100;}
  211.  
  212.         if(p.food == 100 && jjKey[0x52])
  213.                 {p.startSugarRush(1400);
  214.                 p.food = 0;
  215.                 readytorush = false;
  216.                 startrush = false;
  217.         }
  218.  
  219.  
  220.         if(p.ammo[WEAPON::RF] < 1 && p.xPos>162*32 && p.xPos<166*32 && p.yPos<52*32 && p.yPos>49*32 && jjKey[0x50] && p.gems[GEM::RED]<50 && buyammo == false)
  221.                 {p.testForGems(50, GEM::RED);}
  222.         if(p.ammo[WEAPON::RF] < 1 && p.xPos>162*32 && p.xPos<166*32 && p.yPos<52*32 && p.yPos>49*32 && jjKey[0x50] && p.gems[GEM::RED]>=50 && buyammo == false)
  223.                 {p.testForGems(50, GEM::RED);
  224.         jjTriggers[0]=true;
  225.         buyammo = true;}
  226.  
  227.         if(p.ammo[WEAPON::RF] >= 1 && p.xPos>162*32 && p.xPos<166*32 && p.yPos<52*32 && p.yPos>49*32 && jjKey[0x50] && buyammo == false)
  228.                 {p.showText("@@You already have the weapon.");}
  229.  
  230.         if(p.xPos>89*32 && p.xPos<92*32 && p.yPos<2*32 && p.coins < 3)
  231.                 {p.testForCoins(3);}
  232.         if(p.xPos>89*32 && p.xPos<92*32 && p.yPos<2*32 && p.coins >= 3)
  233.                 {jjTriggers[1] = true;}
  234.  
  235.         if(spring > jjGameTicks)
  236.                 {p.ySpeed = -10;}
  237.         p.lightType = LIGHT::NONE;
  238.         if (CountlessDoubleJumps > jjGameTicks && p.keyDown) {
  239.                 p.buttstomp = 90;}
  240.         if (CountlessDoubleJumps > jjGameTicks && p.keyJump) {
  241.                 p.doubleJumpCount = 0;}
  242.         if (p.health == 0){
  243.                 CountlessDoubleJumps  = 0;
  244. }
  245. }
  246.  
  247. bool spike = false;
  248. class SpikeBump : jjBEHAVIORINTERFACE {
  249.  
  250. void onBehave(jjOBJ@ obj) {
  251.  
  252.         switch (obj.state) {
  253.                 case STATE::START:
  254.                         obj.determineCurFrame();
  255.                         obj.state = STATE::FLY;
  256.  
  257.                 case STATE::FLY:
  258.                         obj.playerHandling = HANDLING::SPECIAL;
  259.                         obj.deactivates = false;
  260.                         if (jjMaskedVLine(obj.xSpeed > 0 ? obj.xPos + 16 : obj.xPos - 16, obj.yPos, 1)) {
  261.                                 obj.direction = obj.xSpeed = -obj.xSpeed;
  262.                         }
  263.                        
  264.                         if(obj.xPos > p.xPos + 64 || obj.xPos < p.xPos - 64 || obj.yPos > p.yPos + 64 || obj.yPos < p.yPos - 64)
  265.                         {obj.determineCurAnim(ANIM::BOLLPLAT, 0);
  266.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -8);
  267.                         obj.determineCurFrame();}
  268.                         else {jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -24);}
  269.                         break;
  270.  
  271.                
  272.         }
  273. }
  274.  
  275.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
  276.                 if(bullet is null && play !is null && (force == -1 || force == 1))
  277.                         {spring = jjGameTicks + 1*61;
  278.                         obj.determineCurAnim(ANIM::BOLLPLAT, 0);
  279.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -24);
  280.                         obj.determineCurFrame();
  281.                         p.buttstomp = 121;
  282.                         jjSample(obj.xPos, obj.yPos, SOUND::PINBALL_BELL, 1000);}
  283.                 if(force == 0 && spring < jjGameTicks)
  284.                         {play.hurt();
  285.                         obj.determineCurAnim(ANIM::SPIKEBOLL, 0);  
  286.                         obj.determineCurFrame();
  287.                         if(spike == false)
  288.                         {jjSample(obj.xPos, obj.yPos, SOUND::COMMON_METALHIT, 1000);}
  289.                         spike = true;}
  290.                 return true;
  291.                 }
  292.  
  293. }
  294.  
  295.  
  296. void onMain() {
  297. gem::deleteCollectedGems();
  298. if(jjKey[9] && jjKey[0x51]) {
  299. p.morphTo(CHAR::JAZZ, false);
  300. }
  301. if(jjKey[9] && jjKey[0x57]) {
  302. p.morphTo(CHAR::SPAZ, false);
  303. }
  304. if(jjKey[9] && jjKey[0x45]) {
  305. p.morphTo(CHAR::LORI, false);
  306. }
  307. }
  308.  
  309.  
  310. class CountlessJumps : jjBEHAVIORINTERFACE {
  311.         void onBehave(jjOBJ@ obj) {
  312.  
  313. obj.behave(BEHAVIOR::PICKUP, false);
  314. obj.determineCurAnim(ANIM::PICKUPS, 33);
  315. ++obj.counter;
  316.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  317.  
  318.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 16);
  319.  
  320. }
  321.  
  322.         bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ play, int force) {
  323.                 play.timerStart(600);
  324.                 p.morphTo(CHAR::SPAZ);
  325.                 CountlessDoubleJumps = jjGameTicks + 10 * 61;
  326.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  327.                 obj.frameID = 0;
  328.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PICKUP1, 6000);
  329.  
  330.                 return true;
  331.         }
  332.  
  333. }
  334.  
  335. class Key : jjBEHAVIORINTERFACE {
  336.  
  337.         void onBehave(jjOBJ@ obj) {
  338.                 obj.behave(BEHAVIOR::PICKUP, false);
  339.         jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::AIRBOARD].curAnim];
  340.         anim.frameCount = 1;
  341.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  342.         jjPIXELMAP pump(0, 4*32, 1*32, 1*32, 4);
  343.         pump.save(frame);
  344.                 ++obj.counter;
  345.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  346.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
  347. }
  348.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  349.                 jjSwitchTrigger(3);
  350.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  351.                 jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
  352.  
  353.                 return true;
  354.         }
  355. }
  356.  
  357.  
  358. class Key2 : jjBEHAVIORINTERFACE {
  359.  
  360.         void onBehave(jjOBJ@ obj) {
  361.                 obj.behave(BEHAVIOR::PICKUP, false);
  362. jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::FREEZEENEMIES].curAnim];
  363.         anim.frameCount = 1;
  364.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  365.         jjPIXELMAP pump(0, 3*32, 1*32, 1*32, 4);
  366.         pump.save(frame);
  367.                 ++obj.counter;
  368.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  369.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
  370. }
  371.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  372.                 jjSwitchTrigger(8);
  373.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  374.                 jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
  375.  
  376.                 return true;
  377.         }
  378. }
  379.  
  380.  
  381.  
  382. class Key3 : jjBEHAVIORINTERFACE {
  383.  
  384.         void onBehave(jjOBJ@ obj) {
  385.                 obj.behave(BEHAVIOR::PICKUP, false);
  386.  
  387. jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::STOPWATCH].curAnim];
  388.         anim.frameCount = 1;
  389.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  390.         jjPIXELMAP pump(0, 5*32, 1*32, 1*32, 4);
  391.         pump.save(frame);
  392.                 ++obj.counter;
  393.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  394.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
  395. }
  396.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  397.                 jjSwitchTrigger(15);
  398.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  399.                 jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
  400.  
  401.                 return true;
  402.         }
  403. }
  404. void onLevelReload() {
  405.         for (uint i = 0; i < 32; ++i)
  406.                 jjTriggers[i] = SavedTriggers[i];
  407.         gem::restorePlayerGems();
  408.         jjLocalPlayers[0].lives++;
  409.         buyammo == false;
  410.         jjTriggers[0] = false;
  411.         jjWaterLighting = WATERLIGHT::GLOBAL;
  412. }
  413.  
  414. array<bool> SavedTriggers(32, false);
  415. //Extendable Checkpoints by VioletCLM
  416. void CheckpointWrapper(jjOBJ@ obj) {
  417.   if (obj.state == STATE::STOP) { //don't do anything anymore
  418.     jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
  419.   } else if (obj.state == STATE::DEACTIVATE) { //due to death
  420.     obj.deactivate();
  421.   } else {
  422.     obj.behave(BEHAVIOR::CHECKPOINT);
  423.         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
  424.     if (obj.state == STATE::DONE) { //triggered by the player hitting it
  425.       obj.state = STATE::STOP;
  426.       //save the current state of some properties
  427.       for (uint i = 0; i < 32; ++i)
  428.         SavedTriggers[i] = jjTriggers[i];
  429.  
  430.       //OPTIONAL: this loop makes checkpoints reusable, so only the most recent checkpoint you touched is ever active
  431.       for (int i = jjObjectCount; --i > 0;) {
  432.         jjOBJ@ obj2 = jjObjects[i];
  433.         if (obj2.eventID == OBJECT::CHECKPOINT && i != obj.objectID && obj2.isActive) {
  434.           obj2.state = STATE::SLEEP;
  435.           obj2.var[0] = 0;
  436.         }
  437.       }
  438.     }
  439.   }
  440. }
  441.  
  442. jjTEXTAPPEARANCE SignTextAppearance = STRING::NORMAL;
  443. class Sign {
  444.         private int xPos, yPos; //These pixel-based positions will be generated from tile-based positions in the constructor by multiplying by 32
  445.         private string text;
  446.         private uint widthOfText;
  447.         Sign(){} //AngelScript requires any class that appears in any array to have an explicit default constructor, even if it's never called
  448.         Sign(int xTile, int yTile, const string &in t) {
  449.                 xPos = xTile * 32; //Since this is a constant operation, it could strictly be performed in the draw method instead of the constructor, but this way involves fewer multiplication instructions
  450.                 yPos = yTile * 32; //
  451.                 text = t;
  452.                 SignTextAppearance.newline = STRING::SPECIALSIGN; //Causes the drawString method to interpret instances of the \n character as signals to drop down to a new line, similar to the special effect of the @ character in the STRING::SPIN appearance.
  453.                 SignTextAppearance.spacing = -2; //int jjTEXTAPPEARANCE::spacing is new in 5.2, and this particular value is equivalent to prefixing the string with "ยง2". Make sure to check out bool jjTEXTAPPEARANCE::monospace too, though it didn't end up getting used in this level.
  454.                 widthOfText = jjGetStringWidth(text, STRING::SMALL, SignTextAppearance); //Used for determining how large of a dark rectangle should be drawn behind the text. A matching heightOfText value could of course be generated by counting the number of newline characters--for example, "heightOfText = text.split("\n").length * 20;"--but here the rectangles are constant height instead to limit the temptation to ramble on and on.
  455.         }
  456.         void draw(jjCANVAS@ layer, uint8 textIntensity) const { //Because this method will be called from an onDraw method, it's important to have a jjCANVAS@ passed among the arguments.
  457.                 layer.drawRectangle(xPos, yPos - 16, widthOfText + 8, 55, 0, SPRITE::TRANSLUCENT);
  458.                 layer.drawString(xPos, yPos, text, STRING::SMALL, SignTextAppearance, 0, SPRITE::BLEND_HARDLIGHT, textIntensity);
  459.         }
  460. }
  461. const array<Sign> Signs = {
  462.         Sign(237, 25, "You saved our totems.\nYou saved our gods."),
  463.         Sign(164, 61, "Now you are able to access the temple.\n Good luck!"),
  464.         Sign(18, 61, "You saved our women. Thank you!"),
  465.         Sign(166, 48, "Press P if you'd like to\nbuy Backfire Gun for 50 Gems."),
  466. };
  467.  
  468. void onDrawLayer3(jjPLAYER@, jjCANVAS@ layer) {
  469.         if(jjKey[0x54]){
  470.         const uint8 textIntensity = 200 + int(jjSin(jjGameTicks * 16) * 50);
  471.         for (uint signID = 0; signID < Signs.length; ++signID)
  472.                 Signs[signID].draw(layer, textIntensity);
  473. }
  474. }
  475. bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
  476.         return MLLE::WeaponHook.drawAmmo(player, canvas);
  477. }
  478.