/* Domino-Chain
 *
 * Domino-Chain is the legal property of its developers, whose
 * names are listed in the AUTHORS file, which is included
 * within the source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
 */

#include "figure.h"

#include "soundsys.h"
#include "screen.h"

#include <assert.h>



// the following defines are used for the keymask
#define KEY_LEFT 1
#define KEY_UP 2
#define KEY_RIGHT 4
#define KEY_DOWN 8
#define KEY_ACTION 16

// the statemashine for the figure works as follows:
// - detect what the figure has to do next
// - play the complete animation resuling maybe in a new position or somethin else
// - repeat


// all the figure animations do have a certain fixed number of images
const unsigned char numFigureAnimationsImages[FigureAnimNothing] = {

 6,        // FigureAnimWalkLeft,
 6,        // FigureAnimWalkRight,
 6,        // FigureAnimJunpUpLeft,
 6,        // FigureAnimJunpUpRight,
 4,        // FigureAnimJunpDownLeft,
 4,        // FigureAnimJunpDownRight,
 8,        // FigureAnimLadder1,
 8,        // FigureAnimLadder2,
 8,        // FigureAnimLadder3,
 8,        // FigureAnimLadder4,
 6,        // FigureAnimCarryLeft,
 6,        // FigureAnimCarryRight,
 6,        // FigureAnimCarryUpLeft,
 6,        // FigureAnimCarryUpRight,
 6,        // FigureAnimCarryDownLeft,
 6,        // FigureAnimCarryDownRight,
 8,        // FigureAnimCarryLadder1,
 8,        // FigureAnimCarryLadder2,
 8,        // FigureAnimCarryLadder3,
 8,        // FigureAnimCarryLadder4,
 1,        // FigureAnimCarryStopLeft,
 1,        // FigureAnimCarryStopRight,
 15,       // FigureAnimPullOutLeft,
 15,       // FigureAnimPullOutRight,
 16,       // FigureAnimPushInLeft,
 16,       // FigureAnimPushInRight,
 2,        // FigureAnimXXX1,
 2,        // FigureAnimXXX2,
 2,        // FigureAnimXXX3,
 2,        // FigureAnimXXX4,
 13,       // FigureAnimLoosingDominoRight,
 17,       // FigureAnimLoosingDominoLeft,
 0,        // FigureAnimStepOutForLoosingDomino,
 1,        // FigureAnimStop,
 2,        // FigureAnimTapping,
 6,        // FigureAnimYawning,
 7,        // FigureAnimEnterLeft,
 7,        // FigureAnimEnterRight,
 12,       // FigureAnimPushLeft,
 12,       // FigureAnimPushRight,
 8,        // FigureAnimPushStopperLeft,
 8,        // FigureAnimPushStopperRight,
 4,        // FigureAnimPushRiserLeft,
 4,        // FigureAnimPushRiserRight,
 8,        // FigureAnimPushDelayLeft,
 8,        // FigureAnimPushDelayRight,
 6,        // FigureAnimSuddenFallRight,
 6,        // FigureAnimSuddenFallLeft,
 2,        // FigureAnimFalling,
 3,        // FigureAnimInFrontOfExploder,
 1,        // FigureAnimInFrontOfExploderWait,
 15,       // FigureAnimLanding,
 2,        // FigureAnimGhost1,
 4,        // FigureAnimGhost2,
 7,        // FigureAnimLeaveDoorEnterLevel,
 3,        // FigureAnimStepAsideAfterEnter,
 7,        // FigureAnimEnterDoor,
 4,        // FigureAnimStepBackForDoor,
 11,       // FigureAnimStruggingAgainsFallLeft,
 11,       // FigureAnimStruggingAgainsFallRight,
 8,        // FigureAnimVictory,
 8,        // FigureAnimShrugging,
 8,        // FigureAnimNoNo,
 1,        // FigureAnimXXXA,
 1,        // FigureAnimDominoDying,
 13        // FigureAnimLandDying,

};


uint16_t figure_c::getFigureImages(FigureAnimationState figure)
{
  assert(figure < FigureAnimNothing);

  return numFigureAnimationsImages[figure];
}


// Initialisation
figure_c::figure_c(levelPlayer_c & level): level(level) {

  initForLevel();
}

void figure_c::initForLevel(void) {
  // figure invisible, outside the screen
  blockX = blockY = 200;

  state = animation = FigureAnimLeaveDoorEnterLevel;
  animationImage = 0;
  animationTimer = 0;
  carriedDomino = DominoTypeEmpty;
  inactiveTimer = 0;
  numPushsLeft = 1;
  fallingHight = 0;
  direction = 1;
  pushDelay = 0;
  downChecker = false;
  triggerNotLast = false;

  finalAnimationPlayed = levelFail = levelSuccess = false;
}

// do one animation step for the level
LevelState figure_c::performAnimation(unsigned int keyMask)
{
  // first do the doors
  level.performDoors();

  // first check, if the any has no active action, if not try to find one
  if (state == FigureAnimNothing) {
    state = callStateFunction(state, keyMask);
  }

  // do the action animation
  if (state != FigureAnimNothing) {
    state = callStateFunction(state, keyMask);
  }

  // the trigger must be laying properly before
  // and after the domino update (well or it must
  // have vanished
  bool trigger = level.triggerIsFalln();

  // now the level update for all the dominoes
  level.performDominoes();

  trigger = trigger ? level.triggerIsFalln() : false;

  if (trigger && !level.dominoesFalln())
    triggerNotLast = true;

  // check, whether we open the exit door we can close it
  // again, when something unexpected happens
  if (   trigger
      && !level.rubblePile()
      && !level.dominoesStanding()
      && isVisible()
      && !triggerNotLast
      && (carriedDomino == DominoTypeEmpty || carriedDomino == DominoTypeStopper)
     )
  {
    level.openExitDoor(true);
  }
  else
  {
    level.openExitDoor(false);
  }

  // check, if we want to play the jubilation or shrugging animation
  if (trigger)
  {
    if (!level.rubblePile() && level.dominoesFalln() && carriedDomino == DominoTypeEmpty && isLiving() && !triggerNotLast)
    {
      levelSuccess = true;
    }
    else
    {
      levelFail = true;
    }
  }

  // if level is inactive for a longer time and no pushes are left
  if (numPushsLeft == 0 && level.levelLongInactive())
  {
    // if the trigger has been toppled but it is not completely flat,
    // the level failed because of it
    //
    // on th eother hand even if we don't have any more pushes it might
    // still happen that we can solve the leven, when the tigger is stll
    // upright
    if (level.triggerNotFlat())
      return LS_triggerNotFlat;

    if (level.rubblePile())
      return LS_crashes;

    if (trigger)
    {
      if (carriedDomino != DominoTypeEmpty && carriedDomino != DominoTypeStopper)
        return LS_carrying;

      if (level.dominoesStanding())
        return LS_someLeft;

      if (triggerNotLast)
        return LS_triggerNotLast;
    }
  }

  if (!isLiving())
  {
    return LS_died;
  }

  if (trigger && !isVisible() && level.isExitDoorClosed())
  {
    // level is solved and figure left it
    if (level.someTimeLeft())
    {
      return LS_solved;
    }
    else
    {
      return LS_solvedTime;
    }
  }

  return LS_undecided;
}

