content: package rabbitescape.engine.behaviours;
  
  import static rabbitescape.engine.ChangeDescription.State.*;
  import static rabbitescape.engine.Direction.*;
  import static rabbitescape.engine.Block.Shape.*;
  
  import rabbitescape.engine.*;
  import rabbitescape.engine.ChangeDescription.State;
  
  public class Walking extends Behaviour
  {
      @Override
      public void cancel()
      {
      }
  
      private static class StateCalc
      {
          private final BehaviourTools t;
  
          StateCalc( BehaviourTools t )
          {
              this.t = t;
          }
  
          public State newState()
          {
              if ( t.isOnUpSlope() )
              {
                  Block aboveNext = t.blockAboveNext();
                  Block above = t.blockAbove();
                  int nextX = t.nextX();
                  int nextY = t.rabbit.y - 1;
  
                  if
                      (
                         t.isWall( aboveNext )
                      || Blocking.blockerAt( t.world, nextX, nextY )
                      || t.isRoof( above )
                      || ( t.isCresting() &&
                           Blocking.blockerAt( t.world, nextX, t.rabbit.y ) )
                      )
                  {
                      return rl(
                          RABBIT_TURNING_RIGHT_TO_LEFT_RISING,
                          RABBIT_TURNING_LEFT_TO_RIGHT_RISING
                      );
                  }
                  else if ( t.isUpSlope( aboveNext ) )
                  {
                      return rl(
                          RABBIT_RISING_RIGHT_CONTINUE,
                          RABBIT_RISING_LEFT_CONTINUE
                      );
                  }
                  else if ( t.isDownSlope( t.blockNext() ) )
                  {
                      return rl(
                          RABBIT_RISING_AND_LOWERING_RIGHT,
                          RABBIT_RISING_AND_LOWERING_LEFT
                      );
                  }
                  else
                  {
                      return rl(
                          RABBIT_RISING_RIGHT_END,
                          RABBIT_RISING_LEFT_END
                      );
                  }
              }
              else if ( t.isOnDownSlope() )
              {
                  int nextX = t.nextX();
                  int nextY = t.rabbit.y + 1;
                  Block next = t.blockNext();
                  Block belowNext = t.blockBelowNext();
  
                  if (
                         t.isWall( next )
                      || Blocking.blockerAt( t.world, nextX, nextY )
                      || ( t.isValleying() &&
                           Blocking.blockerAt( t.world, nextX, t.rabbit.y ) )
                      )
                  {
                      return rl(
                          RABBIT_TURNING_RIGHT_TO_LEFT_LOWERING,
                          RABBIT_TURNING_LEFT_TO_RIGHT_LOWERING
                      );
                  }
                  else if ( t.isUpSlope( next ) )
                  {
                      return rl(
                          RABBIT_LOWERING_AND_RISING_RIGHT,
                          RABBIT_LOWERING_AND_RISING_LEFT
                      );
                  }
                  else if ( t.isDownSlope( belowNext ) )
                  {
                      return rl(
                          RABBIT_LOWERING_RIGHT_CONTINUE,
                          RABBIT_LOWERING_LEFT_CONTINUE
                      );
                  }
                  else
                  {
                      if ( Blocking.blockerAt( t.world, nextX, t.rabbit.y ) )
                      {
                          return rl(
                              RABBIT_TURNING_RIGHT_TO_LEFT_LOWERING,
                              RABBIT_TURNING_LEFT_TO_RIGHT_LOWERING
                          );
                      }
                      else
                      {
                          return rl(
                              RABBIT_LOWERING_RIGHT_END,
                              RABBIT_LOWERING_LEFT_END
                          );
                      }
                  }
              }
              else  // On flat ground now
              {
                  int nextX = t.nextX();
                  int nextY = t.rabbit.y;
                  Block next = t.blockNext();
  
                  if
                      (
                         t.isWall( next )
                      || Blocking.blockerAt( t.world, nextX, nextY )
                      )
                  {
                      return rl(
                          RABBIT_TURNING_RIGHT_TO_LEFT,
                          RABBIT_TURNING_LEFT_TO_RIGHT
                      );
                  }
                  else if ( t.isUpSlope( next ) )
                  {
                      return rl(
                          RABBIT_RISING_RIGHT_START,
                          RABBIT_RISING_LEFT_START
                      );
                  }
                  else if ( t.isDownSlope( t.blockBelowNext() ) )
                  {
                      if ( Blocking.blockerAt( t.world, nextX, t.rabbit.y + 1 ) )
                      {
                          return rl(
                              RABBIT_TURNING_RIGHT_TO_LEFT,
                              RABBIT_TURNING_LEFT_TO_RIGHT
                          );
                      }
                      else
                      {
                          return rl(
                              RABBIT_LOWERING_RIGHT_START,
                              RABBIT_LOWERING_LEFT_START
                          );
                      }
                  }
                  else
                  {
                      return rl(
                          RABBIT_WALKING_RIGHT,
                          RABBIT_WALKING_LEFT
                      );
                  }
              }
          }
  
          private State rl( State rightState, State leftState )
          {
              return t.rl( rightState, leftState );
          }
      }
  
      @Override
      public boolean checkTriggered( Rabbit rabbit, World world )
      {
          return false; // To avoid cancelling other behaviours, return false
      }
  
      @Override
      public State newState( BehaviourTools t, boolean triggered )
      {
          return new StateCalc( t ).newState();
      }
  
      @Override
      @SuppressWarnings("fallthrough")
      public boolean behave( World world, Rabbit rabbit, State state )
      {
          switch ( state )
          {
              case RABBIT_WALKING_LEFT:
              {
                  --rabbit.x;
                  rabbit.onSlope = false;
                  return true;
              }
              case RABBIT_WALKING_RIGHT:
              {
                  ++rabbit.x;
                  rabbit.onSlope = false;
                  return true;
              }
              case RABBIT_LOWERING_LEFT_END:
              {
                  --rabbit.x;
                  rabbit.onSlope = false;
                  return true;
              }
              case RABBIT_RISING_LEFT_START:
              case RABBIT_LOWERING_AND_RISING_LEFT:
              case RABBIT_RISING_AND_LOWERING_LEFT:
              {
                  --rabbit.x;
                  rabbit.onSlope = true;
                  return true;
              }
              case RABBIT_LOWERING_RIGHT_END:
              {
                  ++rabbit.x;
                  rabbit.onSlope = false;
                  return true;
              }
              case RABBIT_RISING_RIGHT_START:
              case RABBIT_LOWERING_AND_RISING_RIGHT:
              case RABBIT_RISING_AND_LOWERING_RIGHT:
              {
                  ++rabbit.x;
                  rabbit.onSlope = true;
                  return true;
              }
              case RABBIT_RISING_LEFT_END:
              {
                  --rabbit.y;
                  --rabbit.x;
                  rabbit.onSlope = false;
                  return true;
              }
              case RABBIT_RISING_LEFT_CONTINUE:
              {
                  --rabbit.y;
                  --rabbit.x;
                  rabbit.onSlope = true;
                  return true;
              }
              case RABBIT_RISING_RIGHT_END:
              {
                  --rabbit.y;
                  ++rabbit.x;
                  rabbit.onSlope = false;
                  return true;
              }
              case RABBIT_RISING_RIGHT_CONTINUE:
              {
                  --rabbit.y;
                  ++rabbit.x;
                  rabbit.onSlope = true;
                  return true;
              }
              case RABBIT_LOWERING_LEFT_CONTINUE:
              case RABBIT_LOWERING_LEFT_START:
              {
                  ++rabbit.y;
                  --rabbit.x;
                  rabbit.onSlope = true;
                  return true;
              }
              case RABBIT_LOWERING_RIGHT_CONTINUE:
              case RABBIT_LOWERING_RIGHT_START:
              {
                  ++rabbit.y;
                  ++rabbit.x;
                  rabbit.onSlope = true;
                  return true;
              }
              case RABBIT_TURNING_LEFT_TO_RIGHT:
                  rabbit.onSlope = false; // Intentional fall-through
              case RABBIT_TURNING_LEFT_TO_RIGHT_RISING:
              case RABBIT_TURNING_LEFT_TO_RIGHT_LOWERING:
              {
                  rabbit.dir = RIGHT;
                  checkJumpOntoSlope( world, rabbit );
                  return true;
              }
              case RABBIT_TURNING_RIGHT_TO_LEFT:
                  rabbit.onSlope = false; // Intentional fall-through
              case RABBIT_TURNING_RIGHT_TO_LEFT_RISING:
              case RABBIT_TURNING_RIGHT_TO_LEFT_LOWERING:
              {
                  rabbit.dir = LEFT;
                  checkJumpOntoSlope( world, rabbit );
                  return true;
              }
              default:
              {
                  throw new AssertionError(
                      "Should have handled all states in Walking or before,"
                      + " but we are in state " + state.name()
                  );
              }
          }
      }
  
      /**
       * If we turn around near a slope, we jump onto it
       */
      private void checkJumpOntoSlope( World world, Rabbit rabbit )
      {
          Block thisBlock = world.getBlockAt( rabbit.x, rabbit.y );
          if ( isBridge( thisBlock ) )
          {
              Block aboveBlock = world.getBlockAt( rabbit.x, rabbit.y - 1 );
              if ( rabbit.onSlope && isBridge( aboveBlock ) )
              {
                  rabbit.y--;
              }
              else
              {
                  rabbit.onSlope = true;
              }
          }
      }
  
      private boolean isBridge( Block block )
      {
          return (
                 block != null
              && (
                     block.shape == BRIDGE_UP_LEFT
                  || block.shape == BRIDGE_UP_RIGHT
              )
          );
      }
  }
  