Example usage for com.badlogic.gdx.math Vector2 rotate

List of usage examples for com.badlogic.gdx.math Vector2 rotate

Introduction

In this page you can find the example usage for com.badlogic.gdx.math Vector2 rotate.

Prototype

public Vector2 rotate(float degrees) 

Source Link

Document

Rotates the Vector2 by the given angle, counter-clockwise assuming the y-axis points up.

Usage

From source file:be.ac.ucl.lfsab1509.bouboule.game.body.Bonus.java

License:Open Source License

/**
 * Constructor for a Bonus object /*from  w w  w .j  av a  2 s  .  c  o m*/
 * @param px/py         : initial position
 * @param angle         : initial rotation
 * @param texRegionPath : Path to the image file
 * @param jsonFile      : Path to the jsonFile if needed ( "" else)
 * @param jsonName      : jsonName of the object ( must match the json file attribute )
 *
 * public Bonus( final float px, final float py, 
 *      final float angle, final String texRegionPath, 
 *      final String jsonFile, final String jsonName, final short bonusType)
 */
public Bonus(final float angle, final String texRegionPath, final String jsonFile, final String jsonName,
        final Entity.BonusType bonusType) {

    super();

    int size = GlobalSettings.ARENAWAYPOINTALLOW.size();
    MapNode node = GlobalSettings.ARENAWAYPOINTALLOW.get(random.nextInt(size));

    Vector2 pos = new Vector2(node.xToPixel() - 32, node.yToPixel() - 32);
    Vector2 radius = new Vector2(node.weightToPixel() * random.nextFloat(), 0);
    radius.rotate(random.nextInt(359));
    pos.add(radius);

    this.texture = new TextureRegion(new Texture(texRegionPath));

    this.sprite = new Sprite(texture);

    MakeBody(0, 0, 0, BodyType.StaticBody, 0, 0, true, pos, angle, jsonFile, jsonName,
            GraphicManager.convertToGame(texture.getRegionWidth()));

    //Ensure that the object don't rotate.
    body.setFixedRotation(true);

    //Create the userData of type Bonus and bonusType
    this.entity = new Entity(Entity.BONUS, true, bonusType);

    body.setUserData(this.entity);

    //Ensure that the body image position is set on the origin defined by 
    //the jsonFile
    if (origin != null) {
        pos = positionVector.cpy();
        pos = pos.sub(origin);
        sprite.setPosition(pos.x, pos.y);
        sprite.setOrigin(origin.x, origin.y);
        sprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
    }

    // removed the bonus after a delay
    Timer.Task task = new Timer.Task() {
        @Override
        public void run() {
            entity.setAlive(false);
        }
    };
    Timer timer = new Timer();
    timer.scheduleTask(task, 10f);
}

From source file:CB_Locator.Map.MapViewBase.java

License:Open Source License

public Vector2 worldToScreen(Vector2 point) {

    Vector2 result = new Vector2(0, 0);
    result.x = ((long) point.x - screenCenterW.x) / camera.zoom + (float) mapIntWidth / 2;
    result.y = -(-(long) point.y + screenCenterW.y) / camera.zoom + (float) mapIntHeight / 2;
    result.add(-(float) mapIntWidth / 2, -(float) mapIntHeight / 2);
    result.rotate(mapHeading);
    result.add((float) mapIntWidth / 2, (float) mapIntHeight / 2);
    return result;

}

From source file:com.dongbat.invasion.bullet.types.HomingSplitStunBullet.java

@Override
public void update(Entity bullet) {
    if (PhysicsUtil.isGoingOutOfBound(bullet) != PhysicsUtil.OutOfBound.NONE) {
        BulletUtil.destroy(bullet);/*from   w ww .  j av a  2  s  . co m*/
    }
    long gameTimeSince = TimeUtil.getGameTimeSince(startTime);
    if (splitDisable && gameTimeSince > Constants.BULLET.SPLIT_DELAY) {
        splitDisable = false;
    }
    if (isHoming) {
        if (homingDisable && gameTimeSince > Constants.BULLET.HOMING_DELAY) {
            homingDisable = false;
        }
        if (!homingDisable) {
            if (target == null || !target.isActive()) {
                target = PhysicsUtil.findNearestEnemy(PhysicsUtil.getPosition(bullet));
            }
            if (target != null && target.isActive()) {
                MovementUtil.chase(bullet, target);
            }
        }
    }
    if (splitDisable) {
        return;
    }
    Collision collision = EntityUtil.getComponent(bullet, Collision.class);
    if (collision.collidedList.size == 0) {
        return;
    }
    Entity enemy = collision.collidedList.get(0);
    if (!EntityUtil.isEnemy(enemy)) {
        return;
    }
    DamageUtil.damageSingle(enemy, Constants.BULLET.DEFAULT_DAMAGE);
    BuffUtil.addBuff(bullet, enemy, "stun", duration);
    if (level > 0) {
        float enemyRadius = PhysicsUtil.getRadius(enemy);
        for (int i = 0; i < Constants.BULLET.DEFAULT_NUMBER_SUBBULLET; i++) {
            float angle = (float) (Math.random() * 340 + 20);
            Vector2 enemyPosition = new Vector2(PhysicsUtil.getPosition(enemy));
            Vector2 v = new Vector2(enemyRadius + 10, 0);
            Vector2 vRotation = v.rotate(angle);
            Vector2 fireVector = new Vector2(vRotation).add(enemyPosition);
            Entity subBullet = BulletRegistry.createBullet("homingSplitStun", fireVector);
            HomingSplitStunBullet type = (HomingSplitStunBullet) EntityUtil
                    .getComponent(subBullet, Bullet.class).getType();
            PhysicsUtil.applyImpulse(subBullet, new Vector2(vRotation).scl(10));
            type.level = level - 1;
            type.splitDisable = true;
            type.homingDisable = true;
            type.isHoming = true;
        }
    }
    BulletUtil.destroy(bullet);
}

From source file:com.dongbat.invasion.util.PhysicsUtil.java

public static void applyImpulseOnEnemy(Entity entity, Vector2 impulse) {
    if (EntityUtil.isEnemy(entity)) {
        float angle = impulse.angle();
        float rotateAngle = angle > 90 ? 180 - angle : -angle;
        Vector2 apply = impulse.rotate(rotateAngle);
        applyImpulse(entity, apply);/*from  w ww . j a  va 2  s  . c  om*/
    }
}

From source file:com.dongbat.invasion.util.PhysicsUtil.java

public static void applyForceOnEnemy(Entity entity, Vector2 force) {
    if (EntityUtil.isEnemy(entity)) {
        float angle = force.angle();
        float rotateAngle = angle > 90 ? 180 - angle : -angle;
        Vector2 apply = force.rotate(rotateAngle);
        applyForce(entity, apply);/*from w w  w  . j  a  va2 s . c o m*/
    }
}

From source file:com.me.mylolgame.Levels.java

License:Open Source License

/**
 * We currently have 92 levels, each of which is described in part of the
 * following function./*from w  w  w  .  j  av  a  2s .co  m*/
 */