FigureAnimationState figure_c::callStateFunction(unsigned int state, unsigned int keyMask) {

  switch (state) {

    // 0
    case FigureAnimWalkLeft:                  return SFWalkLeft();
    case FigureAnimWalkRight:                 return SFWalkRight();
    case FigureAnimJunpUpLeft:                return SFJumpUpLeft();
    case FigureAnimJunpUpRight:               return SFJumpUpRight();
    case FigureAnimJunpDownLeft:              return SFJumpDownLeft();
    case FigureAnimJunpDownRight:             return SFJumpDownRight();
    case FigureAnimLadder1:                   return SFLadder1();
    case FigureAnimLadder2:                   return SFLadder2();
    case FigureAnimLadder3:                   return SFLadder3();
    case FigureAnimLadder4:                   return SFLadder3();
    case FigureAnimCarryLeft:                 return SFWalkLeft();
    case FigureAnimCarryRight:                return SFWalkRight();
    // 12
    case FigureAnimCarryUpLeft:               return SFJumpUpLeft();
    case FigureAnimCarryUpRight:              return SFJumpUpRight();
    case FigureAnimCarryDownLeft:             return SFJumpDownLeft();
    case FigureAnimCarryDownRight:            return SFJumpDownRight();
    case FigureAnimCarryLadder1:              return SFLadder1();
    case FigureAnimCarryLadder2:              return SFLadder2();
    case FigureAnimCarryLadder3:              return SFLadder3();
    case FigureAnimCarryLadder4:              return SFLadder3();
    case FigureAnimCarryStopLeft:             return SFInactive();
    case FigureAnimCarryStopRight:            return SFInactive();
    case FigureAnimPullOutLeft:               return SFPullOutLeft();
    case FigureAnimPullOutRight:              return SFPullOutRight();
    // 24
    case FigureAnimPushInLeft:                return SFPushInLeft();
    case FigureAnimPushInRight:               return SFPushInRight();
    case FigureAnimXXX1:                      return SFEnterLadder();
    case FigureAnimXXX2:                      return SFEnterLadder();
    case FigureAnimXXX3:                      return SFLeaveLadderRight();
    case FigureAnimXXX4:                      return SFLeaveLadderLeft();
    case FigureAnimLoosingDominoRight:        return SFLooseRight();
    case FigureAnimLoosingDominoLeft:         return SFLooseLeft();
    case FigureAnimStepOutForLoosingDomino:   return SFStepOutForLoosingDomino();
    case FigureAnimStop:                      return SFInactive();
    case FigureAnimTapping:                   return SFLazying();
    case FigureAnimYawning:                   return SFLazying();
    // 36
    case FigureAnimEnterLeft:                 return SFEnterDominoesLeft();
    case FigureAnimEnterRight:                return SFEnterDominoesRight();
    case FigureAnimPushLeft:                  return SFPushLeft();
    case FigureAnimPushRight:                 return SFPushRight();
    case FigureAnimPushStopperLeft:           return SFPushSpecialLeft();
    case FigureAnimPushStopperRight:          return SFPushSpecialRight();
    case FigureAnimPushRiserLeft:             return SFPushSpecialLeft();
    case FigureAnimPushRiserRight:            return SFPushSpecialRight();
    case FigureAnimPushDelayLeft:             return SFPushDelayLeft();
    case FigureAnimPushDelayRight:            return SFPushDelayRight();
    case FigureAnimSuddenFallRight:           return SFStartFallingRight();
    case FigureAnimSuddenFallLeft:            return SFStartFallingLeft();
    // 48
    case FigureAnimFalling:                   return SFFalling();
    case FigureAnimInFrontOfExploder:         return SFInFrontOfExploder();
    case FigureAnimInFrontOfExploderWait:     return SFInactive();
    case FigureAnimLanding:                   return SFLanding();
    case FigureAnimGhost1:                    return SFGhost1();
    case FigureAnimGhost2:                    return SFGhost2();
    case FigureAnimLeaveDoorEnterLevel:       return SFLeaveDoor();
    case FigureAnimStepAsideAfterEnter:       return SFStepAside();
    case FigureAnimEnterDoor:                 return SFEnterDoor();
    case FigureAnimStepBackForDoor:           return SFStepBackForDoor();
    case FigureAnimStruggingAgainsFallLeft:   return SFFlailing();
    case FigureAnimStruggingAgainsFallRight:  return SFFlailing();
    // 60
    case FigureAnimVictory:                   return SFVictory();
    case FigureAnimShrugging:                 return SFShrugging();
    case FigureAnimNoNo:                      return SFNoNo();
    case FigureAnimXXXA:                      return SFNextAction(keyMask);
    case FigureAnimDominoDying:               return SFStruck();
    case FigureAnimLandDying:                 return SFLandDying();
    case FigureAnimNothing:                   return SFNextAction(keyMask);
    default:                                  return FigureAnimNothing;
  }
}

