content: package rabbitescape.engine.behaviours;
  
  import static rabbitescape.engine.ChangeDescription.State.*;
  import static rabbitescape.engine.Direction.*;
  import static rabbitescape.engine.Token.Type.*;
  
  import java.util.Map;
  
  import rabbitescape.engine.*;
  import rabbitescape.engine.ChangeDescription.State;
  
  public class Climbing extends Behaviour
  {
      boolean hasAbility = false;
      public boolean abilityActive = false;
  
      @Override
      public void cancel()
      {
          abilityActive = false;
      }
  
      @Override
      public boolean checkTriggered( Rabbit rabbit, World world )
      {
          BehaviourTools t = new BehaviourTools( rabbit, world );
  
          return !hasAbility && t.pickUpToken( climb, true );
      }
  
      @Override
      public State newState( BehaviourTools t, boolean triggered )
      {
          if ( triggered )
          {
              hasAbility = true;
          }
  
          if ( !hasAbility )
          {
              return null;
          }
  
          switch ( t.rabbit.state )
          {
              case RABBIT_CLIMBING_RIGHT_START:
              case RABBIT_CLIMBING_LEFT_START:
                  return newStateStart( t );
              case RABBIT_CLIMBING_RIGHT_CONTINUE_1:
              case RABBIT_CLIMBING_LEFT_CONTINUE_1:
                  return newStateCont1( t );
              case RABBIT_CLIMBING_RIGHT_CONTINUE_2:
              case RABBIT_CLIMBING_LEFT_CONTINUE_2:
                  return newStateCont2( t );
              default:
                  return newStateNotClimbing( t );
          }
      }
  
      private State newStateStart( BehaviourTools t )
      {
          Block endBlock = t.blockAboveNext();
  
          if ( t.isWall( endBlock ) )
          {
              return t.rl(
                  RABBIT_CLIMBING_RIGHT_CONTINUE_2,
                  RABBIT_CLIMBING_LEFT_CONTINUE_2
              );
          }
          else
          {
              return t.rl(
                  RABBIT_CLIMBING_RIGHT_END,
                  RABBIT_CLIMBING_LEFT_END
              );
          }
      }
  
      private State newStateCont1( BehaviourTools t )
      {
          return t.rl(
              RABBIT_CLIMBING_RIGHT_CONTINUE_2,
              RABBIT_CLIMBING_LEFT_CONTINUE_2
          );
      }
  
      private State newStateCont2( BehaviourTools t )
      {
          Block aboveBlock = t.blockAbove();
  
          if ( t.isRoof( aboveBlock ) )
          {
              abilityActive = false;
              return t.rl(
                  RABBIT_CLIMBING_RIGHT_BANG_HEAD,
                  RABBIT_CLIMBING_LEFT_BANG_HEAD
              );
          }
  
          Block endBlock = t.blockAboveNext();
  
          if ( t.isWall( endBlock ) )
          {
              return t.rl(
                  RABBIT_CLIMBING_RIGHT_CONTINUE_1,
                  RABBIT_CLIMBING_LEFT_CONTINUE_1
              );
          }
          else
          {
              return t.rl(
                  RABBIT_CLIMBING_RIGHT_END,
                  RABBIT_CLIMBING_LEFT_END
              );
          }
      }
  
      private State newStateNotClimbing( BehaviourTools t )
      {
          int nextX = t.nextX();
          int nextY = t.nextY();
          Block nextBlock = t.world.getBlockAt( nextX, nextY );
          Block aboveBlock = t.world.getBlockAt( t.rabbit.x, t.rabbit.y - 1 );
  
          if ( !t.isRoof( aboveBlock ) && t.isWall( nextBlock ) )
          {
              return t.rl(
                  RABBIT_CLIMBING_RIGHT_START,
                  RABBIT_CLIMBING_LEFT_START
              );
          }
  
          return null;
      }
  
      @Override
      public boolean behave( World world, Rabbit rabbit, State state )
      {
          BehaviourTools t = new BehaviourTools( rabbit, world );
  
          if( t.rabbitIsClimbing() )
          { // Can't be both on a wall and on a slope.
              rabbit.onSlope = false;
          }
  
          switch ( state )
          {
              case RABBIT_CLIMBING_RIGHT_START:
              case RABBIT_CLIMBING_LEFT_START:
              {
                  abilityActive = true;
                  return true;
              }
              case RABBIT_CLIMBING_RIGHT_END:
              case RABBIT_CLIMBING_LEFT_END:
              {
                  rabbit.x = t.nextX();
                  --rabbit.y;
                  if ( t.hereIsUpSlope() )
                  {
                      rabbit.onSlope = true;
                  }
                  abilityActive = false;
                  return true;
              }
              case RABBIT_CLIMBING_RIGHT_CONTINUE_1:
              case RABBIT_CLIMBING_LEFT_CONTINUE_1:
              {
                  abilityActive = true;
                  return true;
              }
              case RABBIT_CLIMBING_RIGHT_CONTINUE_2:
              case RABBIT_CLIMBING_LEFT_CONTINUE_2:
              {
                  abilityActive = true;
                  --rabbit.y;
                  return true;
              }
              case RABBIT_CLIMBING_RIGHT_BANG_HEAD:
              case RABBIT_CLIMBING_LEFT_BANG_HEAD:
              {
                  rabbit.dir = opposite( rabbit.dir );
                  return true;
              }
              default:
              {
                  return false;
              }
          }
      }
  
      @Override
      public void saveState( Map<String, String> saveState )
      {
          BehaviourState.addToStateIfTrue(
              saveState, "Climbing.hasAbility", hasAbility
          );
  
          BehaviourState.addToStateIfTrue(
              saveState, "Climbing.abilityActive", abilityActive
          );
      }
  
      @Override
      public void restoreFromState( Map<String, String> saveState )
      {
          hasAbility = BehaviourState.restoreFromState(
              saveState, "Climbing.hasAbility", hasAbility
          );
  
          abilityActive = BehaviourState.restoreFromState(
              saveState, "Climbing.abilityActive", abilityActive
          );
      }
  }
  