public void display(int whichLevel) {
    /*
     * In this level, all we have is a hero (the green ball) who needs to
     * make it to the destination (a mustard colored ball). The game is
     * configured to use tilt to control the hero.
     */
    if (whichLevel == 1) {
        // set the screen to 48 meters wide by 32 meters high... this is
        // important, because Config.java says the screen is 480x320, and
        // LOL likes a 20:1 pixel to meter ratio. If we went smaller than
        // 48x32, things would get really weird. And, of course, if you make
        // your screen resolution higher in Config.java, these numbers would
        // need to get bigger.
        //
        // Level.configure MUST BE THE FIRST LINE WHEN DRAWING A LEVEL!!!
        Level.configure(48, 32);
        // there is no default gravitational force
        Physics.configure(0, 0);

        // in this level, we'll use tilt to move some things around. The
        // maximum force that tilt can exert on anything is +/- 10 in the X
        // dimension, and +/- 10 in the Y dimension
        Tilt.enable(10, 10);

        // now let's create a hero, and indicate that the hero can move by
        // tilting the phone. "greenball.png" must be registered in
        // the registerMedia() method, which is also in this file. It must
        // also be in your android game's assets folder.
        Hero h = Hero.makeAsBox(0, 10, 3, 3, "greenball.png");
        h.setMoveByTilting();

        // draw a circular destination, and indicate that the level is won
        // when the hero reaches the destination. "mustardball.png" must be
        // registered in registerMedia()
        Destination.makeAsCircle(29, 26, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
    }

    /*
     * In this level, we make the play a bit smoother by adding a bounding
     * box and changing the way that LibLOL interacts with the player
     */
    else if (whichLevel == 2) {
        // start by setting everything up just like in level 1
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Hero h = Hero.makeAsCircle(4, 17, 3, 3, "greenball.png");
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 26, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // add a bounding box so the hero can't fall off the screen
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 0, 0, 0);

        // change the text that we display when the level is won
        WinScene.get().setDefaultWinText("Good job!");

        // add a pop-up message that shows for one second at the
        // beginning of the level. The '50, 50' indicates the bottom left
        // corner of the text we display. 255,255,255 represents the red,
        // green, and blue components of the text color (the color will be
        // white). We'll write our text in the Arial font, with a size of 32
        // pt. The "\n" in the middle of the text causes a line break. Note
        // that "arial.ttf" must be in your android game's assets folder.
        PreScene.get().addText("Reach the destination\nto win this level.", 50, 50, 255, 255, 255, "arial.ttf",
                32);
    }

    /*
     * In this level, we change the physics from level 2 so that things roll
     * and bounce a little bit more nicely.
     */
    else if (whichLevel == 3) {
        // These lines should be familiar after the last two levels
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // give the hero some density and friction, so that it can roll when
        // it encounters a wall... notice that once it has density, it has
        // mass, and it moves a lot slower...
        h.setPhysics(1, 0, 0.6f);

        // the bounding box now also has nonzero density, elasticity, and
        // friction... you should check out what happens if the friction
        // stays at 0.
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        // Let's draw our message in the center of the screen this time
        PreScene.get().addText("Reach the destination\nto win this level.", 255, 255, 255, "arial.ttf", 32);
        // And let's say that instead of touching the message to make it go
        // away, we'll have it go away automatically after 2 seconds
        PreScene.get().setExpire(2);
        // Note that we're going back to the default PostScene text...
    }

    /*
     * It's confusing to have multiple heroes in a level, but we can... this
     * shows how to have multiple destinations and heroes
     */
    else if (whichLevel == 4) {
        // standard stuff...
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        // now let's draw two heroes who can both move by tilting, and
        // who both have density and friction. Note that we lower the
        // density, so they move faster
        Hero h1 = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h1.setPhysics(.1f, 0, 0.6f);
        h1.setMoveByTilting();
        Hero h2 = Hero.makeAsCircle(14, 7, 3, 3, "greenball.png");
        h2.setPhysics(.1f, 0, 0.6f);
        h2.setMoveByTilting();

        // notice that now we will make two destinations, each of which
        // defaults to only holding ONE hero, but we still need to get two
        // heroes to destinations in order to complete the level
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Destination.makeAsCircle(29, 26, 2, 2, "mustardball.png");
        Score.setVictoryDestination(2);

        // Let's show msg1.png instead of text. Note that we had to
        // register it in registerMedia(), and that we're stretching it
        // slightly, since its dimensions are 460x320
        PreScene.get().addImage("msg1.png", 0, 0, 960, 640);
    }

    /*
     * This level demonstrates that we can have many heroes that can reach
     * the same destination. It also shows our first sound effect
     */
    else if (whichLevel == 5) {
        // standard stuff...
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h1 = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h1.setPhysics(.1f, 0, 0.6f);
        h1.setMoveByTilting();
        Hero h2 = Hero.makeAsCircle(14, 7, 3, 3, "greenball.png");
        h2.setPhysics(.1f, 0, 0.6f);
        h2.setMoveByTilting();
        PreScene.get().addText("All heroes must\nreach the destination", 255, 255, 255, "arial.ttf", 32);

        // now let's make a destination, but indicate that it can hold TWO
        // heroes
        Destination d = Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        d.setHeroCount(2);

        // let's also say that whenever a hero reaches the destination, a
        // sound will play
        d.setArrivalSound("hipitch.ogg");

        // Notice that this line didn't change from level 4
        Score.setVictoryDestination(2);
    }

    /*
     * Tilt can be used to control velocity, instead of applying forces to
     * the entities on the screen. It doesn't always work well, but it's a
     * nice option to have...
     */
    else if (whichLevel == 6) {
        // standard stuff...
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        PreScene.get().addText("A different way\nto use tilt.", 255, 255, 255, "arial.ttf", 32);

        // change the behavior or tilt
        Tilt.setAsVelocity(true);
    }

    /*
     * This level adds an enemy, to demonstrate that we can make it possible
     * to lose a level
     */
    else if (whichLevel == 7) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        // Notice that we changed the font size and color
        PreScene.get().addText("Avoid the enemy and\nreach the destination", 25, 255, 255, "arial.ttf", 20);

        // draw an enemy... we don't need to give it physics for now...
        Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");

        // turn off the win and lose scenes... whether the player wins or
        // loses, we'll just start the appropriate level. Be sure to test
        // the game by losing *and* winning!
        WinScene.get().disable();
        LoseScene.get().disable();
    }

    /*
     * This level explores a bit more of what we can do with enemies, by
     * having an enemy with a fixed path.
     */
    else if (whichLevel == 8) {
        // configure a basic level, just like the start of level 2:
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 27, 3, 3, "greenball.png");
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        PreScene.get().addText("Avoid the enemy and\nreach the destination", 255, 255, 255, "arial.ttf", 20);

        // put some extra text on the PreScene.get()
        PreScene.get().addText("(the enemy is red)", 5, 5, 50, 200, 122, "arial.ttf", 10);

        // draw an enemy
        Enemy e = Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");

        // attach a path to the enemy. It starts at (25, 25) and moves to
        // (25, 2). This means it has *2* points on its route. Notice that
        // since it loops, it is not going to gracefully move back to its
        // starting point. Also note that the first point is the same as the
        // enemy's original position. If it wasn't, then there would be an
        // odd glitch at the beginning of the level.
        e.setRoute(new Route(2).to(25, 25).to(25, 2), 10, true);

        // Note that when the level is lost, the default lose text will be
        // displayed on a PostScene
    }

    /*
     * This level explores a bit more of what we can do with paths.
     */
    else if (whichLevel == 9) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        PreScene.get().addText("Avoid the enemy and\nreach the destination", 50, 50, 255, 255, 255, "arial.ttf",
                20);

        // draw an enemy that can move
        Enemy e = Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");
        // This time, we add a third point, which is the same as the
        // starting point. This will give us a nicer sort of movement. Also
        // note the diagonal movement.
        e.setRoute(new Route(3).to(25, 25).to(12, 2).to(25, 25), 2, true);
        // note that any number of points is possible... you could have
        // extremely complex Routes!
    }

    /*
     * We can make enemies move via tilt. We can also configure some other
     * kinds of sounds
     */
    else if (whichLevel == 10) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(0.1f, 0, 0.6f);
        h.setMoveByTilting();
        PreScene.get().addImage("msg2.png", 0, 0, 960, 640);

        // let's make the destination rotate:
        Destination d = Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        d.setRotationSpeed(1);
        Score.setVictoryDestination(1);

        // draw an enemy who moves via tilt
        Enemy e3 = Enemy.makeAsCircle(35, 25, 2, 2, "redball.png");
        e3.setPhysics(1.0f, 0.3f, 0.6f);
        e3.setMoveByTilting();

        // configure some sounds to play on win and lose. Of course, all
        // these sounds must be registered!
        WinScene.get().setSound("winsound.ogg");
        LoseScene.get().setSound("losesound.ogg");

        // set background music
        Level.setMusic("tune.ogg");

        // custom text for when the level is lost
        LoseScene.get().setDefaultText("Better luck next time...");
    }

    /*
     * This shows that it is possible to make a level that is larger than a
     * screen. It also shows that there is a "heads up display" that can be
     * used for providing information and touchable controls
     */
    else if (whichLevel == 11) {
        // make the level really big
        Level.configure(400, 300);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 400, 300, "red.png", 0, 0, 0);

        // put the hero and destination far apart
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(329, 281, 10, 10, "mustardball.png");
        Score.setVictoryDestination(1);

        // We want to be sure that no matter what, the player can see the
        // hero. We achieve this by having the camera follow the hero:
        Level.setCameraChase(h);

        // add zoom buttons. We are using blank images, which means that the
        // buttons will be invisible... that's nice, because we can make the
        // buttons big (covering the left and right halves of the screen).
        // When debug rendering is turned on, we'll be able to see a red
        // outline of the two rectangles. You could also use images (that
        // you registered, of course), but if you did, you'd either need to
        // make them small, or make them semi-transparent.
        Control.addZoomOutButton(0, 0, 480, 640, "", 8);
        Control.addZoomInButton(480, 0, 480, 640, "", .25f);

        PreScene.get().addText("Press left to zoom out\nright to zoom in", 255, 255, 255, "arial.ttf", 32);
    }

    /*
     * this level introduces obstacles, and also shows the difference
     * between "box" and "circle" physics
     */
    else if (whichLevel == 12) {
        // configure a basic level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        // add a hero and destination
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // let's draw an obstacle whose underlying shape is a box, but whose
        // picture is a circle. This can be odd... our hero can roll around
        // an invisible corner on this obstacle. When debug rendering is
        // turned on (in Config.java), you'll be able to see the true shape
        // of the obstacle.
        Obstacle o1 = Obstacle.makeAsBox(0, 0, 3.5f, 3.5f, "purpleball.png");
        o1.setPhysics(1, 0, 1);

        // now let's draw an obstacle whose shape and picture are both
        // circles. The hero rolls around this nicely.
        Obstacle o2 = Obstacle.makeAsCircle(10, 10, 3.5f, 3.5f, "purpleball.png");
        o2.setPhysics(1, 0, 1);

        // draw a wall using circle physics and a stretched rectangular
        // picture. This wall will do really funny things
        Obstacle o3 = Obstacle.makeAsCircle(20, 25, 6, 0.5f, "red.png");
        o3.setPhysics(1, 0, 1);

        // draw a rectangular wall the right way, as a box
        Obstacle o4 = Obstacle.makeAsBox(34, 2, 0.5f, 20, "red.png");
        o4.setPhysics(1, 0, 1);

        PreScene.get().addText("An obstacle's appearance may\nnot match its physics", 255, 255, 255,
                "arial.ttf", 32);
    }

    /*
     * this level just plays around with physics a little bit, to show how
     * friction and elasticity can do interesting things.
     */
    else if (whichLevel == 13) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("These obstacles have\ndifferent physics\nparameters", 255, 255, 255,
                "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        // Colliding the hero with these obstacles can have interesting
        // effects
        Obstacle o1 = Obstacle.makeAsCircle(0, 0, 3.5f, 3.5f, "purpleball.png");
        o1.setPhysics(0, 100, 0);
        Obstacle o2 = Obstacle.makeAsCircle(10, 10, 3.5f, 3.5f, "purpleball.png");
        o2.setPhysics(10, 0, 100);
    }

    /*
     * This level introduces goodies. Goodies are something that we collect.
     * We can make the collection of goodies lead to changes in the behavior
     * of the game, and in this example, the collection of goodies "enables"
     * a destination.
     */
    else if (whichLevel == 14) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        PreScene.get().addText("You must collect\ntwo blue balls", 255, 255, 255, "arial.ttf", 32);

        // Add some stationary goodies. Note that the default is
        // for goodies to not cause a change in the hero's behavior at the
        // time when a collision occurs... this is often called being a
        // "sensor"... it means that collisions are still detected by the
        // code, but they don't cause changes in momentum
        //
        // Note that LibLOL allows goodies to have one of 4 "types". By
        // default, collecting a goodie increases the "type 1" score by 1.
        Goodie.makeAsCircle(0, 30, 2, 2, "blueball.png");
        Goodie.makeAsCircle(0, 15, 2, 2, "blueball.png");

        // here we create a destination. Note that we now set its activation
        // score to 2, so that you must collect two goodies before the
        // destination will "work"
        Destination d = Destination.makeAsCircle(29, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        // we must provide an activation score for each of the 4 types of
        // goodies
        d.setActivationScore(2, 0, 0, 0);

        // let's put a display on the screen to see how many type-1 goodies
        // we've collected. Since the second parameter is "2", we'll display
        // the count as "X/2 Goodies" instead of "X Goodies"
        Display.addGoodieCount(1, 2, " Goodies", 220, 280, "arial.ttf", 255, 0, 255, 20);
    }

    /*
     * earlier, we saw that enemies could move along a Route. So can any
     * other entity, so we'll move destinations, goodies, and obstacles,
     * too.
     */
    else if (whichLevel == 15) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Every entity can move...", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(44, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        // make a destination that moves, and that requires one goodie to be
        // collected before it works
        Destination d = Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        d.setActivationScore(1, 0, 0, 0);
        d.setRoute(new Route(3).to(29, 6).to(29, 26).to(29, 6), 4, true);
        Score.setVictoryDestination(1);

        // make an obstacle that moves
        Obstacle o = Obstacle.makeAsBox(0, 0, 3.5f, 3.5f, "purpleball.png");
        o.setPhysics(0, 100, 0);
        o.setRoute(new Route(3).to(0, 0).to(10, 10).to(0, 0), 2, true);

        // make a goodie that moves
        Goodie g = Goodie.makeAsCircle(5, 5, 2, 2, "blueball.png");
        g.setRoute(new Route(5).to(5, 5).to(5, 25).to(25, 25).to(9, 9).to(5, 5), 10, true);

        // draw a goodie counter in light blue (60, 70, 255) with a 12-point
        // font
        Display.addGoodieCount(1, 0, " Goodies", 220, 280, "arial.ttf", 60, 70, 255, 12);
    }

    /*
     * Sometimes, we don't want a destination, we just want to say that the
     * player wins by collecting enough goodies. This level also shows that
     * we can set a time limit for the level, and we can pause the game.
     */
    else if (whichLevel == 16) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Collect all\nblue balls\nto win", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(2, 20, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        // draw 5 goodies
        Goodie.makeAsCircle(.5f, .5f, 2, 2, "blueball.png");
        Goodie.makeAsCircle(5.5f, 1.5f, 2, 2, "blueball.png");
        Goodie.makeAsCircle(10.5f, 2.5f, 2, 2, "blueball.png");
        Goodie.makeAsCircle(15.5f, 3.5f, 2, 2, "blueball.png");
        Goodie.makeAsCircle(20.5f, 4.5f, 2, 2, "blueball.png");

        // indicate that we win by collecting enough goodies
        Score.setVictoryGoodies(5, 0, 0, 0);

        // put the goodie count on the screen
        Display.addGoodieCount(1, 5, " Goodies", 220, 280, "arial.ttf", 60, 70, 255, 12);

        // put a simple countdown on the screen
        Display.addCountdown(15, "Time Up!", 400, 50);

        // let's also add a screen for pausing the game. In a real game,
        // every level should have a button for pausing the game, and the
        // pause scene should have a button for going back to the main
        // menu... we'll show how to do that later.
        PauseScene.get().addText("Game Paused", 255, 255, 255, "arial.ttf", 32);
        Control.addPauseButton(0, 300, 20, 20, "red.png");
    }

    /*
     * This level shows how "obstacles" need not actually impede the hero's
     * movement. Here, we attach "damping factors" to the hero, which let us
     * make the hero speed up or slow down based on interaction with the
     * obstacle. This level also adds a stopwatch. Stopwatches don't have
     * any meaning, but they are nice to have anyway...
     */
    else if (whichLevel == 17) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Obstacles as zoom\nstrips, friction pads\nand repellers", 255, 255, 255,
                "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // add a stopwatch... note that there are two ways to add a
        // stopwatch, the other of which allows for configuring the font
        Display.addStopwatch(50, 50);

        // Create a pause scene that has a back button on it, and a button
        // for pausing the level. Note that the background image must come
        // first
        PauseScene.get().addImage("fade.png", 0, 0, 960, 640);
        PauseScene.get().addText("Game Paused", 255, 255, 255, "arial.ttf", 32);
        PauseScene.get().addBackButton("greyball.png", 0, 300, 20, 20);
        Control.addPauseButton(0, 300, 20, 20, "red.png");

        // now draw three obstacles. Note that they have different dampening
        // factors. one important thing to notice is that since we place
        // these on the screen *after* we place the hero on the screen, the
        // hero will go *under* these things.

        // this obstacle's dampening factor means that on collision, the
        // hero's velocity is multiplied by -1... he bounces off at an
        // angle.
        Obstacle o = Obstacle.makeAsCircle(10, 10, 3.5f, 3.5f, "purpleball.png");
        o.setPad(-1);

        // this obstacle accelerates the hero... it's like a turbo booster
        o = Obstacle.makeAsCircle(20, 10, 3.5f, 3.5f, "purpleball.png");
        o.setPad(5);

        // this obstacle slows the hero down... it's like running on
        // sandpaper. Note that the hero only slows down on initial
        // collision, not while going under it.
        o = Obstacle.makeAsBox(30, 10, 3.5f, 3.5f, "purpleball.png");
        o.setRotationSpeed(2);
        o.setPad(0.2f);
    }

    /*
     * This level shows that it is possible to give heroes and enemies
     * different strengths, so that a hero doesn't disappear after a single
     * collision. It also shows that when an enemy defeats a hero, we can
     * customize the message that prints
     */
    else if (whichLevel == 18) {
        // set up a basic level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("The hero can defeat \nup to two enemies...", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // draw a hero and give it strength of 10. The default is for
        // enemies to have "2" units of damage, and heroes to have "1" unit
        // of strength, so that any collision defeats the hero without
        // removing the enemy.
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        h.setStrength(10);

        // draw a strength meter to show this hero's strength
        Display.addStrengthMeter(" Strength", 220, 280, h);

        // our first enemy stands still:
        Enemy e = Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setRotationSpeed(1);
        e.setDamage(4);
        // this text will be displayed if this enemy defeats the hero
        e.setDefeatHeroText("How did you hit me?");

        // our second enemy moves along a path
        e = Enemy.makeAsCircle(35, 25, 2, 2, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setRoute(new Route(3).to(35, 25).to(15, 25).to(35, 25), 10, true);
        e.setDamage(4);
        e.setDefeatHeroText("Stay out of my way");

        // our third enemy moves with tilt, which makes it hardest to avoid
        e = Enemy.makeAsCircle(35, 25, 2, 2, "redball.png");
        e.setPhysics(.1f, 0.3f, 0.6f);
        e.setMoveByTilting();
        e.setDamage(4);
        e.setDefeatHeroText("You can't hide!");

        // be sure when testing this level to lose, with each enemy being
        // the last the hero collides with, so that you can see the
        // different messages
    }

    /*
     * This level shows that we can win a level by defeating all enemies
     */
    else if (whichLevel == 19) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("You have 10 seconds\nto defeat the enemies", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        // give the hero enough strength that this will work...
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setStrength(10);
        h.setMoveByTilting();

        // draw a few enemies, and change their "damage" (the amount by
        // which they decrease the hero's strength)
        Enemy e = Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setRotationSpeed(1);
        e.setDamage(4);
        e = Enemy.makeAsCircle(35, 25, 2, 2, "redball.png");
        e.setPhysics(.1f, 0.3f, 0.6f);
        e.setMoveByTilting();
        e.setDamage(4);

        // put a countdown on the screen
        Display.addCountdown(10, "Time Up!", 200, 25);

        // indicate that defeating all of the enemies is the way to win this
        // level
        Score.setVictoryEnemyCount();
    }

    /*
     * This level shows that a goodie can change the hero's strength, and
     * that we can win by defeating a specific number of enemies, instead of
     * all enemies.
     */
    else if (whichLevel == 20) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Collect blue balls\nto increse strength", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        // our default hero only has "1" strength
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        // our default enemy has "2" damage
        Enemy e = Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setRotationSpeed(1);
        e.setDisappearSound("slowdown.ogg");

        // a second enemy
        e = Enemy.makeAsCircle(35, 15, 2, 2, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);

        // this goodie gives an extra "5" strength:
        Goodie g = Goodie.makeAsCircle(0, 30, 2, 2, "blueball.png");
        g.setStrengthBoost(5);
        g.setDisappearSound("woowoowoo.ogg");

        // Display the hero's strength
        Display.addStrengthMeter(" Strength", 220, 280, h);

        // win by defeating one enemy
        Score.setVictoryEnemyCount(1);
        WinScene.get().setDefaultWinText("Good enough...");
    }

    /*
     * this level introduces the idea of invincibility. Collecting the
     * goodie makes the hero invincible for a little while...
     */
    else if (whichLevel == 21) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("The blue ball will\nmake you invincible\nfor 15 seconds", 255, 255, 255,
                "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        // draw a few enemies, and make them rotate
        for (int i = 0; i < 5; ++i) {
            Enemy e = Enemy.makeAsCircle(5 * i + 1, 25, 2, 2, "redball.png");
            e.setPhysics(1.0f, 0.3f, 0.6f);
            e.setRotationSpeed(1);
        }

        // this goodie makes us invincible
        Goodie g = Goodie.makeAsCircle(30, 30, 2, 2, "blueball.png");
        g.setInvincibilityDuration(15);
        g.setRoute(new Route(3).to(30, 30).to(10, 10).to(30, 30), 5, true);
        g.setRotationSpeed(0.25f);

        // we'll still say you win by reaching the destination. Defeating
        // enemies is just for fun...
        Destination.makeAsCircle(29, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // display a goodie count for type-1 goodies
        Display.addGoodieCount(1, 0, " Goodies", 220, 280, "arial.ttf", 60, 70, 255, 12);

        // put a frames-per-second display on the screen. This is going to
        // look funny, because when debug mode is set (in Config.java), a
        // FPS will be shown on every screen anyway
        Display.addFPS(400, 15, "arial.ttf", 200, 200, 100, 12);
    }

    /*
     * Some goodies can "count" for more than one point... they can even
     * count for negative points.
     */
    else if (whichLevel == 22) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Collect 'the right' \nblue balls to\nactivate destination", 255, 255, 255,
                "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination d = Destination.makeAsCircle(29, 1, 2, 2, "mustardball.png");
        d.setActivationScore(7, 0, 0, 0);
        Score.setVictoryDestination(1);

        // create some goodies with special scores. Note that we're still
        // only dealing with type-1 scores
        Goodie g1 = Goodie.makeAsCircle(0, 30, 2, 2, "blueball.png");
        g1.setScore(-2, 0, 0, 0);
        Goodie g2 = Goodie.makeAsCircle(0, 15, 2, 2, "blueball.png");
        g2.setScore(7, 0, 0, 0);

        // create some regular goodies
        Goodie.makeAsCircle(30, 30, 2, 2, "blueball.png");
        Goodie.makeAsCircle(35, 30, 2, 2, "blueball.png");

        // print a goodie count to show how the count goes up and down
        Display.addGoodieCount(1, 0, " Progress", 220, 280, "arial.ttf", 60, 70, 255, 12);
    }

    /*
     * this level demonstrates that we can drag entities (in this case,
     * obstacles), and that we can make rotated obstacles. The latter could
     * be useful for having angled walls in a maze
     */
    else if (whichLevel == 23) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Rotating oblong obstacles\nand draggable obstacles", 255, 255, 255, "arial.ttf",
                32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // draw obstacles that we can drag
        Obstacle o = Obstacle.makeAsBox(0, 0, 3.5f, 3.5f, "purpleball.png");
        o.setPhysics(0, 100, 0);
        o.setCanDrag(true);
        Obstacle o2 = Obstacle.makeAsBox(7, 0, 3.5f, 3.5f, "purpleball.png");
        o2.setPhysics(0, 100, 0);
        o2.setCanDrag(true);

        // draw an obstacle that is oblong (due to its width and height) and
        // that is rotated. Note that this should be a box, or it will not
        // have the right underlying shape.
        o = Obstacle.makeAsBox(12, 12, 3.5f, .5f, "purpleball.png");
        o.setRotation(45);
    }

    /*
     * this level shows how we can use "poking" to move obstacles. In this
     * case, pressing an obstacle selects it, and pressing the screen moves
     * the obstacle to that location. Double-tapping an obstacle removes it.
     */
    else if (whichLevel == 24) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        PreScene.get().addText("Touch the obstacle\nto select, then" + "\ntouch to move it", 255, 255, 255,
                "arial.ttf", 32);

        // draw a picture on the default plane (0)... there are actually 5
        // planes (-2 through 2). Everything drawn on the same plane will be
        // drawn in order, so if we don't put this before the hero, the hero
        // will appear to go "under" the picture.
        Util.drawPicture(0, 0, 48, 32, "greenball.png", 0);

        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make a pokeable obstacle
        Obstacle o = Obstacle.makeAsBox(0, 0, 3.5f, 3.5f, "purpleball.png");
        o.setPhysics(0, 100, 0);
        // '250' is a number of milliseconds. Two presses within 250
        // milliseconds will cause this obstacle to disappear, forever. Make
        // the number 0 if you want it to never disappear due to
        // double-touch.
        o.setPokeToPlace(250);
    }

    /*
     * In this level, the enemy chases the hero
     */
    else if (whichLevel == 25) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("The enemy will chase you", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // create an enemy who chases the hero
        Enemy e3 = Enemy.makeAsCircle(35, 25, 2, 2, "redball.png");
        e3.setPhysics(.1f, 0.3f, 0.6f);
        e3.setChaseSpeed(8, h, true, true);

        // draw a picture late within this block of code, but still cause
        // the picture to be drawn behind everything else by giving it a z
        // index of -1
        Util.drawPicture(0, 0, 48, 32, "greenball.png", -2);

        // We can change the z-index of anything... let's move the enemy to
        // -2. Since we do this after drawing the picture, it will still be
        // drawn on top of the picture, but we should also be able to see it
        // go under the destination.
        e3.setZIndex(-2);
    }

    /*
     * We can make obstacles play sounds either when we collide with them,
     * or touch them
     */
    else if (whichLevel == 26) {
        // set up a basic level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Touch the purple ball \nor collide with it", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up our obstacle so that collision and touch make it play
        // sounds
        Obstacle o = Obstacle.makeAsCircle(10, 10, 3.5f, 3.5f, "purpleball.png");
        o.setPhysics(1, 0, 1);
        o.setTouchSound("lowpitch.ogg");
        o.setCollideSound("hipitch.ogg", 2000);
    }

    /*
     * this hero rotates so that it faces in the direction of movement. This
     * can be useful in games where the perspective is from overhead, and
     * the hero is moving in any X or Y direction
     */
    else if (whichLevel == 27) {
        // set up a big screen
        Level.configure(4 * 48, 2 * 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("The star rotates in\nthe direction of movement", 255, 255, 255, "arial.ttf",
                32);
        Util.drawBoundingBox(0, 0, 4 * 48, 2 * 32, "red.png", 1, 0, 1);
        Destination.makeAsCircle(29, 60, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up a hero who rotates in the direction of movement
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "stars.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setRotationByDirection();
        h.setMoveByTilting();
        Level.setCameraChase(h);
    }

    /*
     * This level shows two things. The first is that a custom motion path
     * can allow things to violate the laws of physics and pass through
     * other things. The second is that motion paths can go off-screen.
     */
    else if (whichLevel == 28) {
        // set up a regular level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Reach the destination\nto win the game.", 255, 255, 255, "arial.ttf", 20);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(21.5f, 29, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(21.5f, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // this enemy starts from off-screen
        Enemy e = Enemy.makeAsCircle(1, -20, 44, 44, "redball.png");
        e.setDefeatHeroText("Ha Ha Ha");
        e.setRoute(new Route(3).to(1, -90).to(1, 26).to(1, -20), 30, true);
    }

    /*
     * This level shows that we can draw on the screen to create obstacles.
     *
     * This is also our first exposure to "callbacks".  A "callback" is a way of providing code
     * that runs in response to some event.  We use a callback to customize the obstacles that
     * are drawn to the screen in response to scribbles.
     */
    else if (whichLevel == 29) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Draw on the screen\nto make obstacles appear", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(21.5f, 29, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(21.5f, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // turn on 'scribble mode'... this says "draw a purple ball that is 1.5x1.5 at the
        // location where the scribble happened, but only do it if we haven't drawn anything in
        // 10 milliseconds."  It also says "when an obstacle is drawn, do some stuff to the
        // obstacle".  If you don't want any of this functionality, you can replace the whole
        // "new LolCallback..." region of code with "null".
        Level.setScribbleMode("purpleball.png", 1.5f, 1.5f, 10, new LolCallback() {
            @Override
            public void onEvent() {
                // each time we draw an obstacle, it will be visible to this code as the
                // callback's "attached Actor".  We'll change its elasticity, make it disappear
                // after 10 seconds, and make it so that the obstacles aren't stationary
                mAttachedActor.setPhysics(0, 2, 0);
                mAttachedActor.setDisappearDelay(10, true);
                mAttachedActor.setCanFall();
            }
        });
    }

    /*
     * This level shows that we can "flick" things to move them. Notice that
     * we do not enable tilt! Instead, we specified that there is a default
     * gravity in the Y dimension pushing everything down. This is much like
     * gravity on earth. The only way to move things, then, is via flicking
     * them.
     */
    else if (whichLevel == 30) {
        // create a level with a constant force downward in the Y dimension
        Level.configure(48, 32);
        Physics.configure(0, -10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Destination.makeAsCircle(30, 10, 2.5f, 2.5f, "mustardball.png");
        Score.setVictoryDestination(1);

        // create a hero who we can flick
        Hero h = Hero.makeAsCircle(4, 27, 3, 3, "stars.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setFlickable(1f);
        h.disableRotation();

        Obstacle o = Obstacle.makeAsCircle(8, 27, 3, 3, "purpleball.png");
        o.setFlickable(.5f);
    }

    /*
     * this level introduces a new concept: side-scrolling games. Just like
     * in level 30, we have a constant force in the negative Y direction.
     * However, in this level, we say that tilt can produce forces in X but
     * not in Y. Thus we can tilt to move the hero left/right. Note, too,
     * that the hero will fall to the floor, since there is a constant
     * downward force, but there is not any mechanism to apply a Y force to
     * make it move back up.
     */
    else if (whichLevel == 31) {
        // make a long level but not a tall level, and provide a constant
        // downward force:
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        // turn on tilt, but only in the X dimension
        Tilt.enable(10, 0);
        PreScene.get().addText("Side scroller / tilt demo", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 1);
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        Level.setCameraChase(h);
    }

    /*
     * In the previous level, it was hard to see that the hero was moving.
     * We can make a background layer to remedy this situation. Notice that
     * the background uses transparency to show the blue color for part of
     * the screen
     */
    else if (whichLevel == 32) {
        // start by repeating the previous level:
        Level.configure(30 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Side scroller / tilt demo", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 30 * 48, 32, "red.png", 1, 0, 1);
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(30 * 48 - 5, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        Level.setCameraChase(h);

        // now paint the background blue
        Background.setColor(23, 180, 255);

        // put in a picture that scrolls at half the speed of the hero in
        // the x direction. Note that background "layers" are all drawn
        // *before* anything that is drawn with a z index... so the
        // background will be behind the hero
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        // make an obstacle that hovers in a fixed place. Note that hovering
        // and zoom do not work together nicely.
        Obstacle o = Obstacle.makeAsCircle(10, 10, 5, 5, "blueball.png");
        o.setHover(100, 100);

        // Add a meter to show how far the hero has traveled
        Display.addDistanceMeter(" m", 5, 300, "arial.ttf", 255, 0, 255, 16, h);

        // Add some text about the previous best score.
        Util.drawText(30, 30, "best: " + Facts.getGameFact("HighScore32", 0) + "M", 0, 0, 0, "arial.ttf", 12,
                0);

        // when this level ends, we save the best score. Once the
        // score is saved, it is saved permanently on the phone, though
        // every re-execution on the desktop resets the best score. Note
        // that we save the score whether we win or lose.
        LolCallback sc = new LolCallback() {
            public void onEvent() {
                int oldBest = Facts.getGameFact("HighScore32", 0);
                if (oldBest < Score.getDistance())
                    Facts.putGameFact("HighScore32", Score.getDistance());
            }
        };

        Level.setWinCallback(sc);
        Level.setLoseCallback(sc);
    }

    /*
     * this level adds multiple background layers, and it also allows the
     * hero to jump via touch
     */
    else if (whichLevel == 33) {
        // set up a standard side scroller with tilt:
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Press the hero to\nmake it jump", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 1);
        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make a hero
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Level.setCameraChase(h);
        // this says that touching makes the hero jump
        h.setTouchToJump();
        // this is the force of a jump. remember that up is positive.
        h.setJumpImpulses(0, 10);
        // the sound to play when we jump
        h.setJumpSound("fwapfwap.ogg");

        // set up our background again, but add a few more layers
        Background.setColor(23, 180, 255);
        // this layer has a scroll factor of 0... it won't move
        Background.addHorizontalLayer(0, 1, "back.png", 0, 960, 640);
        // this layer moves at half the speed of the hero
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 480, 320);
        // this layer is faster than the hero
        Background.addHorizontalLayer(1.25f, 1, "front.png", 20, 454, 80);
    }

    /*
     * tilt doesn't always work so nicely in side scrollers. An alternative
     * is for the hero to have a fixed rate of motion. Another issue was
     * that you had to touch the hero itself to make it jump. Now, we use an
     * invisible button so touching any part of the screen makes the hero
     * jump.
     */
    else if (whichLevel == 34) {
        // set up a side scroller, but don't turn on tilt
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Press anywhere to jump", 255, 255, 255, "arial.ttf", 32);
        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // note: the bounding box does not have friction, and neither does
        // the hero
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 0, 0, 0);

        // make a hero, and ensure that it doesn't rotate
        Hero h = Hero.makeAsCircle(2, 0, 3, 7, "greenball.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0);
        // give the hero a fixed velocity
        h.addVelocity(25, 0, false);
        // center the camera a little ahead of the hero, so he is not
        // centered
        h.setCameraOffset(15, 0);
        // enable jumping
        h.setJumpImpulses(0, 10);
        Level.setCameraChase(h);
        // set up the background
        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        // draw a jump button that covers the whole screen
        Control.addJumpButton(0, 0, 960, 640, "", h);

        // if the hero jumps over the destination, we have a problem. To fix
        // it, let's put an invisible enemy right after the destination, so
        // that if the hero misses the destination, it hits the enemy and we
        // can start over. Of course, we could just do the destination like
        // this instead, but this is more fun...
        Enemy.makeAsBox(130, 0, .5f, 32, "");
    }

    /*
     * the default is that once a hero jumps, it can't jump again until it
     * touches an obstacle (floor or wall). Here, we enable multiple jumps.
     * Coupled with a small jump impulse, this makes jumping feel more like
     * swimming or controlling a helicopter.
     */
    else if (whichLevel == 35) {
        // Note: we can go above the trees
        Level.configure(3 * 48, 38);
        Physics.configure(0, -10);
        PreScene.get().addText("Multi-jump is enabled", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 38, "red.png", 1, 0, 0);
        Hero h = Hero.makeAsBox(2, 0, 3, 7, "greenball.png");
        h.disableRotation();
        h.setPhysics(1, 0, 0);
        h.addVelocity(5, 0, false);
        Level.setCameraChase(h);
        h.setCameraOffset(15, 0);
        // the hero now has multijump, with small jumps:
        h.setMultiJumpOn();
        h.setJumpImpulses(0, 6);

        // this is all the same as before, to include the invisible enemy
        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);
        Control.addJumpButton(0, 0, 960, 640, "", h);
        Destination.makeAsCircle(120, 31, 2, 2, "mustardball.png");
        Enemy.makeAsBox(130, 0, .5f, 38, "");
        Score.setVictoryDestination(1);
    }

    /*
     * This level shows that we can make a hero move based on how we touch
     * the screen
     */
    else if (whichLevel == 36) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, 0);
        PreScene.get().addText("Press screen borders\nto move the hero", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 1);
        Hero h = Hero.makeAsCircle(2, 0, 3, 3, "stars.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0.6f);
        Level.setCameraChase(h);

        // this lets the hero flip its image when it moves backwards
        h.setDefaultAnimation(new Animation("stars.png", 200, true, 0, 0));
        h.setDefaultReverseAnimation(new Animation("stars_flipped.png", 200, true, 7, 7));

        Destination.makeAsCircle(120, 31, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        // let's draw an enemy, just in case anyone wants to try to go to
        // the top left corner
        Enemy.makeAsCircle(3, 27, 3, 3, "redball.png");

        // draw some buttons for moving the hero
        Control.addLeftButton(0, 100, 100, 440, "", 15, h);
        Control.addRightButton(860, 100, 100, 440, "", 15, h);
        Control.addUpButton(100, 540, 760, 100, "", 15, h);
        Control.addDownButton(100, 0, 760, 100, "", 15, h);
    }

    /*
     * In the last level, we had complete control of the hero's movement.
     * Here, we give the hero a fixed velocity, and only control its up/down
     * movement.
     */
    else if (whichLevel == 37) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, 0);
        PreScene.get().addText("Press screen borders\nto move up and down", 255, 255, 255, "arial.ttf", 32);
        // The box and hero should not have friction
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 0);
        Destination.makeAsCircle(120, 31, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0);
        h.addVelocity(10, 0, false);

        Level.setCameraChase(h);

        // draw an enemy to avoid, and one at the end
        Enemy.makeAsCircle(53, 28, 3, 3, "redball.png");
        Enemy.makeAsBox(130, 0, .5f, 32, "");

        // draw the up/down controls
        Control.addDownButton(100, 0, 760, 100, "", 15, h);
        Control.addUpButton(100, 540, 760, 100, "", 15, h);
    }

    /*
     * this level demonstrates crawling heroes. We can use this to simulate
     * crawling, ducking, rolling, spinning, etc. Note, too, that we can use
     * it to make the hero defeat certain enemies via crawl.
     */
    else if (whichLevel == 38) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Press the screen\nto crawl", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, .3f, 0);
        Destination.makeAsCircle(120, 0, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        Hero h = Hero.makeAsBox(2, 1, 3, 7, "greenball.png");
        h.setPhysics(.1f, 0, 0);
        h.addVelocity(5, 0, false);
        Level.setCameraChase(h);
        // to enable crawling, we just draw a crawl button on the screen
        Control.addCrawlButton(0, 0, 960, 640, "", h);

        // make an enemy who we can defeat by colliding with it while
        // crawling
        Enemy e = Enemy.makeAsCircle(110, 1, 5, 5, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setDefeatByCrawl();
    }

    /*
     * We can make a hero start moving only when it is pressed. This can
     * even let the hero hover until it is pressed. We could also use this
     * to have a game where the player puts obstacles in place, then starts
     * the hero moving.
     */
    else if (whichLevel == 39) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Press the hero\nto start moving\n", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 0);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        Destination.makeAsCircle(120, 0, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make a hero who doesn't start moving until it is touched
        //
        // note that this hero is a box, and the hero is "norotate". You
        // will probably get strange behaviors if you choose any other
        // options
        Hero h = Hero.makeAsBox(2, 1, 3, 7, "greenball.png");
        h.disableRotation();
        h.setPhysics(1, 0, 0);
        h.setTouchAndGo(10, 0);
        Level.setCameraChase(h);
    }

    /*
     * LibLOL has limited support for SVG. If you draw a picture in Inkscape
     * or another SVG tool, and it only consists of lines, then you can
     * import it into your game as an obstacle. Drawing a picture on top of
     * the obstacle is probably a good idea, though we don't bother in this
     * level
     */
    else if (whichLevel == 40) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        Tilt.setAsVelocity(true);
        PreScene.get().addText("Obstacles can\nbe drawn from SVG\nfiles", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, .3f, 1);

        // make a hero who can jump
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setJumpImpulses(0, 20);
        h.setTouchToJump();
        h.setMoveByTilting();
        Level.setCameraChase(h);

        // draw an obstacle from SVG
        Svg.importLineDrawing("shape.svg", 2f, .5f, 25f, 15f, new Svg.ActorCallback() {
            @Override
            public void handle(Actor line) {
                // This code is run each time a line of the SVG is drawn.  When we get a line,
                // we'll give it some density and friction.  Remember that the line is
                // actually a rotated obstacle
                line.setPhysics(1, 0, .1f);
            }
        });

        // provide a destination
        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
    }

    /*
     * In a side-scrolling game, it is useful to be able to change the
     * hero's speed either permanently or temporarily. In LibLOL, we can use
     * a collision between a hero and an obstacle to achieve this effect.
     */
    else if (whichLevel == 41) {
        Level.configure(10 * 48, 32);
        Physics.configure(0, 0);
        PreScene.get().addText("Speed boosters and reducers", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 10 * 480, 32, "", 1, 0, 1);

        Hero h = Hero.makeAsCircle(2, 0, 3, 3, "greenball.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0.6f);
        h.addVelocity(10, 0, false);
        Level.setCameraChase(h);

        Destination.makeAsCircle(450, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        // place a speed-up obstacle that lasts for 2 seconds
        Obstacle o1 = Obstacle.makeAsCircle(40, 1, 4, 4, "purpleball.png");
        o1.setSpeedBoost(20, 0, 2);

        // place a slow-down obstacle that lasts for 3 seconds
        Obstacle o2 = Obstacle.makeAsCircle(120, 1, 4, 6, "purpleball.png");
        o2.setSpeedBoost(-9, 0, 3);

        // place a permanent +3 speedup obstacle... the -1 means "forever"
        Obstacle o3 = Obstacle.makeAsCircle(240, 1, 4, 4, "purpleball.png");
        o3.setSpeedBoost(20, 0, -1);
    }

    /*
     * this is a very gross level, which exists just to show that
     * backgrounds can scroll vertically.
     */
    else if (whichLevel == 42) {
        // set up a level where tilt only makes the hero move up and down
        Level.configure(48, 4 * 32);
        Physics.configure(0, 0);
        Tilt.enable(0, 10);
        PreScene.get().addText("Vertical scroller demo", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 4 * 32, "red.png", 1, 0, 1);

        Hero h = Hero.makeAsCircle(2, 120, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Level.setCameraChase(h);

        Destination.makeAsBox(0, 2, 48, 1, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up vertical scrolling backgrounds
        Background.setColor(255, 0, 255);
        Background.addVerticalLayer(1, 0, "back.png", 0, 960, 640);
        Background.addVerticalLayer(1, .5f, "mid.png", 0, 960, 640);
        Background.addVerticalLayer(1, 1, "front.png", 0, 454, 80);
    }

    /*
     * the next few levels demonstrate support for throwing projectiles. In
     * this level, we throw projectiles by touching the hero. Here, the
     * projectile always goes in the same direction
     */
    else if (whichLevel == 43) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Press the hero\nto make it throw\nprojectiles", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // create a hero, and indicate that touching it makes it throw
        // projectiles
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        // when the hero is touched, a projectile will
        // fly straight up, out of the top of the hero.
        h.setTouchToThrow(h, 1.5f, 3, 0, 10);
        h.setMoveByTilting();

        // configure a pool of projectiles. We say that there can be no more
        // than 3 projectiles in flight at any time.
        ProjectilePool.configure(3, 1, 1, "greyball.png", 1, 0, true);
    }

    /*
     * This is another demo of how throwing projectiles works. Like the
     * previous demo, it doesn't actually use projectiles for anything, it
     * is just to show how to get some different behaviors in terms of how
     * the projectiles move. In this case, we show that we can limit the
     * distance that projectiles travel, and that we can put a control on
     * the HUD for throwing projectiles
     */
    else if (whichLevel == 44) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Press anywhere\nto throw a gray\nball", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(2, 30, 3, 3, "greenball.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(120, 0, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up a pool of projectiles, but now once the projectiles travel
        // more than 25 meters, they disappear
        ProjectilePool.configure(100, 1, 1, "greyball.png", 1, 0, true);
        ProjectilePool.setRange(25);

        // add a button for throwing projectiles. Notice that this butotn
        // keeps throwing as long as it is held, but we've capped it to
        // throw no more than once per 100 milliseconds
        Control.addThrowButton(0, 0, 960, 640, "", h, 100, 3, 1.5f, 30, 0);
        Level.setCameraChase(h);
    }

    /*
     * this level demonstrates that we can defeat enemies by throwing
     * projectiles at them
     */
    else if (whichLevel == 45) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        PreScene.get().addText("Defeat all enemies\nto win", 255, 255, 255, "arial.ttf", 32);

        Hero h = Hero.makeAsCircle(4, 27, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        // set up our projectiles... note that now projectiles each do 2
        // units of damage
        ProjectilePool.configure(3, .4f, .1f, "greyball.png", 2, 0, true);

        // draw a few enemies... note that they have different amounts of
        // damage, so it takes different numbers of projectiles to defeat
        // them.
        Enemy e = Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setRotationSpeed(1);
        for (int i = 1; i < 20; i += 5) {
            Enemy ee = Enemy.makeAsCircle(i, i + 5, 2, 2, "redball.png");
            ee.setPhysics(1.0f, 0.3f, 0.6f);
            ee.setDamage(i);
        }

        // win by defeating enemies, of course!
        Score.setVictoryEnemyCount();

        // this button only throws one projectile per press...
        Control.addSingleThrowButton(0, 0, 960, 640, "", h, .2f, -.5f, 0, 10);
    }

    /*
     * This level shows how to throw projectiles in a variety of directions.
     */
    else if (whichLevel == 46) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Press anywhere\nto throw a ball", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Level.setCameraChase(h);

        Destination.makeAsCircle(120, 0, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // draw a button for throwing projectiles in many directions...
        // again, note that if we hold the button, it keeps throwing
        Control.addDirectionalThrowButton(0, 0, 960, 640, "", h, 0, 0, 0);

        // set up our pool of projectiles. The main challenge here is that
        // the farther from the hero we press, the faster the projectile
        // goes, so we multiply the velocity by .8 to slow it down a bit
        ProjectilePool.configure(100, 1, 1, "greyball.png", 1, 0, true);
        ProjectilePool.setProjectileVectorDampeningFactor(.8f);
        ProjectilePool.setRange(30);
    }

    /*
     * this level shows that with the "vector" projectiles, we can still
     * have gravity affect the projectiles. This is very good for
     * basketball-style games.
     */
    else if (whichLevel == 47) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Press anywhere\nto throw a ball", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.disableRotation();
        h.setPhysics(0, 0, 0.6f);
        h.setMoveByTilting();
        Level.setCameraChase(h);

        Destination.makeAsCircle(120, 0, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // we use a "single throw" button so that holding doesn't throw more
        // projectiles.
        Control.addDirectionalSingleThrowButton(0, 0, 960, 640, "", h, 1.5f, 1.5f);

        // we turn on projectile gravity, and then we enable collisions for
        // projectiles. This means that when a projectile collides with
        // something, it will transfer its momentum to that thing, if that
        // thing is moveable. This is a step toward our goal of being able
        // to bounce a basketball off of a backboard, but it's not quite
        // enough...
        ProjectilePool.configure(100, 1, 1, "greyball.png", 1, 0, true);
        ProjectilePool.setProjectileVectorDampeningFactor(.8f);
        ProjectilePool.setRange(40);
        ProjectilePool.setProjectileGravityOn();
        ProjectilePool.enableCollisionsForProjectiles();

        // This next line is interesting... it lets projectiles collide with
        // each other without disappearing
        ProjectilePool.setCollisionOk();

        // Draw an obstacle... this is like our backboard, but we're putting
        // it in a spot that's more useful for testing than for playing a
        // game
        Obstacle o = Obstacle.makeAsBox(10, 20, 2, 2, "red.png");

        // now comes the tricky part... we want to make it so that when the
        // ball hits the obstacle (the backboard), it doesn't disappear. The
        // only time a projectile does not disappear when hitting an
        // obstacle is when you provide custom code to run on a
        // projectile/obstacle collision... in that case, you are
        // responsible for removing the projectile (or for not removing it).
        // That being the case, we can set a "callback" to run custom code
        // when the projectile and obstacle collide, and then just have the
        // custom code do nothing.

        // this line says when a projectile and obstacle collide, if the
        // goodie counts are at least 0,0,0,0, then run the
        // callback code.
        o.setProjectileCollisionCallback(0, 0, 0, 0, new LolCallback() {
            public void onEvent() {
            }
        });
    }

    /*
     * This level shows how we can attach a timer to an enemy. When the
     * timer runs out, if the enemy is still visible, then some custom code
     * will run. We can use this to simulate cancer cells or fire on a
     * building. The value of attaching the timer to the enemy is that we
     * can change the game state at the position where the enemy is. One
     * team even had an enemy that dropped goodies at its current location.
     * Note that the timer only runs once... you'll need to make a new timer
     * from within the code that runs when the timer expires.
     */
    else if (whichLevel == 48) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 0);
        PreScene.get().addText("Throw balls at \nthe enemies before\nthey reproduce", 255, 255, 255,
                "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setTouchToThrow(h, 2, -.5f, 0, 10);
        h.setMoveByTilting();

        // configure a pool of projectiles... now we have a sound that plays
        // when a projectile is thrown, and another for when it disappears
        ProjectilePool.configure(100, .5f, .5f, "greyball.png", 1, 0, true);
        ProjectilePool.setThrowSound("fwapfwap.ogg");
        ProjectilePool.setProjectileDisappearSound("slowdown.ogg");

        // draw an enemy that makes a sound when it disappears
        Enemy e = Enemy.makeAsCircle(23, 20, 1, 1, "redball.png");
        e.setDisappearSound("lowpitch.ogg");

        // This is tricky. We want to make the enemy reproduce. To that end,
        // we're going to set up a Callback that runs in a few seconds. The
        // tricky things are that (a) we don't want an enemy to reproduce if
        // the enemy has been defeated; (b) we want to limit the number of
        // times any enemy reproduces, so that reproductions don't get out
        // of hand; and (c) we want the reproduced enemies to also
        // reproduce.

        // We're going to make a SimpleCallback object to encapsulate the
        // code that we want to run. The trick is that SimpleCallback has a
        // field called "intVal", which is just an integer that we can use
        // however we want, and it has a field called "attachedSprite",
        // which is a way of keeping track of the entity (in this case, an
        // enemy) with which the callback is associated.
        LolCallback sc = new LolCallback() {
            public void onEvent() {
                // only reproduce the enemy if it is visible
                if (mAttachedActor.getVisible()) {
                    // make an enemy to the left of and above attachedSprite
                    Enemy left = Enemy.makeAsCircle(mAttachedActor.getXPosition() - 2 * mIntVal,
                            mAttachedActor.getYPosition() + 2 * mIntVal, mAttachedActor.getWidth(),
                            mAttachedActor.getHeight(), "redball.png");
                    left.setDisappearSound("lowpitch.ogg");

                    // make an enemy to the right of and above
                    // attachedSprite
                    Enemy right = Enemy.makeAsCircle(mAttachedActor.getXPosition() + 2 * mIntVal,
                            mAttachedActor.getYPosition() + 2 * mIntVal, mAttachedActor.getWidth(),
                            mAttachedActor.getHeight(), "redball.png");
                    right.setDisappearSound("lowpitch.ogg");

                    // if there are reproductions left, then have
                    // attachedSprite and its two new children all reproduce
                    // in 2 seconds
                    if (mIntVal > 0) {
                        // first, do the parent
                        mIntVal--;
                        // on the next line, 'this' refers to the
                        // SimpleCallback object
                        Level.setTimerCallback(2, this);
                        // to do the children, create a new callback for
                        // each of them. We can 'clone' this SimpleCallback
                        // and then just change the attachedSprite, so that
                        // we don't have to re-write this code.
                        LolCallback l = this.clone();
                        l.mAttachedActor = left;
                        Level.setTimerCallback(2, l);
                        LolCallback r = this.clone();
                        r.mAttachedActor = right;
                        Level.setTimerCallback(2, r);
                    }
                }
            }
        };
        sc.mIntVal = 2;
        sc.mAttachedActor = e;

        // request that in 2 seconds, if the enemy is still visible,
        // onTimerCallback() will run, with id == 2. Be sure to look at
        // the onTimerCallback code (below) for more information. Note that
        // there are two versions of the function, and this uses the second!
        Level.setTimerCallback(2, sc);

        // win by defeating enemies
        Score.setVictoryEnemyCount();

        // put a count of defeated enemies on the screen
        Display.addDefeatedCount(0, " Enemies Defeated", 20, 20);
    }

    /*
     * This level shows that we can have moveable enemies that reproduce. Be
     * careful... it is possible to make a lot of enemies, really quickly
     */
    else if (whichLevel == 49) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("These enemies are\nreally tricky", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        Destination.makeAsCircle(29, 29, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make our initial enemy
        Enemy e = Enemy.makeAsCircle(23, 2, 1, 1, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setMoveByTilting();

        // set a timer callback on the enemy. warning: "6" is going to lead
        // to lots of enemies eventually, and there's no way to defeat them
        // in this level! Again, be sure to look at onEnemyTimerCallback()
        // below.
        LolCallback sc = new LolCallback() {
            public void onEvent() {
                // Make the new enemy
                Enemy e2 = Enemy.makeAsCircle(mAttachedActor.getXPosition(), mAttachedActor.getYPosition(),
                        mAttachedActor.getWidth(), mAttachedActor.getHeight(), "redball.png");
                e2.setPhysics(1.0f, 0.3f, 0.6f);
                e2.setMoveByTilting();
                // make more enemies?
                if (mIntVal > 0) {
                    mIntVal--;
                    Level.setTimerCallback(2, this);
                    LolCallback c2 = this.clone();
                    c2.mAttachedActor = e2;
                    Level.setTimerCallback(2, c2);
                }
            }
        };
        sc.mAttachedActor = e;
        sc.mIntVal = 6;
        Level.setTimerCallback(2, sc);
    }

    /*
     * this level shows simple animation. Every entity can have a default
     * animation.
     */
    else if (whichLevel == 50) {
        // set up a basic level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Animations", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // this hero will be animated:
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "stars.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        // this says that we scroll through the 0, 1, 2, and 3 cells of the
        // image, and we show each for 200 milliseconds. This is the "easy"
        // animation mechanism, where every cell is shown for the same
        // amount of time
        h.setDefaultAnimation(new Animation("stars.png", 200, true, 0, 1, 2, 3));
    }

    /*
     * this level introduces jumping animations and disappearance animations
     */
    else if (whichLevel == 51) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Press the hero to\nmake it jump", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 1);

        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make a hero, and give it two animations: one for when it is in
        // the air, and another for the rest of the time.
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "stars.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0.6f);
        h.setJumpImpulses(0, 20);
        h.setTouchToJump();
        h.setMoveByTilting();
        Level.setCameraChase(h);

        // this is the more complex form of animation... we show the
        // different cells for different lengths of time
        h.setDefaultAnimation(new Animation("stars.png", 4, true).to(0, 150).to(1, 200).to(2, 300).to(3, 350));
        // we can use the complex form to express the simpler animation, of
        // course
        h.setJumpAnimation(new Animation("stars.png", 4, true).to(4, 200).to(5, 200).to(6, 200).to(7, 200));

        // create a goodie that has a disappearance animation. When the
        // goodie is ready to disappear, we'll remove it, and then we'll run
        // the disappear animation. That means that we can make it have any
        // size we want, but we need to offset it from the (defunct)
        // goodie's position. Note, too, that the final cell is blank, so
        // that we don't leave a residue on the screen.
        Goodie g = Goodie.makeAsCircle(15, 9, 5, 5, "stars.png");
        g.setDisappearAnimation(
                new Animation("starburst.png", 4, false).to(2, 200).to(1, 200).to(0, 200).to(3, 200), 1, 0, 5,
                5);
    }

    /*
     * this level shows that projectiles can be animated, and that we can
     * animate the hero while it throws a projectile
     */
    else if (whichLevel == 52) {
        // set up a basic level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Press the hero\nto make it\nthrow a ball", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up our hero
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "colorstar.png");
        h.setPhysics(1, 0, 0.6f);
        h.setTouchToThrow(h, 0, -.5f, 0, 10);
        h.setMoveByTilting();

        // set up an animation when the hero throws:
        h.setThrowAnimation(new Animation("colorstar.png", 2, false).to(3, 100).to(4, 500));

        // make a projectile pool and give an animation pattern for the
        // projectiles
        ProjectilePool.configure(100, 1, 1, "flystar.png", 1, 0, true);
        ProjectilePool.setAnimation(new Animation("flystar.png", 100, true, 0, 1));
    }

    /*
     * This level explores invincibility animation. While we're at it, we
     * make some enemies that aren't affected by invincibility, and some
     * that can even damage the hero while it is invincible.
     */
    else if (whichLevel == 53) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("The blue ball will\nmake you invincible\nfor 15 seconds", 50, 50, 255, 255, 255,
                "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Destination.makeAsCircle(29, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make an animated hero, and give it an invincibility animation
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "colorstar.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        h.setDefaultAnimation(
                new Animation("colorstar.png", 4, true).to(0, 300).to(1, 300).to(2, 300).to(3, 300));
        h.setInvincibleAnimation(
                new Animation("colorstar.png", 4, true).to(4, 100).to(5, 100).to(6, 100).to(7, 100));

        // make some enemies
        for (int i = 0; i < 5; ++i) {
            Enemy e = Enemy.makeAsCircle(5 * i + 1, 25, 2, 2, "redball.png");
            e.setPhysics(1.0f, 0.3f, 0.6f);
            e.setRotationSpeed(1);
            e.setDamage(4);
            e.setDisappearSound("hipitch.ogg");

            // The first enemy we create will harm the hero even if the hero
            // is invincible
            if (i == 0)
                e.setImmuneToInvincibility();
            // the second enemy will not be harmed by invincibility, but
            // won't harm an invincible hero
            if (i == 1)
                e.setResistInvincibility();
        }
        // neat trick: this enemy does zero damage, but slows the hero down.
        Enemy e = Enemy.makeAsCircle(30, 20, 2, 2, "redball.png");
        e.setPhysics(10, 0.3f, 0.6f);
        e.setMoveByTilting();
        e.setDamage(0);

        // add a goodie that makes the hero invincible
        Goodie g = Goodie.makeAsCircle(30, 30, 2, 2, "blueball.png");
        g.setInvincibilityDuration(15);
        g.setRoute(new Route(3).to(30, 30).to(10, 10).to(30, 30), 5, true);
        g.setRotationSpeed(0.25f);
        Display.addGoodieCount(1, 0, " Goodies", 220, 280, "arial.ttf", 60, 70, 255, 12);

        // draw a picture when the level is won, and don't print text...
        // this particular picture isn't very useful
        WinScene.get().addImage("fade.png", 0, 0, 960, 640);
        WinScene.get().setDefaultWinText("");
    }

    /*
     * demonstrate crawl animation, and also show that on multitouch phones,
     * we can "crawl" in the air while jumping.
     */
    else if (whichLevel == 54) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Press the left side of\nthe screen to crawl\n" + "or the right side\nto jump.",
                255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, .3f, 0);

        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make a hero with fixed velocity, and give it crawl and jump
        // animations
        Hero h = Hero.makeAsBox(2, 1, 3, 7, "stars.png");
        h.setPhysics(1, 0, 0);
        h.addVelocity(15, 0, false);
        h.setCrawlAnimation(new Animation("stars.png", 4, true).to(0, 100).to(1, 300).to(2, 300).to(3, 100));
        h.setJumpAnimation(new Animation("stars.png", 4, true).to(4, 200).to(5, 200).to(6, 200).to(7, 200));

        // enable hero jumping and crawling
        h.setJumpImpulses(0, 15);
        Control.addJumpButton(0, 0, 480, 640, "", h);
        Control.addCrawlButton(480, 0, 480, 640, "", h);

        // add an enemy we can defeat via crawling, just for fun. It should
        // be defeated even by a "jump crawl"
        Enemy e = Enemy.makeAsCircle(110, 1, 5, 5, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setDefeatByCrawl();

        // include a picture on the "try again" screen
        LoseScene.get().addImage("fade.png", 0, 0, 960, 640);
        LoseScene.get().setDefaultText("Oh well...");
        Level.setCameraChase(h);
    }

    /*
     * This isn't quite the same as animation, but it's nice. We can
     * indicate that a hero's image changes depending on its strength. This
     * can, for example, allow a hero to change (e.g., get healthier) by
     * swapping through images as goodies are collected, or allow the hero
     * to switch its animation depending on how many enemies it has collided
     * with
     */
    else if (whichLevel == 55) {
        // set up a basic level with a bunch of goodies
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        // Since colorstar.png has 8 frames, and we're displaying frame 0 as
        // "health == 0", let's add 7 more goodies, each of which adds 1 to
        // the hero's strength.
        for (int i = 0; i < 7; ++i) {
            Goodie g = Goodie.makeAsCircle(5 + 2 * i, 5 + 2 * i, 2, 2, "blueball.png");
            g.setStrengthBoost(1);
        }

        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make 8 enemies, each with strength == 1. This means we can lose
        // the level, and that we can test moving our strength all the way
        // up to 7, and all the way back down to 0.
        for (int i = 0; i < 8; ++i) {
            Enemy e = Enemy.makeAsCircle(5 + 2 * i, 1 + 2 * i, 2, 2, "redball.png");
            e.setDamage(1);
        }

        // Note: colorstar.png has 8 cells...
        Hero h = Hero.makeAsCircle(4, 27, 3, 3, "colorstar.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        // provide some code to run when the hero's strength changes
        h.setStrengthChangeCallback(new LolCallback() {
            public void onEvent() {
                // get the hero's strength. Since the hero isn't dead, the
                // strength is at least 1. Since there are 7 strength
                // booster goodies, the strength is at most 8.
                int s = ((Hero) mAttachedActor).getStrength();
                // set the hero's image index to (s-1), i.e., one of the
                // indices in the range 0..7, depending on strength
                mAttachedActor.setImage("colorstar.png", s - 1);

            }
        });
    }

    /*
     * demonstrate that obstacles can defeat enemies, and that we can use
     * this feature to have obstacles that only defeat certain "marked"
     * enemies
     */
    else if (whichLevel == 56) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        // increase the speed at which tilt affects velocity
        Tilt.setGravityMultiplier(3);
        PreScene.get().addText("You can defeat\ntwo enemies with\nthe blue ball", 255, 255, 255, "arial.ttf",
                32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, 0, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(1, 0, 0.6f);
        h.setMoveByTilting();

        // put an enemy defeated count on the screen, in red with a small
        // font
        Display.addDefeatedCount(2, " Enemies Defeated", 20, 20, "arial.ttf", 255, 0, 0, 10);

        // make a moveable obstacle that can defeat enemies
        Obstacle o = Obstacle.makeAsCircle(10, 2, 4, 4, "blueball.png");
        o.setPhysics(.1f, 0, 0.6f);
        o.setMoveByTilting();
        // this says that we don't need to collect any goodies before this
        // obstacle defeats enemies (0,0,0,0), and that when this obstacle
        // collides with any enemy, the onEnemyCollideCallback() code will
        // run, with id == 14. Notice, too, that there will be a half second
        // delay before the code runs.
        o.setEnemyCollisionCallback(0, 0, 0, 0, .5f, new LolCallback() {
            public void onEvent() {
                // This obstacle can only defeat the big enemy, and it
                // disappears when it defeats the enemy
                if (mCollideActor.getInfoText().equals("big")) {
                    ((Enemy) mCollideActor).defeat(true);
                    mAttachedActor.remove(true);
                }

            }
        });

        // make a small obstacle that can also defeat enemies, but doesn't
        // disappear
        Obstacle o2 = Obstacle.makeAsCircle(.5f, .5f, 2, 2, "blueball.png");
        o2.setPhysics(1, 0, 0.6f);
        o2.setMoveByTilting();
        o2.setEnemyCollisionCallback(0, 0, 0, 0, 0, new LolCallback() {
            public void onEvent() {
                ((Enemy) mCollideActor).defeat(true);
            }
        });

        // make four enemies
        Enemy e = Enemy.makeAsCircle(40, 2, 4, 4, "redball.png");
        e.setPhysics(1, 0, 0.6f);
        e.setMoveByTilting();
        Enemy e1 = Enemy.makeAsCircle(30, 2, 4, 4, "redball.png");
        e1.setPhysics(1, 0, 0.6f);
        Enemy e2 = Enemy.makeAsCircle(40, 22, 2, 2, "redball.png");
        e2.setPhysics(1, 0, 0.6f);
        e2.setMoveByTilting();
        Enemy e3 = Enemy.makeAsCircle(40, 12, 4, 4, "redball.png");
        e3.setPhysics(1, 0, 0.6f);
        e3.setMoveByTilting();

        // now let's put a note into e2 and e3
        e2.setInfoText("small");
        e3.setInfoText("big");

        // win by defeating enemies
        Score.setVictoryEnemyCount(2);

        // be sure to look at onEnemyCollideCallback to see how this level
        // will play out.
    }

    /*
     * this level shows an odd way of moving the hero. There's friction on
     * the floor, so it can only move by tilting while the hero is in the
     * air
     */
    else if (whichLevel == 57) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Press the hero to\nmake it jump", 255, 255, 255, "arial.ttf", 32);
        // note: the floor has friction
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 1);

        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // make a box hero with friction... it won't roll on the floor, so
        // it's stuck!
        Hero h = Hero.makeAsBox(2, 2, 3, 3, "stars.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 5);
        h.setMoveByTilting();
        Level.setCameraChase(h);

        // the hero *can* jump...
        h.setTouchToJump();
        h.setJumpImpulses(0, 15);

        // draw a background
        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);
    }

    /*
     * this level shows that we can put an obstacle on the screen and use it
     * to make the hero throw projectiles. It also shows that we can make
     * entities that shrink over time... growth is possible too, with a
     * negative value.
     */
    else if (whichLevel == 58) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(4, 5, 3, 3, "greenball.png");
        h.setPhysics(1, 0, 0.6f);
        h.setMoveByTilting();

        // make an obstacle that causes the hero to throw Projectiles when
        // touched
        Obstacle o = Obstacle.makeAsCircle(43, 27, 5, 5, "purpleball.png");
        o.setCollisionsEnabled(false);
        o.setTouchToThrow(h, 1.5f, 1.5f, 0, 15);

        // set up our projectiles
        ProjectilePool.configure(3, 1, 1, "colorstar.png", 2, 0, true);
        ProjectilePool.setNumberOfProjectiles(20);
        // there are only 20... throw them carefully

        // Allow the projectile image to be chosen randomly from a sprite
        // sheet
        ProjectilePool.setImageSource("colorstar.png");

        // show how many shots are left
        Display.addProjectileCount(" projectiles left", 5, 300, "arial.ttf", 255, 0, 255, 12);

        // draw a bunch of enemies to defeat
        Enemy e = Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");
        e.setPhysics(1.0f, 0.3f, 0.6f);
        e.setRotationSpeed(1);
        for (int i = 1; i < 20; i += 5)
            Enemy.makeAsCircle(i, i + 8, 2, 2, "redball.png");

        // draw a few obstacles that shrink over time, to show that circles
        // and boxes work, we can shrink the X and Y rates independently,
        // and we can opt to center things as they shrink or grow
        Obstacle floor = Obstacle.makeAsBox(2, 3, 42, 3, "red.png");
        floor.setShrinkOverTime(1, 1, true);

        Obstacle roof = Obstacle.makeAsBox(24, 30, 1, 1, "red.png");
        roof.setShrinkOverTime(-1, 0, false);

        Obstacle ball1 = Obstacle.makeAsCircle(40, 8, 8, 8, "purpleball.png");
        ball1.setShrinkOverTime(1, 2, true);

        Obstacle ball2 = Obstacle.makeAsCircle(40, 16, 8, 8, "purpleball.png");
        ball2.setShrinkOverTime(2, 1, false);

        Score.setVictoryEnemyCount(5);
    }

    /*
     * this level shows that we can make a hero in the air rotate. Rotation
     * doesn't do anything, but it looks nice...
     */
    else if (whichLevel == 59) {
        // make a simple level
        Level.configure(48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        PreScene.get().addText("Press to rotate the hero", 255, 255, 255, "arial.ttf", 32);

        // warning: this destination is just out of the hero's reach when
        // the hero
        // jumps... you'll have to hit the side wall and jump again to reach
        // it!
        Destination.makeAsCircle(46, 8, 2.5f, 2.5f, "mustardball.png");
        Score.setVictoryDestination(1);

        // make the hero jumpable, so that we can see it spin in the air
        Hero h = Hero.makeAsCircle(4, 27, 3, 3, "stars.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        h.setJumpImpulses(0, 10);
        h.setTouchToJump();

        // add rotation buttons
        Control.addRotateButton(0, 480, 160, 160, "", -.5f, h);
        Control.addRotateButton(760, 480, 160, 160, "", .5f, h);
    }

    /**
     * we can attach movement buttons to any moveable entity, so in this
     * case, we attach it to an obstacle to get an arkanoid-like effect.
     */
    else if (whichLevel == 60) {
        // make a simple level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 0, 0, 0);

        Destination.makeAsCircle(30, 10, 2.5f, 2.5f, "mustardball.png");
        Score.setVictoryDestination(1);

        // make a hero who is always moving... note there is no friction,
        // anywhere, and the hero is elastic... it won't ever stop...
        Hero h = Hero.makeAsCircle(4, 4, 3, 3, "greenball.png");
        h.setPhysics(0, 1, .1f);
        h.addVelocity(0, 10, false);

        // make an obstacle and then connect it to some controls
        Obstacle o = Obstacle.makeAsBox(2, 30.9f, 4, 1, "red.png");
        o.setPhysics(100, 1, .1f);
        Control.addLeftButton(0, 0, 480, 640, "", 5, o);
        Control.addRightButton(480, 0, 480, 640, "", 5, o);
    }

    /*
     * this level demonstrates that things can appear and disappear on
     * simple timers
     */
    else if (whichLevel == 61) {
        // set up a basic level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Things will appear \nand disappear...", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // create an enemy that will quietly disappear after 2 seconds
        Enemy e1 = Enemy.makeAsCircle(25, 25, 2, 2, "redball.png");
        e1.setPhysics(1.0f, 0.3f, 0.6f);
        e1.setRotationSpeed(1);
        e1.setDisappearDelay(2, true);

        // create an enemy that will appear after 3 seconds
        Enemy e2 = Enemy.makeAsCircle(35, 25, 2, 2, "redball.png");
        e2.setPhysics(1.0f, 0.3f, 0.6f);
        e2.setRoute(new Route(3).to(35, 25).to(15, 25).to(35, 25), 3, true);
        e2.setAppearDelay(3);
    }

    /*
     * This level demonstrates the use of timer callbacks. We can use timers
     * to make more of the level appear over time. In this case, we'll chain
     * the timer callbacks together, so that we can get more and more things
     * to develop. Be sure to look at the onTimerCallback code to see how
     * the rest of this level works.
     */
    else if (whichLevel == 62) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        PreScene.get().addText("There's nothing to\ndo... yet", 255, 255, 255, "arial.ttf", 20);

        // note: there's no destination yet, but we still say it's how to
        // win... we'll get a destination in this level after a few timers
        // run...
        Score.setVictoryDestination(1);

        // set a timer callback. after three seconds, the callback will run
        Level.setTimerCallback(2, new LolCallback() {
            public void onEvent() {
                // put up a pause scene to interrupt gameplay
                PauseScene.get().reset();
                PauseScene.get().addText("Ooh... a draggable enemy", 255, 255, 0, "arial.ttf", 12);
                PauseScene.get().show();
                // make a draggable enemy
                Enemy e3 = Enemy.makeAsCircle(35, 25, 2, 2, "redball.png");
                e3.setPhysics(1.0f, 0.3f, 0.6f);
                e3.setCanDrag(true);
            }
        });

        // set another callback that runs after 6 seconds (note: time
        // doesn't count while the PauseScene is showing...)
        Level.setTimerCallback(6, new LolCallback() {
            public void onEvent() {
                // clear the pause scene, then put new text on it
                PauseScene.get().reset();
                PauseScene.get().addText("Touch the enemy and it will go away", 255, 0, 255, "arial.ttf", 12);
                PauseScene.get().show();
                // add an enemy that is touch-to-defeat
                Enemy e4 = Enemy.makeAsCircle(35, 5, 2, 2, "redball.png");
                e4.setPhysics(1.0f, 0.3f, 0.6f);
                e4.setDisappearOnTouch();
            }
        });

        // set a callback that runs after 9 seconds. Though it's not
        // necessary in this case, we're going to make the callback an
        // explicit object. This can be useful, as we'll see later on.
        Level.setTimerCallback(9, new LolCallback() {
            public void onEvent() {
                // draw an enemy, a goodie, and a destination, all with
                // fixed velocities
                PauseScene.get().reset();
                PauseScene.get().addText("Now you can see the rest of the level", 255, 255, 0, "arial.ttf", 12);
                PauseScene.get().show();
                Destination d = Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
                d.addVelocity(-.5f, -1, false);

                Enemy e5 = Enemy.makeAsCircle(35, 15, 2, 2, "redball.png");
                e5.setPhysics(1.0f, 0.3f, 0.6f);
                e5.addVelocity(4, 4, false);

                Goodie gg = Goodie.makeAsCircle(10, 10, 2, 2, "blueball.png");
                gg.addVelocity(5, 5, false);
            }
        });

        // Lastly, we can make a timer callback that runs over and over
        // again. This one starts after 2 seconds, then runs every second.
        Level.setTimerCallback(2, 1, new LolCallback() {
            public void onEvent() {
                // note that every SimpleCallback has a field called
                // "intVal" that is initially 0. By using and then modifying
                // that field inside of the timer code, we can ensure that
                // each execution of the timer is slightly different, even
                // if the game state hasn't changed.
                Obstacle.makeAsCircle(mIntVal % 48, mIntVal / 48, 1, 1, "purpleball.png");
                mIntVal++;
            }
        });
    }

    /*
     * This level shows callbacks that run on a collision between hero and
     * obstacle. In this case, it lets us draw out the next part of the
     * level later, instead of drawing the whole thing right now. In a real
     * level, we'd draw a few screens at a time, and not put the callback
     * obstacle at the end of a screen, so that we'd never see the drawing
     * of stuff taking place, but for this demo, that's actually a nice
     * effect. Be sure to look at onCollideCallback for more details.
     */
    else if (whichLevel == 63) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Keep going right!", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 29, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Level.setCameraChase(h);

        Display.addGoodieCount(1, 0, " Goodies", 220, 280, "arial.ttf", 60, 70, 255, 12);
        Score.setVictoryDestination(1);

        // this obstacle is a collision callback... when the hero hits it,
        // the next part of the level appears, via onHeroCollideCallback().
        // Note, too, that it disappears when the hero hits it, so we can
        // play a sound if we want...
        Obstacle o = Obstacle.makeAsBox(30, 0, 1, 32, "purpleball.png");
        o.setPhysics(1, 0, 1);
        // the callback id is 0, there is no delay, and no goodies are
        // needed before it works
        o.setHeroCollisionCallback(0, 0, 0, 0, 0, new LolCallback() {
            public void onEvent() {
                // get rid of the obstacle we just collided with
                mAttachedActor.remove(false);
                // make a goodie
                Goodie.makeAsCircle(45, 1, 2, 2, "blueball.png");
                // make an obstacle that is a callback, but that doesn't
                // work until the goodie count is 1
                Obstacle oo = Obstacle.makeAsBox(60, 0, 1, 32, "purpleball.png");

                // we're going to chain a bunch of callbacks together, and
                // the best way to do that is to make a single callback that
                // behaves differently based on the value of the callback's
                // intVal field.
                LolCallback sc2 = new LolCallback() {
                    public void onEvent() {
                        // The second callback works the same way
                        if (mIntVal == 0) {
                            mAttachedActor.remove(false);
                            Goodie.makeAsCircle(75, 21, 2, 2, "blueball.png");

                            Obstacle oo = Obstacle.makeAsBox(90, 0, 1, 32, "purpleball.png");
                            oo.setHeroCollisionCallback(2, 0, 0, 0, 0, this);
                            mIntVal = 1;
                        }
                        // same for the third callback
                        else if (mIntVal == 1) {
                            mAttachedActor.remove(false);
                            Goodie.makeAsCircle(105, 1, 2, 2, "blueball.png");

                            Obstacle oo = Obstacle.makeAsBox(120, 0, 1, 32, "purpleball.png");
                            oo.setHeroCollisionCallback(3, 0, 0, 0, 0, this);
                            mIntVal = 2;
                        }
                        // The fourth callback draws the destination
                        else if (mIntVal == 2) {
                            mAttachedActor.remove(false);
                            // print a message and pause the game, via
                            // PauseScene
                            PauseScene.get().addText("The destination is\nnow available", 255, 255, 255,
                                    "arial.ttf", 32);
                            Destination.makeAsCircle(120, 20, 2, 2, "mustardball.png");
                        }

                    }
                };
                oo.setHeroCollisionCallback(1, 0, 0, 0, 0, sc2);
            }
        });
        o.setDisappearSound("hipitch.ogg");
    }

    /*
     * this level demonstrates callbacks that happen when we touch an
     * obstacle. Be sure to look at the onTouchCallback() method for more
     * details
     */
    else if (whichLevel == 64) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Activate and then \ntouch the obstacle", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        // make a destination... notice that it needs a lot more goodies
        // than are on the screen...
        Destination d = Destination.makeAsCircle(29, 1, 2, 2, "mustardball.png");
        d.setActivationScore(3, 0, 0, 0);
        Score.setVictoryDestination(1);

        // draw an obstacle, make it a touch callback, and then draw the
        // goodie we need to get in order to activate the obstacle
        Obstacle o = Obstacle.makeAsCircle(10, 5, 3, 3, "purpleball.png");
        o.setPhysics(1, 0, 1);
        // we'll give this callback the id "39", just for fun
        o.setTouchCallback(1, 0, 0, 0, true, new LolCallback() {
            public void onEvent() {
                // note: we could draw a picture of an open chest in the
                // obstacle's place, or even use a disappear animation whose
                // final frame looks like an open treasure chest.
                mAttachedActor.remove(false);
                for (int i = 0; i < 3; ++i)
                    Goodie.makeAsCircle(9 * i, 20 - i, 2, 2, "blueball.png");
            }
        });
        o.setDisappearSound("hipitch.ogg");

        Goodie g = Goodie.makeAsCircle(0, 30, 2, 2, "blueball.png");
        g.setDisappearSound("lowpitch.ogg");
    }

    /*
     * this level shows how to use enemy defeat callbacks. There are four
     * ways to defeat an enemy, so we enable all mechanisms in this level,
     * to see if they all work to cause enemy callbacks to run the
     * onEnemyCallback code. Another important point here is that the IDs
     * don't need to be unique for *any* callbacks. We can use the same ID
     * every time...
     */
    else if (whichLevel == 65) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 0, 0, 0);

        // give the hero strength, so that we can use him to defeat an enemy
        // as a test of enemy callbacks
        Hero h = Hero.makeAsCircle(12, 12, 4, 4, "greenball.png");
        h.setStrength(3);
        h.setMoveByTilting();
        h.setInvincibleAnimation(
                new Animation("colorstar.png", 4, true).to(4, 100).to(5, 100).to(6, 100).to(7, 100));

        // a goodie, so we can do defeat by invincibility
        Goodie g1 = Goodie.makeAsCircle(20, 29, 2, 3, "purpleball.png");
        g1.setInvincibilityDuration(15);

        // enable throwing projectiles, so that we can test enemy callbacks
        // again
        h.setTouchToThrow(h, 4, 2, 30, 0);
        ProjectilePool.configure(100, 1, 1, "greyball.png", 1, 0, true);

        // add an obstacle that has an enemy collision callback, so it can
        // defeat enemies
        Obstacle o = Obstacle.makeAsCircle(30, 10, 5, 5, "blueball.png");
        o.setPhysics(1000, 0, 0);
        o.setCanDrag(false);
        o.setEnemyCollisionCallback(0, 0, 0, 0, 0, new LolCallback() {
            public void onEvent() {
                if (mCollideActor.getInfoText().equals("weak")) {
                    ((Enemy) mCollideActor).defeat(true);
                }
            }
        });

        // now draw our enemies... we need enough to be able to test that
        // all four defeat mechanisms work. Note that we attach defeat
        // callback code to each of them.
        LolCallback sc = new LolCallback() {
            public void onEvent() {
                // always reset the pausescene, in case it has something on
                // it from before...
                PauseScene.get().reset();
                PauseScene.get().addText("good job, here's a prize", 88, 226, 160, "arial.ttf", 16);
                PauseScene.get().show();
                // use random numbers to figure out where to draw a goodie
                // as a reward... picking in the range 0-46,0-30 ensures
                // that with width and height of 2, the goodie stays on
                // screen
                Goodie.makeAsCircle(Util.getRandom(46), Util.getRandom(30), 2, 2, "blueball.png");
            }
        };
        Enemy e1 = Enemy.makeAsCircle(5, 5, 1, 1, "redball.png");
        e1.setDefeatCallback(sc);

        Enemy e2 = Enemy.makeAsCircle(5, 5, 2, 2, "redball.png");
        e2.setDefeatCallback(sc);
        e2.setInfoText("weak");

        Enemy e3 = Enemy.makeAsCircle(40, 3, 1, 1, "redball.png");
        e3.setDefeatCallback(sc);

        Enemy e4 = Enemy.makeAsCircle(25, 25, 1, 1, "redball.png");
        e4.setDefeatCallback(sc);
        e4.setDisappearOnTouch();

        Enemy e5 = Enemy.makeAsCircle(25, 29, 1, 1, "redball.png");
        e5.setDefeatCallback(sc);

        // win by defeating enemies
        Score.setVictoryEnemyCount();
    }

    /*
     * This level shows that we can resize a hero on the fly, and change its
     * image. We use a collision callback to cause the effect. Furthermore,
     * we can increment scores inside of the callback code, which lets us
     * activate the destination on an obstacle collision
     */
    else if (whichLevel == 66) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Only stars can reach\nthe destination", 255, 255, 255, "arial.ttf", 20);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 29, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        Display.addGoodieCount(1, 0, " Goodies", 220, 280, "arial.ttf", 60, 70, 255, 12);

        // the destination won't work until some goodies are collected...
        Destination d = Destination.makeAsBox(46, 2, 2, 2, "colorstar.png");
        d.setActivationScore(4, 1, 3, 0);
        Score.setVictoryDestination(1);

        // Colliding with this star will make the hero into a star... see
        // onHeroCollideCallback for details
        Obstacle o = Obstacle.makeAsBox(30, 0, 3, 3, "stars.png");
        o.setPhysics(1, 0, 1);
        o.setHeroCollisionCallback(0, 0, 0, 0, 1, new LolCallback() {
            public void onEvent() {
                // here's a simple way to increment a goodie count
                Score.incrementGoodiesCollected2();
                // here's a way to set a goodie count
                Score.setGoodiesCollected3(3);
                // here's a way to read and write a goodie count
                Score.setGoodiesCollected1(4 + Score.getGoodiesCollected1());
                // get rid of the star, so we know it's been used
                mAttachedActor.remove(true);
                // resize the hero, and change its image
                mCollideActor.resize(mCollideActor.getXPosition(), mCollideActor.getYPosition(), 5, 5);
                mCollideActor.setImage("stars.png", 0);
            }
        });
    }

    /*
     * This level shows how to use countdown timers to win a level, tests
     * some color features, and introduces a vector throw mechanism with
     * fixed velocity
     */
    else if (whichLevel == 67) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Press anywhere\nto throw a ball", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        // Here's a simple pause button and pause scene
        PauseScene.get().addText("Game Paused", 255, 255, 255, "arial.ttf", 32);
        Control.addPauseButton(0, 300, 20, 20, "red.png");

        // draw a hero, and a button for throwing projectiles in many
        // directions. Note that this is going to look like an "asteroids"
        // game, with a hero covering the bottom of the screen, so that
        // anything that falls to the bottom counts against the player
        Hero h = Hero.makeAsBox(1, 0, 46, 1, "greenball.png");
        Control.addDirectionalThrowButton(0, 0, 960, 640, "", h, 100, 0, 1);

        // set up our pool of projectiles, then set them to have a fixed
        // velocity when using the vector throw mechanism
        ProjectilePool.configure(100, 1, 1, "greyball.png", 1, 0, true);
        ProjectilePool.setRange(50);
        ProjectilePool.setFixedVectorThrowVelocity(5);

        // we're going to win by "surviving" for 25 seconds... with no
        // enemies, that shouldn't be too hard
        Display.addWinCountdown(25, 28, 250, "arial.ttf", 192, 192, 192, 16);
        // just to play it safe, let's say that we win on destination...
        // this ensures that collecting goodies or defeating enemies won't
        // accidentally cause us to win. Of course, with no destination,
        // there's no way to win now, except surviving.
        Score.setVictoryDestination(1);
    }

    /*
     * We can make a hero hover, and then have it stop hovering when it is
     * flicked or moved via "touchToMove". This demonstrates the effect via
     * flick. It also shows that an enemy (or obstacle/goodie/destination)
     * can fall due to gravity.
     */
    else if (whichLevel == 68) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Flick the hero into the destination", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsBox(21, 23, 3, 3, "greenball.png");
        h.setHover(21, 23);
        h.setFlickable(0.7f);

        // place an enemy, let it fall
        Enemy e = Enemy.makeAsCircle(31, 25, 3, 3, "redball.png");
        e.setCanFall();

        Destination.makeAsCircle(25, 25, 5, 5, "mustardball.png");
        Score.setVictoryDestination(1);
    }

    /*
     * The default behavior is for a hero to be able to jump any time it
     * collides with an obstacle. This isn't, of course, the smartest way to
     * do things, since a hero in the air shouldn't jump. One way to solve
     * the problem is by altering the presolve code in Physics.java. Another
     * approach, which is much simpler, is to mark some walls so that the
     * hero doesn't have jump re-enabled upon a collision.
     */
    else if (whichLevel == 69) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Press the hero to\nmake it jump", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 1);

        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.5f, 0, 0.6f);
        h.setMoveByTilting();
        h.setTouchToJump();
        h.setJumpImpulses(0, 15);
        Level.setCameraChase(h);

        // hero can jump while on this obstacle
        Obstacle.makeAsBox(10, 3, 10, 1, "red.png");

        // hero can't jump while on this obstacle
        Obstacle o = Obstacle.makeAsBox(40, 3, 10, 1, "red.png");
        o.setReJump(false);
    }

    /*
     * When something chases an entity, we might not want it to chase in
     * both the X and Y dimensions... this shows how we can chase in a
     * single direction.
     */
    else if (whichLevel == 70) {
        // set up a simple level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("You can walk through the wall", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "stars.png");
        h.setMoveByTilting();

        Destination.makeAsCircle(42, 31, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // These obstacles chase the hero, but only in one dimension
        Obstacle e = Obstacle.makeAsCircle(0, 0, 1, 1, "red.png");
        e.setChaseSpeed(15, h, false, true);
        e.setCollisionsEnabled(true);
        Obstacle e2 = Obstacle.makeAsCircle(0, 0, 1, 1, "red.png");
        e2.setChaseSpeed(15, h, true, false);
        e2.setCollisionsEnabled(true);

        // Here's a wall, and a movable round obstacle
        Obstacle o = Obstacle.makeAsBox(40, 1, .5f, 20, "red.png");
        Obstacle o2 = Obstacle.makeAsCircle(8, 8, 2, 2, "blueball.png");
        o2.setMoveByTilting();

        // The hero can pass through this wall, because both have the same
        // passthrough value
        h.setPassThrough(7);
        o.setPassThrough(7);
    }

    /*
     * PokeToPlace is nice, but sometimes it's nicer to use Poke to cause
     * movement to the destination, instead of an immediate jump.
     */
    else if (whichLevel == 71) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 0, 0, 0);
        PreScene.get().addText("Poke the hero, then\n where you want it\nto go.", 255, 255, 255, "arial.ttf",
                32);

        // This hero moves via poking. the "false" means that we don't have
        // to poke hero, poke location, poke hero, poke location, ...
        // Instead, we can poke hero, poke location, poke location. the
        // first "true" means that as we drag our finger, the hero will
        // change its direction of travel. The second "true" means the hero
        // will stop immediately when we release our finger.
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "stars.png");
        h.setDefaultAnimation(new Animation("stars.png", 200, true, 0, 0));
        h.setDefaultReverseAnimation(new Animation("stars_flipped.png", 200, true, 7, 7));
        h.setPokePath(4, false);

        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // sometimes a control needs to have a large touchable area, but a
        // small image. One way to do it is to make an invisible control,
        // then put a picture on top of it. This next line shows how to draw
        // a picture on the HUD
        Control.addImage(40, 40, 40, 40, "red.png");
    }

    /*
     * It can be useful to make a Hero stick to an obstacle. As an example,
     * if the hero should stand on a platform that moves along a route, then
     * we will want the hero to "stick" to it, even as the platform moves
     * downward.
     */
    else if (whichLevel == 72) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Press screen borders\nto move the hero", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, 0, 1);
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.disableRotation();
        h.setJumpImpulses(0, 15);
        h.setTouchToJump();
        // give a little friction, to help the hero stick to platforms
        h.setPhysics(2, 0, .5f);

        // create a destination
        Destination.makeAsCircle(20, 15, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // This obstacle is sticky on top... Jump onto it and watch what
        // happens
        Obstacle o = Obstacle.makeAsBox(10, 5, 8, .5f, "red.png");
        o.setRoute(new Route(5).to(10, 5).to(5, 15).to(10, 25).to(15, 15).to(10, 5), 5, true);
        o.setPhysics(100, 0, .1f);
        o.setSticky(true, false, false, false);

        // This obstacle is not sticky... it's not nearly as much fun
        Obstacle o2 = Obstacle.makeAsBox(30, 5, 8, .5f, "red.png");
        o2.setRoute(new Route(5).to(30, 5).to(25, 15).to(30, 25).to(45, 15).to(30, 5), 5, true);
        o2.setPhysics(100, 0, 1f);

        // draw some buttons for moving the hero
        Control.addLeftButton(0, 100, 100, 440, "", 5, h);
        Control.addRightButton(860, 100, 100, 440, "", 5, h);
    }

    /*
     * When using "vector" projectiles, if the projectile isn't a circle we
     * might want to rotate it in the direction of travel. Also, this level
     * shows how to do walls that can be passed through in one direction.
     */
    else if (whichLevel == 73) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Press anywhere\nto shoot a laserbeam", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();

        Destination.makeAsCircle(42, 31, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // draw a button for throwing projectiles in many directions. It
        // only covers half the screen, to show how such an effect would
        // behave
        Control.addDirectionalThrowButton(0, 0, 480, 640, "", h, 100, 0, 0);

        // set up a pool of projectiles with fixed velocity, and with
        // rotation
        ProjectilePool.configure(100, .1f, 3, "red.png", 1, 0, false);
        ProjectilePool.setFixedVectorThrowVelocity(10);
        ProjectilePool.setRotateVectorThrow();

        // create a box that is easy to fall into, but hard to get out of,
        // by making its sides each "one-sided"
        Obstacle bottom = Obstacle.makeAsBox(10, 10, 10, .2f, "red.png");
        bottom.setOneSided(2);
        Obstacle left = Obstacle.makeAsBox(10, 10, .2f, 10, "red.png");
        left.setOneSided(1);
        Obstacle right = Obstacle.makeAsBox(20, 10, .2f, 10, "red.png");
        right.setOneSided(3);
        Obstacle top = Obstacle.makeAsBox(10, 25, 10, .2f, "red.png");
        top.setOneSided(0);
    }

    /*
     * This level shows how to use multiple types of goodie scores
     */
    else if (whichLevel == 74) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Green, Red, and Grey\nballs are goodies", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "stars.png");
        h.setMoveByTilting();

        // the destination requires lots of goodies of different types
        Destination d = Destination.makeAsCircle(42, 31, 2, 2, "mustardball.png");
        d.setActivationScore(1, 1, 3, 0);
        Score.setVictoryDestination(1);

        Display.addGoodieCount(1, 0, " blue", 10, 110, "arial.ttf", 0, 255, 255, 16);
        Display.addGoodieCount(2, 0, " green", 10, 140, "arial.ttf", 0, 255, 255, 16);
        Display.addGoodieCount(3, 0, " red", 10, 170, "arial.ttf", 0, 255, 255, 16);

        Display.addCountdown(100, "", 250, 30);

        // draw the goodies
        for (int i = 0; i < 3; ++i) {
            Goodie b = Goodie.makeAsCircle(10 * i, 30, 2, 2, "blueball.png");
            b.setScore(1, 0, 0, 0);
            Goodie g = Goodie.makeAsCircle(10 * i + 2.5f, 30, 1, 1, "greenball.png");
            g.setScore(0, 1, 0, 0);
            Goodie r = Goodie.makeAsCircle(10 * i + 6, 30, 1, 1, "redball.png");
            r.setScore(0, 0, 1, 0);
        }

        // When the hero collides with this obstacle, we'll increase the
        // time remaining. See onHeroCollideCallback()
        Obstacle o = Obstacle.makeAsBox(40, 0, 5, 200, "red.png");
        o.setHeroCollisionCallback(1, 1, 1, 0, 0, new LolCallback() {
            public void onEvent() {
                // add 15 seconds to the timer
                Score.updateTimerExpiration(15);
                mAttachedActor.remove(true);
            }
        });
    }

    /*
     * this level shows passthrough objects and chase again, to help
     * demonstrate how chase works
     */
    else if (whichLevel == 75) {
        // set up a simple level
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("You can walk through the wall", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "stars.png");
        h.setMoveByTilting();
        h.setPassThrough(7); // make sure obstacle has same value

        // the destination requires lots of goodies of different types
        Destination.makeAsCircle(42, 31, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // the enemy chases the hero, but can't get through the wall
        Enemy e = Enemy.makeAsCircle(42, 1, 5, 4, "red.png");
        e.setChaseSpeed(1, h, true, true);

        Obstacle o = Obstacle.makeAsBox(40, 1, .5f, 20, "red.png");
        o.setPassThrough(7);
    }

    /*
     * We can have a control that increases the hero's speed while pressed,
     * and decreases it upon release
     */
    else if (whichLevel == 76) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, 10);
        PreScene.get().addText("Press anywhere to speed up", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 0);

        Destination.makeAsCircle(120, 31, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        Hero h = Hero.makeAsBox(2, 25, 3, 7, "greenball.png");
        h.disableRotation();
        h.setPhysics(1, 0, 0);
        // give the hero a fixed velocity
        h.addVelocity(4, 0, false);
        // center the camera a little ahead of the hero
        h.setCameraOffset(15, 0);
        Level.setCameraChase(h);

        // set up the background
        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        // draw a turbo boost button that covers the whole screen... make
        // sure its "up" speeds match the hero velocity
        Control.addTurboButton(0, 0, 960, 640, "", 15, 0, 4, 0, h);
    }

    /*
     * Sometimes, we want to make the hero move when we press a control, but
     * when we release we don't want an immediate stop. This shows how to
     * get that effect.
     */
    else if (whichLevel == 77) {
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Press anywhere to start moving", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 0);

        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        Hero h = Hero.makeAsBox(2, 1, 3, 7, "greenball.png");
        h.setCameraOffset(15, 0);
        Level.setCameraChase(h);

        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 960, 640);

        // This control has a dampening effect, so that on release, the hero
        // slowly stops
        Control.addDampenedMotionButton(0, 0, 960, 640, "", 10, 0, 4, h);
    }

    /*
     * One-sided obstacles can be callback obstacles. This allows, among
     * other things, games like doodle jump. This level shows how it all
     * interacts.
     */
    else if (whichLevel == 78) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("One-sided + Callbacks", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.disableRotation();
        h.setPhysics(.1f, 0, 0);
        h.setMoveByTilting();
        h.setJumpImpulses(0, 15);
        h.setTouchToJump();

        Destination.makeAsCircle(42, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // create a platform that we can jump through from above
        Obstacle platform = Obstacle.makeAsBox(10, 5, 10, .2f, "red.png");
        platform.setOneSided(2);
        // Set a callback, then re-enable the platform's collision effect.
        // Be sure to check onHeroCollideCallback
        platform.setHeroCollisionCallback(0, 0, 0, 0, 0, new LolCallback() {
            public void onEvent() {
                mCollideActor.setAbsoluteVelocity(mCollideActor.getXVelocity(), 5, false);
            }
        });
        platform.setCollisionsEnabled(true);

        // make the z index of the platform -1, so that the hero (index 0)
        // will be drawn on top of the box, not under it
        platform.setZIndex(-1);
    }

    /*
     * This level fleshes out some more poke-to-move stuff. Now we'll say
     * that once a hero starts moving, the player must re-poke the hero
     * before it can be given a new destination. Also, the hero will keep
     * moving after the screen is released. We will also show the Fact
     * interface.
     */
    else if (whichLevel == 79) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 0, 0, 0);
        PreScene.get().addText("Poke the hero, then\n where you want it\nto go.", 255, 255, 255, "arial.ttf",
                32);

        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setMoveByTilting();
        h.setFingerChase(4, false, true);

        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // A callback control is a way to run arbitrary code whenever the
        // control is pressed. This is something of a catch-all for any sort
        // of behavior we might want. See onControlPressCallback().
        Control.addCallbackControl(40, 40, 40, 40, "red.png", new LolCallback() {
            public void onEvent() {
                PauseScene.get().reset();
                PauseScene.get().addText("Current score " + Score.getGoodiesCollected1(), 255, 255, 255,
                        "arial.ttf", 20);
                PauseScene.get().show();
                Score.incrementGoodiesCollected1();
            }
        });

        Display.addLevelFact("level test", 240, 40, "arial.ttf", 0, 0, 0, 12, "-", ".");
        Display.addSessionFact("session test", 240, 80, "arial.ttf", 0, 0, 0, 12, "-", ".");
        Display.addGameFact("game test", 240, 120, "arial.ttf", 0, 0, 0, 12, "-", ".");
        Control.addCallbackControl(40, 90, 40, 40, "red.png", new LolCallback() {
            public void onEvent() {
                Facts.putLevelFact("level test", 1 + Facts.getLevelFact("level test", -1));
            }
        });
        Control.addCallbackControl(40, 140, 40, 40, "red.png", new LolCallback() {
            public void onEvent() {
                Facts.putSessionFact("session test", 1 + Facts.getSessionFact("session test", -1));
            }
        });
        Control.addCallbackControl(40, 190, 40, 40, "red.png", new LolCallback() {
            public void onEvent() {
                Facts.putGameFact("game test", 1 + Facts.getGameFact("game test", -1));
            }
        });
    }

    /*
     * Sometimes we need to manually force an entity to be immune to
     * gravity.
     */
    else if (whichLevel == 80) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Testing Gravity Defy?", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.disableRotation();
        h.setPhysics(1, 0, 0.6f);
        h.setMoveByTilting();
        h.setJumpImpulses(0, 15);
        h.setTouchToJump();

        Destination d = Destination.makeAsCircle(42, 14, 2, 2, "mustardball.png");
        // note: it must not be immune to physics (third parameter true), or
        // it will pass through the bounding box, but we do want it to move
        // and not fall downward
        d.setAbsoluteVelocity(-2, 0, false);
        d.setGravityDefy();
        Score.setVictoryDestination(1);
    }

    /*
     * Test to show that we can have obstacles with a polygon shape
     */
    else if (whichLevel == 81) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Testing Polygons", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.disableRotation();
        h.setMoveByTilting();

        Destination.makeAsCircle(42, 14, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // create a polygon obstacle
        Obstacle o = Obstacle.makeAsPolygon(10, 10, 2, 5, "blueball.png", -1, 2, -1, 0, 0, -3, 1, 0, 1, 1);
        o.setShrinkOverTime(1, 1, true);
    }

    /*
     * A place for playing with a side-scrolling platformer that has lots of
     * features
     */
    else if (whichLevel == 82) {
        // set up a standard side scroller with tilt:
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        PreScene.get().addText("Press the hero to\nmake it jump", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 1);
        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up a simple jumping hero
        Hero h = Hero.makeAsBox(5, 0, 2, 6, "greenball.png");
        h.setJumpImpulses(0, 15);
        h.setTouchToJump();
        h.setMoveByTilting();
        Level.setCameraChase(h);

        // This enemy can be defeated by jumping. Note that the hero's
        // bottom must be higher than the enemy's middle point, or the jump
        // won't defeat the enemy.
        Enemy e = Enemy.makeAsCircle(15, 0, 5, 5, "redball.png");
        e.setDefeatByJump();
    }

    /*
     * Demonstrate the ability to set up paddles that rotate back and forth
     */
    else if (whichLevel == 83) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Avoid revolving obstacles", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, 0, 1);

        Hero h = Hero.makeAsCircle(5, 0, 2, 6, "greenball.png");
        h.setMoveByTilting();

        // Note: you must give density to the revolving part...
        Obstacle revolving = Obstacle.makeAsBox(20, 10, 2, 8, "red.png");
        revolving.setPhysics(1, 0, 0);
        Obstacle anchor = Obstacle.makeAsBox(20, 19, 2, 2, "blueball.png");

        revolving.setRevoluteJoint(anchor, 0, 0, 0, 6);
        revolving.setRevoluteJointLimits(1.7f, -1.7f);
        revolving.setRevoluteJointMotor(4, Float.POSITIVE_INFINITY);
        Destination.makeAsCircle(40, 30, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
    }

    /*
     * Demonstrate panning to view more of the level
     */
    else if (whichLevel == 84) {
        // set up a big screen
        Level.configure(4 * 48, 2 * 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("The star rotates in\nthe direction of movement", 255, 255, 255, "arial.ttf",
                32);
        Util.drawBoundingBox(0, 0, 4 * 48, 2 * 32, "red.png", 1, 0, 1);
        Destination.makeAsCircle(29, 60, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up a hero who rotates in the direction of movement
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "stars.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setRotationByDirection();
        h.setMoveByTilting();
        Level.setCameraChase(h);

        // zoom buttons
        Control.addZoomOutButton(0, 0, 480, 640, "", 8);
        Control.addZoomInButton(480, 0, 480, 640, "", .25f);

        // turn on panning
        Control.addPanControl(0, 0, 960, 640, "");
    }

    /*
     * Demonstrate pinch-to-zoom, and also demonstrate one-time callback
     * controls
     */
    else if (whichLevel == 85) {
        // set up a big screen
        Level.configure(4 * 48, 2 * 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("The star rotates in\nthe direction of movement", 255, 255, 255, "arial.ttf",
                32);
        Util.drawBoundingBox(0, 0, 4 * 48, 2 * 32, "red.png", 1, 0, 1);
        Destination.makeAsCircle(29, 60, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up a hero who rotates in the direction of movement
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "stars.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setRotationByDirection();
        h.setMoveByTilting();
        Level.setCameraChase(h);

        // turn on pinch zoomg
        Control.addPinchZoomControl(0, 0, 960, 640, "", 8, .25f);

        // add a one-time callback control
        Control.addOneTimeCallbackControl(40, 40, 40, 40, "blueball.png", "greenball.png", new LolCallback() {
            public void onEvent() {
                PauseScene.get().addText("you can only pause once...", 255, 255, 255, "arial.ttf", 20);
                PauseScene.get().show();
            }
        });
    }

    /*
     * Demonstrate some advanced controls
     */
    else if (whichLevel == 86) {
        // set up a screen
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, 0, 1);
        Destination.makeAsCircle(29, 30, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up a hero who rotates in the direction of movement
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        Level.setCameraChase(h);
        h.setDamping(1);
        h.setAngularDamping(1);
        // when the hero stops, we'll run code that turns the hero red
        h.setStopCallback(new LolCallback() {
            public void onEvent() {
                // NB: the setStopCallback call sets the callback's
                // attachedSprite to the hero.
                mAttachedActor.setImage("red.png", 0);
            }
        });

        // add some new controls for setting the rotation of the hero and
        // making the hero move based on a speed
        LolCallback rotatorSC = new LolCallback() {
            public void onEvent() {
                // rotator... save the rotation and rotate the hero
                mAttachedActor.setRotation(mFloatVal * (float) Math.PI / 180);
                // multiply float val by 100 to preserve some decimal places
                Facts.putLevelFact("rotation", (int) (100 * mFloatVal));
            }
        };
        rotatorSC.mAttachedActor = h;
        Control.addRotator(215, 135, 50, 50, "stars.png", 2, rotatorSC);
        LolCallback barSC = new LolCallback() {
            public void onEvent() {
                // vertical bar... make the entity move
                int rotation = Facts.getLevelFact("rotation", 0) / 100;
                // create a unit vector
                Vector2 v = new Vector2(1, 0);
                v.scl(mFloatVal);
                v.rotate(rotation + 90);
                mAttachedActor.setDamping(2f);
                mAttachedActor.setAbsoluteVelocity(v.x, v.y, false);
            }
        };
        barSC.mAttachedActor = h;
        Control.addVerticalBar(470, 0, 10, 320, "greenball.png", barSC);
    }

    /*
     * Weld joints
     */
    else if (whichLevel == 87) {
        // set up a screen
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, 0, 1);
        Destination.makeAsCircle(29, 30, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // set up a hero and fuse an obstacle to it
        Hero h = Hero.makeAsCircle(4, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Obstacle o = Obstacle.makeAsCircle(1, 1, 1, 1, "blueball.png");
        o.setCanFall();
        h.setWeldJoint(o, 3, 0, 0, 0, 45);
    }

    /*
     * Demonstrate that we can have callback buttons on PauseScenes
     */
    else if (whichLevel == 88) {
        Level.configure(48, 32);
        Physics.configure(0, 0);
        Tilt.enable(10, 10);
        PreScene.get().addText("Interactive Pause Scenes\n(click the red square)", 255, 255, 255, "arial.ttf",
                32);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);
        Hero h = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // Demonstrate the ability to chase while keeping existing velocity
        // in one direction
        Obstacle o = Obstacle.makeAsCircle(15, 15, 2, 2, "purpleball.png");
        o.setAbsoluteVelocity(5, 1, false);
        o.setChaseFixedMagnitude(h, 3, 0, false, true);

        // Create a pause scene that has a back button on it, and a button
        // for pausing the level
        PauseScene.get().addText("Game Paused", 255, 255, 255, "arial.ttf", 32);
        PauseScene.get().addBackButton("red.png", 0, 600, 40, 40);
        PauseScene.get().addCallbackButton(10, 10, 20, 20, new LolCallback() {
            public void onEvent() {
                Score.winLevel();
            }
        });
        PauseScene.get().addCallbackButton(190, 190, 20, 20, new LolCallback() {
            public void onEvent() {
                Score.loseLevel();
            }
        });
        PauseScene.get().suppressClearClick();
        Control.addPauseButton(0, 300, 20, 20, "red.png");
    }

    /*
     * Use multiple heroes to combine positive and negative results
     */
    else if (whichLevel == 89) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, .3f, 1);

        // now let's draw two heroes who can both move by tilting, and
        // who both have density and friction. Note that we lower the
        // density, so they move faster
        Hero h1 = Hero.makeAsCircle(4, 7, 3, 3, "greenball.png");
        h1.setPhysics(.1f, 0, 0.6f);
        h1.setMoveByTilting();
        h1.setJumpImpulses(0, 10);
        h1.setTouchToJump();
        h1.setMustSurvive();
        Hero h2 = Hero.makeAsBox(0, 0, 48, .1f, "");
        h2.setMustSurvive();
        h1.setPassThrough(1);
        h2.setPassThrough(1);

        Enemy e1 = Enemy.makeAsCircle(29, 29, 1, 1, "redball.png");
        e1.setAbsoluteVelocity(0, -1, true);

        // notice that now we will make two destinations, each of which
        // defaults to only holding ONE hero, but we still need to get two
        // heroes to destinations in order to complete the level
        Destination.makeAsCircle(29, 6, 2, 2, "mustardball.png");
    }

    /*
     * Demonstrate that we can save entities so that we can access them from
     * a callback
     */
    else if (whichLevel == 90) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 0, 0, 5);
        PreScene.get().addText("Keep pressing until\na hero makes it to\nthe destination", 255, 255, 255,
                "arial.ttf", 32);

        for (int i = 0; i < 10; ++i) {
            Hero h = Hero.makeAsBox(4 * i + 2, 0.1f, 2, 2, "greenball.png");
            h.setPhysics(1, 1, 5);
            Facts.putLevelActor("" + i, h);
        }

        Destination.makeAsCircle(29, 16, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);

        // A callback control is a way to run arbitrary code whenever the
        // control is pressed. This is something of a catch-all for any sort
        // of behavior we might want. See onControlPressCallback().
        Control.addCallbackControl(0, 0, 960, 640, "", new LolCallback() {
            public void onEvent() {
                for (int i = 0; i < 10; ++i) {
                    Actor p = Facts.getLevelActor("" + i);
                    p.setAbsoluteVelocity(5 - Util.getRandom(10), 10, false);
                }
            }
        });
    }

    /**
     * Demo a truck, using distance and revolute joints
     */
    else if (whichLevel == 91) {
        Level.configure(48, 32);
        Physics.configure(0, -10);
        Util.drawBoundingBox(0, 0, 48, 32, "red.png", 1, 0, 1);

        Hero truck = Hero.makeAsBox(3, 3, 4, 1.5f, "red.png");
        truck.setPhysics(1, 0, 0);
        Obstacle head = Obstacle.makeAsCircle(4.5f, 4, 1, 1, "blueball.png");
        head.setPhysics(1, 0, 0);
        Obstacle backWheel = Obstacle.makeAsCircle(3, 2, 1.5f, 1.5f, "blueball.png");
        backWheel.setPhysics(3, 0, 1);
        Obstacle frontWheel = Obstacle.makeAsCircle(5.5f, 2, 1.5f, 1.5f, "blueball.png");
        frontWheel.setPhysics(3, 0, 1);

        backWheel.setRevoluteJoint(truck, -1.5f, -1, 0, 0);
        backWheel.setRevoluteJointMotor(-10f, 10f);
        frontWheel.setRevoluteJoint(truck, 1.5f, -1, 0, 0);
        frontWheel.setRevoluteJointMotor(-10f, 10f);

        // this is not how we want the head to look, but it makes for a nice
        // demo
        head.setDistanceJoint(truck, 0, 1, 0, 0);

        Destination.makeAsBox(47, 0, .1f, 32, "");
        Score.setVictoryDestination(1);
    }

    /**
     * Demonstrate how we can chain pausescenes together, and also show how to use particle
     * effects
     */
    else if (whichLevel == 92) {
        // start with a basic tilt-based side-scroller
        Level.configure(3 * 48, 32);
        Physics.configure(0, -10);
        Tilt.enable(10, 0);
        Util.drawBoundingBox(0, 0, 3 * 48, 32, "red.png", 1, 0, 1);
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        h.setPhysics(.1f, 0, 0.6f);
        h.setMoveByTilting();
        Destination.makeAsCircle(120, 1, 2, 2, "mustardball.png");
        Score.setVictoryDestination(1);
        Level.setCameraChase(h);

        // put some flame effects on a black background
        Background.setColor(0, 0, 0);
        for (int i = 5; i < 150; i += 15) {
            Effect e = Effect.makeParticleSystem("flame.txt", -2, i, 5);
            e.setRepeat(true);
        }

        // here's a weak attempt at snow
        Effect e = Effect.makeParticleSystem("snow.txt", 2, 15, 40);
        e.setRepeat(true);
        e = Effect.makeParticleSystem("snow.txt", 2, 55, 40);
        e.setRepeat(true);
        e = Effect.makeParticleSystem("snow.txt", 2, 85, 40);
        e.setRepeat(true);
        // the trick for getting one PauseScene's dismissal to result in another PauseScene
        // drawing right away is to use the PauseScene CallbackButton facility.  When the first
        // PauseScene is touched, we dismiss it and immediately draw another PauseScene

        // set up a simple PauseScene
        PauseScene.get().reset();
        PauseScene.get().addText("test", 255, 255, 255, "arial.ttf", 32);
        // this is the code to run when the *second* pausescene is touched.  Making it "final"
        // means that we can refer to it inside of the other callback
        final LolCallback sc2 = new LolCallback() {
            public void onEvent() {
                PauseScene.get().dismiss();
            }
        };
        // this is the code to run when the *first* pausescene is touched
        LolCallback sc1 = new LolCallback() {
            public void onEvent() {
                // clear the pausescene, draw another one
                PauseScene.get().dismiss();
                PauseScene.get().reset();
                PauseScene.get().addText("test2", 255, 255, 255, "arial.ttf", 32);
                PauseScene.get().addCallbackButton(0, 0, 960, 640, sc2);
                PauseScene.get().show();
            }
        };
        // set the callback for the first pausescene, and show it
        PauseScene.get().addCallbackButton(0, 0, 960, 640, sc1);
        PauseScene.get().show();

        Control.addZoomOutButton(0, 0, 480, 640, "", 8);
        Control.addZoomInButton(480, 0, 480, 640, "", .25f);
    }

    // Show how to make an "infinite" level
    else if (whichLevel == 93) {
        // set up a standard side scroller with tilt, but make it really really long:
        Level.configure(300000, 32);
        Physics.configure(0, -10);
        PreScene.get().addText("Press to make\nthe hero go up", 255, 255, 255, "arial.ttf", 32);
        Util.drawBoundingBox(0, 0, 300000, 32, "red.png", 0, 0, 0);

        // make a hero
        Hero h = Hero.makeAsCircle(2, 2, 3, 3, "greenball.png");
        Level.setCameraChase(h);
        h.setAbsoluteVelocity(10, 0, false);
        h.disableRotation();
        h.setPhysics(.1f, 0, 0);

        // touching the screen makes the hero go upwards
        Control.addUpButton(0, 0, 960, 640, "", 20, h);

        // set up our background, with a few layers
        Background.setColor(23, 180, 255);
        Background.addHorizontalLayer(0, 1, "back.png", 0, 960, 640);
        Background.addHorizontalLayer(.5f, 1, "mid.png", 0, 480, 320);
        Background.addHorizontalLayer(1.25f, 1, "front.png", 20, 454, 80);

        // we win by collecting 10 goodies...
        Score.setVictoryGoodies(10, 0, 0, 0);
        Display.addGoodieCount(1, 0, " goodies", 15, 600, "arial.ttf", 255, 255, 255, 20);

        // now set up an obstacle and attach a callback to it
        //
        // Note that the obstacle needs to be final or we can't access it within the callback
        final Obstacle trigger = Obstacle.makeAsBox(30, 0, 1, 32, "");
        LolCallback lc = new LolCallback() {
            /**
             * Each time the hero hits the obstacle, we'll run this code to draw a new enemy
             * and a new obstacle on the screen.  We'll randomize their placement just a bit.
             * Also move the obstacle forward, so we can hit it again.
             */
            public void onEvent() {
                // make a random enemy and a random goodie.  Put them in X coordinates relative to the trigger
                Enemy.makeAsCircle(trigger.getXPosition() + 40 + Util.getRandom(10), Util.getRandom(30), 2, 2,
                        "redball.png");
                Goodie.makeAsCircle(trigger.getXPosition() + 50 + Util.getRandom(10), Util.getRandom(30), 2, 2,
                        "blueball.png");
                // move the trigger so we can hit it again
                trigger.setPosition(trigger.getXPosition() + 50, trigger.getYPosition());
            }
        };
        trigger.setHeroCollisionCallback(0, 0, 0, 0, 0, lc);
        // No transfer of momeuntum when the hero collides with the trigger
        trigger.setCollisionsEnabled(false);
    }
}