// this function performs the animation by
// increasing the animation step counter when
// the animation delay has passed
//
// when the animation is finished the function returns true
bool figure_c::animateFigure(unsigned int delay) {

  // to conform the the original, we should use
  // animationTimer > 1 here, so the original
  // game is a tiny bit faster for some animations
  //
  // but doing that would destroy a huge set of our
  // recordings, so we don't and live with the tiny
  // difference
  if (animationTimer > 0) {
    animationTimer--;
    return false;
  }

  animationImage++;
  if (animationImage >= getFigureImages(animation)) {
    animationImage = 0;
    animationTimer = 0;
    return true;
  }

  animationTimer = delay;

  return false;
}

// now lot of functions follow that are called when the figure is within
// a certain state. The figure state and the figure animation is nearly always
// the same but it can be different. For example when the current animation
// is finished and we return to the state that decides on the next animation
// only the state changes, the animation stays the same.

// I will not comment all these functions

FigureAnimationState figure_c::SFStruck(void) {
  if (animateFigure(2))
  {
  }

  return animation;
}

FigureAnimationState figure_c::SFNoNo(void) {
  if (animateFigure(3))
    return FigureAnimNothing;

  return animation;
}

FigureAnimationState figure_c::SFShrugging(void) {

  if (animationImage == 1)
    soundSystem_c::instance()->startSound(soundSystem_c::SE_SHRUGGING);

  if (animateFigure(3))
    return FigureAnimNothing;

  return animation;
}

FigureAnimationState figure_c::SFVictory(void) {

  if (animationImage == 1)
    soundSystem_c::instance()->startSound(soundSystem_c::SE_VICTORY);

  if (animateFigure(3))
    return FigureAnimNothing;

  return animation;
}

FigureAnimationState figure_c::SFStepBackForDoor(void) {
  if (animateFigure(0)) {
    animation = FigureAnimEnterDoor;
    blockX--;
  }

  return animation;
}

FigureAnimationState figure_c::SFEnterDoor(void) {

  if (animateFigure(0)) {

    // make figure invisible
    blockX = 200;
    blockY = 200;

    // close door
    level.openExitDoor(false);
  }

  return animation;
}

FigureAnimationState figure_c::SFGhost1(void) {
  if (animateFigure(0))
    animation = FigureAnimGhost2;

  return animation;
}

FigureAnimationState figure_c::SFGhost2(void) {

  if (animateFigure(2)) {
    animationImage = getFigureImages(animation) - 1;
    animationTimer = 2;
  }

  return animation;
}

FigureAnimationState figure_c::SFLandDying(void) {

  if (animationImage == 3)
    soundSystem_c::instance()->startSound(soundSystem_c::SE_FIGURE_LANDING);

  if (animateFigure(2))
    animationImage = 12;

  return animation;
}

// in the delay push functions we wait until the domino finally falls

FigureAnimationState figure_c::SFPushDelayLeft(void) {
  if (animateFigure(5) || level.pushDomino(blockX-1, blockY, -1)) {
    animationImage = 9;
    animation = FigureAnimPushLeft;
  }

  return animation;
}

FigureAnimationState figure_c::SFPushDelayRight(void) {
  if (animateFigure(5) || level.pushDomino(blockX+1, blockY, -1)) {
    animationImage = 9;
    animation = FigureAnimPushRight;
  }

  return animation;
}

// This is the push stopper animation, after a while
// we simply abort the animation and return
FigureAnimationState figure_c::SFPushSpecialLeft(void) {
  if (animateFigure(5)) {
    animationImage = 9;
    animation = FigureAnimPushLeft;
  }

  return animation;
}

FigureAnimationState figure_c::SFPushSpecialRight(void) {
  if (animateFigure(5)) {
    animationImage = 9;
    animation = FigureAnimPushRight;
  }

  return animation;
}

// pushing dominoes functions, when the domino is not falling
// normally the pushDOmino function returns false then
// we branch into another animation

FigureAnimationState figure_c::SFPushLeft(void) {
  if (animationImage == 1) {
    if (!level.pushDomino(blockX-1, blockY, -1)) {
      if (pushDelay == 0) {

        switch(level.getDominoType(blockX-1, blockY)) {
          case DominoTypeStopper:
          case DominoTypeCounter1:
          case DominoTypeCounter2:
          case DominoTypeCounter3:
            pushDelay = 5;
            pushAnimation = FigureAnimPushStopperLeft;
            break;

          case DominoTypeExploder:
            animationImage = 9;
            break;

          case DominoTypeDelay:
            pushDelay = 5;
            pushAnimation = FigureAnimPushDelayLeft;
            break;

          case DominoTypeAscender:
            pushDelay = 2;
            pushAnimation = FigureAnimPushRiserLeft;
            break;

          default:
            assert(0);
            break;
        }
      }
    }
  }

  if (pushDelay) {
    pushDelay--;
    if (pushDelay == 0)
      animation = pushAnimation;

  } else {
    if (animateFigure(0)) {
      blockX--;
      animation = FigureAnimStop;
      return FigureAnimNothing;
    }
  }

  return animation;
}

FigureAnimationState figure_c::SFPushRight(void) {
  if (animationImage == 1) {
    if (!level.pushDomino(blockX+1, blockY, 1)) {
      if (pushDelay == 0) {

        switch(level.getDominoType(blockX+1, blockY)) {
          case DominoTypeStopper:
          case DominoTypeCounter1:
          case DominoTypeCounter2:
          case DominoTypeCounter3:
            pushDelay = 5;
            pushAnimation = FigureAnimPushStopperRight;
            break;

          case DominoTypeExploder:
            animationImage = 9;
            break;

          case DominoTypeDelay:
            pushDelay = 5;
            pushAnimation = FigureAnimPushDelayRight;
            break;

          case DominoTypeAscender:
            pushDelay = 2;
            pushAnimation = FigureAnimPushRiserRight;
            break;

          default:
            assert(0);
            break;
        }
      }
    }
  }

  if (pushDelay) {
    pushDelay--;
    if (pushDelay == 0)
      animation = pushAnimation;

  } else {
    if (animateFigure(0)) {
      blockX++;
      animation = FigureAnimStop;
      return FigureAnimNothing;
    }
  }

  return animation;
}

