Downloads containing SEminimirv.asc

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

File preview

  1. #pragma require "SEweapon.asc"
  2. #pragma require "SEminimirv.j2a"
  3. #pragma offer "SEminimirv1.wav"
  4. #pragma offer "SEminimirv2.wav"
  5. #include "SEweapon.asc"
  6. namespace se {
  7. namespace detail {
  8.         shared class MiniMirvData {
  9.                 private int m_objectID, m_warheadCount;
  10.                 private ::array<float> m_warheadXSpeed, m_warheadYSpeed;
  11.                 MiniMirvData(int id) {
  12.                         m_objectID = id;
  13.                         m_warheadCount = 0;
  14.                 }
  15.                 bool opEquals(const MiniMirvData@ other) const {
  16.                         return m_objectID == other.m_objectID;
  17.                 }
  18.                 bool isEmpty() const {
  19.                         return m_warheadCount == 0;
  20.                 }
  21.                 int get_objectID() const {
  22.                         return m_objectID;
  23.                 }
  24.                 int get_warheadCount() const {
  25.                         return m_warheadCount;
  26.                 }
  27.                 float get_xSpeed(int index) const {
  28.                         return m_warheadXSpeed[index];
  29.                 }
  30.                 float get_ySpeed(int index) const {
  31.                         return m_warheadYSpeed[index];
  32.                 }
  33.                 void clear() {
  34.                         m_warheadCount = 0;
  35.                         m_warheadXSpeed.resize(0);
  36.                         m_warheadYSpeed.resize(0);
  37.                 }
  38.                 void addWarhead(float xSpeed, float ySpeed) {
  39.                         m_warheadCount++;
  40.                         m_warheadXSpeed.insertLast(xSpeed);
  41.                         m_warheadYSpeed.insertLast(ySpeed);
  42.                 }
  43.         }
  44.         shared class MiniMirvPolynomial {
  45.                 private ::array<double> m_coefficients;
  46.                 MiniMirvPolynomial() {}
  47.                 MiniMirvPolynomial(const ::array<double> &in coefficients) {
  48.                         m_coefficients = coefficients;
  49.                 }
  50.                 double opCall(double x) const {
  51.                         double y = 0.0;
  52.                         for (int i = m_coefficients.length(); i-- != 0;) {
  53.                                 y = y * x + m_coefficients[i];
  54.                         }
  55.                         return y;
  56.                 }
  57.         }
  58. }
  59. shared class MiniMirvWeapon : WeaponInterface {
  60.         private ::jjANIMSET@ m_animSet;
  61.         private ::array<SOUND::Sample> m_samples = {SOUND::AMMO_MISSILE, SOUND::AMMO_BOEM1};
  62.         private PacketConstructor@ m_packetConstructor;
  63.         private ::array<se::detail::MiniMirvData@> m_data;
  64.         protected ::jjANIMSET@ getAnimSet() const {
  65.                 return @m_animSet;
  66.         }
  67.         protected const array<SOUND::Sample>& getSamples() const {
  68.                 return m_samples;
  69.         }
  70.         protected PacketConstructor@ getPacketConstructor() const {
  71.                 return @m_packetConstructor;
  72.         }
  73.         protected bool loadAnimSet(::jjANIMSET@ animSet, const ::string &in filename, uint setID) {
  74.                 if (animSet !is null && !::jjSTREAM(filename).isEmpty()) {
  75.                         @m_animSet = @animSet.load(setID, filename);
  76.                         return true;
  77.                 }
  78.                 return false;
  79.         }
  80.         protected bool loadSample(SOUND::Sample sample, const ::string &in filename, int index) {
  81.                 if (::jjSampleLoad(sample, filename)) {
  82.                         m_samples[index] = sample;
  83.                         return true;
  84.                 }
  85.                 return false;
  86.         }
  87.         protected int getPlayerTeam(const ::jjPLAYER@ player) const {
  88.                 if (::jjGameMode == GAME::SP || ::jjGameMode == GAME::COOP)
  89.                         return 0;
  90.                 if (::jjGameMode == GAME::CTF)
  91.                         return player.team;
  92.                 if (::jjGameCustom == GAME::PEST)
  93.                         return player.isZombie ? 1 : 0;
  94.                 if (::jjGameCustom == GAME::RT) {
  95.                         if (player is ::jjTokenOwner)
  96.                                 return 1;
  97.                         if (player is ::jjBottomFeeder)
  98.                                 return 2;
  99.                         return 0;
  100.                 }
  101.                 return player.playerID;
  102.         }
  103.         protected void behaveExplosion(::jjOBJ@ obj) const {
  104.                 switch (obj.state) {
  105.                         case STATE::START:
  106.                                 {
  107.                                         float xAvg = 0.f, yAvg = 0.f;
  108.                                         for (int i = 0; i < 1024; i += 16) {
  109.                                                 float cosine = ::jjCos(i);
  110.                                                 float sine = ::jjSin(i);
  111.                                                 float x = obj.xPos + cosine * 10.f;
  112.                                                 float y = obj.yPos + sine * 10.f;
  113.                                                 if (::jjMaskedPixel(int(x + 0.5f), int(y + 0.5f))) {
  114.                                                         xAvg += cosine;
  115.                                                         yAvg += sine;
  116.                                                 }
  117.                                         }
  118.                                         obj.special = int(::atan2(xAvg, yAvg) * 162.975f);
  119.                                 }
  120.                                 ::jjSample(obj.xPos, obj.yPos, m_samples[1]);
  121.                                 obj.state = STATE::EXPLODE;
  122.                                 break;
  123.                         case STATE::EXPLODE:
  124.                                 obj.counter++;
  125.                                 if (obj.counter & 3 == 0) {
  126.                                         obj.frameID++;
  127.                                         if (obj.counter == 4 && (::jjIsServer || ::jjGameConnection == GAME::LOCAL)) {
  128.                                                 const int maxPlayers = 32;
  129.                                                 for (int i = 0; i < maxPlayers; i++) {
  130.                                                         ::jjPLAYER@ player = @::jjPlayers[i];
  131.                                                         if (player.isInGame && !player.isJailed && obj.var[1] != getPlayerTeam(player)) {
  132.                                                                 float x = player.xPos - obj.xPos;
  133.                                                                 float y = player.yPos - obj.yPos;
  134.                                                                 if (::abs(::jjCos(obj.special) * x + ::jjSin(obj.special) * y) < 280.f && x * x + y * y < 589824.f)
  135.                                                                         player.hurt(5, true, obj.creatorType == CREATOR::PLAYER ? @::jjPlayers[obj.creatorID] : null);
  136.                                                         }
  137.                                                 }
  138.                                         }
  139.                                 }
  140.                                 if (obj.frameID < int(::jjAnimations[obj.curAnim].frameCount)) {
  141.                                         obj.determineCurFrame();
  142.                                         ::jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.special, 4.f, 4.f, SPRITE::TRANSLUCENTTILE);
  143.                                 } else {
  144.                                         obj.state = STATE::KILL;
  145.                                 }
  146.                                 break;
  147.                         case STATE::KILL:
  148.                         case STATE::DEACTIVATE:
  149.                                 obj.delete();
  150.                                 break;
  151.                 }
  152.         }
  153.         protected void explode(::jjOBJ@ obj) const {
  154.                 obj.state = STATE::KILL;
  155.                 int id = ::jjAddObject(obj.eventID, obj.xPos, obj.yPos, obj.creatorID, obj.creatorType, @::jjVOIDFUNCOBJ(behaveExplosion));
  156.                 if (id != 0) {
  157.                         ::jjOBJ@ explosion = @::jjObjects[id];
  158.                         explosion.animSpeed = obj.animSpeed;
  159.                         explosion.curAnim = obj.killAnim;
  160.                         explosion.curFrame = ::jjAnimations[explosion.curAnim];
  161.                         explosion.playerHandling = HANDLING::EXPLOSION;
  162.                         explosion.var[1] = obj.var[1];
  163.                 }
  164.         }
  165.         protected void draw(::jjOBJ@ obj) const {
  166.                 if (::jjGameTicks & 7 == 0) {
  167.                         obj.frameID++;
  168.                         if (obj.frameID >= int(::jjAnimations[obj.curAnim].frameCount))
  169.                                 obj.frameID = 0;
  170.                 }
  171.                 obj.determineCurFrame();
  172.                 float dir = obj.xSpeed < 0.f ? -1.f : 1.f;
  173.                 int angle = int(::atan2(-obj.ySpeed, obj.xSpeed) * 162.975f);
  174.                 if (dir < 0.f)
  175.                         angle += 512;
  176.                 ::jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, angle, dir);
  177.         }
  178.         protected void behaveWarhead(::jjOBJ@ obj) const {
  179.                 switch (obj.state) {
  180.                         case STATE::START:
  181.                                 obj.state = STATE::FALL;
  182.                                 break;
  183.                         case STATE::FALL:
  184.                                 obj.xSpeed += obj.xAcc;
  185.                                 obj.ySpeed += obj.yAcc;
  186.                                 {
  187.                                         float width = ::jjLayerWidth[4] << 5, height = ::jjLayerHeight[4] << 5;
  188.                                         float xSpeed = ::abs(obj.xSpeed), ySpeed = ::abs(obj.ySpeed);
  189.                                         float maxSpeed = xSpeed > ySpeed ? xSpeed : ySpeed;
  190.                                         float steps = ::ceil(maxSpeed / 8.f);
  191.                                         float xStep = obj.xSpeed / steps, yStep = obj.ySpeed / steps;
  192.                                         for (int i = int(steps + 0.5f); i-- != 0;) {
  193.                                                 obj.xPos += xStep;
  194.                                                 obj.yPos += yStep;
  195.                                                 if (obj.yPos >= height || obj.xPos >= 0.f && obj.yPos >= 0.f && obj.xPos < width && ::jjMaskedPixel(int(obj.xPos), int(obj.yPos))) {
  196.                                                         obj.state = STATE::EXPLODE;
  197.                                                         break;
  198.                                                 }
  199.                                         }
  200.                                 }
  201.                                 draw(obj);
  202.                                 break;
  203.                         case STATE::EXPLODE:
  204.                                 explode(obj);
  205.                                 break;
  206.                         case STATE::KILL:
  207.                         case STATE::DEACTIVATE:
  208.                                 obj.delete();
  209.                                 break;
  210.                 }
  211.         }
  212.         protected float findReasonableArrivalTime(float xDist, float yDist, float xSpeed, float ySpeed, float yAcc) const {
  213.                 ::array<double> coefficients = {
  214.                         xDist * xDist + yDist * yDist,
  215.                         -2.f * (xSpeed * xDist + ySpeed * yDist),
  216.                         -3.f * (xSpeed * xSpeed + ySpeed * ySpeed) - yDist * yAcc,
  217.                         ySpeed * yAcc,
  218.                         0.25f * yAcc * yAcc
  219.                 };
  220.                 se::detail::MiniMirvPolynomial f(coefficients);
  221.                 for (int i = 1; i < 5; i++) {
  222.                         coefficients[i] *= i;
  223.                 }
  224.                 coefficients.removeAt(0);
  225.                 se::detail::MiniMirvPolynomial df(coefficients);
  226.                 double left = 0.0, right = 20.0;
  227.                 double fl = f(left), fr = f(right);
  228.                 while (fl * fr >= 0.0) {
  229.                         left = right;
  230.                         fl = fr;
  231.                         right += 20.0;
  232.                         if (right > 420.0)
  233.                                 return -1.f;
  234.                         fr = f(right);
  235.                 }
  236.                 double dfl = df(left), dfr = df(right);
  237.                 if (dfl * dfr > 0.0) {
  238.                         double x = (dfr - dfl) * dfl < 0.0 ? left : right;
  239.                         double fx = f(x);
  240.                         bool ok = true;
  241.                         for (int i = 40; !::closeTo(fx, 0.0, 1e-7); i--) {
  242.                                 x -= fx / df(x);
  243.                                 if (i == 0 || x < left || x > right) {
  244.                                         ok = false;
  245.                                         break;
  246.                                 }
  247.                                 fx = f(x);
  248.                         }
  249.                         if (ok)
  250.                                 return float(x);
  251.                 }
  252.                 double x = (left + right) / 2.0;
  253.                 double fx = f(x);
  254.                 for (int i = 40; !::closeTo(fx, 0.0, 1e-7); i--) {
  255.                         if (fx * fl < 0.0) {
  256.                                 right = x;
  257.                                 fr = f(right);
  258.                         } else {
  259.                                 left = x;
  260.                                 fl = f(left);
  261.                         }
  262.                         if (i == 0)
  263.                                 break;
  264.                         fx = f(x);
  265.                 }
  266.                 return float(x);
  267.         }
  268.         protected void predictPlayer(const ::jjPLAYER@ player, int time, float xAcc, float &out x, float &out y) const {
  269.                 x = player.xPos;
  270.                 y = player.yPos;
  271.                 float xSpeed = player.xSpeed, ySpeed = player.ySpeed;
  272.                 for (int i = 0; i < time; i++) {
  273.                         if (ySpeed < 0.f) {
  274.                                 ySpeed += 0.25f;
  275.                         } else {
  276.                                 ySpeed += 0.125f;
  277.                                 if (ySpeed > 12.f)
  278.                                         ySpeed = 12.f;
  279.                         }
  280.                         float effectiveYSpeed = ySpeed;
  281.                         if (effectiveYSpeed < -8.f)
  282.                                 effectiveYSpeed = -8.f;
  283.                         y += effectiveYSpeed;
  284.                         if (ySpeed > 0.f) {
  285.                                 if (::jjMaskedHLine(int(x) - 12, 24, int(y) + 20)) {
  286.                                         while (::jjMaskedHLine(int(x) - 12, 24, int(y) + 19)) {
  287.                                                 y--;
  288.                                         }
  289.                                         ySpeed = 0.f;
  290.                                 }
  291.                         } else {
  292.                                 if (::jjMaskedHLine(int(x) - 12, 24, int(y) - 4)) {
  293.                                         while (::jjMaskedHLine(int(x) - 12, 24, int(y) - 3)) {
  294.                                                 y++;
  295.                                         }
  296.                                         ySpeed = 0.f;
  297.                                 }
  298.                         }
  299.                         xSpeed += xAcc;
  300.                         if (xSpeed > 16.f)
  301.                                 xSpeed = 16.f;
  302.                         else if (xSpeed < -16.f)
  303.                                 xSpeed = -16.f;
  304.                         float effectiveXSpeed = xSpeed;
  305.                         if (effectiveXSpeed > 8.f)
  306.                                 effectiveXSpeed = 8.f;
  307.                         else if (effectiveXSpeed < -8.f)
  308.                                 effectiveXSpeed = -8.f;
  309.                         x += effectiveXSpeed;
  310.                         if (xSpeed > 0.f) {
  311.                                 int mask = 25 - ::jjMaskedTopVLine(int(x) + 12, int(y) - 4, 24);
  312.                                 if (mask != 0) {
  313.                                         if (mask - 1 < xSpeed) {
  314.                                                 y -= mask;
  315.                                                 ySpeed = 0.f;
  316.                                         } else {
  317.                                                 xSpeed = 0.f;
  318.                                         }
  319.                                 }
  320.                         } else {
  321.                                 int mask = 25 - ::jjMaskedTopVLine(int(x) - 12, int(y) - 4, 24);
  322.                                 if (mask != 0) {
  323.                                         if (mask - 1 < -xSpeed) {
  324.                                                 y -= mask;
  325.                                                 ySpeed = 0.f;
  326.                                         } else {
  327.                                                 xSpeed = 0.f;
  328.                                         }
  329.                                 }
  330.                         }
  331.                 }
  332.         }
  333.         protected void selectTargets(const ::jjOBJ@ obj, se::detail::MiniMirvData@ data) const {
  334.                 if (::jjIsServer || ::jjGameConnection == GAME::LOCAL) {
  335.                         const int minWarheads = 3, maxWarheads = 5;
  336.                         const int maxPlayers = 32;
  337.                         float timeLeft = obj.state == STATE::ACTION ? 0.f : obj.counterEnd - obj.counter + 1;
  338.                         float xReal = obj.xPos + timeLeft * obj.xSpeed;
  339.                         float yReal = obj.yPos + timeLeft * obj.ySpeed - timeLeft * timeLeft * 0.0125f;
  340.                         float xSpeedReal = obj.xSpeed;
  341.                         float ySpeedReal = obj.ySpeed + timeLeft * obj.yAcc;
  342.                         float speed = ::sqrt(xSpeedReal * xSpeedReal + ySpeedReal * ySpeedReal) * 2.f;
  343.                         {
  344.                                 ::array<float> interest, times, targetsX, targetsY;
  345.                                 int targetCount = 0;
  346.                                 for (int i = 0; i < maxPlayers; i++) {
  347.                                         const ::jjPLAYER@ target = @::jjPlayers[i];
  348.                                         if (target.isInGame && !target.isJailed && obj.var[1] != getPlayerTeam(target)) {
  349.                                                 float thisInterest = 1.f;
  350.                                                 if (target.flag != 0 && ::jjObjects[target.flag].var[0] == target.playerID + 0x8000 || ::jjGameCustom == GAME::RT && target is ::jjTokenOwner)
  351.                                                         thisInterest = 5.f;
  352.                                                 if (::jjGameMode == GAME::TREASURE || ::jjGameCustom == GAME::HEAD)
  353.                                                         thisInterest += target.gems[GEM::RED];
  354.                                                 if (target.lrsLives >= 0)
  355.                                                         thisInterest += ::sqrt(float(target.lrsLives));
  356.                                                 else if (target.deaths >= 0)
  357.                                                         thisInterest += 1.f / (target.deaths + 1);
  358.                                                 bool inMovement = !::closeTo(target.xSpeed, 0.f, 0.01f);
  359.                                                 float xAcc = inMovement ? 375.f / 1024.f : 0.f;
  360.                                                 for (int j = inMovement ? 2 : 1; j-- != 0; xAcc = -xAcc) {
  361.                                                         float xPos, yPos;
  362.                                                         float time = timeLeft, prevTime = -128.f;
  363.                                                         for (int k = 20; k != 0 && time >= 0.f && !::closeTo(time, prevTime, 1.f); k--) {
  364.                                                                 predictPlayer(@target, int(time), xAcc, xPos, yPos);
  365.                                                                 yPos += 20.f;
  366.                                                                 prevTime = time;
  367.                                                                 time = findReasonableArrivalTime(xPos - xReal, yPos - yReal, xSpeedReal, ySpeedReal, obj.yAcc);
  368.                                                         }
  369.                                                         if (time > 0.f) {
  370.                                                                 interest.insertLast(thisInterest / time);
  371.                                                                 times.insertLast(time);
  372.                                                                 targetsX.insertLast(xPos);
  373.                                                                 targetsY.insertLast(yPos);
  374.                                                                 targetCount++;
  375.                                                         }
  376.                                                 }
  377.                                         }
  378.                                 }
  379.                                 while (targetCount > 0 && data.warheadCount < maxWarheads) {
  380.                                         float maxInterest = interest[0];
  381.                                         int targetID = 0;
  382.                                         for (int i = 1; i < targetCount; i++) {
  383.                                                 if (interest[i] > maxInterest)
  384.                                                         targetID = i;
  385.                                         }
  386.                                         float xPos = targetsX[targetID];
  387.                                         float yPos = targetsY[targetID];
  388.                                         float xSpeed = (xPos - xReal) / times[targetID];
  389.                                         float ySpeed = (yPos - yReal) / times[targetID] - obj.yAcc * times[targetID] / 2.f;
  390.                                         data.addWarhead(xSpeed, ySpeed);
  391.                                         interest.removeAt(targetID);
  392.                                         times.removeAt(targetID);
  393.                                         targetsX.removeAt(targetID);
  394.                                         targetsY.removeAt(targetID);
  395.                                         targetCount--;
  396.                                 }
  397.                         }
  398.                         if (data.warheadCount < maxWarheads) {
  399.                                 ::array<float> interest, times;
  400.                                 ::array<const ::jjOBJ@> potentialTargets;
  401.                                 int targetCount = 0;
  402.                                 for (int i = 0; i < ::jjObjectCount; i++) {
  403.                                         const ::jjOBJ@ target = @::jjObjects[i];
  404.                                         if (target.isActive && target.isTarget) {
  405.                                                 float time = findReasonableArrivalTime(target.xPos - xReal, target.yPos - yReal, xSpeedReal, ySpeedReal, obj.yAcc);
  406.                                                 if (time > 0.f) {
  407.                                                         interest.insertLast((target.points + 100) / time);
  408.                                                         times.insertLast(time);
  409.                                                         potentialTargets.insertLast(@target);
  410.                                                         targetCount++;
  411.                                                 }
  412.                                         }
  413.                                 }
  414.                                 while (targetCount > 0 && data.warheadCount < maxWarheads) {
  415.                                         float maxInterest = interest[0];
  416.                                         int targetID = 0;
  417.                                         for (int i = 1; i < targetCount; i++) {
  418.                                                 if (interest[i] > maxInterest)
  419.                                                         targetID = i;
  420.                                         }
  421.                                         const ::jjOBJ@ target = potentialTargets[targetID];
  422.                                         float xSpeed = (target.xPos - xReal) / times[targetID];
  423.                                         float ySpeed = (target.yPos - yReal) / times[targetID] - obj.yAcc * times[targetID] / 2.f;
  424.                                         data.addWarhead(xSpeed, ySpeed);
  425.                                         interest.removeAt(targetID);
  426.                                         times.removeAt(targetID);
  427.                                         potentialTargets.removeAt(targetID);
  428.                                         targetCount--;
  429.                                 }
  430.                         }
  431.                         while (data.warheadCount < minWarheads) {
  432.                                 float angle = ::jjRandom() * 6.28318531f / 0x100000000;
  433.                                 data.addWarhead(xSpeedReal + ::sin(angle) * speed, ySpeedReal + ::cos(angle) * speed);
  434.                         }
  435.                         if (::jjIsServer) {
  436.                                 ::jjSTREAM packet = m_packetConstructor();
  437.                                 packet.push(uint8(obj.creatorID));
  438.                                 packet.push(uint8(data.warheadCount));
  439.                                 for (int i = 0; i < data.warheadCount; i++) {
  440.                                         packet.push(float(data.xSpeed[i]));
  441.                                         packet.push(float(data.ySpeed[i]));
  442.                                 }
  443.                                 ::jjSendPacket(packet);
  444.                         }
  445.                 }
  446.         }
  447.         protected void detach(::jjOBJ@ obj, const se::detail::MiniMirvData@ data) const {
  448.                 obj.state = STATE::KILL;
  449.                 int damage = data.warheadCount == 0 ? 0 : obj.animSpeed / data.warheadCount;
  450.                 for (int i = 0; i < data.warheadCount; i++) {
  451.                         int id = ::jjAddObject(obj.eventID, obj.xPos, obj.yPos, obj.creatorID, obj.creatorType, @::jjVOIDFUNCOBJ(behaveWarhead));
  452.                         if (id != 0) {
  453.                                 ::jjOBJ@ warhead = @::jjObjects[id];
  454.                                 warhead.animSpeed = damage;
  455.                                 warhead.curAnim = warhead.special = obj.curAnim + 1;
  456.                                 warhead.curFrame = ::jjAnimations[warhead.curAnim];
  457.                                 warhead.killAnim = obj.killAnim;
  458.                                 warhead.var[1] = obj.var[1];
  459.                                 warhead.xAcc = 0.f;
  460.                                 warhead.yAcc = obj.yAcc;
  461.                                 warhead.xSpeed = data.xSpeed[i];
  462.                                 warhead.ySpeed = data.ySpeed[i];
  463.                         }
  464.                 }
  465.         }
  466.         protected void behave(::jjOBJ@ obj) {
  467.                 se::detail::MiniMirvData tempData(obj.objectID);
  468.                 int dataID = m_data.find(@tempData);
  469.                 if (dataID == -1) {
  470.                         dataID = m_data.length();
  471.                         m_data.insertLast(@tempData);
  472.                 }
  473.                 switch (obj.state) {
  474.                         case STATE::START:
  475.                                 m_data[dataID].clear();
  476.                                 if (obj.creatorType == CREATOR::PLAYER) {
  477.                                         obj.var[1] = getPlayerTeam(@::jjPlayers[obj.creatorID]);
  478.                                         if (::jjGameMode == GAME::CTF)
  479.                                                 obj.curAnim += (obj.var[1] + 1) << 1;
  480.                                         if (::jjPlayers[obj.creatorID].isLocal)
  481.                                                 ::jjSample(obj.xPos, obj.yPos, m_samples[0]);
  482.                                 } else {
  483.                                         obj.var[1] = -1;
  484.                                 }
  485.                                 obj.xAcc = 0.f;
  486.                                 obj.yAcc = ::jjObjectPresets[obj.eventID].yAcc;
  487.                                 obj.state = STATE::ROCKETFLY;
  488.                         case STATE::ROCKETFLY:
  489.                                 obj.xPos += obj.xSpeed;
  490.                                 obj.yPos += obj.ySpeed -= 0.025f;
  491.                                 obj.counter++;
  492.                                 if (::jjMaskedPixel(int(obj.xPos), int(obj.yPos)))
  493.                                         obj.state = STATE::EXPLODE;
  494.                                 else if (obj.counter > int(obj.counterEnd))
  495.                                         obj.state = STATE::ACTION;
  496.                                 else if (obj.counter > int(obj.counterEnd) - 35 && m_data[dataID].isEmpty())
  497.                                         selectTargets(obj, @m_data[dataID]);
  498.                                 draw(obj);
  499.                                 break;
  500.                         case STATE::ACTION:
  501.                                 if (m_data[dataID].isEmpty())
  502.                                         selectTargets(obj, @m_data[dataID]);
  503.                                 else
  504.                                         detach(obj, @m_data[dataID]);
  505.                                 obj.counter--;
  506.                                 if (obj.counter < 0)
  507.                                         obj.state = STATE::KILL;
  508.                                 draw(obj);
  509.                                 break;
  510.                         case STATE::EXPLODE:
  511.                                 explode(obj);
  512.                                 break;
  513.                         case STATE::KILL:
  514.                         case STATE::DEACTIVATE:
  515.                                 m_data.removeAt(dataID);
  516.                                 obj.delete();
  517.                                 break;
  518.                 }
  519.         }
  520.         protected void prepareWeaponProfile(::jjWEAPON@ weapon) const {
  521.                 weapon.comesFromGunCrates = false;
  522.                 weapon.defaultSample = false;
  523.                 weapon.gradualAim = false;
  524.                 weapon.maximum = 1;
  525.                 weapon.multiplier = 1;
  526.                 weapon.replacedByBubbles = false;
  527.                 weapon.spread = SPREAD::NORMAL;
  528.                 weapon.style = WEAPON::MISSILE;
  529.         }
  530.         protected void prepareBulletPreset(::jjOBJ@ preset, uint number) const {
  531.                 preset.behavior = @::jjVOIDFUNCOBJ(behave);
  532.                 preset.animSpeed = 50;
  533.                 preset.counterEnd = 140;
  534.                 preset.curAnim = preset.special = m_animSet + 2;
  535.                 preset.curFrame = ::jjAnimations[preset.curAnim];
  536.                 preset.deactivates = false;
  537.                 preset.direction = 1;
  538.                 preset.energy = preset.freeze = 0;
  539.                 preset.frameID = 0;
  540.                 preset.killAnim = m_animSet + 1;
  541.                 preset.lightType = LIGHT::POINT;
  542.                 preset.playerHandling = HANDLING::PARTICLE;
  543.                 preset.var[3] = number;
  544.                 preset.var[6] = 24;
  545.                 preset.xAcc = 0.f;
  546.                 preset.yAcc = 0.125f;
  547.                 preset.xSpeed = 11.f;
  548.                 preset.ySpeed = 0.f;
  549.         }
  550.         protected void preparePickupPreset(::jjOBJ@ preset, uint number) const {
  551.                 preset.behavior = @AmmoPickup(::jjAnimations[m_animSet], ::jjAnimations[m_animSet], 1);
  552.                 preset.curAnim = m_animSet;
  553.                 preset.direction = 1;
  554.                 preset.energy = 0;
  555.                 preset.frameID = 0;
  556.                 preset.killAnim = ::jjAnimSets[ANIM::PICKUPS] + 86;
  557.                 preset.playerHandling = HANDLING::PICKUP;
  558.                 preset.points = 1000;
  559.                 preset.var[2] = 0;
  560.                 preset.var[3] = number - 1;
  561.                 preset.determineCurFrame();
  562.         }
  563.         protected void processPacket(::jjSTREAM& packet, int) const {
  564.                 if (!::jjIsServer) {
  565.                         uint8 creatorID, warheadCount;
  566.                         if (packet.pop(creatorID) && packet.pop(warheadCount)) {
  567.                                 int dataID = -1, maxCounter = -1;
  568.                                 for (int i = m_data.length(); i-- != 0;) {
  569.                                         const ::jjOBJ@ obj = @::jjObjects[m_data[i].objectID];
  570.                                         if (m_data[i].isEmpty() && obj.isActive && obj.creatorID == creatorID && obj.counter > maxCounter) {
  571.                                                 maxCounter = obj.counter;
  572.                                                 dataID = i;
  573.                                         }
  574.                                 }
  575.                                 if (dataID != -1) {
  576.                                         while (warheadCount-- != 0) {
  577.                                                 float xSpeed, ySpeed;
  578.                                                 if (packet.pop(xSpeed) && packet.pop(ySpeed))
  579.                                                         m_data[dataID].addWarhead(xSpeed, ySpeed);
  580.                                         }
  581.                                 }
  582.                         }
  583.                 }
  584.         }
  585.         ::jjANIMSET@ loadAnims(::jjANIMSET@ animSet) override {
  586.                 loadAnimSet(animSet, "SEminimirv.j2a", 0);
  587.                 return @animSet;
  588.         }
  589.         ::array<bool>@ loadSamples(const ::array<SOUND::Sample>& samples) override {
  590.                 if (samples.length() != 2)
  591.                         return @::array<bool>(2, false);
  592.                 ::array<bool> result = {loadSample(samples[0], "SEminimirv1.wav", 0), loadSample(samples[1], "SEminimirv2.wav", 1)};
  593.                 return @result;
  594.         }
  595.         uint getSampleCount() const override {
  596.                 return 1;
  597.         }
  598.         uint getTraits(bool) const override {
  599.                 return weapon_deals_damage | weapon_causes_splash_damage | weapon_is_super_weapon | weapon_is_effective_against_all_targets | weapon_works_in_all_modes | weapon_has_ammo_pickups;
  600.         }
  601.         uint getMaxDamage(bool) const override {
  602.                 return 5;
  603.         }
  604.         bool setAsWeapon(uint number, WeaponHook@ weaponHook = null) override {
  605.                 if (m_animSet !is null && isValidWeapon(number)) {
  606.                         uint basic = getBasicBulletOfWeapon(number);
  607.                         uint powered = getPoweredBulletOfWeapon(number);
  608.                         uint ammo3 = getAmmoPickupOfWeapon(number);
  609.                         uint ammo15 = getAmmoCrateOfWeapon(number);
  610.                         uint powerup = getPowerupMonitorOfWeapon(number);
  611.                         if (weaponHook !is null) {
  612.                                 weaponHook.resetCallbacks(number);
  613.                                 weaponHook.setWeaponSprite(number, false, ::jjAnimations[m_animSet]);
  614.                                 weaponHook.setWeaponSprite(number, true, ::jjAnimations[m_animSet]);
  615.                                 @m_packetConstructor = @weaponHook.addPacketCallback(@PacketCallback(processPacket));
  616.                         }
  617.                         prepareWeaponProfile(@::jjWeapons[number]);
  618.                         prepareBulletPreset(@::jjObjectPresets[basic], number);
  619.                         if (basic != powered)
  620.                                 prepareBulletPreset(@::jjObjectPresets[powered], number);
  621.                         if (ammo3 != 0)
  622.                                 preparePickupPreset(@::jjObjectPresets[ammo3], number);
  623.                         if (ammo15 != 0)
  624.                                 preparePickupPreset(@::jjObjectPresets[ammo15], number);
  625.                         preparePickupPreset(@::jjObjectPresets[powerup], number);
  626.                         return true;
  627.                 }
  628.                 return false;
  629.         }
  630. }
  631. MiniMirvWeapon miniMirv;
  632. }
  633.