From source file:com.tnf.ptm.common.PtmMath.java

License:Apache License

/**
 * rotates a vector to an angle. if not precise, works faster, but the actual angle might slightly differ from the given one
 *//*from ww w.  j a  v  a2s.c  o m*/
public static void rotate(Vector2 v, float angle, boolean precise) {
    if (precise) {
        v.rotate(angle);
    } else {
        float cos = cos(angle);
        float sin = sin(angle);
        float newX = v.x * cos - v.y * sin;
        float newY = v.x * sin + v.y * cos;
        v.x = newX;
        v.y = newY;
    }
}

From source file:de.hasait.tanks.app.common.model.AbstractMovableGameObject.java

License:Apache License

private void updateMoveVector(final S pState) {
    final Vector2 newMoveVector = new Vector2();
    newMoveVector.x = 0;//from   w w  w .j a  v  a  2  s.c o  m
    newMoveVector.y = 1;
    newMoveVector.rotate(pState._rotation);
    _moveVectorHolder.set(newMoveVector);
}

From source file:es.eucm.ead.editor.view.widgets.groupeditor.Modifier.java

License:Open Source License

/**
 * Applies the current transformation represented by the handles to given
 * actor//ww w  .j a  v  a  2 s. co m
 */
public void applyTransformation(Actor influencedActor, Vector2 origin, Vector2 tangent, Vector2 normal) {
    /*
     * We are going to calculate the affine transformation for the actor to
     * fit the bounds represented by the handles. The affine transformation
     * is defined as follows:
     */
    // |a b tx|
    // |c d ty|=|Translation Matrix| x |Scale Matrix| x |Rotation
    // Matrix|
    // |0 0 1 |
    /*
     * More info about affine transformations:
     * https://people.gnome.org/~mathieu
     * /libart/libart-affine-transformation-matrices.html, To obtain the
     * matrix, we want to resolve the following equation system:
     */
    // | a b tx| |0| |o.x|
    // | c d ty|*|0|=|o.y|
    // | 0 0 1 | |1| | 1 |
    //
    // | a b tx| |w| |t.x|
    // | c d ty|*|0|=|t.y|
    // | 0 0 1 | |1| | 1 |
    //
    // | a b tx| |0| |n.x|
    // | c d ty|*|h|=|n.y|
    // | 0 0 1 | |1| | 1 |
    /*
     * where o is handles[0] (origin), t is handles[2] (tangent) and n is
     * handles[6] (normal), w is actor.getWidth() and h is
     * actor.getHeight().
     * 
     * This matrix defines that the 3 points defining actor bounds are
     * transformed to the 3 points defining modifier bounds. E.g., we want
     * that actor origin (0,0) is transformed to (handles[0].x,
     * handles[0].y), and that is expressed in the first equation.
     * 
     * Resolving these equations is obtained:
     */
    // a = (t.x - o.y) / w
    // b = (t.y - o.y) / w
    // c = (n.x - o.x) / h
    // d = (n.y - o.y) / h
    /*
     * Values for translation, scale and rotation contained by the matrix
     * can be obtained directly making operations over a, b, c and d:
     */
    // tx = o.x
    // ty = o.y
    // sx = sqrt(a^2+b^2)
    // sy = sqrt(c^2+d^2)
    // rotation = atan(c/d)
    // or
    // rotation = atan(-b/a)
    /*
     * Rotation can give two different values (this happens when there is
     * more than one way of obtaining the same transformation). To avoid
     * that, we ignore the rotation to obtain the final values.
     */

    Vector2 o = tmp1.set(origin.x, origin.y);
    Vector2 t = tmp2.set(tangent.x, tangent.y);
    Vector2 n = tmp3.set(normal.x, normal.y);

    Vector2 vt = tmp4.set(t).sub(o);
    Vector2 vn = tmp5.set(n).sub(o);

    // Ignore rotation
    float rotation = influencedActor.getRotation();
    vt.rotate(-rotation);
    vn.rotate(-rotation);

    t.set(vt).add(o);
    n.set(vn).add(o);

    float a = (t.x - o.x) / influencedActor.getWidth();
    float c = (t.y - o.y) / influencedActor.getWidth();
    float b = (n.x - o.x) / influencedActor.getHeight();
    float d = (n.y - o.y) / influencedActor.getHeight();

    // Math.sqrt gives a positive value, but it also have a negatives.
    // The
    // signum is calculated computing the current rotation
    float signumX = vt.angle() > 90.0f && vt.angle() < 270.0f ? -1.0f : 1.0f;
    float signumY = vn.angle() > 180.0f ? -1.0f : 1.0f;

    float scaleX = (float) Math.sqrt(a * a + b * b) * signumX;
    float scaleY = (float) Math.sqrt(c * c + d * d) * signumY;

    influencedActor.setScale(scaleX, scaleY);

    /*
     * To obtain the correct translation value we need to subtract the
     * amount of translation due to the origin.
     */
    tmpMatrix.setToTranslation(influencedActor.getOriginX(), influencedActor.getOriginY());
    tmpMatrix.rotate(influencedActor.getRotation());
    tmpMatrix.scale(influencedActor.getScaleX(), influencedActor.getScaleY());
    tmpMatrix.translate(-influencedActor.getOriginX(), -influencedActor.getOriginY());

    /*
     * Now, the matrix has how much translation is due to the origin
     * involved in the rotation and scaling operations
     */
    float x = o.x - tmpMatrix.getValues()[Matrix3.M02];
    float y = o.y - tmpMatrix.getValues()[Matrix3.M12];
    influencedActor.setPosition(x, y);
}

