Downloads containing IceKnight.asc

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Holiday Hare '18Featured Download SmokeNC Single player 8.9 Download file

File preview

#pragma require "HH18E1.j2a"
#include "HH18savegems.asc"

/*Ice Knight by Smoke , made for HappyGreenFrog's HH18 level. sprites ripped by Rasgar */

const int _jjDifficulty = 4 - jjDifficulty;
bool FreezeInput = false;
const array<int> KnightHP={-5,26,26,26};
namespace SMOKE {

  jjOBJ @ ICEKNIGHT(uint8 eventID, int minX = 0, int maxX = 4, int minY = 6, int maxY = 12, int jumpDelay = 35, int minDistance = 224, int hp = 3) {
    //Kangaroo::Private::loadAnims();
    jjAnimSets[ANIM::CUSTOM[27]].load(12, "HH18E1.j2a");
    jjAnimSets[ANIM::CUSTOM[17]].load(8, "HH18E1.j2a");
    jjOBJ @ preset = jjObjectPresets[eventID];
    preset.behavior = IceKnight();

    preset.playerHandling = HANDLING::SPECIAL;
    preset.bulletHandling = HANDLING::DETECTBULLET;
    preset.causesRicochet = false;
    preset.isBlastable = false;
    preset.triggersTNT = false;
    preset.isFreezable = true;
    preset.isTarget = true;
    preset.scriptedCollisions = true;
    preset.direction = 1;
    preset.freeze = 0;

    preset.frameID = 0;
    preset.determineCurFrame();
    preset.determineCurAnim(ANIM::CUSTOM[27], 1);

    preset.deactivates = false;
    preset.energy = 100+KnightHP[jjDifficulty];
    preset.points = 200;
    preset.yAcc = 0.3;
    preset.counter = 0;

    int height = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[27]].firstAnim + 0].firstFrame + 0].height;
    int hotY = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[27]].firstAnim + 0].firstFrame + 0].hotSpotY;
    jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[27]].firstAnim + 4].firstFrame + 0].hotSpotY += 40;
	    jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[27]].firstAnim + 4].firstFrame + 0].coldSpotY += 40;

    int distanceFromGround = height + hotY;
    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < jjAnimations[jjAnimSets[ANIM::CUSTOM[27]].firstAnim + i].frameCount; j++) {
        jjANIMFRAME @ frame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[27]].firstAnim + i].firstFrame + j];
        frame.hotSpotY = -(frame.height - distanceFromGround);
      }
    }
    return preset;
  }

 

  bool GravityField(jjOBJ @ obj) {
    bool airborne = false;
    int width = jjAnimFrames[obj.curFrame].width;
    int height = jjAnimFrames[obj.curFrame].height;
    int hotY = jjAnimFrames[obj.curFrame].hotSpotY;

    int newXPos = int(obj.xPos + obj.xSpeed) + (width * obj.direction) / 2;
    int newYPos = int(obj.yPos - obj.ySpeed);

    //Check horizontal collision, flip horizontal speed if hit barrier
    if ((newXPos < 0) || (newXPos > jjLayerWidth[4] * 32) || jjMaskedVLine(newXPos, int(obj.yPos + hotY), height - 8)) {
      obj.xSpeed = -obj.xSpeed;
      if (obj.xSpeed != 0)
        obj.direction = -obj.direction;
    }
    //move horizontally
    obj.xPos += obj.xSpeed;
	
	//update horizonal speed
	obj.xSpeed+=obj.xAcc;

    //Check ceiling collision, set vertical speed to 0 if going to hit
    if ((newYPos < 0) || jjMaskedHLine(int(obj.xPos) - 20 / 2, 20, newYPos + hotY))
      obj.ySpeed = 0;

    //Move vertically
    obj.yPos -= obj.ySpeed;

    //Check ground collision, if airborne start accelerating
    if ((newYPos > jjLayerHeight[4] * 32) || jjMaskedHLine(int(obj.xPos) - 20 / 2, 20, newYPos + height + hotY - 3 * obj.ySpeed)) {
      //obj on ground
      airborne = false;
      obj.ySpeed = 0;
    } else { //obj airborne
      airborne = true;
    }
    obj.determineCurFrame();
    if (airborne == false) { //if object is on ground / landed
      //obj.frameID = 0;
      //obj.yPos -= 10;
      putEnemyOnGround(obj);
      // jjPrint("speed: "+obj.ySpeed+" acc: "+ obj.yAcc+" special: " +obj.special+" totalacc :"+obj.special * obj.yAcc);

      return true;
    }

    if (airborne == true)
      obj.ySpeed -= obj.yAcc; //update vertical speed
    // jjPrint("speed: "+obj.ySpeed+" acc: "+ obj.yAcc+" special: " +obj.special+" totalacc :"+obj.special * obj.yAcc);
    return false;
  }

  class IceKnight: jjBEHAVIORINTERFACE {
  bool done = false;
    void onBehave(jjOBJ @ obj) {
      float X = jjLocalPlayers[0].xPos - obj.xPos;
      float Y = jjLocalPlayers[0].yPos - obj.yPos;
      // const int width = obj.var[kvWIDTH];
      // const int height = obj.var[kvHEIGHT];
      switch (obj.state) {

      case STATE::START:
        {
		  jjMusicLoad("TheIncredibleMachine3_Progressive.ogg");
          obj.frameID = 0;
          obj.counter = 0;
          obj.energy = 0;
          obj.determineCurFrame();
          obj.state = STATE::STILL;
          putEnemyOnGround(obj);
          liftEnemyFromGround(obj);
          obj.direction = -1;
         // obj.xSpeed = 0.01 * obj.direction;
          jjLocalPlayers[0].boss = obj.objectID;
          jjLocalPlayers[0].activateBoss();
       
		  
          FreezeInput = true;
        }
        break;
      case STATE::STILL:
        {
		if(obj.counter==1)
				 jjAlert("The Jailer: You will pay for this!", false, STRING::SMALL);
if(obj.counter==135)
jjAlert("Defeat The Jailer", false, STRING::MEDIUM);
          obj.counter++;
          if (obj.counter % 3 == 0)
            obj.energy++;
          if (obj.energy > 100+KnightHP[jjDifficulty]) {
            FreezeInput = false;
            obj.state = STATE::IDLE;
            obj.counter = 100;
          }
        }
        break;
      case STATE::IDLE:
        obj.yAcc = 0.3;
        //obj.direction = (obj.xPos >= jjLocalPlayers[0].xPos) ? -1 : 1;

        obj.frameID = int((jjGameTicks / 7) % 2);
        if (obj.counter == -30+7*jjDifficulty || --obj.counter == -30+7*jjDifficulty ) {

          RandomKnightAttack(obj);
          //jjSample(obj.xPos, obj.yPos, ((jjRandom() & 1) == 0) ? SOUND::BUBBA_BUBBABOUNCE1 : SOUND::BUBBA_BUBBABOUNCE2);

        }
        obj.determineCurFrame();

        if (obj.state == STATE::IDLE)
          GravityField(obj);
        break;

      case STATE::DUCK:
        {
          obj.age++;obj.determineCurAnim(ANIM::CUSTOM[27], 2);
          if (obj.frameID != 6) obj.frameID = int((obj.age / 7) % 7);
          if (obj.age > 8 * 7 && obj.ySpeed == 0) {
		 /* if(jjDifficulty>1)
		  {
		   // obj.yAcc = 0;
          obj.xAcc = 0;
          obj.direction = X / abs(X);
          obj.state = STATE::BOUNCE;
          obj.xSpeed = (7) * X / sqrt(X * X + Y * Y);
          obj.ySpeed = -7 * Y / sqrt(X * X + Y * Y);
          obj.age = 0;
		      jjSamplePriority(SOUND::INTRO_SWISH3);
			  }
			  else*/ {
            obj.determineCurAnim(ANIM::CUSTOM[27], 0);
            obj.direction = (obj.xPos < jjLocalPlayers[0].xPos) ? 1 : -1;
            obj.age = 0;
            obj.state = STATE::IDLE;
            obj.xSpeed = obj.direction;
            obj.ySpeed = 0;
            obj.counter = 100;
			}
          }
          GravityField(obj);
          break;
        }
		case STATE::PUSH:
		{
		 obj.age++;obj.determineCurAnim(ANIM::CUSTOM[27], 2);
		 if(abs(obj.xSpeed)<7)
		 {
		 if(obj.ySpeed==0)
		 obj.xAcc=(0.2+0.05*jjDifficulty)*obj.direction;
		 else
		 obj.xAcc=(0.1+0.05*jjDifficulty)*obj.direction;
		 }
		 else obj.xAcc=0;
		 
          if (obj.frameID != 6) obj.frameID = int((obj.age / 7) % 7);
		  
		   if (obj.age%5==0) {
          int bulletID3 = jjAddObject(OBJECT::TOASTERBULLET, obj.xPos - obj.direction * 32, obj.yPos, obj.objectID, CREATOR::OBJECT);
          jjOBJ @ o = jjObjects[bulletID3];
          o.behavior = IceCloud;
          o.direction = obj.direction;
          o.xSpeed = 0;
          o.ySpeed = 0;

          o.curAnim = o.determineCurAnim(ANIM::AMMO, 82, true);
		  o.frameID=5;
		  o.determineCurFrame();
          o.counterEnd=20;
          o.freeze = 210;
          o.playerHandling = HANDLING::ENEMYBULLET;

        }
		
          if (obj.age > (14+jjDifficulty) * 7 && obj.ySpeed == 0) {
            obj.determineCurAnim(ANIM::CUSTOM[27], 0);
            obj.direction = (obj.xPos < jjLocalPlayers[0].xPos) ? 1 : -1;
            obj.age = 0;
			obj.xAcc=0;
            obj.state = STATE::IDLE;
            obj.xSpeed = obj.direction;
            obj.ySpeed = 0;
            obj.counter = 100;
          }
          GravityField(obj);
          break;
		
		
		}
      case STATE::FIRE:
        {
          GravityField(obj);
          //jjPrint("speed: "+obj.ySpeed+" acc: "+ obj.yAcc+" special: " +obj.special+" totalacc :"+obj.special * obj.yAcc);
          obj.ySpeed = 0;
          obj.determineCurAnim(ANIM::CUSTOM[27], 2);
          obj.age++;
          Frame(obj);
          if (CurrFrame(obj, 3) == true || CurrFrame(obj, 9) == true || (CurrFrame(obj, 15) == true &&jjDifficulty>0)) { //Fire IceWolf
            int bulletID2 = jjAddObject(OBJECT::BLASTERBULLET, obj.xPos + obj.direction * 20, obj.yPos);
            jjOBJ @ bull2 = jjObjects[bulletID2];
            bull2.determineCurAnim(ANIM::CUSTOM[27], 5);
            bull2.behavior = SMOKE::IceWolf;
            bull2.counterEnd = 70;
            bull2.killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
            // bull2.xSpeed = obj.direction * 4;
            bull2.xSpeed = obj.direction * abs(X) / 38 + jjDifficulty * obj.direction;
            bull2.ySpeed = -1.5;
            bull2.yAcc = 0;
            bull2.xAcc = 0;
            // bull2.yAcc=-0.1;
            bull2.playerHandling = HANDLING::ENEMYBULLET;
            jjSamplePriority(SOUND::AMMO_ICEGUNPU);
			            jjSamplePriority(SOUND::DOG_AGRESSIV);
          if(CurrFrame(obj, 15) == true && jjDifficulty>1)
		  {
			  int bulletID2 = jjAddObject(OBJECT::BLASTERBULLET, obj.xPos + obj.direction * 20, obj.yPos);
            jjOBJ @ bull2 = jjObjects[bulletID2];
            bull2.determineCurAnim(ANIM::CUSTOM[27], 5);
            bull2.behavior = SMOKE::IceWolf;
            bull2.counterEnd = 220;
            bull2.killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
            // bull2.xSpeed = obj.direction * 4;
            bull2.xSpeed = (3-obj.counter*0.5)*obj.direction;
            bull2.ySpeed = -9;
            bull2.yAcc = 0;
            bull2.xAcc = 0;
            // bull2.yAcc=-0.1;
            bull2.playerHandling = HANDLING::ENEMYBULLET;
            jjSamplePriority(SOUND::AMMO_ICEGUNPU);
			            jjSamplePriority(SOUND::DOG_AGRESSIV);
						
						obj.counter++;
			}			

          }
          if ((CurrFrame(obj,12)&&jjDifficulty==0)||CurrFrame(obj, 18) == true) {
            obj.state = STATE::IDLE;
            obj.age = 0;
            obj.counter = 150;
            obj.determineCurAnim(ANIM::CUSTOM[27], 1);
          }

          break;
        }

      case STATE::HIT:
        {
          obj.determineCurAnim(ANIM::CUSTOM[27], 2);
          obj.age++;
          Frame(obj);
          if (CurrFrame(obj, 3) == true || CurrFrame(obj, 9) == true || (CurrFrame(obj, 15) == true && jjDifficulty>1)) { //Fire IceWolf
            int bulletID2 = jjAddObject(OBJECT::BOUNCERBULLET, obj.xPos + 50 * obj.direction, obj.yPos);
            jjOBJ @ bull2 = jjObjects[bulletID2];
            bull2.determineCurAnim(ANIM::CUSTOM[17], 2);
            bull2.counterEnd = 150+10*jjDifficulty;
            bull2.behavior = SMOKE::KnightSpikeBall;
            // bull2.xSpeed = obj.direction * 4;
            bull2.xSpeed = 4 * obj.direction;
            bull2.ySpeed = 0;
            bull2.yAcc = 0;
            bull2.xAcc = 0;
            // bull2.yAcc=-0.1;
            bull2.playerHandling = HANDLING::ENEMYBULLET;
            jjSamplePriority(SOUND::AMMO_ICEGUNPU);

          }

          if (CurrFrame(obj, 6) == true) {
		  if(jjDifficulty<=1)
		  {
             obj.state = STATE::IDLE;
		  obj.ySpeed=0;
		  obj.yAcc=0;
          obj.age = 0;
          obj.counter = 150;
          obj.determineCurAnim(ANIM::CUSTOM[27], 1);
		  }
		  else
		  {
            obj.age = 0;
            obj.yAcc = 0.3;
            obj.xSpeed = (7 + jjDifficulty) * obj.direction;
            obj.ySpeed =  9;
            obj.determineCurAnim(ANIM::CUSTOM[27], 2);
            obj.state = STATE::DUCK;
			    jjSamplePriority(SOUND::INTRO_SWISH3);
				}

          }
          GravityField(obj);
          break;
        }

      case STATE::ACTION:
        obj.age++;
        obj.determineCurAnim(ANIM::CUSTOM[27], 3);
        if (obj.frameID != 6) obj.frameID = int((obj.age / 7) % 7);
        if (abs(obj.ySpeed) < 2) {
          // jjPrint("lol");
		  obj.counter--;
          obj.yAcc = 0;
          obj.xAcc = 0;
          obj.direction = X / abs(X);
          obj.state = STATE::BOUNCE;
          obj.xSpeed = (9+jjDifficulty) * X / sqrt(X * X + Y * Y);
          obj.ySpeed = -10 * Y / sqrt(X * X + Y * Y);
          obj.age = 0;
		      jjSamplePriority(SOUND::INTRO_SWISH3);

        }
        GravityField(obj);
        break;

      case STATE::BOUNCE:
        //jjPrint("lol");
        obj.age++;
        obj.determineCurAnim(ANIM::CUSTOM[27], 2);
        if (obj.frameID != 6) obj.frameID = int((obj.age / 7) % 7);
		 if (obj.age%5==0) {
          int bulletID3 = jjAddObject(OBJECT::TOASTERBULLET, obj.xPos - obj.direction * 32, obj.yPos, obj.objectID, CREATOR::OBJECT);
          jjOBJ @ o = jjObjects[bulletID3];
          o.behavior = IceCloud;
          o.direction = obj.direction;
          o.xSpeed = 0;
          o.ySpeed = 0;

          o.curAnim = o.determineCurAnim(ANIM::AMMO, 82, true);
		  o.frameID=5;
		  o.determineCurFrame();
          o.counterEnd=20;
          o.freeze = 210;
          o.playerHandling = HANDLING::ENEMYBULLET;

        }
        if (CurrFrame(obj, 6) == true) {
          //jjPrint("lol");
          obj.determineCurAnim(ANIM::CUSTOM[27], 0);
          obj.direction = (obj.xPos < jjLocalPlayers[0].xPos) ? 1 : -1;
          obj.age = 0;
          obj.state = STATE::IDLE;
          obj.xSpeed = obj.direction;
          obj.ySpeed = 0;
          obj.counter = 100;
          obj.yAcc = 0.3;
        }
        GravityField(obj);
        break;
		

      case STATE::FREEZE:
        if (obj.freeze > 0)
          --obj.freeze;
        if (obj.freeze <= 0) {
          obj.state = obj.oldState;
          obj.unfreeze(0);
        }
        break;
      case STATE::DEACTIVATE:
        obj.deactivate();
        break;
      case STATE::KILL:
        obj.counter = 0;
        obj.state = STATE::DONE;
        obj.determineCurAnim(ANIM::CUSTOM[27], 4);
        // obj.delete();
        break;
      case STATE::DONE:
        obj.xSpeed = 0;
        obj.determineCurAnim(ANIM::CUSTOM[27], 4);
        obj.frameID = 0;
        obj.determineCurFrame();
        obj.counter++;
		if(obj.counter==80)
jjAlert("The Jailer:", false, STRING::MEDIUM);
	if(obj.counter==300)
jjAlert("You may have defeated me...", false, STRING::MEDIUM);
	if(obj.counter==530)
jjAlert("But your journey won't last long...", false, STRING::MEDIUM);
        if (obj.counter == 820) {
          obj.particlePixelExplosion(31); //loops for scrubs
          obj.particlePixelExplosion(31);

          obj.particlePixelExplosion(31);

          obj.particlePixelExplosion(31);

          obj.particlePixelExplosion(31);

          obj.particlePixelExplosion(31);

          obj.delete();
		  gem::saveGemData();
          jjNxt();
        }
        GravityField(obj);
        break;

      default:

        obj.draw();

        break;

      }

      obj.draw();

    }
    bool onObjectHit(jjOBJ @ obj, jjOBJ @ bullet, jjPLAYER @ player, int force) { //As described in the signs in-level, this is a nearly 100% faithful recreation of standard enemy collision code.
      if (obj.energy <= 0 && done==false && obj.state != STATE::START && obj.state != STATE::STILL) { //killed
        done=true;
        obj.counter = 0;
        obj.xSpeed = 0;
        obj.ySpeed = 0;
        obj.yAcc = 0.3;
        obj.state = STATE::DONE;
        obj.determineCurAnim(ANIM::CUSTOM[27], 4);
        obj.frameID = 0;
        obj.bulletHandling = HANDLING::IGNOREBULLET;
        return true;

        //obj.particlePixelExplosion(2);
      } 
	  if(done==true)
	  return true;
	  else if (bullet!is null) {
        //recreation of HANDLING::HURTBYBULLET with HANDLING::ENEMY
        if (obj.causesRicochet) {
          if ((bullet.var[6] & 6) == 0) //not fire-based, not a laser beam
            bullet.ricochet();
          else if ((bullet.var[6] & 4) == 0) //not a laser beam
            bullet.delete();
        } else if ((bullet.var[6] & 16) == 0) //not a fireball
          bullet.state = STATE::EXPLODE;
        if (obj.freeze > 0 && force < 3)
          force = 3;
        obj.energy -= force;
        obj.justHit = 5; //flash white for 5 ticks--jjOBJ::justHit is automatically deincremented by the JJ2 engine, so individual behavior functions don't need to worry about doing that.
        if (obj.energy <= 0) { //killed
          obj.energy = 0;
          obj.state = STATE::KILL;
          if (obj.freeze > 0)
            obj.unfreeze(0);
          /*else if ((bullet.var[6] & 2) == 0) //not fire-based
            obj.particlePixelExplosion(0);
          else {
            jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BURN);
            if ((bullet.var[6] & 4) != 0) //laser beam
              obj.particlePixelExplosion(72); //gray
            else
              obj.particlePixelExplosion(((bullet.var[6] & 8) != 0) ? 32 : 40); //powered-up (blue) or not (orange)
          }*/
          if (player!is null) {
            //obj.grantPickup(player, (uint(bullet.curAnim) == jjAnimSets[ANIM::AMMO].firstAnim + 17) ? 5 : 10);
            //givePlayerPointsForObject(player, obj);
          }
        } else
          obj.freeze = 0;
      } else { //recreation of HANDLING::ENEMY; player guaranteed to be non-null
        if (force != 0) { //attacking via special attack, e.g. buttstomp
                    player.hurt(); //constant amount of damage for special attacks
          if (obj.energy <= 0) { //killed
            obj.energy = 0;
            obj.state = STATE::KILL;
          } /*else { //only wounded
            obj.justHit = 5;
          }*/

          if (obj.freeze > 0) {
            obj.unfreeze(1);
          }

          if (force > 0) { //buttstomp or sugar rush
            player.buttstomp = 50; //landing
            player.ySpeed = player.ySpeed / -2 - 8;
            player.yAcc = 0;
            player.extendInvincibility(-70);
          } else if (force == -101) { //running into frozen enemy
            player.xAcc = 0;
            player.xSpeed /= -2;
            player.ySpeed = -6;
            player.extendInvincibility(-10);
          }
        } else { //not attacking
          player.hurt();
        }
      }
      return true;
    }
  }
  void KnightSpikeBall(jjOBJ @ obj) {
    obj.behave(BEHAVIOR::BOUNCERBULLET, true);
    if (obj.state == STATE::EXPLODE && obj.isActive) {
      obj.unfreeze(1);
      obj.delete();

    }
  }
  void IceWolf(jjOBJ @ obj) {
   float X = jjLocalPlayers[0].xPos - obj.xPos;
    float Y = jjLocalPlayers[0].yPos - obj.yPos;
    if (obj.state == STATE::EXPLODE && obj.isActive) {
     // obj.particlePixelExplosion(31);
	 
      obj.xPos-=5*obj.xSpeed;
      obj.yPos-=5*obj.ySpeed;
      obj.unfreeze(1);
      obj.delete();
    
    }
    obj.behave(BEHAVIOR::BULLET, true);
     obj.xAcc=(obj.xSpeed*obj.xAcc>0 ? 0.1 : 0.3 )*X/abs(X);
    obj.yAcc = (obj.ySpeed*obj.yAcc>0 ? 0.1 : 0.3 ) * Y / abs(Y);
  }

  void Icicle(jjOBJ @ obj) {
    if (obj.state == STATE::EXPLODE && obj.isActive) {
      //obj.xPos-=8*obj.xSpeed;
      //obj.yPos-=8*obj.ySpeed;
      obj.unfreeze(1);
      obj.delete();
    }
    //obj.frameID=3;
    obj.behave(BEHAVIOR::BULLET, true);
    if (obj.counter == 7 * 2) {
      obj.determineCurAnim(ANIM::CUSTOM[27], 7);
      obj.frameID = 0;
      obj.determineCurFrame();

    }
    if (obj.counter == 7 * 3)
      obj.xAcc = 0.15 * obj.xSpeed / abs(obj.xSpeed);

    int width = jjAnimFrames[obj.curFrame].width;
    int height = jjAnimFrames[obj.curFrame].height;
    int hotY = jjAnimFrames[obj.curFrame].hotSpotY;

    int newXPos = int(obj.xPos + obj.xSpeed) + (width * obj.direction) / 2;
    //Check horizonal collision
    if ((newXPos < 0) || (newXPos > jjLayerWidth[4] * 32) || jjMaskedVLine(newXPos, int(obj.yPos + hotY), height - 8)) {
      obj.state = STATE::EXPLODE;
    }

  }
}

