Downloads containing mo4a_1-1_revisited2.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, WeaponVMega::IceCloud::Weapon(), WeaponVMega::Voranj::Weapon(), WeaponVMega::Backfire::Weapon(), null, ArcaneWeapons::FusionCannon::Weapon(), null, ArcaneWeapons::MortarLauncher::Weapon()}); ///@MLLE-Generated
  2. #include "MLLE-Include-1.5w.asc" ///@MLLE-Generated
  3. #pragma require "mo4a_1-1_revisited2.j2l" ///@MLLE-Generated
  4. #include "ArcaneWeapon4.asc" ///@MLLE-Generated
  5. #pragma require "ArcaneWeapon4.asc" ///@MLLE-Generated
  6. #include "ArcaneWeapon9.asc" ///@MLLE-Generated
  7. #pragma require "ArcaneWeapon9.asc" ///@MLLE-Generated
  8. #include "WeaponVMega5.asc" ///@MLLE-Generated
  9. #pragma require "WeaponVMega5.asc" ///@MLLE-Generated
  10. #include "WeaponVMega8.asc" ///@MLLE-Generated
  11. #pragma require "WeaponVMega8.asc" ///@MLLE-Generated
  12. #include "WeaponVMega3.asc" ///@MLLE-Generated
  13. #pragma require "WeaponVMega3.asc" ///@MLLE-Generated
  14. #include "Jazz1Enemies v05.asc"
  15. #include "Resize v11.asc"
  16. #include "TrueColor v13.asc"
  17. #include "HH18savegems.asc"
  18.  
  19.  
  20.  
  21. bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {return true;}
  22.  
  23. void onLevelLoad()  {
  24.         gem::restorePlayerGems();
  25.         jjLevelName = ("@@@@@@@@@Central Area");
  26.         jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckpointWrapper;
  27.         jjObjectPresets[OBJECT::SAVEPOST].deactivates = false;
  28.  
  29.         jjObjectPresets[OBJECT::GRASSPLATFORM].scriptedCollisions = true;
  30.         jjObjectPresets[OBJECT::GRASSPLATFORM].behavior = SpikeBump();
  31.         jjObjectPresets[OBJECT::GRASSPLATFORM].deactivates = false;
  32.  
  33.         jjObjectPresets[OBJECT::AIRBOARD].behavior = Key();
  34.         jjObjectPresets[OBJECT::AIRBOARD].scriptedCollisions = true;
  35.  
  36.         jjObjectPresets[OBJECT::FREEZEENEMIES].behavior = Key2();
  37.         jjObjectPresets[OBJECT::FREEZEENEMIES].scriptedCollisions = true;
  38.  
  39.         jjObjectPresets[OBJECT::RFAMMO15].behavior = Backfiregun();
  40.         jjObjectPresets[OBJECT::RFAMMO15].scriptedCollisions = true;
  41.         jjObjectPresets[OBJECT::RFAMMO15].playerHandling = HANDLING::SPECIAL;
  42.  
  43.         jjObjectPresets[OBJECT::RFAMMO3].deactivates = false;
  44.  
  45.         jjObjectPresets[OBJECT::MOTH].behavior = Magic;
  46.  
  47.         jjObjectPresets[OBJECT::STOPWATCH].behavior = Key3();
  48.         jjObjectPresets[OBJECT::STOPWATCH].scriptedCollisions = true;
  49.  
  50.         jjObjectPresets[OBJECT::FASTFEET].behavior = CountlessJumps();
  51.         jjObjectPresets[OBJECT::FASTFEET].scriptedCollisions = true;
  52.  
  53.         jjObjectPresets[OBJECT::EVA].behavior = TimeMachine;
  54.         jjObjectPresets[OBJECT::EVA].determineCurAnim(ANIM::FLAG, 1);
  55.         jjObjectPresets[OBJECT::EVA].putOnGround(false);
  56.         jjObjectPresets[OBJECT::EVA].scriptedCollisions = true;
  57.         jjANIMATION@ tmAnim = jjAnimations[jjObjectPresets[OBJECT::EVA].curAnim];
  58.         for (uint i = 0; i < tmAnim.frameCount; ++i)
  59.                 jjAnimFrames[tmAnim.firstFrame + i].coldSpotY = -44;
  60.         for (uint i = 0; i < tmAnim.frameCount; ++i)
  61.                 jjAnimFrames[tmAnim.firstFrame + i].hotSpotY = -64;
  62.         for (uint i = 0; i < tmAnim.frameCount; ++i)
  63.                 jjAnimFrames[tmAnim.firstFrame + i].hotSpotX = -54;
  64.  
  65.         jjObjectPresets[OBJECT::FLYCARROT].determineCurAnim(ANIM::PLUS_WARP, 0);
  66.         jjObjectPresets[OBJECT::FLYCARROT].behavior = Bonus;
  67.         jjObjectPresets[OBJECT::FLYCARROT].scriptedCollisions = true;
  68.         jjANIMATION@ BAnim = jjAnimations[jjObjectPresets[OBJECT::FLYCARROT].curAnim];
  69.         for (uint i = 0; i < BAnim.frameCount; ++i)
  70.                 jjAnimFrames[BAnim.firstFrame + i].hotSpotY = -85;
  71.         for (uint i = 0; i < BAnim.frameCount; ++i)
  72.                 jjAnimFrames[BAnim.firstFrame + i].hotSpotX = 20;
  73.  
  74.         jjObjectPresets[OBJECT::INVINCIBILITY].determineCurAnim(ANIM::PLUS_WARP, 1);
  75.         jjObjectPresets[OBJECT::INVINCIBILITY].behavior = Bonuseye;
  76.         jjObjectPresets[OBJECT::INVINCIBILITY].scriptedCollisions = true;
  77.         jjObjectPresets[OBJECT::INVINCIBILITY].bulletHandling = HANDLING::IGNOREBULLET;
  78.         jjObjectPresets[OBJECT::INVINCIBILITY].playerHandling = HANDLING::PLAYERBULLET;
  79.         jjANIMATION@ BeAnim = jjAnimations[jjObjectPresets[OBJECT::INVINCIBILITY].curAnim];
  80.  
  81.         for (uint i = 0; i < BeAnim.frameCount; ++i)
  82.                 jjAnimFrames[BeAnim.firstFrame + i].hotSpotY = 16;
  83.         for (uint i = 0; i < BeAnim.frameCount; ++i)
  84.                 jjAnimFrames[BeAnim.firstFrame + i].hotSpotX = 26;
  85.  
  86.         Jazz1::MakeEnemy(OBJECT::DRAGONFLY, Jazz1::Enemies::Jungrock_RedBuzzer).SetUsesJJ2StyleDeathAnimation(true);
  87.         Jazz1::MakeEnemy(OBJECT::LIZARD, Jazz1::Enemies::Holidaius_SnowMonkey).SetUsesJJ2StyleDeathAnimation(true);
  88.  
  89.         jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::SPIKEBOLL3D].curAnim];
  90.         anim.frameCount = 1;
  91.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  92.         jjPIXELMAP pump(0, 44*32, 3*32, 3*32, 3);
  93.         pump.save(frame);
  94.         frame.hotSpotY = -47;
  95.         frame.hotSpotX = -47;
  96.  
  97. }
  98. void Bonuseye(jjOBJ@ obj){
  99.         obj.direction = -1;
  100.         obj.behave(BEHAVIOR::PICKUP, false);
  101.         obj.draw();
  102. }
  103. void Bonus(jjOBJ@ obj){
  104.         obj.putOnGround();
  105.         obj.direction = -1;
  106.         obj.behave(BEHAVIOR::PICKUP, false);
  107.         obj.draw();
  108. }
  109.  
  110.  
  111. int spring = 0;
  112. int CountlessDoubleJumps = 0;
  113.  
  114. void TimeMachine(jjOBJ@ obj){
  115.         obj.behave(BEHAVIOR::EVA, false);
  116.                         jjDrawResizedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, 1, SPRITE::PALSHIFT, 8);
  117.  
  118. }
  119.  
  120. void Magic(jjOBJ@ obj) {
  121.         obj.behave(BEHAVIOR::MOTH,false);
  122.         if(jjTriggers[1] == true){
  123.                 jjPARTICLE@ particle = jjAddParticle(PARTICLE::FLOWER);
  124.                 particle.xPos = obj.xPos;
  125.                 particle.yPos = obj.yPos;
  126.         }
  127. }
  128. void onFunction0(jjPLAYER@ p) {
  129.                 {jjNxt("mo4a_save6", false, true);
  130.                 gem::saveGemData();}
  131. }
  132.  
  133. void onFunction1(jjPLAYER@ p) {
  134.         p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@Sacred Woods", STRING::MEDIUM);
  135.         jjEnabledASFunctions[1] = false;}
  136.  
  137. void onFunction2(jjPLAYER@ p) {
  138.         p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@Totem Hills", STRING::MEDIUM);
  139.         jjEnabledASFunctions[2] = false;}
  140.  
  141. void onFunction3(jjPLAYER@ p) {
  142.         p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@Temple of Sun", STRING::MEDIUM);
  143.         jjEnabledASFunctions[3] = false;}
  144.  
  145. bool buyammo = false;
  146. void onPlayer(jjPLAYER@ p) {
  147.         gem::trackPlayerGems(p);
  148.         gem::upgradeHealth(p);
  149.  
  150.         if(p.ammo[WEAPON::RF] < 1 && p.xPos>31*32 && p.xPos<36*32 && p.yPos<52*32 && p.yPos>49*32 && jjKey[0x50] && p.gems[GEM::RED]<50 && buyammo == false)
  151.                 {p.testForGems(50, GEM::RED);}
  152.         if(p.ammo[WEAPON::RF] < 1 && p.xPos>31*32 && p.xPos<36*32 && p.yPos<52*32 && p.yPos>49*32 && jjKey[0x50] && p.gems[GEM::RED]>=50 && buyammo == false)
  153.                 {p.testForGems(50, GEM::RED);
  154.         jjTriggers[0]=true;
  155.         buyammo = true;}
  156.         if(p.ammo[WEAPON::RF] >= 1 && p.xPos>31*32 && p.xPos<36*32 && p.yPos<52*32 && p.yPos>49*32 && jjKey[0x50] && buyammo == false)
  157.                 {p.showText("@@You already have the weapon.");}
  158.  
  159.         if(spring > jjGameTicks)
  160.                 {p.ySpeed = -10;}
  161.         p.lightType = LIGHT::NONE;
  162.         if (CountlessDoubleJumps > jjGameTicks && p.keyDown) {
  163.                 p.buttstomp = 90;}
  164.         if (CountlessDoubleJumps > jjGameTicks && p.keyJump) {
  165.                 p.doubleJumpCount = 0;}
  166.         if (p.health == 0){
  167.                 CountlessDoubleJumps  = 0;
  168. }
  169. }
  170.  
  171.  
  172. class Backfiregun : jjBEHAVIORINTERFACE {
  173.  
  174.         void onBehave(jjOBJ@ obj) {
  175.                 if(p.ammo[WEAPON::RF] > 1)
  176.                 {obj.delete();}
  177.                 obj.behave(BEHAVIOR::MONITOR); 
  178.         }
  179.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
  180.                 if(bullet !is null && play !is null)
  181.                         {obj.behave(BEHAVIOR::EXPLOSION2);
  182.                         jjAddObject(OBJECT::RFAMMO3, 32*32, 42*32);
  183.                         jjAddObject(OBJECT::RFAMMO3, 33*32, 42*32);                    
  184.                         jjAddObject(OBJECT::RFAMMO3, 32*32, 43*32);
  185.                         jjAddObject(OBJECT::RFAMMO3, 33*32, 43*32);
  186.                         jjAddObject(OBJECT::RFAMMO3, 58*32, 49*32);
  187.                         jjAddObject(OBJECT::RFAMMO3, 57*32, 50*32);
  188.                         jjAddObject(OBJECT::RFAMMO3, 58*32, 51*32);
  189.                         jjAddObject(OBJECT::RFAMMO3, 57*32, 52*32);
  190.                         jjAddObject(OBJECT::RFAMMO3, 58*32, 53*32);
  191.                         jjSample(p.xPos, p.yPos, SOUND::COMMON_HARP1, 1000);}
  192.                 return true;
  193.         }
  194.        
  195. }
  196.  
  197. bool spike = false;
  198. class SpikeBump : jjBEHAVIORINTERFACE {
  199.  
  200. void onBehave(jjOBJ@ obj) {
  201.  
  202.         switch (obj.state) {
  203.                 case STATE::START:
  204.                         obj.determineCurFrame();
  205.                         obj.state = STATE::FLY;
  206.  
  207.                 case STATE::FLY:
  208.                         obj.playerHandling = HANDLING::SPECIAL;
  209.                         obj.deactivates = false;
  210.                         if (jjMaskedVLine(obj.xSpeed > 0 ? obj.xPos + 16 : obj.xPos - 16, obj.yPos, 1)) {
  211.                                 obj.direction = obj.xSpeed = -obj.xSpeed;
  212.                         }
  213.                        
  214.                         if(obj.xPos > p.xPos + 64 || obj.xPos < p.xPos - 64 || obj.yPos > p.yPos + 64 || obj.yPos < p.yPos - 64)
  215.                         {obj.determineCurAnim(ANIM::BOLLPLAT, 0);
  216.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -8);
  217.                         obj.determineCurFrame();}
  218.                         else {jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -24);}
  219.                         break;
  220.  
  221.                
  222.         }
  223. }
  224.  
  225.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
  226.                 if(bullet is null && play !is null && (force == -1 || force == 1))
  227.                         {spring = jjGameTicks + 1*61;
  228.                         obj.determineCurAnim(ANIM::BOLLPLAT, 0);
  229.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -24);
  230.                         obj.determineCurFrame();
  231.                         p.buttstomp = 121;
  232.                         jjSample(obj.xPos, obj.yPos, SOUND::PINBALL_BELL, 1000);}
  233.                 if(force == 0 && spring < jjGameTicks)
  234.                         {play.hurt();
  235.                         obj.determineCurAnim(ANIM::SPIKEBOLL, 0);  
  236.                         obj.determineCurFrame();
  237.                         if(spike == false)
  238.                         {jjSample(obj.xPos, obj.yPos, SOUND::COMMON_METALHIT, 1000);}
  239.                         spike = true;}
  240.                 return true;
  241.                 }
  242.  
  243. }
  244.  
  245. void onMain() {
  246. gem::deleteCollectedGems();
  247. if(jjKey[9] && jjKey[0x51]) {
  248. p.morphTo(CHAR::JAZZ, false);
  249. }
  250. if(jjKey[9] && jjKey[0x57]) {
  251. p.morphTo(CHAR::SPAZ, false);
  252. }
  253. if(jjKey[9] && jjKey[0x45]) {
  254. p.morphTo(CHAR::LORI, false);
  255. }
  256. }
  257.  
  258.  
  259. class CountlessJumps : jjBEHAVIORINTERFACE {
  260.         void onBehave(jjOBJ@ obj) {
  261.  
  262. obj.behave(BEHAVIOR::PICKUP, false);
  263. obj.determineCurAnim(ANIM::PICKUPS, 33);
  264. ++obj.counter;
  265.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  266.  
  267.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 16);
  268.  
  269. }
  270.  
  271.         bool onObjectHit(jjOBJ@ obj, jjOBJ@, jjPLAYER@ play, int force) {
  272.                 play.timerStart(600);
  273.                 p.morphTo(CHAR::SPAZ);
  274.                 CountlessDoubleJumps = jjGameTicks + 10 * 61;
  275.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  276.                 obj.frameID = 0;
  277.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PICKUP1, 6000);
  278.  
  279.                 return true;
  280.         }
  281.  
  282. }
  283.  
  284. class Key : jjBEHAVIORINTERFACE {
  285.  
  286.         void onBehave(jjOBJ@ obj) {
  287.                 obj.behave(BEHAVIOR::PICKUP, false);
  288.         jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::AIRBOARD].curAnim];
  289.         anim.frameCount = 1;
  290.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  291.         jjPIXELMAP pump(0, 4*32, 1*32, 1*32, 4);
  292.         pump.save(frame);
  293.                 ++obj.counter;
  294.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  295.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
  296. }
  297.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  298.                 jjSwitchTrigger(3);
  299.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  300.                 jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
  301.  
  302.                 return true;
  303.         }
  304. }
  305.  
  306.  
  307. class Key2 : jjBEHAVIORINTERFACE {
  308.  
  309.         void onBehave(jjOBJ@ obj) {
  310.                 obj.behave(BEHAVIOR::PICKUP, false);
  311. jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::FREEZEENEMIES].curAnim];
  312.         anim.frameCount = 1;
  313.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  314.         jjPIXELMAP pump(0, 3*32, 1*32, 1*32, 4);
  315.         pump.save(frame);
  316.                 ++obj.counter;
  317.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  318.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
  319. }
  320.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  321.                 jjSwitchTrigger(8);
  322.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  323.                 jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
  324.  
  325.                 return true;
  326.         }
  327. }
  328.  
  329.  
  330.  
  331. class Key3 : jjBEHAVIORINTERFACE {
  332.  
  333.         void onBehave(jjOBJ@ obj) {
  334.                 obj.behave(BEHAVIOR::PICKUP, false);
  335.  
  336. jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::STOPWATCH].curAnim];
  337.         anim.frameCount = 1;
  338.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  339.         jjPIXELMAP pump(0, 5*32, 1*32, 1*32, 4);
  340.         pump.save(frame);
  341.                 ++obj.counter;
  342.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  343.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
  344. }
  345.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  346.                 jjSwitchTrigger(15);
  347.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  348.                 jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
  349.  
  350.                 return true;
  351.         }
  352. }
  353. void onLevelReload() {
  354.         for (uint i = 0; i < 32; ++i)
  355.                 jjTriggers[i] = SavedTriggers[i];
  356.         gem::restorePlayerGems();
  357.         jjLocalPlayers[0].lives++;
  358.         buyammo == false;
  359.         jjTriggers[0] = false;
  360.         jjWaterLighting = WATERLIGHT::GLOBAL;
  361. }
  362.  
  363. array<bool> SavedTriggers(32, false);
  364. //Extendable Checkpoints by VioletCLM
  365. void CheckpointWrapper(jjOBJ@ obj) {
  366.   if (obj.state == STATE::STOP) { //don't do anything anymore
  367.     jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
  368.   } else if (obj.state == STATE::DEACTIVATE) { //due to death
  369.     obj.deactivate();
  370.   } else {
  371.     obj.behave(BEHAVIOR::CHECKPOINT);
  372.         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
  373.     if (obj.state == STATE::DONE) { //triggered by the player hitting it
  374.       obj.state = STATE::STOP;
  375.       //save the current state of some properties
  376.       for (uint i = 0; i < 32; ++i)
  377.         SavedTriggers[i] = jjTriggers[i];
  378.  
  379.       //OPTIONAL: this loop makes checkpoints reusable, so only the most recent checkpoint you touched is ever active
  380.       for (int i = jjObjectCount; --i > 0;) {
  381.         jjOBJ@ obj2 = jjObjects[i];
  382.         if (obj2.eventID == OBJECT::CHECKPOINT && i != obj.objectID && obj2.isActive) {
  383.           obj2.state = STATE::SLEEP;
  384.           obj2.var[0] = 0;
  385.         }
  386.       }
  387.     }
  388.   }
  389. }
  390.  
  391. jjTEXTAPPEARANCE SignTextAppearance = STRING::NORMAL;
  392. class Sign {
  393.         private int xPos, yPos; //These pixel-based positions will be generated from tile-based positions in the constructor by multiplying by 32
  394.         private string text;
  395.         private uint widthOfText;
  396.         Sign(){} //AngelScript requires any class that appears in any array to have an explicit default constructor, even if it's never called
  397.         Sign(int xTile, int yTile, const string &in t) {
  398.                 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
  399.                 yPos = yTile * 32; //
  400.                 text = t;
  401.                 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.
  402.                 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.
  403.                 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.
  404.         }
  405.         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.
  406.                 layer.drawRectangle(xPos, yPos - 16, widthOfText + 8, 55, 0, SPRITE::TRANSLUCENT);
  407.                 layer.drawString(xPos, yPos, text, STRING::SMALL, SignTextAppearance, 0, SPRITE::BLEND_HARDLIGHT, textIntensity);
  408.         }
  409. }
  410. const array<Sign> Signs = {
  411.         Sign(164, 61, "Thank you for everything, stanger.\nVisit anytime."),
  412.         Sign(35, 49, "Press P if you'd like to\nbuy Backfire Gun for 50 Gems."),
  413. };
  414.  
  415. void onDrawLayer3(jjPLAYER@, jjCANVAS@ layer) {
  416.         if(jjKey[0x54]){
  417.         const uint8 textIntensity = 200 + int(jjSin(jjGameTicks * 16) * 50);
  418.         for (uint signID = 0; signID < Signs.length; ++signID)
  419.                 Signs[signID].draw(layer, textIntensity);
  420. }
  421. }
  422. bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
  423.         return MLLE::WeaponHook.drawAmmo(player, canvas);
  424. }
  425.