Downloads containing mo4a_1-3.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-3.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. #pragma require "mo4a_1.pal"
  13. bool ElevatorUp = false, totemfixed = false;
  14. int exit = 0;
  15.  
  16.  
  17. void onLevelLoad()  {
  18.         gem::restorePlayerGems();
  19.         jjWaterLighting = WATERLIGHT::GLOBAL;
  20.         jjSetWaterLevel(2456, true);
  21.         jjLevelName = ("@@@@@@@@@Totem Hills");
  22.         jjIsSnowing = true;
  23.         jjSnowingType = SNOWING::RAIN;
  24.         jjSnowingIntensity = 22;
  25.  
  26.         if(jjTriggers[9] == false) {
  27.         jjPAL firePal;
  28.         firePal.load("mo4a_1.pal");
  29.         firePal.apply();}
  30.  
  31.         jjObjectPresets[OBJECT::AIRBOARD].behavior = Key();
  32.         jjObjectPresets[OBJECT::AIRBOARD].scriptedCollisions = true;
  33.  
  34.         Jazz1::MakeEnemy(OBJECT::DRAGONFLY, Jazz1::Enemies::Jungrock_RedBuzzer).SetUsesJJ2StyleDeathAnimation(true);
  35.         Jazz1::MakeEnemy(OBJECT::NORMTURTLE, Jazz1::Enemies::Holidaius_SnowMonkey).SetUsesJJ2StyleDeathAnimation(true);
  36.         Jazz1::MakeEnemy(OBJECT::HATTER, Jazz1::Enemies::Marbelara_Schwarzenguard, true).SetUsesJJ2StyleDeathAnimation(true).SetBulletFireSound(SOUND::INTRO_SHOT1).SetBulletExplosionSound(SOUND::COMMON_GUNSM1);
  37.  
  38.         jjObjectPresets[OBJECT::SILVERCOIN].behavior = PlatinCoin();
  39.         jjObjectPresets[OBJECT::SILVERCOIN].scriptedCollisions = true;
  40.  
  41.         jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckpointWrapper;
  42.         jjObjectPresets[OBJECT::SAVEPOST].deactivates = false;
  43.  
  44.         jjObjectPresets[OBJECT::SONICPLATFORM].scriptedCollisions = true;
  45.         jjObjectPresets[OBJECT::SONICPLATFORM].behavior = Elevator;
  46.         jjObjectPresets[OBJECT::SONICPLATFORM].deactivates = false;
  47.  
  48.  jjPIXELMAP rain(32,32);
  49.   for (uint x = 0; x < rain.width; ++x) {
  50.     for (uint y = 0; y < rain.height; ++y) {
  51.       if (x == 16) { //draw in the middle of the tile, xPixel 16
  52.         if (y <= 24) rain[x,y] = 75; //if at yPixel 24 or less, use color 75
  53.         else rain[x,y] = 74; //use color 74 for yPixels 25-32
  54.       } else {
  55.         rain[x,y] = 0;
  56.       }
  57.     }
  58.   }
  59.  
  60.   jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::COMMON].firstAnim + 2];
  61.   for (uint frameID = 0; frameID < anim.frameCount; ++frameID) {
  62.     jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame + frameID];
  63.     rain.save(frame);
  64.     frame.hotSpotX = -frame.width/2;
  65.     frame.hotSpotY = -frame.height;
  66.   }
  67.  
  68. }
  69.  
  70. bool keypicked = false;
  71.  
  72. class Key : jjBEHAVIORINTERFACE {
  73.  
  74.         void onBehave(jjOBJ@ obj) {
  75.                 obj.behave(BEHAVIOR::PICKUP, false);
  76.         jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::AIRBOARD].curAnim];
  77.         anim.frameCount = 1;
  78.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  79.         jjPIXELMAP key(0, 40*32, 1*32, 1*32, 4);
  80.         frame.hotSpotY = -frame.width/2;
  81.         key.save(frame);
  82.                 ++obj.counter;
  83.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  84.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL);
  85. }
  86.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  87.                 jjTriggers[10] = true;
  88.                 keypicked = true;
  89.                 p.cameraFreeze(118*32, 58*32, true, true);
  90.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  91.                 jjSample(obj.xPos, obj.yPos, SOUND::MENUSOUNDS_TYPEENTER, 1000);
  92.  
  93.                 return true;
  94.         }
  95. }
  96.  
  97. void Elevator (jjOBJ@ obj) {
  98.  
  99.         switch (obj.state) {
  100.                 case STATE::START:
  101.                         obj.direction = obj.ySpeed = 2;
  102.                         obj.determineCurAnim(ANIM::SONICPLAT, 0);  
  103.                         obj.determineCurFrame();
  104.                         obj.playerHandling = HANDLING::SPECIAL;
  105.                         obj.state = STATE::FLY;
  106.                         obj.beSolid();
  107.                 case STATE::FLY:
  108.                         obj.beSolid();
  109.                         if(p.keyUp && ElevatorUp == true && p.xPos>obj.xPos-32 && p.xPos<obj.xPos+32) {
  110.                         obj.yPos = obj.yPos + obj.ySpeed;
  111.                         obj.direction = obj.ySpeed = -2;
  112.                          }
  113.                         if(p.keyDown && ElevatorUp == true && p.xPos>obj.xPos-32 && p.xPos<obj.xPos+32) {
  114.                         obj.yPos = obj.yPos + obj.ySpeed;
  115.                         p.buttstomp = 90;
  116.                         p.ySpeed=3;
  117.                         obj.direction = obj.ySpeed = 2;
  118.                         if (jjMaskedVLine(obj.xSpeed > 0 ? obj.xPos + 16 : obj.xPos - 16, obj.yPos, 1)) {
  119.                                 obj.ySpeed = 0;
  120.                         }
  121.                         }
  122.                         obj.draw();
  123.                         break;
  124.  
  125.         }
  126. }
  127.  
  128. void onMain() {
  129.         jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::ORANGE].curAnim];
  130.         anim.frameCount = 1;
  131.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  132.         jjPIXELMAP fruit(0, 41*32, 1*32, 1*32, 4);
  133.         fruit.save(frame);
  134. gem::deleteCollectedGems();
  135.  
  136. if(jjKey[9] && jjKey[0x51]) {
  137. p.morphTo(CHAR::JAZZ, false);
  138. }
  139. if(jjKey[9] && jjKey[0x57]) {
  140. p.morphTo(CHAR::SPAZ, false);
  141. }
  142. if(jjKey[9] && jjKey[0x45]) {
  143. p.morphTo(CHAR::LORI, false);
  144. }
  145.  
  146. }
  147.  
  148.  
  149. class PlatinCoin : jjBEHAVIORINTERFACE {
  150.  
  151.         void onBehave(jjOBJ@ obj) {
  152.                 if(p.coins >= 3)
  153.                 {obj.delete();}
  154.                 obj.behave(BEHAVIOR::PICKUP, false);
  155.                 ++obj.counter;
  156.                 obj.yPos = jjSin(obj.counter*15 + 5)*4 + obj.yOrg;
  157.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, -8);
  158. }
  159.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  160.                 p.coins += 1;
  161.                 obj.behavior = BEHAVIOR::EXPLOSION2;
  162.                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_COIN, 1000);
  163.  
  164.                 return true;
  165.         }
  166. }
  167.  
  168. bool startrush = false, readytorush = false, control = true;
  169.  
  170. void onPlayer(jjPLAYER@ play) {
  171.                 if(control==false)
  172.                 {p.keyLeft = false;
  173.                 p.keyRight = false;
  174.                 p.keyDown = false;}
  175.  
  176.                 if(p.idle > 100)
  177.                 {p.cameraUnfreeze(true);
  178.                 control=true;}
  179.                 else if (p.idle > 5 && (p.keyLeft || p.keyRight || p.keyJump || p.keyFire))
  180.                 {p.cameraUnfreeze(true);
  181.                 control=true;}
  182.  
  183.         jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::EXTRALIFE].curAnim];
  184.         anim.frameCount = 1;
  185.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  186.         jjPIXELMAP to(0, 39*32, 1*32, 1*32, 4);
  187.         frame.hotSpotY = -frame.width/2;
  188.         to.save(frame);
  189.  
  190.  
  191.         if(p.xPos > 116*32) {
  192.         jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::AIRBOARD].curAnim];
  193.         anim.frameCount = 1;
  194.         jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
  195.         jjPIXELMAP key(0, 40*32, 1*32, 1*32, 4);
  196.         frame.hotSpotY = -frame.width/2; }
  197.  
  198.   for (int i = 0; i < 1024; i++) { //loop through the global array jjParticles[1024]
  199.     jjPARTICLE@ particle = jjParticles[i];
  200.     if (particle.type == PARTICLE::RAIN) {
  201.       particle.xSpeed = 0; //make rain fall straight down
  202.       particle.ySpeed = play.ySpeed < 0? 10 : int(10 + play.ySpeed); //the rain speed accounts for differences in the player speed, and so won't appear to fall more slowly when the player is falling
  203.     }
  204.   }
  205.         gem::trackPlayerGems(p);
  206.         gem::upgradeHealth(p);
  207.  
  208.         if(p.food == 100 && jjKey[0x52] == false && startrush == false)
  209.                 {p.showText("@@@@@@@@@@@@@@@@Press 'R' when you need to use Sugar Rush!", STRING::MEDIUM);
  210.                 p.startSugarRush(0);
  211.                 startrush = true;
  212.                 readytorush = true;}
  213.  
  214.         if(readytorush == true)
  215.                 {p.food = 100;}
  216.  
  217.         if(p.food == 100 && jjKey[0x52])
  218.                 {p.startSugarRush(1400);
  219.                 p.food = 0;
  220.                 readytorush = false;
  221.                 startrush = false;
  222.         }
  223.  
  224.         exit = 0;
  225.         if(jjTriggers[9]==true)
  226.                 {jjIsSnowing = false;
  227.                 exit += 1;}
  228.         if(jjTriggers[7]==true)
  229.                 {exit += 1;}
  230.         if(jjTriggers[5]==true)
  231.                 {exit += 1;}
  232.         if(jjTriggers[4]==true)
  233.                 {exit += 1;}
  234.         if(jjTriggers[3]==true)
  235.                 {exit += 1;}
  236.         if(jjTriggers[2]==true)
  237.                 {exit += 1;}
  238.         if(jjTriggers[1]==true)
  239.                 {exit += 1;}
  240.  
  241.         if(jjTriggers[1]==true && jjTriggers[2]==true && jjTriggers[3]==true && jjTriggers[4]==true && jjTriggers[5]==true && jjTriggers[7]==true && jjTriggers[9]==true)
  242.                 {ElevatorUp = true;}
  243.  
  244.         p.lightType = LIGHT::NONE;
  245.         if(p.coins==0)
  246.                 {p.coins +=2;}
  247.         if(p.yPos > jjWaterLevel && totemfixed == false)
  248.                 {p.health = 0;}
  249.  
  250.         for (int i = 1; i < jjObjectCount; i++) {
  251.         jjOBJ@ o = jjObjects[i];
  252.                 if (o.isActive && o.eventID == OBJECT::SILVERCOIN && p.coins >= 3) {
  253.                 o.state = STATE::KILL;
  254.                 }
  255.  
  256.         }
  257.  
  258.         for (int i = 1; i < jjObjectCount; i++) {
  259.                 jjOBJ@ o = jjObjects[i];
  260.                 if (o.isActive && o.eventID == OBJECT::SEEKERAMMO3 && p.ammo[WEAPON::SEEKER] < 1) {
  261.                         o.state = STATE::KILL;}
  262.         }
  263.         for (int i = 1; i < jjObjectCount; i++) {
  264.                 jjOBJ@ o = jjObjects[i];
  265.                 if (o.isActive && o.eventID == OBJECT::RFAMMO3 && p.ammo[WEAPON::RF] < 1) {
  266.                         o.state = STATE::KILL;}
  267.         }
  268.         for (int i = 1; i < jjObjectCount; i++) {
  269.                 jjOBJ@ o = jjObjects[i];
  270.                 if (o.isActive && o.eventID == OBJECT::TNTAMMO3 && p.ammo[WEAPON::TNT] < 1) {
  271.                         o.state = STATE::KILL;}
  272.         }
  273.         for (int i = 1; i < jjObjectCount; i++) {
  274.                 jjOBJ@ o = jjObjects[i];
  275.                 if (o.isActive && o.eventID == OBJECT::GUN9AMMO3 && p.ammo[WEAPON::GUN9] < 1) {
  276.                         o.state = STATE::KILL;}
  277.         }
  278.         for (int i = 1; i < jjObjectCount; i++) {
  279.                 jjOBJ@ o = jjObjects[i];
  280.                 if (o.isActive && o.eventID == OBJECT::SEEKERPOWERUP && p.ammo[WEAPON::SEEKER] < 1) {
  281.                         o.state = STATE::KILL;}
  282.         }
  283.         for (int i = 1; i < jjObjectCount; i++) {
  284.                 jjOBJ@ o = jjObjects[i];
  285.                 if (o.isActive && o.eventID == OBJECT::RFPOWERUP && p.ammo[WEAPON::RF] < 1) {
  286.                         o.state = STATE::KILL;}
  287.         }
  288.         for (int i = 1; i < jjObjectCount; i++) {
  289.                 jjOBJ@ o = jjObjects[i];
  290.                 if (o.isActive && o.eventID == OBJECT::TNTPOWERUP && p.ammo[WEAPON::TNT] < 1) {
  291.                         o.state = STATE::KILL;}
  292.         }
  293.         for (int i = 1; i < jjObjectCount; i++) {
  294.                 jjOBJ@ o = jjObjects[i];
  295.                 if (o.isActive && o.eventID == OBJECT::GUN9POWERUP && p.ammo[WEAPON::GUN9] < 1) {
  296.                         o.state = STATE::KILL;}
  297.         }
  298. }
  299.  
  300.  
  301. void onFunction0(jjPLAYER@ p){
  302.         p.cameraFreeze(122*32, 2*32, true, true);
  303.  
  304.  
  305. }
  306. void onFunction1(jjPLAYER@ p){
  307.         p.cameraUnfreeze();
  308. }
  309.  
  310. void onFunction2(jjPLAYER@ p){
  311.         if(jjTriggers[2] == false && totemfixed == false){
  312.         jjSetWaterLevel(320, false);
  313.         jjTriggers[2] = true;}
  314.  
  315. }
  316.  
  317. void onFunction3(jjPLAYER@ p){
  318.         if(jjTriggers[3] == false){
  319.         jjSetWaterLevel(72*32, false);
  320.         jjTriggers[3] = true;}
  321. }
  322.  
  323. void onFunction4(jjPLAYER@ p){
  324.         if(jjTriggers[4] == false && totemfixed == false){
  325.         jjSetWaterLevel(1472, false);
  326.         jjTriggers[4] = true;}
  327. }
  328.  
  329. void onFunction5(jjPLAYER@ p){
  330.         if(jjTriggers[5] == false && totemfixed == false){
  331.         jjSetWaterLevel(77*32, false);
  332.         jjTriggers[5] = true;}
  333.  
  334. }
  335.  
  336. void onFunction6(jjPLAYER@ p){
  337.         if(ElevatorUp == true)
  338.                 {p.showText("@@Press UP and DOWN to move the platform.");}
  339.         if(ElevatorUp == false)
  340.                 {p.showText("@@I have to fix all of the totems@to be able to use this platform.");}
  341. }
  342.  
  343.  
  344. void onFunction7(jjPLAYER@ p){
  345.         if(jjTriggers[7] == false && totemfixed == false){
  346.         jjSetWaterLevel(320, false);
  347.         jjTriggers[7] = true;}
  348.  
  349. }
  350.  
  351. void onFunction8(jjPLAYER@ p){
  352.         if(jjTriggers[1]==false && totemfixed == false)
  353.         jjSetWaterLevel(320, false);
  354.  
  355. }
  356.  
  357.  
  358. void onFunction9(jjPLAYER@ p){
  359.         jjPAL resPal;
  360.         resPal.load("Aztec Empire.j2t");
  361.         resPal.apply();
  362.         if(jjTriggers[9] == false && totemfixed == false){
  363.         p.showText("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Totems fixed.");
  364.         jjSetWaterLevel(3000, true);
  365.         totemfixed = true;
  366.         jjMusicLoad("mo4a_South Plain - Part 1.ogg");
  367.         jjLayerOrderSet(array<jjLAYER@> = {jjLayers[2], jjLayers[3], jjLayers[4], jjLayers[5], jjLayers[6], jjLayers[7], jjLayers[8], jjLayers[1]});
  368.         jjTriggers[9] = true;
  369.         }
  370. }
  371.  
  372. void onFunction10(jjPLAYER@ p){
  373.         if(jjTriggers[1] == false){
  374.         jjSetWaterLevel(68*32, false);
  375.         jjTriggers[1] = true;}
  376.  
  377. }
  378.  
  379. void onFunction11(jjPLAYER@ p){
  380.         if(jjTriggers[3]==false){
  381.         jjSetWaterLevel(49*32, true);
  382.         jjTriggers[2] = false;}
  383.  
  384. }
  385.  
  386. void onFunction12(jjPLAYER@ p){
  387.         if(jjTriggers[3]==true){
  388.         jjSetWaterLevel(77*32, true);}
  389.  
  390. }
  391.  
  392. void onFunction13(jjPLAYER@ p){
  393.         if(jjTriggers[5] == false)
  394.         {jjSetWaterLevel(59*32, false);}
  395. }
  396.  
  397. void onFunction14(jjPLAYER@ p){
  398.         if(p.coins >=3){
  399.         jjNxt(false, true);
  400.         gem::saveGemData();}
  401.         if(p.coins <3){
  402.         p.testForCoins(3);}
  403. }
  404.  
  405. void onFunction15(jjPLAYER@ p){
  406.         if(jjTriggers[5] == false)
  407.         {jjSetWaterLevel(77*32, true);
  408.         keypicked = false;}
  409. }
  410.  
  411. void onFunction16(jjPLAYER@ p) {
  412.                 keypicked = false;
  413. }
  414.  
  415.  
  416. bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
  417.     canvas.drawSprite(20, 585, ANIM::PICKUPS, 0, jjGameTicks>>2, -1, SPRITE::NORMAL);
  418.     canvas.drawString(30, 585, formatInt(exit%8, "1") + "/7", STRING::SMALL, STRING::NORMAL);
  419. if(keypicked == true && p.xPos > 116*32 && jjTriggers[10] == true)
  420.     {canvas.drawSprite(100, 585, ANIM::PICKUPS, 36, jjGameTicks>>2, -1, SPRITE::NORMAL);}
  421.     return false;
  422. }
  423.  
  424. bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {return true;}
  425.  
  426.  
  427.  
  428. void onLevelReload() {
  429.         if(jjTriggers[9] == false) {
  430.         jjPAL firePal;
  431.         firePal.load("mo4a_1.pal");
  432.         firePal.apply();}
  433.  
  434.         if(jjTriggers[9] == true) {
  435.         jjPAL resPal;
  436.         resPal.load("Aztec Empire.j2t");
  437.         resPal.apply();}
  438.  
  439.         if(jjTriggers[9]==false)
  440.         {jjIsSnowing = true;
  441.         jjTriggers[7] = false;
  442.         jjSnowingType = SNOWING::RAIN;}
  443.  
  444.         if(jjTriggers[2]==false)
  445.         {jjIsSnowing = true;
  446.         jjTriggers[4] = false;
  447.         jjSnowingType = SNOWING::RAIN;}
  448.  
  449.         if(jjTriggers[3]==false)
  450.         {jjIsSnowing = true;
  451.         jjTriggers[2] = false;
  452.         jjSnowingType = SNOWING::RAIN;}
  453.  
  454.         if(p.xPos>192*32 && p.yPos >50*32 && jjTriggers[2] == false)
  455.                 {jjSetWaterLevel(68*32, true);}
  456.         if(p.xPos>160*32 && p.xPos<185*32 && p.yPos <28*32 && jjTriggers[3] == false)
  457.                 {jjSetWaterLevel(49*32, true);}
  458.         if(p.xPos>102*32 && p.xPos<124*32 && p.yPos <60*32 && p.yPos >55*32 && jjTriggers[5] == false)
  459.                 {jjSetWaterLevel(72*32, true);}
  460.         if(p.xPos<15*32 && p.xPos > 7*32 && p.yPos >66*32 && jjTriggers[9] == false)
  461.                 {jjSetWaterLevel(77*32, true);}
  462.         if(p.xPos<36*32 && p.yPos >56*32 && jjTriggers[7] == false)
  463.                 {jjSetWaterLevel(77*32, true);}
  464.  
  465.         jjEnableEachASFunction();
  466.         gem::restorePlayerGems();
  467.         jjLocalPlayers[0].lives++;
  468.         jjWaterLighting = WATERLIGHT::GLOBAL;
  469.         for (uint i = 0; i < 32; ++i)
  470.                 jjTriggers[i] = SavedTriggers[i];
  471. }
  472.  
  473. array<bool> SavedTriggers(32, false);
  474. //Extendable Checkpoints by VioletCLM
  475. void CheckpointWrapper(jjOBJ@ obj) {
  476.   if (obj.state == STATE::STOP) { //don't do anything anymore
  477.     jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
  478.   } else if (obj.state == STATE::DEACTIVATE) { //due to death
  479.     obj.deactivate();
  480.   } else {
  481.     obj.behave(BEHAVIOR::CHECKPOINT);
  482.         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, 8);
  483.     if (obj.state == STATE::DONE) { //triggered by the player hitting it
  484.       obj.state = STATE::STOP;
  485.       //save the current state of some properties
  486.       for (uint i = 0; i < 32; ++i)
  487.         SavedTriggers[i] = jjTriggers[i];
  488.  
  489.  
  490.     }
  491.   }
  492. }
  493.  
  494. jjTEXTAPPEARANCE SignTextAppearance = STRING::NORMAL;
  495. class Sign {
  496.         private int xPos, yPos; //These pixel-based positions will be generated from tile-based positions in the constructor by multiplying by 32
  497.         private string text;
  498.         private uint widthOfText;
  499.         Sign(){} //AngelScript requires any class that appears in any array to have an explicit default constructor, even if it's never called
  500.         Sign(int xTile, int yTile, const string &in t) {
  501.                 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
  502.                 yPos = yTile * 32; //
  503.                 text = t;
  504.                 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.
  505.                 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.
  506.                 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.
  507.         }
  508.         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.
  509.                 layer.drawRectangle(xPos, yPos - 16, widthOfText + 8, 55, 0, SPRITE::TRANSLUCENT);
  510.                 layer.drawString(xPos, yPos, text, STRING::SMALL, SignTextAppearance, 0, SPRITE::BLEND_HARDLIGHT, textIntensity);
  511.         }
  512. }
  513. const array<Sign> Signs = {
  514.         Sign(134, 58, "Find the 9 distorted totems and fix them.\nOr the city will be flooded."),
  515. };
  516.  
  517. void onDrawLayer3(jjPLAYER@, jjCANVAS@ layer) {
  518.         if(jjKey[0x54] && (jjTriggers[9] == false || p.yPos >68*32)){
  519.         const uint8 textIntensity = 200 + int(jjSin(jjGameTicks * 16) * 50);
  520.         for (uint signID = 0; signID < Signs.length; ++signID)
  521.                 Signs[signID].draw(layer, textIntensity);
  522. }
  523. }
  524. bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
  525.         return MLLE::WeaponHook.drawAmmo(player, canvas);
  526. }
  527.