void Frame(jjOBJ @ obj) {
  obj.frameID = int((obj.age / 7) % (jjAnimations[obj.curAnim].frameCount));
  obj.determineCurFrame();
}
bool CurrFrame(jjOBJ @ obj, int id) {
  return obj.age == 7 * id + 6;
}

void RandomKnightAttack(jjOBJ @ obj,int attack=(jjRandom()%4)) {
  int rand = jjRandom() % 4;
  obj.direction = (obj.xPos < jjLocalPlayers[0].xPos) ? 1 : -1;
  switch (attack%4) {
  case 0:
    obj.yAcc = 0.3;
    obj.xSpeed = 0*(4 + jjDifficulty) * obj.direction;
    obj.ySpeed = jjDifficulty==0 ? 0 : (jjRandom()%2)*10;
    obj.determineCurAnim(ANIM::CUSTOM[27], 2);
    obj.state = STATE::PUSH;
    jjSamplePriority(SOUND::INTRO_SWISH3);

    break;
  case 1:
    obj.determineCurAnim(ANIM::CUSTOM[27], 2);
    obj.xSpeed = 0;
    obj.ySpeed = 0;
    obj.state = STATE::FIRE;
	
    break;
  case 2:
    obj.determineCurAnim(ANIM::CUSTOM[27], 2);
    obj.xSpeed = 0;
    obj.ySpeed = 10;
    obj.state = STATE::HIT;

    break;
  case 3:
    obj.determineCurAnim(ANIM::CUSTOM[27], 3);
    obj.xSpeed = 0;
    obj.ySpeed = 10;
    obj.state = STATE::ACTION;

    break;
  }
  obj.frameID = 0;
  //jjPrint("State: " +obj.state);
  obj.age = obj.counter = 0;
}