FigureAnimationState figure_c::SFEnterDominoesLeft(void) {
  if (!animateFigure(0))
    return animation;

  animation = FigureAnimPushLeft;

  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFEnterDominoesRight(void) {
  if (!animateFigure(0))
    return animation;

  animation = FigureAnimPushRight;

  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFStepOutForLoosingDomino(void) {
  if (animateFigure(0))
  {
    animation = FigureAnimLoosingDominoRight;
    return animation;
  }
  return FigureAnimStepOutForLoosingDomino;
}

FigureAnimationState figure_c::SFLooseRight(void) {
  if (animateFigure(0)) {
    // start falling, we fall 2 blocks at the start as if there is a block one step below us
    // we don't fall but rather jump down
    animation = FigureAnimFalling;
    blockY += 2;
    if ((size_t)(blockY+1) > level.levelY()) blockY = level.levelY()-1;
    fallingHight++;
    return FigureAnimNothing;
  }

  if (animationImage == 4) {
    level.putDownDomino(blockX+1, blockY, carriedDomino, false);
    level.fallingDomino(blockX+1, blockY);
    carriedDomino = DominoTypeEmpty;
  } else if (animationImage == 6) {
    blockX++;
  } else if (animationImage == 10) {
    soundSystem_c::instance()->startSound(soundSystem_c::SE_FIGURE_FALLING);
  }

  return animation;
}

FigureAnimationState figure_c::SFLooseLeft(void) {
  if (animateFigure(0)) {
    animation = FigureAnimFalling;
    blockY += 2;
    if ((size_t)(blockY+1) > level.levelY()) blockY = level.levelY()-1;
    fallingHight++;
    return FigureAnimNothing;
  }

  if (animationImage == 4) {
    blockX--;
  } else if (animationImage == 8) {
    level.putDownDomino(blockX, blockY, carriedDomino, false);
    level.fallingDomino(blockX, blockY);
    carriedDomino = DominoTypeEmpty;
  } else if (animationImage == 14) {
    soundSystem_c::instance()->startSound(soundSystem_c::SE_FIGURE_FALLING);
  }

  return animation;
}

FigureAnimationState figure_c::SFLeaveLadderRight(void) {

  if (!animateFigure(0))
    return animation;

  animation = FigureAnimCarryRight;
  return animation;

}

FigureAnimationState figure_c::SFLeaveLadderLeft(void) {

  if (!animateFigure(0))
    return animation;

  animation = FigureAnimCarryLeft;
  return animation;
}

FigureAnimationState figure_c::SFEnterLadder(void) {

  if (!animateFigure(0))
    return animation;

  if (direction == -20) {
    animation = FigureAnimCarryLadder1;

  } else {
    animation = FigureAnimCarryLadder4;
  }

  return animation;
}

FigureAnimationState figure_c::SFPushInLeft(void) {

  if (!animateFigure(0))
    return animation;

  blockX--;
  animation = FigureAnimWalkLeft;
  level.putDownDomino(blockX, blockY, carriedDomino, true);
  carriedDomino = DominoTypeEmpty;

  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFPushInRight(void) {
  if (!animateFigure(0))
    return animation;

  blockX++;
  animation = FigureAnimWalkRight;
  level.putDownDomino(blockX, blockY, carriedDomino, true);
  carriedDomino = DominoTypeEmpty;

  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFPullOutLeft(void) {

  if (animationImage == 3)
    soundSystem_c::instance()->startSound(soundSystem_c::SE_PICK_UP_DOMINO);

  if (!animateFigure(0))
    return animation;

  animation = FigureAnimCarryLeft;
  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFPullOutRight(void) {

  if (animationImage == 3)
    soundSystem_c::instance()->startSound(soundSystem_c::SE_PICK_UP_DOMINO);

  if (!animateFigure(0))
    return animation;

  animation = FigureAnimCarryRight;
  return FigureAnimNothing;

}

FigureAnimationState figure_c::SFLadder1(void) {

  if (!animateFigure(0))
    return animation;

  blockY -= 2;
  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFLadder2(void) {

  if (!animateFigure(0))
    return animation;

  if (carriedDomino != DominoTypeEmpty)
  {
    animation = FigureAnimCarryLadder4;
  } else {
    animation = FigureAnimLadder4;
  }

  blockY -= 2;
  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFLadder3(void) {

  if (!animateFigure(0))
    return animation;

  blockY += 2;
  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFWalkLeft(void) {

  if (animateFigure(0)) {
    blockX--;
    return FigureAnimNothing;
  }

  return animation;
}

FigureAnimationState figure_c::SFWalkRight(void) {

  if (animateFigure(0)) {
    blockX++;
    return FigureAnimNothing;
  }

  return animation;
}

FigureAnimationState figure_c::SFJumpUpLeft(void) {
  if (!animateFigure(0)) {
    return animation;
  }

  if (carriedDomino != DominoTypeEmpty) {
    animation = FigureAnimCarryStopLeft;
  } else {
    animation = FigureAnimWalkLeft;
  }

  blockX--;
  blockY--;

  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFJumpUpRight(void) {
  if (!animateFigure(0)) {
    return animation;
  }

  if (carriedDomino != DominoTypeEmpty) {
    animation = FigureAnimCarryStopRight;
  } else {
    animation = FigureAnimWalkRight;
  }

  blockX++;
  blockY--;

  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFJumpDownLeft(void) {
  if (!animateFigure(0)) {
    return animation;
  }

  if (carriedDomino != DominoTypeEmpty) {
    animation = FigureAnimCarryStopLeft;
  } else {
    animation = FigureAnimWalkLeft;
  }

  blockX--;
  blockY++;

  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFJumpDownRight(void) {
  if (!animateFigure(0)) {
    return animation;
  }

  if (carriedDomino != DominoTypeEmpty) {
    animation = FigureAnimCarryStopRight;
  } else {
    animation = FigureAnimWalkRight;
  }

  blockX++;
  blockY++;

  return FigureAnimNothing;
}


// we wait until the door is open to enter
// the level
FigureAnimationState figure_c::SFLeaveDoor(void) {
  level.openEntryDoor(true);

  if (!level.isEntryDoorOpen())
    return state;

  if (animationImage == 0) {
    blockX = level.getEntryX();
    blockY = level.getEntryY();
  }

  if (animateFigure(0)) {
    animation = FigureAnimStepAsideAfterEnter;
  }

  return animation;
}

FigureAnimationState figure_c::SFStepAside(void) {

  if (animateFigure(0)) {
    animation = FigureAnimStop;
    level.openEntryDoor(false);
    return FigureAnimNothing;
  }

  return animation;
}

FigureAnimationState figure_c::SFInFrontOfExploder(void) {

  if (animateFigure(3)) {
    animation = FigureAnimInFrontOfExploderWait;
  }

  return animation;
}

FigureAnimationState figure_c::SFLazying(void) {

  if (animateFigure(3)) {
    return FigureAnimNothing;
  }

  return animation;
}

FigureAnimationState figure_c::SFInactive(void) {
  return FigureAnimNothing;
}

FigureAnimationState figure_c::SFFlailing(void) {
  if (animateFigure(0)) {
    return FigureAnimNothing;
  }

  return animation;
}

FigureAnimationState figure_c::SFStartFallingLeft(void) {

  if (animationImage == 2)
  {
    soundSystem_c::instance()->startSound(soundSystem_c::SE_FIGURE_FALLING);
  }

  if (animateFigure(0)) {
    blockX--;
    animation = FigureAnimFalling;
    blockY = (size_t)(blockY+2) < level.levelY() ? blockY+2 : level.levelY();
    fallingHight++;
    return FigureAnimNothing;
  }

  return animation;
}

FigureAnimationState figure_c::SFStartFallingRight(void) {

  if (animationImage == 2)
  {
    soundSystem_c::instance()->startSound(soundSystem_c::SE_FIGURE_FALLING);
  }

  if (animateFigure(0)) {
    blockX++;

    animation = FigureAnimFalling;
    blockY = (size_t)(blockY+2) < level.levelY() ? blockY+2 : level.levelY();
    fallingHight++;
    return FigureAnimNothing;
  }

  return animation;
}

FigureAnimationState figure_c::SFFalling(void) {

  if (animateFigure(0)) {

    if (fallingHight == 1 && carriedDomino != DominoTypeEmpty) {
      level.putDownDomino(blockX, blockY, carriedDomino, false);
      level.fallingDomino(blockX, blockY);
      carriedDomino = DominoTypeEmpty;
    }

    animation = FigureAnimFalling;
    if ((size_t)(blockY+1) < level.levelY()) {
      blockY += 1;
    } else {
      animation = FigureAnimGhost1;
      return animation;
    }

    return FigureAnimNothing;
  }

  return animation;
}

FigureAnimationState figure_c::SFLanding(void) {

  if (animationImage == 2)
    soundSystem_c::instance()->startSound(soundSystem_c::SE_FIGURE_LANDING);

  if (animateFigure(0)) {
    animation = FigureAnimStop;
    return FigureAnimNothing;
  }

  return animation;
}

// this function checks for possible actions when
// the user is inactive and the figure in stop state
FigureAnimationState figure_c::checkForNoKeyActions(void) {

  FigureAnimationState ReturnFigureState = FigureAnimStop;

  // if the user has prepared a push, we don't do anything
  if (animation == FigureAnimPushRight || animation == FigureAnimPushLeft)
  {
    return FigureAnimNothing;
  }

  // if we are on a ladder
  if (direction == -20 || direction == 20)
  {

    // not long enough inactive
    if (inactiveTimer <= 40)
    {
      return ReturnFigureState;
    }

    if (level.getLadder(blockX, blockY))
    {
      if (level.getPlatform(blockX-1, blockY+1))
      {
        if (carriedDomino != DominoTypeEmpty)
        {
          animation = ReturnFigureState = FigureAnimXXX4;
        }
        else
        {
          animation = ReturnFigureState = FigureAnimWalkLeft;
        }

        direction = -1;
        return ReturnFigureState;
      }
      if (!level.getPlatform(blockX+1, blockY+1))
      {
        direction = -1;
        return ReturnFigureState;
      }
      if (level.getLadder(blockX+1, blockY+1))
      {
        direction = -1;
        return ReturnFigureState;
      }
      if (carriedDomino != DominoTypeEmpty)
      {
        animation = ReturnFigureState = FigureAnimXXX3;
        direction = 1;
        return ReturnFigureState;
      }
      else
      {
        animation = ReturnFigureState = FigureAnimWalkRight;
        direction = 1;
        return ReturnFigureState;
      }
    }
    if (level.getLadder(blockX, blockY-1) &&
        inactiveTimer > 0x0A0)
    {
      animation = ReturnFigureState = FigureAnimLadder1;
      return ReturnFigureState;
    }
    if (level.getLadder(blockX, blockY+2) &&
        level.getLadder(blockX, blockY+1))
    {
      animation = ReturnFigureState = FigureAnimLadder3;
    }
    return ReturnFigureState;
  }

  // if we carry a domino
  if (carriedDomino != DominoTypeEmpty)
  {
    // stop immediately when no cursor key is given
    if (inactiveTimer == 1)
    {
      if (direction ==  -1)
      {
        animation = ReturnFigureState = FigureAnimCarryStopLeft;
        return ReturnFigureState;
      }
      else
      {
        animation = ReturnFigureState = FigureAnimCarryStopRight;
        return ReturnFigureState;
      }
    }
    return ReturnFigureState;
  }

  // is we are in front of an exploder -> push hands on ears
  if (level.getDominoType(blockX, blockY) == DominoTypeExploder)
  {
    if (animation == FigureAnimInFrontOfExploderWait)
    {
      return ReturnFigureState;
    }
    animation = FigureAnimInFrontOfExploder;
    return FigureAnimInFrontOfExploder;
  }

  // tapping and yawning when nothing else is going on
  if (((inactiveTimer & 0x20) == 0x20) && ((inactiveTimer & 0x1F) == 0))
  {
    soundSystem_c::instance()->startSound(soundSystem_c::SE_NU_WHAT);
    animation = FigureAnimTapping;
    return FigureAnimTapping;
  }

  if (((inactiveTimer & 0x20) == 0x20) && ((inactiveTimer & 0x1F) < 5))
  {
    animation = FigureAnimTapping;
    return FigureAnimTapping;
  }
  if (inactiveTimer == 204)
  {
    animation = FigureAnimYawning;
    return FigureAnimYawning;
  }

  if (inactiveTimer == 220)
  {
    inactiveTimer = 0;
    return ReturnFigureState;
  }
  if (inactiveTimer > 4)
  {
    animation = FigureAnimStop;
  }

  return ReturnFigureState;
}

bool figure_c::CanPlaceDomino(int x, int y, int ofs) {

  if (ofs < -1 || ofs > 1) return false;

  x += ofs;

  // outside of the level
  if (x < 0) return false;
  if ((size_t)(x) >= level.levelX()) return false;

  // if the target position is not empty, we can't put the domino there
  if (level.getDominoType(x, y) != DominoTypeEmpty) return false;
  if (!level.getPlatform(x, y+1)) return false;

  // all stones except for the vanisher may not be placed in front of doors
  if (   (carriedDomino != DominoTypeVanish)
      && (   ((x == level.getEntryX()) && (y == level.getEntryY()))
          || ((x == level.getExitX())  && (y == level.getExitY()))
         )
     )
  {
    return false;
  }

  // check neighbor places, if there is a domino falling in our direction, we
  // must not place the domino there....
  if (level.getDominoType(x+1, y) != DominoTypeEmpty && level.getDominoState(x+1, y) < DO_ST_UPRIGHT)
    return false;

  if (level.getDominoType(x-1, y) != DominoTypeEmpty && level.getDominoState(x-1, y) > DO_ST_UPRIGHT && level.getDominoState(x-1, y) <= DO_ST_RIGHT)
    return false;

  // no other reason to not place the domino
  return true;
}

bool figure_c::PushableDomino(int x, int y, int ofs) {

  if (carriedDomino != DominoTypeEmpty) return false;

  if (level.getDominoType(x+ofs, y) == DominoTypeEmpty) return false;
  if (!level.getPlatform(x+ofs, y+1)) return false;
//  if (level.getLadder(x+ofs, y-1)) return false;  // TODO there is something with ladders in the original
  if (level.getDominoType(x+ofs, y) == DominoTypeSplitter) return false;
  if (level.getDominoState(x+ofs, y) != DO_ST_UPRIGHT) return false;

  if (level.getDominoType(x, y) == DominoTypeEmpty) return true;

  if (ofs == -1)
  {
      return level.getDominoState(x, y) >= DO_ST_UPRIGHT;
  }

  return level.getDominoState(x, y) <= DO_ST_UPRIGHT;
}

// ok, this huge function determines what comes next
// it decides this on the currently pressed keys and the surroundings
FigureAnimationState figure_c::SFNextAction(unsigned int keyMask) {

  animationImage = 0;
  FigureAnimationState returnState;

  // is true, when the figure is on a ladder
  bool onLadder = (animation >= FigureAnimLadder1 && animation <= FigureAnimLadder4) ||
                  (animation >= FigureAnimCarryLadder1 && animation <= FigureAnimCarryLadder4);

  // when we have no ground below us and are not on a ladder we need
  // to fall down
  if (!level.getPlatform(blockX, blockY+1) && (!onLadder || !level.getLadder(blockX, blockY)))
  {
    fallingHight++;
    if ((size_t)blockY >= level.levelY()+1)
    {
      animation = returnState = FigureAnimLandDying;
      animationImage = 12;
      return returnState;
    }
    animation = returnState = FigureAnimFalling;
    if (fallingHight == 1)
      soundSystem_c::instance()->startSound(soundSystem_c::SE_FIGURE_FALLING);
    return returnState;
  }

  // we can get killed by a domino when we are currently pushing something and a
  // domino comes falling towards us
  if (animation == FigureAnimPushLeft &&
      animationImage == 0)
  {
    if ((level.getDominoType(blockX, blockY) != DominoTypeEmpty &&
         level.getDominoState(blockX, blockY) < DO_ST_UPRIGHT) ||
        (level.getDominoType(blockX-1, blockY) != DominoTypeEmpty &&
         level.getDominoState(blockX-1, blockY) > DO_ST_UPRIGHT))
    {
      animation = returnState = FigureAnimDominoDying;
      return returnState;
    }
  }
  if (animation == FigureAnimPushRight &&
      animationImage == 0)
  {
    if ((level.getDominoType(blockX, blockY) != DominoTypeEmpty &&
         level.getDominoState(blockX, blockY) > DO_ST_UPRIGHT) ||
        (level.getDominoType(blockX+1, blockY) != DominoTypeEmpty &&
         level.getDominoState(blockX+1, blockY) < DO_ST_UPRIGHT))
    {
      if ((size_t)(blockX+1) < level.levelX())
      {
        blockX++;
        animation = returnState = FigureAnimDominoDying;
        return returnState;
      }
      animation = returnState = FigureAnimDominoDying;
      return returnState;
    }
  }

  returnState = FigureAnimStop;

  if (!(keyMask & KEY_DOWN))
  {
    downChecker = false;
  }
  if (!(keyMask & KEY_UP))
  {
    upChecker = false;
  }
  if (fallingHight != 0)
  {
    if (fallingHight > 6)
    {
      animation = returnState = FigureAnimLandDying;
    }
    else
    {
      animation = returnState = FigureAnimLanding;
    }
  }
  else if (keyMask & KEY_LEFT)
  {
    if (blockX <= 0)
    {
      direction = 1;
    }
    else if (animation == FigureAnimPushLeft)
    {
      downChecker = true;
      if ((numPushsLeft > 0) && (keyMask & KEY_ACTION))
      {
        numPushsLeft--;
        returnState = FigureAnimPushLeft;
        direction = -1;
      }
    }
    else if (animation == FigureAnimPushRight)
    {
      downChecker = true;
      if (numPushsLeft && keyMask & KEY_ACTION)
      {
        blockX++;
        animation = returnState = FigureAnimPushLeft;
        if (!PushableDomino(blockX, blockY, -1))
        {
          animationImage = 9;
          direction = -1;
        }
        else
        {
          numPushsLeft--;
          direction = -1;
        }
      }
      else if (PushableDomino(blockX+1, blockY, -1))
      {
        blockX++;
        animation = FigureAnimPushLeft;
        direction = -1;
      }
    }
    else if (level.getPlatform(blockX-1, blockY))
    {
      animation = returnState = FigureAnimJunpUpLeft;
      direction = -1;
    }
    else if (!level.getPlatform(blockX-1, blockY+1) && level.getPlatform(blockX-1, blockY+2))
    {
      animation = returnState = FigureAnimJunpDownLeft;
      direction = -1;
    }
    else if (!level.getPlatform(blockX, blockY+1) && level.getLadder(blockX, blockY))
    {
    }
    else if (!level.getPlatform(blockX-1, blockY+1))
    {
      if (carriedDomino != DominoTypeEmpty)
      {
        animation = returnState = FigureAnimLoosingDominoLeft;
        direction = -1;
      }
      else if (animation == FigureAnimStruggingAgainsFallLeft)
      {
        animation = returnState = FigureAnimSuddenFallLeft;
        direction = -1;
      }
      else
      {
        animation = returnState = FigureAnimStruggingAgainsFallLeft;
        direction = -1;
      }
    }
    else if (carriedDomino != DominoTypeEmpty &&
        (direction == 20 || direction == -20))
    {
      animation = returnState = FigureAnimXXX4;
      direction = -1;
    }
    else if ((keyMask & KEY_UP) && PushableDomino(blockX, blockY, -1))
    {
      animation = returnState = FigureAnimEnterLeft;
      direction = -1;
    }
    else
    {
      animation = returnState = FigureAnimWalkLeft;
      direction = -1;
    }
  }
  else if (keyMask & KEY_RIGHT)
  {
    if ((size_t)(blockX+1) >= level.levelX())
    {
      direction = -1;
    }
    else if (animation == FigureAnimPushRight)
    {
      downChecker = true;
      if ((numPushsLeft > 0) && (keyMask & KEY_ACTION))
      {
        numPushsLeft--;
        returnState = FigureAnimPushRight;
        direction = 1;
      }
    }
    else if (animation == FigureAnimPushLeft)
    {
      downChecker = true;
      if (numPushsLeft != 0 && keyMask & KEY_ACTION)
      {
        blockX--;
        animation = returnState = FigureAnimPushRight;
        if (!PushableDomino(blockX, blockY, 1))
        {
          animationImage = 9;
          direction = 1;
        }
        else
        {
          numPushsLeft--;
          direction = 1;
        }
      }
      else if (PushableDomino(blockX-1, blockY, 1))
      {
        blockX--;
        animation = FigureAnimPushRight;
        direction = 1;
      }
    }
    else if (!level.getPlatform(blockX+1, blockY+1) && level.getPlatform(blockX+1, blockY+2))
    {
      animation = returnState = FigureAnimJunpDownRight;
      direction = 1;
    }
    else if (level.getPlatform(blockX+1, blockY))
    {
      animation = returnState = FigureAnimJunpUpRight;
      direction = 1;
    }
    else if (!level.getPlatform(blockX, blockY+1) && level.getLadder(blockX, blockY))
    {
    }
    else if (!level.getPlatform(blockX+1, blockY+1))
    {
      if (carriedDomino != DominoTypeEmpty)
      {
        returnState = FigureAnimStepOutForLoosingDomino;
        animation = FigureAnimCarryRight;
        direction = 1;
      }
      else if (animation == FigureAnimStruggingAgainsFallRight)
      {
        animation = returnState = FigureAnimSuddenFallRight;
        direction = 1;
      }
      else
      {
        animation = returnState = FigureAnimStruggingAgainsFallRight;
        direction = 1;
      }
    }
    else if (carriedDomino != DominoTypeEmpty &&
        (direction == 20 || direction == -20))
    {
      animation = returnState = FigureAnimXXX3;
      direction = 1;
    }
    else if ((keyMask & KEY_UP) && PushableDomino(blockX, blockY, 1))
    {
      animation = returnState = FigureAnimEnterRight;
      direction = 1;
    }
    else
    {
      animation = returnState = FigureAnimWalkRight;
      direction = 1;
    }
  }
  else if (keyMask & KEY_UP)
  {
    if ((level.getExitX() == blockX && level.getExitY() == blockY && level.isExitDoorOpen()) &&
        (level.getDominoType(blockX, blockY) == DominoTypeEmpty ||
         level.getDominoState(blockX, blockY) > DO_ST_UPRIGHT) &&
        (level.getDominoType(blockX-1, blockY) == DominoTypeEmpty ||
         level.getDominoState(blockX-1, blockY) <= DO_ST_UPRIGHT)
       )
    {
      if (direction == -1)
      {
        animation = returnState = FigureAnimEnterDoor;
        blockX--;
        upChecker = true;
      }
      else
      {
        animation = returnState = FigureAnimStepBackForDoor;
        upChecker = true;
      }
    }
    else if (level.getLadder(blockX, blockY) && !level.getLadder(blockX, blockY+1))
    {
      if (carriedDomino == DominoTypeEmpty)
      {
        animation = returnState = FigureAnimLadder1;
        direction = -20;
        upChecker = true;
      }
      else if (direction == -1)
      {
        animation = returnState = FigureAnimXXX2;
        direction = -20;
        upChecker = true;
      }
      else
      {
        animation = returnState = FigureAnimXXX1;
        direction = -20;
        upChecker = true;
      }
    }
    else if (level.getLadder(blockX, blockY-1) && !level.getPlatform(blockX, blockY-1))
    {
      animation = returnState = FigureAnimLadder1;
      direction = -20;
      upChecker = true;
    }
    else if (level.getLadder(blockX, blockY-1) && level.getPlatform(blockX, blockY-1))
    {
      animation = returnState = FigureAnimLadder2;
      direction = -20;
      upChecker = true;
    }
    else if (animation == FigureAnimPushLeft
        || animation == FigureAnimPushRight)
    {
      returnState = FigureAnimNothing;
      upChecker = true;
    }
    else if (carriedDomino != DominoTypeEmpty)
    {
      returnState = FigureAnimStop;
      upChecker = true;
    }
    else if (direction == -1 &&
        PushableDomino(blockX, blockY, -1))
    {
      animation = returnState = FigureAnimEnterLeft;
      upChecker = true;
    }
    else if (direction == 1 &&
      PushableDomino(blockX, blockY, 1))
    {
      animation = returnState = FigureAnimEnterRight;
      upChecker = true;
    }
    else if (!upChecker)
    {
      if (PushableDomino(blockX, blockY, -1))
      {
        animation = returnState = FigureAnimEnterLeft;
        upChecker = true;
      }
      else if (PushableDomino(blockX, blockY, 1))
      {
        animation = returnState = FigureAnimEnterRight;
        upChecker = true;
      }
      else
      {
        upChecker = true;
      }
    }
  }
  else if (keyMask & KEY_DOWN)
  {
    if (animation == FigureAnimPushLeft ||
        animation == FigureAnimPushRight)
    {
      returnState = animation ;
      animationImage = 9;
      downChecker = true;
    }
    else if (level.getPlatform(blockX, blockY+1) && level.getLadder(blockX, blockY+1))
    {
      if (carriedDomino != DominoTypeEmpty)
      {
        if (direction == -1)
        {
          animation = returnState = FigureAnimXXX2;
          direction = 20;
        }
        else
        {
          animation = returnState = FigureAnimXXX1;
          direction = 20;
        }
      }
      else if (!downChecker)
      {
        animation = returnState = FigureAnimLadder4;
        direction = 20;
      }
    }
    else if (level.getLadder(blockX, blockY+2))
    {
      animation = returnState = FigureAnimLadder3;
      direction = 20;
    }
  }
  else if (keyMask & KEY_ACTION)
  {
    if ((animation != FigureAnimPushRight) && (animation != FigureAnimPushLeft) && (carriedDomino == DominoTypeEmpty)
        && (level.getDominoType(blockX, blockY) != DominoTypeEmpty)
        && (level.getDominoState(blockX, blockY) == DO_ST_UPRIGHT)
        && (level.getDominoType(blockX, blockY) != DominoTypeAscender || level.getDominoExtra(blockX, blockY) != 0x60)
        && level.getPlatform(blockX, blockY+1)
          )
    {
      if (level.getDominoType(blockX, blockY) == DominoTypeTrigger)
      {
        animation = returnState = FigureAnimNoNo;
      }
      else
      {
        carriedDomino = level.pickUpDomino(blockX, blockY);

        if (direction == -1)
        {
          animation = returnState = FigureAnimPullOutLeft;
        }
        else
        {
          animation = returnState = FigureAnimPullOutRight;
        }
      }
    }
    else if (carriedDomino != DominoTypeEmpty)
    {
      if (CanPlaceDomino(blockX, blockY, 0))
      {
        if (direction == -1)
        {
          blockX++;
          animation = returnState = FigureAnimPushInLeft;
          animationImage = 4;
        }
        else
        {
          blockX--;
          animation = returnState = FigureAnimPushInRight;
          animationImage = 4;
        }
      }
      else if (CanPlaceDomino(blockX, blockY, direction))
      {
        if (direction == -1)
        {
          animation = returnState = FigureAnimPushInLeft;
        }
        else if (direction == 1)
        {
          animation = returnState = FigureAnimPushInRight;
        }
      }
    }
  }
  else if (direction == -20)
  {
    if (level.getLadder(blockX, blockY-1) && !level.getPlatform(blockX, blockY-1))
    {
      animation = returnState = FigureAnimLadder1;
      direction = -20;
    }
    else if (!level.getLadder(blockX, blockY-1) || !level.getPlatform(blockX, blockY-1))
    {
      direction = -1;
    }
    else
    {
      animation = returnState = FigureAnimLadder2;
      direction = -1;
    }
  }
  else if (direction == 20)
  {
    if (level.getLadder(blockX, blockY+2) && !level.getPlatform(blockX, blockY+2))
    {
      if (level.getLadder(blockX, blockY+1) && level.getPlatform(blockX, blockY+1))
      {
        direction = 20;
      }
      else
      {
        animation = returnState = FigureAnimLadder3;
        direction = 20;
      }
    }
  }

  // some final checks

  if (returnState != FigureAnimStop)
  {
    inactiveTimer = 0;
  }
  else if (carriedDomino != DominoTypeEmpty || (level.getLadder(blockX, blockY+1) && !level.getPlatform(blockX, blockY+1)) || finalAnimationPlayed)
  {
    inactiveTimer++;
    returnState = checkForNoKeyActions();
  }
  else if (levelSuccess)
  {
    animation = returnState = FigureAnimVictory;
    finalAnimationPlayed = 1;
  }
  else if (levelFail)
  {
    animation = returnState = FigureAnimShrugging;
    finalAnimationPlayed = 1;
  }
  else
  {
    inactiveTimer++;
    returnState = checkForNoKeyActions();
  }

  if ((carriedDomino != DominoTypeEmpty) && animation < FigureAnimCarryLeft)
  {
    switch (animation)
    {
      case FigureAnimWalkLeft      : animation = FigureAnimCarryLeft;      break;
      case FigureAnimWalkRight     : animation = FigureAnimCarryRight;     break;
      case FigureAnimJunpUpLeft    : animation = FigureAnimCarryUpLeft;    break;
      case FigureAnimJunpUpRight   : animation = FigureAnimCarryUpRight;   break;
      case FigureAnimJunpDownLeft  : animation = FigureAnimCarryDownLeft;  break;
      case FigureAnimJunpDownRight : animation = FigureAnimCarryDownRight; break;
      case FigureAnimLadder1       : animation = FigureAnimCarryLadder1;   break;
      case FigureAnimLadder2       : animation = FigureAnimCarryLadder2;   break;
      case FigureAnimLadder3       : animation = FigureAnimCarryLadder3;   break;
      case FigureAnimLadder4       : animation = FigureAnimCarryLadder4;   break;
      default: break;
    }
  }

  fallingHight = 0;
  return returnState;

}

bool figure_c::isVisible(void) const
{
  return blockX >= 0 && (size_t)blockX < level.levelX() && blockY >= 0 && (size_t)blockY < level.levelY();
}

unsigned int getKeyMask(void) {
  unsigned int keyMask = 0;

  const Uint8 *keystate = SDL_GetKeyboardState(NULL);

  if ( keystate[SDL_SCANCODE_UP] ) keyMask |= KEY_UP;
  if ( keystate[SDL_SCANCODE_DOWN] ) keyMask |= KEY_DOWN;
  if ( keystate[SDL_SCANCODE_LEFT] ) keyMask |= KEY_LEFT;
  if ( keystate[SDL_SCANCODE_RIGHT] ) keyMask |= KEY_RIGHT;
  if ( keystate[SDL_SCANCODE_SPACE] ) keyMask |= KEY_ACTION;

  return keyMask;
}

