Downloads containing kangaroo.asc

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Holiday Hare '17Featured Download ShadowGPW Single player 8.8 Download file
JJ2+ Only: Foo Single Player 2/14:...Featured Download Violet CLM Single player 10 Download file
JJ2+ Only: KangarooFeatured Download Violet CLM Single player 9.1 Download file

File preview

  1. #pragma require "kangaroo.j2a"
  2. /*
  3. API (all expected to be called in onLevelLoad):
  4.         jjOBJ@ Kangaroo::MakeEventJoey(uint8 eventID, int minX = 0, int maxX = 4, int minY = 6, int maxY = 12, int jumpDelay = 35, int minDistance = 224)
  5.                 Assign a specific event slot to the Joey enemy, e.g. replacing another enemy type or a food object or something. If you assign multiple event slots, you can get Joey enemies with different parameters.
  6.         jjOBJ@ Kangaroo::MakeEventJill(uint8 eventID, uint8 spawn = 0, bool secondStage = false, int textID = -1)
  7.                 Assign a specific event slot to the Jill boss.
  8.                         "spawn", if non-zero, is an eventID that the boss will occasionally create from her pouch. This is assumed to be a Joey enemy but may be other objects as well.
  9.                         The "secondStage" bool causes Jill to turn red and jump faster after she has lost three-quarters of her health.
  10.                         If "textID" is 0-16, defeating Jill will display that text ID.
  11.         void Kangaroo::OnJillDefeat(JILLCALLBACKFUNC@ callback = null)
  12.                 Three seconds after defeating Jill, this function will be called. The JILLCALLBACKFUNC pattern is the same as the behavior pattern: a void-returning function taking a jjOBJ@ as its only argument. If "callback" is left null, or if OnJillDefeat is never called, defeating a Jill will simply end the level.
  13.         void Kangaroo::Joey(jjOBJ@ obj)
  14.                 The behavior function for the Joey enemy
  15.         void Kangaroo::Jill(jjOBJ@ obj)
  16.                 The behavior function for the Jill boss
  17. */
  18.  
  19.  
  20. namespace Kangaroo {
  21.         namespace Private {
  22.                 void jillDefeatedDefaultAction(jjOBJ@) {
  23.                         jjNxt();
  24.                 }
  25.                 JILLCALLBACKFUNC@ jillCallback = jillDefeatedDefaultAction;
  26.                
  27.                 bool animsLoaded = false;
  28.                 uint customAnimID = 0;
  29.                 void loadAnims() {
  30.                         if (!animsLoaded) {
  31.                                 animsLoaded = true;
  32.                                 while (jjAnimSets[ANIM::CUSTOM[customAnimID]] != 0)
  33.                                         ++customAnimID;
  34.                                 customAnimID = ANIM::CUSTOM[customAnimID];
  35.                                 jjAnimSets[customAnimID].load(0, "kangaroo.j2a");
  36.                                 if (!jjSampleIsLoaded(SOUND::BUBBA_BUBBABOUNCE1))
  37.                                         jjAnimSets[ANIM::BUBBA].load();
  38.                         }
  39.                 }
  40.                
  41.                 void applyGenericEnemySettingsToPreset(jjOBJ@ preset) {
  42.                         preset.playerHandling = HANDLING::ENEMY;
  43.                         preset.bulletHandling = HANDLING::HURTBYBULLET;
  44.                         preset.causesRicochet = false;
  45.                         preset.isBlastable = false;
  46.                         preset.triggersTNT = true;
  47.                         preset.isFreezable = true;
  48.                         preset.isTarget = true;
  49.                         preset.scriptedCollisions = false;
  50.                         preset.direction = 1;
  51.                         preset.freeze = 0;
  52.                 }
  53.                
  54.                 void putKangarooOnGround(jjOBJ@ obj, int width, int height) {
  55.                         while (!jjMaskedHLine(int(obj.xPos) - width/2, width, int(obj.yPos) + height/2))
  56.                                 obj.yPos += 1;
  57.                 }
  58.                
  59.                 uint firstGloveAnimationFrame;
  60.                 const jjANIMFRAME@ roundExplosionFrame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::AMMO] + 5] + 2];;
  61.                
  62.                 void doGloveAt(int x, int y) {
  63.                         for (int i = 0; i < jjLocalPlayerCount; ++i) {
  64.                                 jjPLAYER@ localPlayer = jjLocalPlayers[i];
  65.                                 if (localPlayer.blink == 0 && roundExplosionFrame.doesCollide(x, y, 0, jjAnimFrames[localPlayer.curFrame], int(localPlayer.xPos), int(localPlayer.yPos), localPlayer.direction))
  66.                                         localPlayer.hurt();
  67.                         }
  68.                         for (int i = jjObjectCount - 1; i > 0; --i) {
  69.                                 jjOBJ@ obj = jjObjects[i];
  70.                                 if (obj.playerHandling == HANDLING::PLAYERBULLET && obj.state != STATE::EXPLODE && roundExplosionFrame.doesCollide(x, y, 0, jjAnimFrames[obj.curFrame], int(obj.xPos), int(obj.yPos), obj.direction)) {
  71.                                         obj.ricochet();
  72.                                         //obj.playerHandling = HANDLING::ENEMYBULLET;
  73.                                 }
  74.                         }
  75.                 }
  76.         }
  77.         enum KangarooVariables {
  78.                 kvWIDTH = 0, kvHEIGHT, kvMINX, kvMAXX, kvMINY, kvMAXY, kvJUMPDELAY, kvMINDISTANCE, kvGLOVE1FRAME, kvGLOVE2FRAME, kvSECONDSTAGE
  79.         }
  80.         jjOBJ@ MakeEventJoey(uint8 eventID, int minX = 0, int maxX = 4, int minY = 6, int maxY = 12, int jumpDelay = 35, int minDistance = 224) {
  81.                 Kangaroo::Private::loadAnims();
  82.                
  83.                 jjOBJ@ preset = jjObjectPresets[eventID];
  84.                 preset.behavior = Joey;
  85.                 preset.determineCurAnim(Kangaroo::Private::customAnimID, 0);
  86.                 preset.frameID = 0; preset.determineCurFrame();
  87.                
  88.                 Kangaroo::Private::applyGenericEnemySettingsToPreset(preset);
  89.                
  90.                 preset.deactivates = true;
  91.                 preset.energy = 1;
  92.                 preset.points = 200;
  93.                 preset.yAcc = 0.33f;
  94.                 preset.counter = 0;
  95.                 preset.var[kvWIDTH] = 12;
  96.                 preset.var[kvHEIGHT] = 28;
  97.                 preset.var[kvMINX] = minX;
  98.                 preset.var[kvMAXX] = maxX;
  99.                 preset.var[kvMINY] = minY;
  100.                 preset.var[kvMAXY] = maxY;
  101.                 preset.var[kvJUMPDELAY] = jumpDelay;
  102.                 preset.var[kvMINDISTANCE] = minDistance;
  103.                
  104.                 return preset;
  105.         }
  106.        
  107.         funcdef void JILLCALLBACKFUNC(jjOBJ@);
  108.         jjOBJ@ MakeEventJill(uint8 eventID, uint8 spawn = 0, bool secondStage = false, int textID = -1) {
  109.                 Kangaroo::Private::loadAnims();
  110.                 if (jjAnimSets[ANIM::GLOVE] == 0)
  111.                         jjAnimSets[ANIM::GLOVE].load();
  112.                 Kangaroo::Private::firstGloveAnimationFrame = jjAnimations[jjAnimSets[ANIM::GLOVE] + 3];
  113.                
  114.                 jjOBJ@ preset = jjObjectPresets[eventID];
  115.                 preset.behavior = Jill;
  116.                 preset.determineCurAnim(Kangaroo::Private::customAnimID, 1);
  117.                 preset.frameID = 0; preset.determineCurFrame();
  118.                
  119.                 Kangaroo::Private::applyGenericEnemySettingsToPreset(preset);
  120.                
  121.                 preset.doesHurt = spawn;
  122.                 preset.yAcc = 0.16f;
  123.                 preset.energy = 100;
  124.                 preset.points = 5000;
  125.                 preset.counterEnd = 210; //death wait
  126.                 preset.special = textID;
  127.                 preset.playerHandling = HANDLING::DYING; //no initial collision damage
  128.                 preset.var[kvWIDTH] = 32;
  129.                 preset.var[kvHEIGHT] = 98;
  130.                 preset.var[kvMINX] = 2;
  131.                 preset.var[kvMAXX] = 4;
  132.                 preset.var[kvMINY] = 5;
  133.                 preset.var[kvMAXY] = 10;
  134.                 preset.var[kvJUMPDELAY] = 140;
  135.                 preset.var[kvMINDISTANCE] = 400;
  136.                 preset.var[kvGLOVE1FRAME] = 0;
  137.                 preset.var[kvGLOVE2FRAME] = 0;
  138.                 preset.var[kvSECONDSTAGE] = secondStage ? 1 : 0;
  139.                
  140.                 return preset;
  141.         }
  142.         void OnJillDefeat(JILLCALLBACKFUNC@ callback = null) {
  143.                 if (callback !is null)
  144.                         @Kangaroo::Private::jillCallback = callback;
  145.         }
  146.        
  147.        
  148.         void Joey(jjOBJ@ obj) {
  149.                 const int width = obj.var[kvWIDTH];
  150.                 const int height = obj.var[kvHEIGHT];
  151.                 switch (obj.state) {
  152.                         case STATE::START:
  153.                                 obj.state = STATE::IDLE;
  154.                                 Kangaroo::Private::putKangarooOnGround(obj, width, height);
  155.                         case STATE::IDLE:
  156.                                 if (obj.counter == 0 || --obj.counter == 0) {
  157.                                         const int nearestPlayerID = obj.findNearestPlayer(int(pow(obj.var[kvMINDISTANCE], 2)));
  158.                                         if (nearestPlayerID >= 0) {
  159.                                                 jjPLAYER@ nearestPlayer = jjPlayers[nearestPlayerID];
  160.                                                 obj.xSpeed = (nearestPlayer.xPos - obj.xPos) / 20.0f;
  161.                                                 obj.direction = (obj.xSpeed >= 0) ? 1 : -1;
  162.                                                
  163.                                                 float xSpeed = abs(obj.xSpeed);
  164.                                                 if (xSpeed > obj.var[kvMAXX]) xSpeed = obj.var[kvMAXX];
  165.                                                 else if (xSpeed < obj.var[kvMINX]) xSpeed = obj.var[kvMINX];
  166.                                                 obj.xSpeed = xSpeed * obj.direction;
  167.                                                
  168.                                                 float ySpeed = abs((nearestPlayer.yPos - obj.yPos) / 20.0f);
  169.                                                 if (ySpeed > obj.var[kvMAXY]) ySpeed = obj.var[kvMAXY];
  170.                                                 else if (ySpeed < obj.var[kvMINY]) ySpeed = obj.var[kvMINY];
  171.                                                 obj.ySpeed = -ySpeed;
  172.                                                
  173.                                                 obj.state = STATE::JUMP;
  174.                                                 obj.counter = obj.var[kvJUMPDELAY];
  175.                                                 jjSample(obj.xPos, obj.yPos, ((jjRandom() & 1) == 0) ? SOUND::BUBBA_BUBBABOUNCE1 : SOUND::BUBBA_BUBBABOUNCE2);
  176.                                         } else
  177.                                                 obj.direction = (obj.xPos > jjLocalPlayers[0].xPos) ? -1 : 1;
  178.                                 }
  179.                                 break;
  180.                         case STATE::FREEZE:
  181.                                 if (obj.freeze > 0)
  182.                                         --obj.freeze;
  183.                                 if (obj.freeze <= 0) {
  184.                                         obj.state = obj.oldState;
  185.                                         obj.unfreeze(0);
  186.                                 }
  187.                                 break;
  188.                         case STATE::JUMP:{
  189.                                 obj.yPos += (obj.ySpeed += obj.yAcc);
  190.                                 const int newXPos = int(obj.xPos + obj.xSpeed) + (width * obj.direction)/2;
  191.                                 if ((newXPos < 0) || (newXPos > jjLayerWidth[4]*32) || jjMaskedVLine(newXPos, int(obj.yPos - height/2), height)) {
  192.                                         obj.xSpeed = -obj.xSpeed;
  193.                                         obj.direction = -obj.direction;
  194.                                 }
  195.                                 obj.xPos += obj.xSpeed;
  196.                                 int newYPos = int(obj.yPos + obj.ySpeed);
  197.                                 if (obj.ySpeed < 0) {
  198.                                         if ((newYPos < 0) || jjMaskedHLine(int(obj.xPos) - width/2, width, newYPos - height/2)) {
  199.                                                 obj.ySpeed = obj.yAcc;
  200.                                                 obj.frameID = 2;
  201.                                         } else obj.frameID = 1;
  202.                                 }
  203.                                 if (obj.ySpeed > 0) {
  204.                                         if ((newYPos > jjLayerHeight[4]*32) || jjMaskedHLine(int(obj.xPos) - width/2, width, newYPos + height/2)) {
  205.                                                 obj.state = STATE::IDLE;
  206.                                                 obj.ySpeed = 0;
  207.                                                 obj.frameID = 0;
  208.                                                 Kangaroo::Private::putKangarooOnGround(obj, width, height);
  209.                                         } else obj.frameID = 2;
  210.                                 }
  211.                                 obj.determineCurFrame();
  212.                                 break;
  213.                         } case STATE::DEACTIVATE:
  214.                                 obj.deactivate();
  215.                                 return;
  216.                         case STATE::KILL:
  217.                                 obj.delete();
  218.                                 return;
  219.                 }
  220.                 obj.draw();
  221.         }
  222.         void Jill(jjOBJ@ obj) {
  223.                 switch (obj.state) {
  224.                         case STATE::START:
  225.                                 obj.state = STATE::DELAYEDSTART;
  226.                         case STATE::DELAYEDSTART:
  227.                                 for (int i = 0; i < jjLocalPlayerCount; ++i) {
  228.                                         jjPLAYER@ localPlayer = jjLocalPlayers[i];
  229.                                         if (localPlayer.bossActivated) {
  230.                                                 localPlayer.boss = obj.objectID;
  231.                                                 obj.state = STATE::START;
  232.                                         }
  233.                                 }
  234.                                 if (obj.state == STATE::START) {
  235.                                         obj.playerHandling = HANDLING::ENEMY;
  236.                                         break;
  237.                                 }
  238.                                 return;
  239.                         case STATE::KILL:
  240.                                 if (obj.special >= 0) //textID
  241.                                         jjLocalPlayers[0].showText(obj.special, 0);
  242.                                 obj.playerHandling = HANDLING::DYING;
  243.                                 obj.state = STATE::DONE;
  244.                         case STATE::DONE:
  245.                                 if (--obj.counterEnd == 0) {
  246.                                         obj.delete();
  247.                                         Kangaroo::Private::jillCallback(obj);
  248.                                 }
  249.                                 return;
  250.                         default:
  251.                                 break;
  252.                 }
  253.                
  254.                 int oldState = obj.state;
  255.                 obj.behave(Joey, false);
  256.                 obj.frameID = 0;
  257.                 obj.determineCurFrame();
  258.                
  259.                 const int direction = obj.direction;
  260.                 const bool secondStage = (obj.var[kvSECONDSTAGE] != 0 && obj.energy < 25);
  261.                 if (secondStage) {
  262.                         if (obj.var[kvSECONDSTAGE] == 1) {
  263.                                 obj.var[kvSECONDSTAGE] = 2;
  264.                                 obj.var[kvMINX] = 3;
  265.                                 obj.var[kvMAXX] = 5;
  266.                                 obj.var[kvJUMPDELAY] = 50;
  267.                                 obj.var[kvMINDISTANCE] = 600;
  268.                         }
  269.                 }
  270.                 if (obj.doesHurt != 0 && (jjRandom() & 255) == 0) {
  271.                         jjOBJ@ spawn = jjObjects[jjAddObject(obj.doesHurt, obj.xPos, obj.yPos + 11, obj.objectID, CREATOR::OBJECT, BEHAVIOR::INACTIVE)];
  272.                         jjBEHAVIOR originalBehavior = jjObjectPresets[obj.doesHurt].behavior;
  273.                         if (originalBehavior == Joey) {
  274.                                 spawn.direction = obj.direction;
  275.                                 spawn.xSpeed = spawn.direction * (1 + (jjRandom() & 3));
  276.                                 spawn.ySpeed = -6;
  277.                                 spawn.state = STATE::JUMP;
  278.                         }
  279.                         spawn.behavior = originalBehavior;
  280.                 }
  281.                
  282.                 SPRITE::Mode mode = SPRITE::NORMAL;
  283.                 uint8 param = 15;
  284.                 SPRITE::Mode modeDark = SPRITE::BRIGHTNESS;
  285.                 uint8 paramDark = 96;
  286.                 if (obj.justHit != 0) {
  287.                         mode = modeDark = SPRITE::SINGLECOLOR;
  288.                         paramDark = param;
  289.                         const int nearestPlayerID = obj.findNearestPlayer(64);
  290.                         for (int i = 0; i < jjLocalPlayerCount; ++i) {
  291.                                 jjPLAYER@ localPlayer = jjLocalPlayers[i];
  292.                                 if (localPlayer.specialMove != 0) {
  293.                                         localPlayer.specialMove = 0;
  294.                                         localPlayer.ySpeed -= 1;
  295.                                         localPlayer.extendInvincibility(-35);
  296.                                 }
  297.                         }
  298.                        
  299.                 } else if (obj.state == STATE::FREEZE) {
  300.                         mode = modeDark = SPRITE::FROZEN;
  301.                 } else if (secondStage) {
  302.                         mode = modeDark = SPRITE::TINTED;
  303.                         param = 25;
  304.                         paramDark = 29;
  305.                 }
  306.                
  307.                 int armAngle = obj.age;
  308.                 if (obj.state != STATE::FREEZE) {
  309.                         const int nearestPlayerID = obj.findNearestPlayer(1000000);
  310.                         if (nearestPlayerID >= 0) {
  311.                                 jjPLAYER@ nearestPlayer = jjPlayers[nearestPlayerID];
  312.                                 const float deltaX = nearestPlayer.xPos - obj.xPos;
  313.                                 const float deltaY = nearestPlayer.yPos - obj.yPos;
  314.                                 armAngle = int(atan2(
  315.                                     (direction == 1) ? deltaY : -deltaY,
  316.                                     abs(deltaX)
  317.                                 ) * -512.0 * 0.318309886142228);
  318.                         }
  319.                         obj.age = armAngle;
  320.                 }
  321.                 const float armSin = jjSin(armAngle);
  322.                 const float armCos = jjCos(armAngle);
  323.                 int tailAngle = int(obj.ySpeed*-16)*direction;
  324.                 const int tailX = int(obj.xPos) - 32 * direction;
  325.                 const int tailY = int(obj.yPos) + 23;
  326.                 const int gloveLength = (12 + 29 + 29) * obj.direction;
  327.                 const int legAngle = int(obj.ySpeed*8)*direction;
  328.                 const int arm1X = int(obj.xPos) - 2 * direction;
  329.                 const int arm1Y = int(obj.yPos) - 7;
  330.                 const int arm2X = int(obj.xPos) + 4 * direction;
  331.                 const int arm2Y = int(obj.yPos - 11);
  332.                 //if (tailAngle > 0) tailAngle = 0;
  333.                
  334.                 if (obj.ySpeed < 0) {
  335.                         obj.animSpeed = jjSampleLooped(obj.xPos, obj.yPos, SOUND::COMMON_FLAMER, obj.animSpeed);
  336.                         if (obj.state != STATE::FREEZE && (jjRandom() & 1) == 0) {
  337.                                 jjPARTICLE@ part = jjAddParticle(PARTICLE::FIRE);
  338.                                 if (part !is null) {
  339.                                         part.xSpeed = jjSin(tailAngle) * 2;
  340.                                         part.ySpeed = jjCos(tailAngle) * 2;
  341.                                         part.xPos = tailX + part.xSpeed * 8;
  342.                                         part.yPos = tailY + part.ySpeed * 8;
  343.                                         part.xSpeed += abs(obj.xSpeed) * -obj.direction;
  344.                                 }
  345.                         }
  346.                         if (oldState == STATE::IDLE) {
  347.                                 obj.var[kvGLOVE1FRAME] = 1;
  348.                                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PISTOL1);
  349.                                 obj.var[kvGLOVE2FRAME] = 0;
  350.                         }
  351.                 }
  352.                 {
  353.                         int oldGloveFrame = obj.var[kvGLOVE1FRAME];
  354.                         if (oldGloveFrame > 0 && oldGloveFrame < 12 && (jjGameTicks & 3) == 1)
  355.                                 obj.var[kvGLOVE1FRAME] = oldGloveFrame + 1;
  356.                        
  357.                         oldGloveFrame = obj.var[kvGLOVE2FRAME];
  358.                         if (obj.state == STATE::IDLE && oldGloveFrame == 0) {
  359.                                 obj.var[kvGLOVE2FRAME] = 1;
  360.                                 jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PISTOL1);
  361.                         }
  362.                         if (oldGloveFrame > 0 && oldGloveFrame < 12 && (jjGameTicks & 3) == 1)
  363.                                 obj.var[kvGLOVE2FRAME] = oldGloveFrame + 1;
  364.                 }
  365.                
  366.                 const int glove2FrameID = Kangaroo::Private::firstGloveAnimationFrame + (obj.var[kvGLOVE2FRAME] + 3) % 12;
  367.                 jjDrawRotatedSpriteFromCurFrame(
  368.                         arm2X + 12*armSin + gloveLength*armCos,
  369.                         arm2Y + 12*armCos - gloveLength*armSin,
  370.                          glove2FrameID,
  371.                         armAngle - 256 * obj.direction,
  372.                         direction, 2, modeDark, paramDark
  373.                 ); //glove
  374.                 if (obj.state != STATE::FREEZE) {
  375.                         const int glove2Length = gloveLength + (jjAnimFrames[glove2FrameID].height - 30) * 2 * obj.direction;
  376.                         Kangaroo::Private::doGloveAt(
  377.                                 int(arm2X + 12*armSin + glove2Length*armCos),
  378.                                 int(arm2Y + 12*armCos - glove2Length*armSin)
  379.                         );
  380.                 }
  381.                 jjDrawRotatedSpriteFromCurFrame(arm2X, arm2Y, obj.curFrame + 1, armAngle, direction, 1, modeDark, paramDark); //back arm
  382.                
  383.                 jjDrawRotatedSpriteFromCurFrame(obj.xPos - 24 * direction, obj.yPos + 24, obj.curFrame + 2, legAngle, direction, 1, modeDark, paramDark); //back leg
  384.                 jjDrawRotatedSpriteFromCurFrame(tailX, tailY, obj.curFrame + 3, tailAngle, direction, 1, mode, param); //tail
  385.                 jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, direction, mode, param); //body
  386.                 jjDrawRotatedSpriteFromCurFrame(obj.xPos - 30 * direction, obj.yPos + 28, obj.curFrame + 2, legAngle, direction, 1, mode, param); //leg
  387.                
  388.                 jjDrawRotatedSpriteFromCurFrame(arm1X, arm1Y, obj.curFrame + 1, armAngle, direction, 1, mode, param); //arm
  389.                 const int glove1FrameID = Kangaroo::Private::firstGloveAnimationFrame + (obj.var[kvGLOVE1FRAME] + 3) % 12;
  390.                 jjDrawRotatedSpriteFromCurFrame(
  391.                         arm1X + 12*armSin + gloveLength*armCos,
  392.                         arm1Y + 12*armCos - gloveLength*armSin,
  393.                          glove1FrameID,
  394.                         armAngle - 256 * obj.direction,
  395.                         direction, 2, mode, param
  396.                 ); //glove
  397.                 if (obj.state != STATE::FREEZE) {
  398.                         const int glove1Length = gloveLength + (jjAnimFrames[glove1FrameID].height - 30) * 2 * obj.direction;
  399.                         Kangaroo::Private::doGloveAt(
  400.                                 int(arm2X + 12*armSin + glove1Length*armCos),
  401.                                 int(arm2Y + 12*armCos - glove1Length*armSin)
  402.                         );
  403.                 }
  404.         }
  405. }