void IceCloud(jjOBJ @ obj) {
    obj.behave(BEHAVIOR::TOASTERBULLET, true);
    int playerID = obj.findNearestPlayer(30000);
    if (obj.state == STATE::FLY && obj.doesCollide(jjPlayers[playerID], true)) {
      jjPlayers[playerID].freeze(true);
      jjPlayers[playerID].xSpeed -= (obj.xSpeed / (obj.direction == -1 ? 8 : -8)) * obj.direction;
    }
  }


 void putEnemyOnGround(jjOBJ @ obj) {
    int width = jjAnimFrames[obj.curFrame].width;
    int height = jjAnimFrames[obj.curFrame].height;
    int hotY = jjAnimFrames[obj.curFrame].hotSpotY;
    while (!jjMaskedHLine(int(obj.xPos) - width / 2, width, int(obj.yPos) + height + hotY))
      obj.yPos += 1;
  }
  void liftEnemyFromGround(jjOBJ @ obj) {
    int width = jjAnimFrames[obj.curFrame].width;
    int height = jjAnimFrames[obj.curFrame].height;
    int hotY = jjAnimFrames[obj.curFrame].hotSpotY;
    while (jjMaskedHLine(int(obj.xPos) - width / 2, width, int(obj.yPos) + height + hotY))
      obj.yPos -= 1;
  }