// ----------------------------------------------------------------------------
// DoGameStuff.c
// ----------------------------------------------------------------------------

#include "DoGameStuff.h"


static void CallAnimation(int si, byte bl);
static boolean IsToBeAnimated(int bl);

int AnimationPosTable[SP_MAX_PLAYFIELD_SIZE];
byte AnimationSubTable[SP_MAX_PLAYFIELD_SIZE];


// ==========================================================================
//                              SUBROUTINE
// Do game stuff
// ==========================================================================

void subDoGameStuff(byte action)
{
  int si, cx, dx, bl;
  int InfotronsNeeded_last = InfotronsNeeded;
  boolean is_action_diagonal = ((action & MV_HORIZONTAL) && (action & MV_VERTICAL));
  boolean is_action_moving = !(action & KEY_BUTTON);
  boolean is_action_moving_diagonal = (is_action_moving && is_action_diagonal);
  boolean can_move = (PlayField16[MurphyPosIndex] == fiMurphy);
  int move_dir = (action & MV_ALL_DIRECTIONS);

  if (game_sp.zigzag_movement &&
      is_action_moving_diagonal &&
      can_move)
  {
    if (game_sp.last_move_dir & MV_HORIZONTAL)
    {
      move_dir = (action & MV_VERTICAL);
      subProcessKeyboardInput(move_dir);
      subAnimateMurphy(&MurphyPosIndex);	// move Murphy in vertical direction

      if (PlayField16[MurphyPosIndex] == fiMurphy)
      {
	move_dir = (action & MV_HORIZONTAL);
	subProcessKeyboardInput(move_dir);
	subAnimateMurphy(&MurphyPosIndex);	// move Murphy in horizontal direction
      }
    }
    else
    {
      move_dir = (action & MV_HORIZONTAL);
      subProcessKeyboardInput(move_dir);
      subAnimateMurphy(&MurphyPosIndex);	// move Murphy in horizontal direction

      if (PlayField16[MurphyPosIndex] == fiMurphy)
      {
	move_dir = (action & MV_VERTICAL);
	subProcessKeyboardInput(move_dir);
	subAnimateMurphy(&MurphyPosIndex);	// move Murphy in vertical direction
      }
    }
  }
  else
  {
    subAnimateMurphy(&MurphyPosIndex);		// move Murphy (or snap) in any direction
  }

  // if moved, remember last movement direction
  if (can_move && PlayField16[MurphyPosIndex] != fiMurphy)
    game_sp.last_move_dir = move_dir;

  if (InfotronsNeeded != InfotronsNeeded_last)
    game.snapshot.collected_item = TRUE;

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // Build a database of locations and subs-to-call of animatable fields only:
  // Make a snapshot from the field before the animation cycle starts.
  // first and last line are not animated.
  si = FieldWidth + 1;
  cx = LevelMax - 2 * FieldWidth - 1;
  dx = 0;
  do // locloop_g_2282:
  {
    bl = LowByte(PlayField16[si]);
    if (((bl & 0xD) != 0) && (bl < 0x20)) // all animatables have 1's in &H0D' above &H1F? (&H1F=explosion!)
    {
      if (IsToBeAnimated(bl))
      {
        AnimationPosTable[dx] = si;
        AnimationSubTable[dx] = bl;
        dx = dx + 1; // count database entries
      }
    }

    si = si + 1; // next field
    cx = cx - 1;
  }
  while (0 < cx); // locloop_g_2282' until all lines scanned(not top- and bottom edge)

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // Now use the database to animate all animatables the fastest way.
  // All the other fields are not checked anymore: those have no database entry.
  // The field from before animation is frozen in the database in order not to
  // do follow-up animations in the same loop.
  if (dx != 0) // any database entries?
  {
    dx = dx - 1;
    for (cx = 0; cx <= dx; cx++)
    {
      CallAnimation(AnimationPosTable[cx], AnimationSubTable[cx]);
    } // loop    locloop_g_22B8          ' until all animatables done
  }

  // All animations are done now
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  if (KillMurphyFlag == 1 || MurphyMoveCounter == 0)
  {
    if (LeadOutCounter == 0 &&
	!game_sp.level_solved &&
	!game_sp.game_over)
    {
      KillMurphyFlag = 0;			// no more "kill Murphy"
      ExplodeFieldSP(MurphyExplodePos);		// Explode
      LeadOutCounter = 0x40;			// quit: start lead-out

      // give Murphy some more time (LeadOutCounter) to reach the exit
    }
  } //  loc_g_22FB:
}

static boolean IsToBeAnimated(int bl)
{
  static boolean IsToBeAnimated;

  switch (bl)
  {
    case fiZonk:
    case fiInfotron:
    case fiOrangeDisk:
    case fiSnikSnak:
    case fiTerminal:
    case fiElectron:
    case fiBug:
    case fiExplosion:
      IsToBeAnimated = True;
      break;

    default:
      IsToBeAnimated = False;
      break;
  }

  return IsToBeAnimated;
}

static void CallAnimation(int si, byte bl)
{
  switch (bl)
  {
    case fiZonk:
      subAnimateZonks(si);
      break;

    case fiInfotron:
      subAnimateInfotrons(si);
      break;

    case fiOrangeDisk:
      subAnimateOrangeDisks(si);
      break;

    case fiSnikSnak:
      subAnimateSnikSnaks(si);
      break;

    case fiTerminal:
      subAnimateTerminals(si);
      break;

    case fiElectron:
      subAnimateElectrons(si);
      break;

    case fiBug:
      subAnimateBugs(si);
      break;

    case fiExplosion:
      subAnimateExplosion(si);
      break;

    default:
      // Debug.Assert(False);
      break;
  }
}

