Downloads containing ab21btl11.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Anniversary Bash 21 Levels Jazz2Online Multiple N/A Download file

File preview

  1. #include "MLLE-Include-1.4.asc"
  2. const bool MLLESetupSuccessful = MLLE::Setup();
  3. #pragma require "ab21btl11.j2l"
  4. #pragma require "Castle2N.j2t"
  5. #pragma require "ab21btl11-MLLE-Data-1.j2l"
  6. #pragma require "Sirius1.j2t"
  7. #pragma require "SwampsD.j2t"
  8. #pragma require "LaserBlaster.j2a"
  9. #pragma require "f_laser1.wav"
  10. #pragma require "f_laser2.wav"
  11. #pragma require "f_laser3.wav"
  12. #pragma require "f_laser4.wav"
  13. #pragma require "bow_fire_arrow.wav"
  14. #pragma require "floorslide.wav"
  15. #pragma require "floorslideclose.wav"
  16. #pragma require "expmine.wav"
  17. #pragma require "meteor.j2a"
  18. #pragma require "ArrowGun.j2a"
  19. #pragma require "SEroller.asc"
  20. #include "SEroller.asc"
  21. enum PacketType {
  22.         trapPacket,
  23.         weaponPacket
  24. }
  25. enum ProjectileDrawingMethod {
  26.         simple,
  27.         angled,
  28.         rotating
  29. }
  30. const int quitTrapSelectionKey = 0x08;
  31. se::DefaultWeaponHook weaponHook(weaponPacket);
  32. array<PlayerData> playerData(jjLocalPlayerCount);
  33. array<Trap> traps;
  34. interface TrapBehavior {
  35.         bool isActive() const;
  36.         void activate(jjPLAYER@ player);
  37.         void process();
  38. }
  39. class PlayerData {
  40.         int trapSelection = -1;
  41.         int trapPrice = 0;
  42.         bool prevKeyFire = false;
  43.         bool prevKeyLeft = false;
  44.         bool prevKeyRight = false;
  45. }
  46. class Trap {
  47.         string name;
  48.         string description;
  49.         float x;
  50.         float y;
  51.         TrapBehavior@ behavior;
  52. }
  53. mixin class TimedTrapBehavior : TrapBehavior {
  54.         bool isActive() const override {
  55.                 return timer != 0;
  56.         }
  57.         private int timer = 0;
  58. }
  59. class SpikeTrap : TimedTrapBehavior {
  60.         SpikeTrap(int xTile, int yTile, int width, int xorTile) {
  61.                 x1 = xTile;
  62.                 x2 = xTile + width;
  63.                 y = yTile;
  64.                 mask = xorTile;
  65.         }
  66.         void activate(jjPLAYER@ player) override {
  67.                 auto@ data = playerData[player.localPlayerID];
  68.                 if (!isActive()) {
  69.                         switchTiles();
  70.                         if (data.trapSelection >= 0) {
  71.                                 jjSample(73*32, 57*32, SOUND::COMMON_COLLAPS, 0, 0);
  72.                         } else {
  73.                                 jjSample(37*32, 80*32, SOUND::COMMON_COLLAPS, 0, 0);
  74.                         }
  75.                 }
  76.                 @activator = player;
  77.                 timer = 210;
  78.                 if (!player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Crystal Spike Trap", true, STRING::SMALL);
  79.                 if (jjIsServer && player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Crystal Spike Trap", false, STRING::SMALL);
  80.         }
  81.         void process() override {
  82.                 for (int i = 0; i < jjLocalPlayerCount; i++) {
  83.                         auto@ player = jjLocalPlayers[i];
  84.                         auto@ data = playerData[player.localPlayerID];
  85.                        
  86.                         if (isActive()) {
  87.                                 timer--;
  88.                                 if (!isActive()) {
  89.                                         switchTiles();
  90.                                         if (data.trapSelection >= 0) {
  91.                                                 jjSample(73*32, 57*32, SOUND::CATERPIL_RIDOE, 0, 0);
  92.                                         } else {
  93.                                                 jjSample(37*32, 80*32, SOUND::CATERPIL_RIDOE, 0, 0);
  94.                                         }
  95.                                 }
  96.                         }
  97.                         if (player.isInGame && player.xPos >= x1 << 5 && player.xPos < x2 << 5 && player.yPos > y << 5)
  98.                                 killLocalPlayerWithTrap(player, activator);
  99.                 }
  100.         }
  101.         protected void switchTiles() const {
  102.                 for (int i = x1; i < x2; i++) {
  103.                         jjTileSet(4, i, y, jjTileGet(4, i, y) ^ mask);
  104.                 }
  105.         }
  106.         private int x1;
  107.         private int x2;
  108.         private int y;
  109.         private int mask;
  110.         private jjPLAYER@ activator;
  111. }
  112. class FireTrap : TimedTrapBehavior {
  113.         FireTrap(int xTile, int yTile, int height) {
  114.                 x = xTile << 5;
  115.                 y = yTile << 5 | 16;
  116.                 count = height;
  117.         }
  118.         void activate(jjPLAYER@ player) override {
  119.                 @activator = player;
  120.                 timer = 210;
  121.                 if (!player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Fire Trap", true, STRING::SMALL);
  122.                 if (jjIsServer && player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Fire Trap", false, STRING::SMALL);
  123.         }
  124.         void process() override {
  125.                 if (isActive()) {
  126.                         timer--;
  127.                         for (int i = 0; i < 4; i++) {
  128.                                 sampleSource = jjSampleLooped(x, y + (i*32), SOUND::COMMON_FLAMER, sampleSource, 0, 0);
  129.                                 if (activator.isLocal) sampleActivator = jjSampleLooped(75*32, 58*32, SOUND::COMMON_FLAMER, sampleActivator, 0, 0);
  130.                         }
  131.                         if (timer & 1 == 0) {
  132.                                 int id = jjAddObject(OBJECT::BULLET, x, y + ((timer >> 1) % count << 5), activator.playerID, CREATOR::PLAYER);
  133.                                 if (id > 0) {
  134.                                         auto@ obj = jjObjects[id];
  135.                                         obj.age = 4;
  136.                                         obj.behavior = Projectile(simple);
  137.                                         obj.counterEnd = 36;
  138.                                         obj.determineCurAnim(ANIM::AMMO, 55);
  139.                                         obj.determineCurFrame();
  140.                                         obj.playerHandling = HANDLING::SPECIAL;
  141.                                         obj.scriptedCollisions = true;
  142.                                         obj.xSpeed = 9.f;
  143.                                        
  144.                                 }
  145.                         }
  146.                 }
  147.         }
  148.         private float x;
  149.         private float y;
  150.         private int count;
  151.         private int sampleSource, sampleActivator;
  152.         private jjPLAYER@ activator;
  153. }
  154. class VolcanoTrap : TimedTrapBehavior {
  155.         VolcanoTrap(int xTile, int yTile) {
  156.                 x = xTile << 5;
  157.                 y = yTile << 5;
  158.         }
  159.         void activate(jjPLAYER@ player) override {
  160.                 @activator = player;
  161.                 timer = 49;
  162.                 if (!player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Volcano Trap", true, STRING::SMALL);
  163.                 if (jjIsServer && player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Volcano Trap", false, STRING::SMALL);
  164.         }
  165.         void process() override {
  166.                 if (isActive()) {
  167.                         timer--;
  168.                         if (timer & 7 == 0) {
  169.                                 for (int i = timer != 0 ? -1 : 0; i <= 1; i += 2) {
  170.                                         int id = jjAddObject(OBJECT::BULLET, x, y, activator.playerID, CREATOR::PLAYER);
  171.                                         if (id > 0) {
  172.                                                 auto@ obj = jjObjects[id];
  173.                                                 jjSample(x, y - 64, jjRandom()%2 > 0? SOUND::AMMO_FIREGUN1A : SOUND::AMMO_FIREGUN2A, 0, 0);
  174.                                                 if (activator.isLocal) jjSample(75*32, 58*32, jjRandom()%2 > 0? SOUND::AMMO_FIREGUN1A : SOUND::AMMO_FIREGUN2A, 0, 0);
  175.                                                 obj.age = 7;
  176.                                                 obj.behavior = Projectile(angled);
  177.                                                 obj.determineCurAnim(ANIM::BILSBOSS, 3);
  178.                                                 obj.determineCurFrame();
  179.                                                 obj.killAnim = jjAnimSets[ANIM::AMMO] + 5;
  180.                                                 obj.playerHandling = HANDLING::SPECIAL;
  181.                                                 obj.scriptedCollisions = true;
  182.                                                 obj.xSpeed = (timer >> 3) * i * 0.55f;
  183.                                                 obj.ySpeed = -13.f;
  184.                                                 obj.yAcc = 0.25f;
  185.                                                 obj.var[1] = 1;
  186.                                                 if (activator.isLocal) obj.var[2] = 1;
  187.                                         }
  188.                                 }
  189.                         }
  190.                 }
  191.         }
  192.         private float x;
  193.         private float y;
  194.         private int sampleSource, sampleActivator;
  195.         private jjPLAYER@ activator;
  196. }
  197. class DartTrap : TimedTrapBehavior {
  198.         DartTrap(int xTile, int yTile) {
  199.                 x = xTile << 5;
  200.                 y = yTile << 5;
  201.         }
  202.         void activate(jjPLAYER@ player) override {
  203.                 @activator = player;
  204.                 timer = 1;
  205.                 if (!player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Dart Trap", true, STRING::SMALL);
  206.                 if (jjIsServer && player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Dart Trap", false, STRING::SMALL);
  207.         }
  208.         void process() override {
  209.                 if (isActive()) {
  210.                         timer--;
  211.                         if (timer < 1) {
  212.                                 for (int i = 0; i < 4; i++) {
  213.                                         jjSample(x, y + (i*32), SOUND::P2_PTOEI, 0, 0);
  214.                                         if (activator.isLocal) jjSample(75*32, 58*32, SOUND::P2_PTOEI, 0, 0);
  215.                                 }
  216.                         }
  217.                         if (timer & 1 == 0) {
  218.                                 for (int i = 0; i < 4; i++) {
  219.                                         int id = jjAddObject(OBJECT::BULLET, x, (y - 16) + (i*32), activator.playerID, CREATOR::PLAYER);
  220.                                         if (id > 0) {
  221.                                                 auto@ obj = jjObjects[id];
  222.                                                 obj.age = 7;
  223.                                                 obj.behavior = Projectile(simple);
  224.                                                 obj.determineCurAnim(ANIM::CUSTOM[2], 0);
  225.                                                 obj.determineCurFrame();
  226.                                                 obj.killAnim = jjAnimSets[ANIM::AMMO] + 4;
  227.                                                 obj.playerHandling = HANDLING::SPECIAL;
  228.                                                 obj.scriptedCollisions = true;
  229.                                                 obj.xSpeed = -12;
  230.                                                 obj.ySpeed = 0.5;
  231.                                                 obj.direction = -1;
  232.                                                 obj.var[1] = 2;
  233.                                                 if (activator.isLocal) obj.var[2] = 1;
  234.                                         }
  235.                                 }
  236.                         }
  237.                 }
  238.         }
  239.         private float x;
  240.         private float y;
  241.         private jjPLAYER@ activator;
  242. }
  243. class RockTrap : TimedTrapBehavior {
  244.         RockTrap(int xTile, int yTile, int width) {
  245.                 int x = xTile << 5 | 16;
  246.                 y = yTile << 5;
  247.                 order.resize(width);
  248.                 for (int i = 0; i < width; i++) {
  249.                         order[i] = x + (i << 5);
  250.                 }
  251.                 shuffle(order, jjRNG());
  252.         }
  253.         void activate(jjPLAYER@ player) override {
  254.                 @activator = player;
  255.                 timer = 64;
  256.                 if (!player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Rock Trap", true, STRING::SMALL);
  257.                 if (jjIsServer && player.isLocal) jjAlert(""+player.nameUnformatted + " activated the |||Rock Trap", false, STRING::SMALL);
  258.                 if (activator.isLocal) jjSample(75*32, 58*32, SOUND::ORANGE_BOEMR, 0, 0);
  259.                 for (int i = 0; i < 8; i++) {
  260.                         jjSample((79*32 + (i*5)), y + 96, SOUND::ORANGE_BOEMR, 0, 0);
  261.                 }
  262.         }
  263.         void process() override {
  264.                 if (isActive()) {
  265.                         timer--;
  266.                         if (timer & 1 == 0) {
  267.                                 int xCur = order[timer >> 1];
  268.                                 int yCur = y;
  269.                                 while (!jjMaskedPixel(xCur, yCur)) {
  270.                                         yCur--;
  271.                                 }
  272.                                 int id = jjAddObject(OBJECT::BULLET, xCur, yCur, activator.playerID, CREATOR::PLAYER);
  273.                                 if (id > 0) {
  274.                                         auto@ obj = jjObjects[id];
  275.                                         obj.age = 4;
  276.                                         obj.behavior = Projectile(rotating);
  277.                                         obj.determineCurAnim(ANIM::CUSTOM[1], 2);
  278.                                         obj.determineCurFrame();
  279.                                         obj.killAnim = jjAnimSets[ANIM::AMMO] + 72;
  280.                                         obj.playerHandling = HANDLING::SPECIAL;
  281.                                         obj.scriptedCollisions = true;
  282.                                         obj.ySpeed = 1.f;
  283.                                         obj.yAcc = 0.25f;
  284.                                         obj.var[1] = 3;
  285.                                         if (activator.isLocal) obj.var[2] = 1;
  286.                                 }
  287.                         }
  288.                 }
  289.         }
  290.         private int y;
  291.         private jjPLAYER@ activator;
  292.         private array<int> order;
  293. }
  294. class BladeTrap : TrapBehavior {
  295.         bool isActive() const override {
  296.                 // TODO
  297.                 return false;
  298.         }
  299.         void activate(jjPLAYER@ player) override {
  300.                 @activator = player;
  301.                 // TODO
  302.         }
  303.         void process() override {
  304.                 // TODO
  305.         }
  306.         private jjPLAYER@ activator;
  307. }
  308. class Projectile : jjBEHAVIORINTERFACE {
  309.         Projectile(ProjectileDrawingMethod method) {
  310.                 drawingMethod = method;
  311.         }
  312.         void onBehave(jjOBJ@ obj) override {
  313.                 if (obj.state == STATE::START) {
  314.                         obj.state = STATE::FLY;
  315.                         obj.direction = obj.xSpeed < 0.f ? -1 : 1;
  316.                 }
  317.                 obj.counter++;
  318.                 if (obj.state == STATE::FLY) {
  319.                         obj.xPos += obj.xSpeed += obj.xAcc;
  320.                         obj.yPos += obj.ySpeed += obj.yAcc;
  321.                         if (uint(obj.counter) == obj.counterEnd || jjMaskedPixel(int(obj.xPos), int(obj.yPos))) {
  322.                                 if (obj.var[2] == 1) {
  323.                                         switch (obj.var[1]) {
  324.                                                 case 1: jjSamplePriority(SOUND::BILSBOSS_FIRE); break;
  325.                                                 case 2: jjSamplePriority(SOUND::COMMON_LANDCAN1); break;
  326.                                                 case 3: jjSamplePriority(SOUND::COMMON_BENZIN1); break;
  327.                                                 default: break;
  328.                                         }
  329.                                 } else {
  330.                                         switch (obj.var[1]) {
  331.                                                 case 1: jjSample(obj.xPos, obj.yPos, SOUND::BILSBOSS_FIRE, 0, 0); break;
  332.                                                 case 2: jjSample(obj.xPos, obj.yPos, SOUND::COMMON_LANDCAN1, 0, 0); break;
  333.                                                 case 3: jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BENZIN1, 0, 0); break;
  334.                                                 default: break;
  335.                                         }
  336.                                 }
  337.                                 obj.state = STATE::KILL;
  338.                                 obj.curAnim = obj.killAnim;
  339.                                 obj.frameID = 0;
  340.                                 obj.determineCurFrame();
  341.                         }
  342.                 }
  343.                 if (obj.counter % obj.age == 0) {
  344.                         const auto@ anim = jjAnimations[obj.curAnim];
  345.                         obj.frameID++;
  346.                         if (obj.frameID == int(anim.frameCount)) {
  347.                                 if (obj.state == STATE::KILL) {
  348.                                         obj.delete();
  349.                                         return;
  350.                                 } else {
  351.                                         obj.frameID = 0;
  352.                                 }
  353.                         }
  354.                         obj.determineCurFrame();
  355.                 }
  356.         }
  357.         void onDraw(jjOBJ@ obj) {
  358.                 if (obj.state == STATE::FLY) {
  359.                         switch (drawingMethod) {
  360.                                 case simple:
  361.                                         obj.draw();
  362.                                         break;
  363.                                 case angled: {
  364.                                         int angle = int(atan2(-obj.ySpeed, obj.xSpeed) * 163.f);
  365.                                         jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, angle);
  366.                                         break;
  367.                                 }
  368.                                 case rotating:
  369.                                         jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.counter << 5);
  370.                                         break;
  371.                         }
  372.                 } else {
  373.                         obj.draw();
  374.                 }
  375.         }
  376.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  377.                 if (bullet is null && obj.state == STATE::FLY)
  378.                         killLocalPlayerWithTrap(player, obj.creatorType == CREATOR::PLAYER ? jjPlayers[obj.creatorID] : null);
  379.                 return true;
  380.         }
  381.         private ProjectileDrawingMethod drawingMethod;
  382. }
  383. class CannotBeShotDown : jjBEHAVIORINTERFACE {
  384.         CannotBeShotDown(const jjBEHAVIOR &in behavior) {
  385.                 originalBehavior = behavior;
  386.         }
  387.         void onBehave(jjOBJ@ obj) {
  388.                 obj.behave(originalBehavior);
  389.                 if (obj.state == STATE::FLOATFALL)
  390.                         obj.state = STATE::FLOAT;
  391.         }
  392.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  393.                 if (bullet is null) {
  394.                         obj.behavior = originalBehavior;
  395.                         if (player.objectHit(obj, force, obj.playerHandling))
  396.                                 return true;
  397.                         obj.behavior = this;
  398.                 }
  399.                 return false;
  400.         }
  401.         private jjBEHAVIOR originalBehavior;
  402. }
  403. void LaserGun(jjOBJ@ obj) {
  404.         jjPLAYER@ creator = jjPlayers[obj.creatorID];
  405.         if (obj.state == STATE::START) {
  406.                 obj.state = STATE::FLY;
  407.                 obj.direction = creator.direction;
  408.         }
  409.         if (jjMaskedPixel(int(obj.xPos + obj.xSpeed + obj.var[7] / 65536.f), int(obj.yPos))) {
  410.                 obj.xSpeed = -obj.xSpeed;
  411.                 obj.var[7] = -obj.var[7];
  412.                 obj.xAcc = -obj.xAcc;
  413.                 obj.ySpeed -= 1.5;
  414.                 obj.counter -= 5;
  415.                 if (obj.state == STATE::FLY) randomSample(obj);
  416.         }
  417.         else if (jjMaskedPixel(int(obj.xPos), int(obj.yPos + obj.ySpeed))) {
  418.                 obj.ySpeed = -obj.ySpeed;
  419.                 obj.yAcc = -obj.yAcc;
  420.                 obj.xSpeed -= obj.direction == 1? -1.5:1.5;
  421.                 obj.counter -= 5;
  422.                 if (obj.state == STATE::FLY) randomSample(obj);
  423.         }
  424.         obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
  425.         if (obj.state == STATE::FLY) jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[0], 1, 0, obj.var[0], 0.9, 0.9, SPRITE::NORMAL);
  426.         obj.behave(BEHAVIOR::BULLET, obj.state == STATE::FLY? false:true);
  427. }
  428. void LaserGunPU(jjOBJ@ obj) {
  429.         jjPLAYER@ creator = jjPlayers[obj.creatorID];
  430.         if (obj.state == STATE::START) {
  431.                 obj.state = STATE::FLY;
  432.                 obj.direction = creator.direction;
  433.         }
  434.         if (jjMaskedPixel(int(obj.xPos + obj.xSpeed + obj.var[7] / 65536.f), int(obj.yPos))) {
  435.                 obj.xSpeed = -obj.xSpeed;
  436.                 obj.var[7] = -obj.var[7];
  437.                 obj.xAcc = -obj.xAcc;
  438.                 obj.ySpeed -= 3;
  439.                 obj.counter -= 5;
  440.                 if (obj.state == STATE::FLY) randomSample(obj);
  441.         }
  442.         else if (jjMaskedPixel(int(obj.xPos), int(obj.yPos + obj.ySpeed))) {
  443.                 obj.ySpeed = -obj.ySpeed;
  444.                 obj.yAcc = -obj.yAcc;
  445.                 obj.xSpeed -= obj.direction == 1? -3:3;
  446.                 obj.counter -= 5;
  447.                 if (obj.state == STATE::FLY) randomSample(obj);
  448.         }
  449.         obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
  450.        
  451.         if (obj.state == STATE::FLY) jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::CUSTOM[0], 0, 0, obj.var[0], 1, 1, SPRITE::NORMAL);
  452.         obj.behave(BEHAVIOR::BULLET, obj.state == STATE::FLY? false:true);
  453. }
  454. void randomSample(jjOBJ@ obj) {
  455.         switch (jjRandom() % 4) {
  456.                 case 0: jjSample(obj.xPos, obj.yPos, SOUND::AMMO_BUL1, 0, 0); break;
  457.                 case 1: jjSample(obj.xPos, obj.yPos, SOUND::AMMO_BULFL1, 0, 0); break;
  458.                 case 2: jjSample(obj.xPos, obj.yPos, SOUND::AMMO_BULFL2, 0, 0); break;
  459.                 case 3: jjSample(obj.xPos, obj.yPos, SOUND::AMMO_BULFL3, 0, 0); break;
  460.         }
  461. }
  462. void coinBooth(jjOBJ@ obj) {
  463.         switch (obj.state) {
  464.                 case STATE::DEACTIVATE:
  465.                         obj.deactivate();
  466.                         break;
  467.                 case STATE::START: {
  468.                                 int x = int(obj.xOrg) >>> 5;
  469.                                 int y = int(obj.yOrg) >>> 5;
  470.                                 if (jjParameterGet(x, y, 17, 1) == 0) {
  471.                                         obj.delete();
  472.                                         return;
  473.                                 }
  474.                                 obj.var[0] = jjParameterGet(x, y, 8, 8);
  475.                                 obj.var[1] = jjParameterGet(x, y, 0, 8);
  476.                                 obj.var[2] = jjParameterGet(x, y, 16, 1);
  477.                                 obj.var[3] = jjParameterGet(x, y, 18, 1);
  478.                                 const array<int> colors = {248, 48, 0, 24, 8, 32, 40};
  479.                                 obj.var[4] = colors[obj.var[0] % 7];
  480.                                 obj.frameID = 0;
  481.                                 obj.determineCurFrame();
  482.                                 obj.xPos -= 16.f;
  483.                                 obj.yPos -= 16.f;
  484.                                 obj.putOnGround(true);
  485.                                 obj.state = STATE::STILL;
  486.                         }
  487.                         break;
  488.                 default: {
  489.                         const auto@ boothAnim = jjAnimations[obj.special];
  490.                         obj.counter++;
  491.                         if (obj.counter & 7 == 0) {
  492.                                 obj.frameID++;
  493.                                 if (obj.frameID >= int(jjAnimations[obj.curAnim].frameCount))
  494.                                         obj.frameID = 0;
  495.                                 obj.determineCurFrame();
  496.                         }
  497.                         jjDrawSpriteFromCurFrame(obj.xPos - 1.f, obj.yPos + 3.f, boothAnim + (obj.counter >> 3) % boothAnim.frameCount, obj.direction);
  498.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame + jjAnimations[obj.curAnim].frameCount, obj.direction);
  499.                         jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, obj.var[4]);
  500.                         if (obj.var[0] != 0) {
  501.                                 jjTEXTAPPEARANCE style;
  502.                                 style.align = STRING::CENTER;
  503.                                 style.yAmp = 1;
  504.                                 jjDrawString(obj.xPos - 4.f, obj.yPos - 12.f, formatInt(obj.var[0]), STRING::SMALL, style, 0, SPRITE::PALSHIFT, 208);
  505.                         }
  506.                         for (int i = 0; i < jjLocalPlayerCount; i++) {
  507.                                 auto@ player = jjLocalPlayers[i];
  508.                                 auto@ data = playerData[i];
  509.                                 if (player.isInGame && data.trapSelection == -1) {
  510.                                         float xDist = player.xPos - obj.xPos;
  511.                                         float yDist = player.yPos - obj.yPos;
  512.                                         if (xDist * xDist + yDist * yDist < 1024.f) {
  513.                                                 data.trapPrice = obj.var[0];
  514.                                                 if (player.coins < data.trapPrice)
  515.                                                         player.testForCoins(data.trapPrice);
  516.                                                 else
  517.                                                         data.trapSelection = 0;
  518.                                         }
  519.                                 }
  520.                         }
  521.                 }
  522.         }
  523. }
  524. void createMinimap(jjANIMSET@ animSet) {
  525.         animSet.allocate(array<uint> = {1});
  526.         auto@ frame = jjAnimFrames[jjAnimations[animSet]];
  527.         const int shift = 1;
  528.         const int width = jjLayerWidth[4] << 1;
  529.         const int height = jjLayerHeight[4] << 1;
  530.         const int cornerRadius = 32;
  531.         jjPIXELMAP image(width, height);
  532.         const auto@ order = jjLayerOrderGet();
  533.         uint firstLayer = 0;
  534.         while (firstLayer != order.length() && !hasLayer4Speed(order[firstLayer])) {
  535.                 firstLayer++;
  536.         }
  537.         uint lastLayer = firstLayer;
  538.         while (lastLayer != order.length() && hasLayer4Speed(order[lastLayer])) {
  539.                 lastLayer++;
  540.         }
  541.         const jjPIXELMAP background(0, 0, 256, 256, 8);
  542.         for (int i = 0; i < height; i++) {
  543.                 for (int j = 0; j < width; j++) {
  544.                         if (j < cornerRadius) {
  545.                                 int xDist = cornerRadius - j;
  546.                                 int yDist = 0;
  547.                                 if (i < cornerRadius)
  548.                                         yDist = cornerRadius - i;
  549.                                 else if (i >= height - cornerRadius)
  550.                                         yDist = i - (height - cornerRadius);
  551.                                 if (xDist * xDist + yDist * yDist > cornerRadius * cornerRadius)
  552.                                         continue;
  553.                         }
  554.                         uint layer = firstLayer;
  555.                         int color = 0;
  556.                         while (color == 0 && layer != lastLayer) {
  557.                                 int tile = jjGetStaticTile(jjTileGet(layer, j >> shift, i >> shift));
  558.                                 jjPIXELMAP src(tile);
  559.                                 color = src[(j & 1) << 4 | 8, (i & 1) << 4 | 8];
  560.                                 layer++;
  561.                         }
  562.                         if (color == 0)
  563.                                 color = background[(j >> 3) % background.width, (i >> 3) % background.height];
  564.                         image[j, i] = color;
  565.                 }
  566.         }
  567.         image.save(frame);
  568. }
  569. void setUpTraps() {
  570.         Trap trap;
  571.        
  572.         trap.name = "Crystal Spike Pit";
  573.         trap.description = """A floor opens, exposing players to sharp crystal spikes.
  574.                 Players will die upon touching the Crystal Spikes.""";
  575.         trap.x = 37 << 5;
  576.         trap.y = 80 << 5;
  577.         @trap.behavior = SpikeTrap(34, 81, 6, 271 ^ 376);
  578.         traps.insertLast(trap);
  579.        
  580.         /*trap.name = "Spinning Blade";
  581.         trap.description = """A spinning blade rises up from the floor, quickly moves
  582.                 to the left before stopping, then returns to its original position.
  583.                 Players will die by touching the blade.""";
  584.         trap.x = 33 << 5;
  585.         trap.y = 47 << 5;
  586.         @trap.behavior = BladeTrap();
  587.         traps.insertLast(trap);*/
  588.        
  589.         trap.name = "Poison Darts";
  590.         trap.description = """Once activated, poisoned darts quickly shoot out of the wall.
  591.                 Players hit by poisoned darts will die.""";
  592.         trap.x = 69 << 5;
  593.         trap.y = 31 << 5;
  594.         @trap.behavior = DartTrap(69, 31);
  595.         traps.insertLast(trap);
  596.        
  597.         trap.name = "Volcano";
  598.         trap.description = """Fireballs quickly shoot up from the temple roof, then rain down.
  599.                 Players will die by touching the Fireballs.""";
  600.         trap.x = 88 << 5;
  601.         trap.y = 9 << 5;
  602.         @trap.behavior = VolcanoTrap(88, 11);
  603.         traps.insertLast(trap);
  604.        
  605.         trap.name = "Fire Wall";
  606.         trap.description = """Flames shoot out of the wall, then dissipate after 3 seconds.
  607.                 Players will die by touching the flames.""";
  608.         trap.x = 125 << 5;
  609.         trap.y = 38 << 5;
  610.         @trap.behavior = FireTrap(118, 37, 4);
  611.         traps.insertLast(trap);
  612.        
  613.         trap.name = "Falling Rocks";
  614.         trap.description = """Rocks fall haphazardly. Players will die by being crushed by the rocks.""";
  615.         trap.x = 97 << 5;
  616.         trap.y = 72 << 5;
  617.         @trap.behavior = RockTrap(81, 71, 32);
  618.         traps.insertLast(trap);
  619. }
  620. bool hasLayer4Speed(const jjLAYER@ layer) {
  621.         return layer.xSpeed == 1.f && layer.ySpeed == 1.f && layer.xAutoSpeed == 0.f && layer.yAutoSpeed == 0.f;
  622. }
  623. void killLocalPlayerWithTrap(jjPLAYER@ player, jjPLAYER@ activator) {
  624.         if (player.health > 0)
  625.                 player.hurt(player.health, true, activator);
  626. }
  627. void shuffle(array<int>& input, jjRNG& rng) {
  628.         for (uint i = input.length(); i != 0;) {
  629.                 int index = rng() % i;
  630.                 i--;
  631.                 auto temp = input[index];
  632.                 input[index] = input[i];
  633.                 input[i] = temp;
  634.         }
  635. }
  636. bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
  637.         const auto@ data = playerData[player.localPlayerID];
  638.         if (data.trapSelection >= 0) {
  639.                 const auto@ minimap = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[3]]]];
  640.                 const int xMap = jjSubscreenWidth - minimap.width;
  641.                 const int yMap = jjSubscreenHeight - 96 - minimap.height;
  642.                 const float levelWidth = jjLayerWidth[4] << 5;
  643.                 const float levelHeight = jjLayerHeight[4] << 5;
  644.                 canvas.drawSprite(xMap, yMap, ANIM::CUSTOM[3], 0, 0);
  645.                 for (int i = traps.length(); i-- != 0;) {
  646.                         const auto@ target = traps[i];
  647.                         int xTrap = xMap + int(target.x / levelWidth * minimap.width);
  648.                         int yTrap = yMap + int(target.y / levelHeight * minimap.height);
  649.                         canvas.drawResizedSprite(xTrap, yTrap, ANIM::FLAG, i == data.trapSelection ? 3 : 7, 0, 0.5f, 0.5f);
  650.                 }
  651.                 for (int i = 0; i < 32; i++) {
  652.                         const auto@ target = jjPlayers[i];
  653.                         if (target.isInGame) {
  654.                                 int xPlayer = xMap + int(target.xPos / levelWidth * minimap.width);
  655.                                 int yPlayer = yMap + int(target.yPos / levelHeight * minimap.height);
  656.                                 int face = 3;
  657.                                 switch (target.charCurr) {
  658.                                         case CHAR::BIRD:
  659.                                                 face = 0;
  660.                                                 break;
  661.                                         case CHAR::BIRD2:
  662.                                                 face = 1;
  663.                                                 break;
  664.                                         case CHAR::FROG:
  665.                                                 face = 2;
  666.                                                 break;
  667.                                         case CHAR::JAZZ:
  668.                                                 face = 3;
  669.                                                 break;
  670.                                         case CHAR::LORI:
  671.                                                 face = 4;
  672.                                                 break;
  673.                                         case CHAR::SPAZ:
  674.                                                 face = jjIsTSF ? 5 : 4;
  675.                                                 break;
  676.                                 }
  677.                                 canvas.drawResizedSprite(xPlayer, yPlayer, ANIM::FACES, face, 0, 0.5f, 0.5f, SPRITE::PLAYER, i);
  678.                         }
  679.                 }
  680.                 const auto@ trap = traps[data.trapSelection];
  681.                 jjTEXTAPPEARANCE style;
  682.                 style.align = STRING::CENTER;
  683.                 int middle = jjSubscreenWidth / 2;
  684.                 canvas.drawString(middle, 64, "Activate a Trap?", STRING::LARGE, style);
  685.                 canvas.drawString(middle, 128, trap.name, STRING::MEDIUM, style);
  686.                 canvas.drawString(middle, 160, trap.description, STRING::SMALL, style);
  687.                 canvas.drawString(middle, jjSubscreenHeight - 64, "Press fire to activate a trap (costs 25 coins)", STRING::SMALL, style);
  688.                 canvas.drawString(middle, jjSubscreenHeight - 48, "Use the arrow keys to switch between traps", STRING::SMALL, style);
  689.                 canvas.drawString(middle, jjSubscreenHeight - 32, "Press backspace to quit", STRING::SMALL, style);
  690.                 if (trap.behavior.isActive())
  691.                         canvas.drawString(middle, jjSubscreenHeight / 2, "Trap already active", STRING::MEDIUM, style, 0, SPRITE::PALSHIFT, 216);
  692.         }
  693.         return weaponHook.drawAmmo(player, canvas);
  694. }
  695. void onLevelBegin() {
  696.         for (int i = 1; i < 255; i++) {
  697.                 jjOBJ@ preset = jjObjectPresets[i];
  698.                 if (preset.playerHandling == HANDLING::PICKUP) {
  699.                         preset.behavior = CannotBeShotDown(preset.behavior);
  700.                 }
  701.         }
  702.        
  703.         jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::CUSTOM[0]] + 4];
  704.         for (uint i = 0; i < anim.frameCount; ++i) {
  705.                 jjANIMFRAME@ frame = jjAnimFrames[anim + i];
  706.                 jjPIXELMAP sprite(frame);
  707.                 for (uint x = 0; x < sprite.width; ++x)
  708.                         for (uint y = 0; y < sprite.height; ++y)
  709.                         if (sprite[x,y] < 8 && x <= 16 && y >= 2) sprite[x,y] = 71;
  710.                 sprite.save(frame);
  711.         }
  712.        
  713.         jjSampleLoad(SOUND::P2_PTOEI, "bow_fire_arrow.wav");
  714.         jjSampleLoad(SOUND::ORANGE_BOEMR, "expmine.wav");
  715.         jjSampleLoad(SOUND::COMMON_COLLAPS, "floorslide.wav");
  716.         jjSampleLoad(SOUND::CATERPIL_RIDOE, "floorslide.wav");
  717. }
  718. void onLevelLoad() {
  719.         createMinimap(jjAnimSets[ANIM::CUSTOM[3]]);
  720.         setUpTraps();
  721.         jjUseLayer8Speeds = true;      
  722.         se::roller.loadAnims(jjAnimSets[ANIM::CUSTOM[2]]);
  723.         se::roller.loadSamples(array<SOUND::Sample> = {SOUND::INTRO_BRAKE});
  724.         se::roller.setAsWeapon(2, weaponHook);
  725.         jjAnimSets[ANIM::CUSTOM[2]].load(0, "arrowgun.j2a");
  726.         jjAnimSets[ANIM::CUSTOM[1]].load(0, "meteor.j2a");
  727.         jjAnimSets[ANIM::CUSTOM[0]].load(0, "LaserBlaster.j2a");
  728.         jjAnimations[jjAnimSets[ANIM::AMMO] + 29] = jjAnimations[jjAnimSets[ANIM::CUSTOM[0]] + 2];
  729.         jjAnimations[jjAnimSets[ANIM::AMMO] + 28] = jjAnimations[jjAnimSets[ANIM::CUSTOM[0]] + 3];
  730.         jjAnimations[jjAnimSets[ANIM::PICKUPS] + 62] = jjAnimations[jjAnimSets[ANIM::CUSTOM[0]] + 4];
  731.        
  732.         jjObjectPresets[OBJECT::ICEBULLET].behavior = LaserGun;
  733.         jjObjectPresets[OBJECT::ICEBULLET].special = jjObjectPresets[OBJECT::ICEBULLET].determineCurAnim(ANIM::CUSTOM[0], 0);
  734.         jjObjectPresets[OBJECT::ICEBULLET].xSpeed = 1.5;
  735.         jjObjectPresets[OBJECT::ICEBULLET].counterEnd = 125;
  736.         jjObjectPresets[OBJECT::ICEBULLET].freeze = 0;
  737.         jjObjectPresets[OBJECT::ICEBULLET].eventID = OBJECT::FIREBALLBULLET;
  738.         jjObjectPresets[OBJECT::ICEBULLET].lightType = jjObjectPresets[OBJECT::FIREBALLBULLET].lightType;
  739.  
  740.         jjObjectPresets[OBJECT::ICEBULLETPU].behavior = LaserGunPU;
  741.         jjObjectPresets[OBJECT::ICEBULLETPU].special = jjObjectPresets[OBJECT::ICEBULLET].determineCurAnim(ANIM::CUSTOM[0], 1);
  742.         jjObjectPresets[OBJECT::ICEBULLETPU].xSpeed = 2;
  743.         jjObjectPresets[OBJECT::ICEBULLETPU].counterEnd = 95;
  744.         jjObjectPresets[OBJECT::ICEBULLETPU].var[6] = 8;
  745.         jjObjectPresets[OBJECT::ICEBULLETPU].freeze = 0;
  746.         jjObjectPresets[OBJECT::ICEBULLETPU].eventID = OBJECT::FIREBALLBULLETPU;
  747.         jjObjectPresets[OBJECT::ICEBULLET].lightType = jjObjectPresets[OBJECT::FIREBALLBULLET].lightType;
  748.        
  749.         jjObjectPresets[OBJECT::ICEPOWERUP].determineCurAnim(ANIM::CUSTOM[0], 4, true);
  750.         jjObjectPresets[OBJECT::ICEPOWERUP].determineCurFrame();
  751.        
  752.         jjObjectPresets[OBJECT::WARP].behavior = coinBooth;
  753.        
  754.         jjWeapons[WEAPON::ICE].spread = SPREAD::NORMAL;
  755.         jjSampleLoad(SOUND::AMMO_ICEGUN, "f_laser1.wav");
  756.         jjSampleLoad(SOUND::AMMO_ICEGUN2, "f_laser2.wav");
  757.         jjSampleLoad(SOUND::AMMO_ICEGUNPU, "f_laser2.wav");
  758.         jjSampleLoad(SOUND::AMMO_ICEPU1, "f_laser3.wav");
  759.         jjSampleLoad(SOUND::AMMO_ICEPU2, "f_laser3.wav");
  760.         jjSampleLoad(SOUND::AMMO_ICEPU3, "f_laser4.wav");
  761.         jjSampleLoad(SOUND::AMMO_ICEPU4, "f_laser4.wav");
  762.  
  763.         jjANIMATION@ anim = jjAnimations[jjAnimSets[ANIM::AMMO] + 82];
  764.         for (uint i = 0; i < anim.frameCount; ++i) {
  765.                 jjANIMFRAME@ frame = jjAnimFrames[anim + i];
  766.                 jjPIXELMAP sprite(frame);
  767.                 for (uint x = 0; x < sprite.width; ++x) {
  768.                         for (uint y = 0; y < sprite.height; ++y) {
  769.                                 if (sprite[x, y] != 0 && sprite[x, y] != 15)
  770.                                         sprite[x, y] += 33;
  771.                         }
  772.                 }
  773.                 sprite.save(frame);
  774.         }
  775. }
  776. void onMain() {
  777.         for (int i = traps.length(); i-- != 0;) {
  778.                 traps[i].behavior.process();
  779.         }
  780.         weaponHook.processMain();
  781.         array<jjLAYER@> layers = jjLayerOrderGet();
  782.         layers[8].xAutoSpeed = layers[9].xAutoSpeed = 0;
  783.         layers[8].xOffset += 0.2f;
  784.         layers[9].xOffset += 0.08f;
  785. }
  786. void onPlayer(jjPLAYER@ player) {
  787.         auto@ data = playerData[player.localPlayerID];
  788.         if (data.trapSelection >= 0) {
  789.                 if (!player.isInGame || player.health == 0 || jjKey[quitTrapSelectionKey]) {
  790.                         data.trapSelection = -210;
  791.                         player.cameraUnfreeze();
  792.                 } else {
  793.                         const auto@ trap = traps[data.trapSelection];
  794.                         player.cameraFreeze(trap.x, trap.y, true, true);
  795.                         player.showText("");
  796.                 }
  797.         } else if (data.trapSelection < -1) {
  798.                 data.trapSelection++;
  799.         }
  800.         weaponHook.processPlayer(player);
  801.         if (jjGameMode == GAME::SP && jjGameTicks == 1) {
  802.                 player.warpToTile(73,57,true);
  803.         }
  804. }
  805. void onPlayerInput(jjPLAYER@ player) {
  806.         auto@ data = playerData[player.localPlayerID];
  807.         if (data.trapSelection >= 0) {
  808.                 if (player.keyLeft && !data.prevKeyLeft) {
  809.                         data.trapSelection--;
  810.                         if (data.trapSelection < 0)
  811.                                 data.trapSelection = traps.length() - 1;
  812.                 }
  813.                 if (player.keyRight && !data.prevKeyRight) {
  814.                         data.trapSelection++;
  815.                         if (data.trapSelection >= int(traps.length()))
  816.                                 data.trapSelection = 0;
  817.                 }
  818.                 if (player.keyFire && !data.prevKeyFire) {
  819.                         auto@ trap = traps[data.trapSelection];
  820.                         if (!trap.behavior.isActive() && player.testForCoins(data.trapPrice)) {
  821.                                 trap.behavior.activate(player);
  822.                                 jjSTREAM packet;
  823.                                 packet.push(uint8(trapPacket));
  824.                                 packet.push(uint8(data.trapSelection));
  825.                                 packet.push(uint8(player.playerID));
  826.                                 jjSendPacket(packet);
  827.                         }
  828.                 }
  829.         }
  830.         data.prevKeyFire = player.keyFire;
  831.         data.prevKeyLeft = player.keyLeft;
  832.         data.prevKeyRight = player.keyRight;
  833.         if (data.trapSelection >= 0) {
  834.                 player.keyDown = false;
  835.                 player.keyFire = false;
  836.                 player.keyLeft = false;
  837.                 player.keyJump = false;
  838.                 player.keyRight = false;
  839.                 player.keyRun = false;
  840.                 player.keyUp = false;
  841.         }
  842.         weaponHook.processPlayerInput(player);
  843. }
  844. void onReceive(jjSTREAM &in packet, int clientID) {
  845.         if (!weaponHook.processPacket(packet, clientID)) {
  846.                 jjSTREAM copy = packet;
  847.                 uint8 type;
  848.                 uint8 trapID;
  849.                 uint8 playerID;
  850.                 if (packet.pop(type) && packet.pop(trapID) && packet.pop(playerID)) {
  851.                         if (type == trapPacket && trapID < traps.length() && playerID < 32) {
  852.                                 auto@ player = jjPlayers[playerID];
  853.                                 if (!jjIsServer || player.clientID == clientID) {
  854.                                         traps[trapID].behavior.activate(player);
  855.                                         if (jjIsServer)
  856.                                                 jjSendPacket(copy, -clientID);
  857.                                 }
  858.                         }
  859.                 }
  860.         }
  861. }
  862.