From source file:es.eucm.ead.engine.utils.EngineUtils.java

License:Open Source License

/**
 * Sets position, rotation, scale and origin in actor to meet the 3 given
 * points/*w ww  .  jav a 2 s  . co  m*/
 */
public static void applyTransformation(Actor actor, Vector2 origin, Vector2 tangent, Vector2 normal) {
    /*
     * We are going to calculate the affine transformation for the actor to
     * fit the bounds represented by the handles. The affine transformation
     * is defined as follows:
     */
    // |a b tx|
    // |c d ty|=|Translation Matrix| x |Scale Matrix| x |Rotation
    // Matrix|
    // |0 0 1 |
    /*
     * More info about affine transformations:
     * https://people.gnome.org/~mathieu
     * /libart/libart-affine-transformation-matrices.html, To obtain the
     * matrix, we want to resolve the following equation system:
     */
    // | a b tx| |0| |o.x|
    // | c d ty|*|0|=|o.y|
    // | 0 0 1 | |1| | 1 |
    //
    // | a b tx| |w| |t.x|
    // | c d ty|*|0|=|t.y|
    // | 0 0 1 | |1| | 1 |
    //
    // | a b tx| |0| |n.x|
    // | c d ty|*|h|=|n.y|
    // | 0 0 1 | |1| | 1 |
    /*
     * where o is handles[0] (origin), t is handles[2] (tangent) and n is
     * handles[6] (normal), w is actor.getWidth() and h is
     * actor.getHeight().
     * 
     * This matrix defines that the 3 points defining actor bounds are
     * transformed to the 3 points defining modifier bounds. E.g., we want
     * that actor origin (0,0) is transformed to (handles[0].x,
     * handles[0].y), and that is expressed in the first equation.
     * 
     * Resolving these equations is obtained:
     */
    // a = (t.x - o.y) / w
    // b = (t.y - o.y) / w
    // c = (n.x - o.x) / h
    // d = (n.y - o.y) / h
    /*
     * Values for translation, scale and rotation contained by the matrix
     * can be obtained directly making operations over a, b, c and d:
     */
    // tx = o.x
    // ty = o.y
    // sx = sqrt(a^2+b^2)
    // sy = sqrt(c^2+d^2)
    // rotation = atan(c/d)
    // or
    // rotation = atan(-b/a)
    /*
     * Rotation can give two different values (this happens when there is
     * more than one way of obtaining the same transformation). To avoid
     * that, we ignore the rotation to obtain the final values.
     */

    Vector2 tmp1 = Pools.obtain(Vector2.class);
    Vector2 tmp2 = Pools.obtain(Vector2.class);
    Vector2 tmp3 = Pools.obtain(Vector2.class);
    Vector2 tmp4 = Pools.obtain(Vector2.class);
    Vector2 tmp5 = Pools.obtain(Vector2.class);

    Vector2 o = tmp1.set(origin.x, origin.y);
    Vector2 t = tmp2.set(tangent.x, tangent.y);
    Vector2 n = tmp3.set(normal.x, normal.y);

    Vector2 vt = tmp4.set(t).sub(o);
    Vector2 vn = tmp5.set(n).sub(o);

    // Ignore rotation
    float rotation = actor.getRotation();
    vt.rotate(-rotation);
    vn.rotate(-rotation);

    t.set(vt).add(o);
    n.set(vn).add(o);

    Vector2 bottomLeft = Pools.obtain(Vector2.class);
    Vector2 size = Pools.obtain(Vector2.class);

    calculateBounds(actor, bottomLeft, size);

    float a = (t.x - o.x) / size.x;
    float c = (t.y - o.y) / size.x;
    float b = (n.x - o.x) / size.y;
    float d = (n.y - o.y) / size.y;

    Pools.free(tmp1);
    Pools.free(tmp2);
    Pools.free(tmp3);
    Pools.free(tmp4);
    Pools.free(tmp5);
    Pools.free(bottomLeft);
    Pools.free(size);

    // Math.sqrt gives a positive value, but it also have a negatives.
    // The
    // signum is calculated computing the current rotation
    float signumX = vt.angle() > 90.0f && vt.angle() < 270.0f ? -1.0f : 1.0f;
    float signumY = vn.angle() > 180.0f ? -1.0f : 1.0f;

    float scaleX = (float) Math.sqrt(a * a + b * b) * signumX;
    float scaleY = (float) Math.sqrt(c * c + d * d) * signumY;

    actor.setScale(scaleX, scaleY);

    /*
     * To obtain the correct translation value we need to subtract the
     * amount of translation due to the origin.
     */
    tmpMatrix.setToTranslation(actor.getOriginX(), actor.getOriginY());
    tmpMatrix.rotate(actor.getRotation());
    tmpMatrix.scale(actor.getScaleX(), actor.getScaleY());
    tmpMatrix.translate(-actor.getOriginX(), -actor.getOriginY());

    /*
     * Now, the matrix has how much translation is due to the origin
     * involved in the rotation and scaling operations
     */
    float x = o.x - tmpMatrix.getValues()[Matrix3.M02];
    float y = o.y - tmpMatrix.getValues()[Matrix3.M12];
    actor.setPosition(x, y);
}