Downloads containing ab22btl04.j2as

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

File preview

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