/*
 * This file is part of the planetblupi source code
 * Copyright (C) 1997, Daniel Roux & EPSITEC SA
 * Copyright (C) 2017-2018, Mathieu Schroeter
 * http://epsitec.ch; http://www.blupi.org; http://github.com/blupi-games
 *
 * 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, see http://gnu.org/licenses
 */

#include "decmove.h"
#include "decor.h"
#include "misc.h"

// clang-format off
/**
 * Movements to shake a tree
 * (synchronized with ACTION_PIOCHE).
 */
static Sint16 table_move1[] =
{
    9 * 4,  // nb
     0,  0,
     0,  0,
     0,  0,
     0,  0,
    -2,  0,
    +2,  0,
    -1,  0,
    +1,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
    -2,  0,
    +2,  0,
    -1,  0,
    +1,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
    -2,  0,
    +2,  0,
    -1,  0,
    +1,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
    -2,  0,
    +2,  0,
    -1,  0,
    +1,  0,
};

/**
 * Movements to jump an object (east) on Blupi
 * (synchronized with ACTION_TAKE).
 */
static Sint16 table_move2[] =
{
    9 + 19,     // nb
      0,   0,
      0,   0,
      0,   0,
      0,   0,
      0,   0,
      0,   0,
      0,   0,
      0,   0,
      0,   0,
      0, -10,
      0, -20,
      0, -30,
      0, -40,
     -1, -50,
     -3, -60,
     -7, -70,
    -15, -80,
    -22, -70,
    -30, -56,
    -30, -56,
    -30, -56,
    -30, -56,
    -30, -56,
    -30, -56,
    -30, -56,
    -30, -56,
    -30, -56,
    -30, -56,
};

/**
 * Movements to jump an object (south) on Blupi
 * (synchronized with ACTION_TAKE2).
 */
static Sint16 table_move3[] =
{
    9 + 19,     // nb
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0, -10,
     0, -20,
     0, -30,
     0, -40,
     1, -50,
     3, -60,
     7, -70,
    15, -80,
    22, -70,
    30, -56,
    30, -56,
    30, -56,
    30, -56,
    30, -56,
    30, -56,
    30, -56,
    30, -56,
    30, -56,
    30, -56,
};

/**
 * Movements to drop and object (east) on Blupi
 * (synchronized with ACTION_DEPOSE).
 */
static Sint16 table_move4[] =
{
    10,     // nb
    -30, -60,
    -22, -70,
    -15, -80,
     -7, -70,
     -3, -60,
     -1, -50,
      0, -40,
      0, -30,
      0, -20,
      0, -10,
};

/**
 * Movements to drop an object (south) on Blupi
 * (synchronized with ACTION_DEPOSE2).
 */
static Sint16 table_move5[] =
{
    10,     // nb
    30, -60,
    22, -70,
    15, -80,
     7, -70,
     3, -60,
     1, -50,
     0, -40,
     0, -30,
     0, -20,
     0, -10,
};

/**
 * Movements to push a bridge to the east.
 */
static Sint16 table_move6[] =
{
    40,     // nb
     +2,  +1,
     +4,  +2,
     +6,  +3,
     +8,  +4,
    +10,  +5,
    +12,  +6,
    +14,  +7,
    +16,  +8,
    +18,  +9,
    +20, +10,
    +22, +11,
    +24, +12,
    +26, +13,
    +28, +14,
    +30, +15,
    +32, +16,
    +34, +17,
    +36, +18,
    +38, +19,
    +40, +20,
    +42, +21,
    +44, +22,
    +46, +23,
    +48, +24,
    +50, +25,
    +52, +26,
    +54, +27,
    +56, +28,
    +58, +29,
    +60, +30,
    +60, +30,
    +60, +30,
    +60, +30,
    +60, +30,
    +60, +30,
    +60, +30,
    +60, +30,
    +60, +30,
    +60, +30,
    +60, +30,
};

/**
 * Movements to push a bridge to the west.
 */
static Sint16 table_move7[] =
{
    40,     // nb
     -2,  -1,
     -4,  -2,
     -6,  -3,
     -8,  -4,
    -10,  -5,
    -12,  -6,
    -14,  -7,
    -16,  -8,
    -18,  -9,
    -20, -10,
    -22, -11,
    -24, -12,
    -26, -13,
    -28, -14,
    -30, -15,
    -32, -16,
    -34, -17,
    -36, -18,
    -38, -19,
    -40, -20,
    -42, -21,
    -44, -22,
    -46, -23,
    -48, -24,
    -50, -25,
    -52, -26,
    -54, -27,
    -56, -28,
    -58, -29,
    -60, -30,
    -60, -30,
    -60, -30,
    -60, -30,
    -60, -30,
    -60, -30,
    -60, -30,
    -60, -30,
    -60, -30,
    -60, -30,
    -60, -30,
};

/**
 * Movements to push a bridge to the south.
 */
static Sint16 table_move8[] =
{
    40,     // nb
     -2,  +1,
     -4,  +2,
     -6,  +3,
     -8,  +4,
    -10,  +5,
    -12,  +6,
    -14,  +7,
    -16,  +8,
    -18,  +9,
    -20, +10,
    -22, +11,
    -24, +12,
    -26, +13,
    -28, +14,
    -30, +15,
    -32, +16,
    -34, +17,
    -36, +18,
    -38, +19,
    -40, +20,
    -42, +21,
    -44, +22,
    -46, +23,
    -48, +24,
    -50, +25,
    -52, +26,
    -54, +27,
    -56, +28,
    -58, +29,
    -60, +30,
    -60, +30,
    -60, +30,
    -60, +30,
    -60, +30,
    -60, +30,
    -60, +30,
    -60, +30,
    -60, +30,
    -60, +30,
    -60, +30,
};

/**
 * Movements to push a bridge to the north.
 */
static Sint16 table_move9[] =
{
    40,     // nb
     +2,  -1,
     +4,  -2,
     +6,  -3,
     +8,  -4,
    +10,  -5,
    +12,  -6,
    +14,  -7,
    +16,  -8,
    +18,  -9,
    +20, -10,
    +22, -11,
    +24, -12,
    +26, -13,
    +28, -14,
    +30, -15,
    +32, -16,
    +34, -17,
    +36, -18,
    +38, -19,
    +40, -20,
    +42, -21,
    +44, -22,
    +46, -23,
    +48, -24,
    +50, -25,
    +52, -26,
    +54, -27,
    +56, -28,
    +58, -29,
    +60, -30,
    +60, -30,
    +60, -30,
    +60, -30,
    +60, -30,
    +60, -30,
    +60, -30,
    +60, -30,
    +60, -30,
    +60, -30,
    +60, -30,
};

/**
 * Movements to shake a laboratory.
 */
static Sint16 table_move10[] =
{
    16 * 4, // nb
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
     0, 0,
     0, 0,
     0, 0,
     0, 0,
     0, 0,
     0, 0,
     0, 0,
     0, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -2, 0,
    +2, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
    -1, 0,
    +1, 0,
};

/**
 * Movements to shake a mine.
 */
static Sint16 table_move11[] =
{
    20 * 4, // nb
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0,  0,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
    0, -1,
    0, +1,
};

/**
 * Movements to shake a factory.
 */
static Sint16 table_move12[] =
{
    20 * 4, // nb
    -1,  0,
    +1,  0,
    -1,  0,
    +1,  0,
     0, -1,
     0, +1,
     0, -1,
     0, +1,
    -1,  0,
    +1,  0,
    -1,  0,
    +1,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
    -2,  0,
    +2,  0,
     0, +1,
     0, +1,
    -2,  0,
    +2,  0,
     0, +1,
     0, +1,
    -1,  0,
    +1,  0,
    -1,  0,
    +1,  0,
    -2,  0,
    +2,  0,
     0, +1,
     0, +1,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
    -1,  0,
    +1,  0,
    -1,  0,
    +1,  0,
    -1,  0,
    +1,  0,
    -1,  0,
    +1,  0,
     0, -1,
     0, +1,
     0, -2,
     0, +2,
    -1,  0,
    +1,  0,
    -1,  0,
    +1,  0,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
    -1,  0,
    +1,  0,
    -1,  0,
    +1,  0,
    -2,  0,
    +2,  0,
     0, +1,
     0, +1,
    -2,  0,
    +2,  0,
     0, +1,
     0, +1,
     0,  0,
     0,  0,
     0,  0,
     0,  0,
    -2,  0,
    +2,  0,
     0, +1,
     0, +1,
};
// clang-format on

Sint16 *
GetListMoves (Sint32 rank)
{
  switch (rank)
  {
  case 1:
    return table_move1;
  case 2:
    return table_move2;
  case 3:
    return table_move3;
  case 4:
    return table_move4;
  case 5:
    return table_move5;
  case 6:
    return table_move6;
  case 7:
    return table_move7;
  case 8:
    return table_move8;
  case 9:
    return table_move9;
  case 10:
    return table_move10;
  case 11:
    return table_move11;
  case 12:
    return table_move12;
  }

  return nullptr;
}

// clang-format off
/**
 * Small fire.
 */
static Sint16 table_icon1[] =
{
    8 * 3,
    49, 50, 51, 52, 50, 49, 51, 52,
    49, 52, 51, 50, 49, 52, 50, 49,
    52, 51, 50, 49, 51, 52, 49, 51,
};

/**
 * Large fire.
 */
static Sint16 table_icon2[] =
{
    8 * 3,
    45, 46, 47, 48, 46, 45, 48, 47,
    46, 45, 48, 47, 45, 48, 47, 48,
    46, 45, 48, 47, 45, 48, 47, 46,
};

// Eclairs de la dalle nurserie.
static Sint16 table_icon3[] =
{
    4,      // nb
    53, 54, 55, 56,
};

// Eclairs n-s entre les tours.
static Sint16 table_icon4[] =
{
    8 * 4,      // nb
    74, 76, 78, 76, 74, 78, 74, 76,
    78, 76, 74, 76, 74, 76, 78, 76,
    74, 78, 76, 74, 76, 78, 74, 76,
    78, 74, 76, 78, 74, 76, 74, 78,
};

// Eclairs e-o entre les tours.
static Sint16 table_icon5[] =
{
    8 * 4,      // nb
    75, 77, 75, 79, 75, 77, 79, 75,
    79, 77, 75, 77, 75, 79, 75, 79,
    75, 77, 79, 75, 75, 77, 79, 75,
    79, 75, 77, 79, 75, 79, 75, 79,
};

// Explosion de la dynamite.
static Sint16 table_icon6[] =
{
    7 + 30, // nb
    91, 90, 89, 88, 89, 90, 91,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};

// Mèche de la dynamite.
static Sint16 table_icon7[] =
{
    12 * 4, // nb
    86, 87, 86, 87, 86, 87, 86, 87, 86, 87, 86, 87,
    86, 87, 86, 87, 86, 87, 86, 87, 86, 87, 86, 87,
    86, 87, 86, 87, 86, 87, 86, 87, 86, 87, 86, 87,
    86, 87, 86, 87, 86, 87, 86, 87, 86, 87, 86, 87,
};

// Rayons de  l'électrocuteur.
static Sint16 table_icon8[] =
{
    10 * 10,    // nb
    75, -1, -1, 74, -1, 75, -1, 74, 75, -1,
    74, 73, -1, 75, 74, 73, 72, 74, -1, 72,
    73, 74, 72, 73, 75, 73, 72, 73, 74, 72,
    75, 72, 73, 72, -1, 74, 73, 72, 75, 74,
    -1, 73, 74, 75, -1, 74, 75, -1, 75, -1,
    74, 75, -1, -1, 74, -1, -1, -1, 75, -1,
    -1, -1, -1, -1, -1, 74, -1, -1, -1, 75,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, 75, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, 74,
};

// Blupi écrasé.
static Sint16 table_icon9[] =
{
    10 * 10,    // nb
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
    76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
};

// Blupi malade écrasé.
static Sint16 table_icon10[] =
{
    10 * 10,    // nb
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
    77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
};

// Eclairs de la dalle téléporteur.
static Sint16 table_icon11[] =
{
    4,      // nb
    81, 82, 83, 84,
};
// clang-format on

Sint16 *
GetListIcons (Sint32 rank)
{
  switch (rank)
  {
  case 1:
    return table_icon1;
  case 2:
    return table_icon2;
  case 3:
    return table_icon3;
  case 4:
    return table_icon4;
  case 5:
    return table_icon5;
  case 6:
    return table_icon6;
  case 7:
    return table_icon7;
  case 8:
    return table_icon8;
  case 9:
    return table_icon9;
  case 10:
    return table_icon10;
  case 11:
    return table_icon11;
  }

  return nullptr;
}

/**
 * \brief Remove all animated environments.
 */
void
CDecor::MoveFlush ()
{
  Sint32 i, x, y;

  for (i = 0; i < MAXMOVE; i++)
    m_move[i].bExist = false;

  for (x = 0; x < MAXCELX / 2; x++)
  {
    for (y = 0; y < MAXCELY / 2; y++)
      m_decor[x][y].rankMove = -1;
  }
}

/**
 * \brief Get the fire duration.
 *
 * \returns the duration.
 */
Sint32
CDecor::MoveMaxFire ()
{
  if (m_skill >= 1)
    return (MAXFIRE / 4) * 3;
  return MAXFIRE;
}

/**
 * \brief Initialize all environment perpetual movements.
 */
void
CDecor::MoveFixInit ()
{
  Sint32 x, y;

  MoveFlush ();

  for (x = 0; x < MAXCELX; x += 2)
  {
    for (y = 0; y < MAXCELY; y += 2)
    {
      // Démarre le feu.
      if (
        m_decor[x / 2][y / 2].fire > 0 &&
        m_decor[x / 2][y / 2].fire < MoveMaxFire ())
        MoveStartFire (GetCel (x, y));

      // Démarre les éclairs entre les tours.
      if (m_decor[x / 2][y / 2].objectIcon == 10000) // éclair n-s
      {
        if (MoveCreate (
              GetCel (x, y), -1, false, CHOBJECT, -1, -1, -1, 9999, 1, 0, true))
        {
          MoveAddIcons (GetCel (x, y), 4, true); // éclairs n-s
        }
      }

      if (m_decor[x / 2][y / 2].objectIcon == 10001) // éclair e-o
      {
        if (MoveCreate (
              GetCel (x, y), -1, false, CHOBJECT, -1, -1, -1, 9999, 1, 0, true))
        {
          MoveAddIcons (GetCel (x, y), 5, true); // éclairs e-o
        }
      }
    }
  }
}

/**
 * \brief Create a new animated environment.
 *
 * If \p bMisc is true, we keep 10 movements back for important
 * actions (\p bMisc == false).
 *
 * \param[in] cel - ?
 * \param[in] rankBlupi - ?
 * \param[in] bFloor - ?
 * \param[in] channel - ?
 * \param[in] icon - ?
 * \param[in] maskChannel - ?
 * \param[in] maskIcon - ?
 * \param[in] total - ?
 * \param[in] delai - ?
 * \param[in] stepY - ?
 * \param[in] bMisc - ?
 * \param[in] bNotIfExist - ?
 * \returns true on success.
 */
bool
CDecor::MoveCreate (
  Point cel, Sint32 rankBlupi, bool bFloor, Sint32 channel, Sint32 icon,
  Sint32 maskChannel, Sint32 maskIcon, Sint32 total, Sint32 delai, Sint32 stepY,
  bool bMisc, bool bNotIfExist)
{
  Sint32 rank, max;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (
      m_move[rank].bExist && m_move[rank].cel.x == cel.x &&
      m_move[rank].cel.y == cel.y)
    {
      if (bNotIfExist)
        return false;
      goto create;
    }
  }

  max = 0;
  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (!m_move[rank].bExist)
    {
      if (bMisc && max > MAXMOVE - 10)
        return false;
      goto create;
    }
    max++;
  }
  return false;

create:
  m_move[rank].bExist      = true;
  m_move[rank].cel         = cel;
  m_move[rank].rankBlupi   = rankBlupi;
  m_move[rank].bFloor      = bFloor;
  m_move[rank].channel     = channel;
  m_move[rank].icon        = icon;
  m_move[rank].maskChannel = maskChannel;
  m_move[rank].maskIcon    = maskIcon;
  m_move[rank].total       = total;
  m_move[rank].delai       = delai;
  m_move[rank].stepY       = stepY;
  m_move[rank].cTotal      = 0;
  m_move[rank].cDelai      = 0;
  m_move[rank].rankMoves   = 0;
  m_move[rank].rankIcons   = 0;
  m_move[rank].phase       = 0;

  m_decor[cel.x / 2][cel.y / 2].rankMove = rank;
  return true;
}

// Ajoute un mouvement.

bool
CDecor::MoveAddMoves (Point cel, Sint32 rankMoves)
{
  Sint32 rank;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (
      m_move[rank].bExist && m_move[rank].cel.x == cel.x &&
      m_move[rank].cel.y == cel.y)
    {
      m_move[rank].rankMoves = rankMoves;
      m_move[rank].phase     = 0;

      return true;
    }
  }

  return false;
}

// Ajoute un mouvement.

bool
CDecor::MoveAddIcons (Point cel, Sint32 rankIcons, bool bContinue)
{
  Sint32 rank;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (
      m_move[rank].bExist && m_move[rank].cel.x == cel.x &&
      m_move[rank].cel.y == cel.y)
    {
      m_move[rank].rankIcons = rankIcons;
      if (!bContinue)
        m_move[rank].phase = 0;

      if (rankIcons == 4 || rankIcons == 5) // éclairs entre tours ?
        m_move[rank].cTotal = Random (0, 10);

      return true;
    }
  }

  return false;
}

/**
 * \brief Test if something can burn at a specified cell.
 *
 * \param[in] cel - Position.
 * \returns true if possible.
 */
bool
CDecor::CanBurn (Point cel)
{
  Sint32 channel, icon;
  bool   canBurn = false;

  cel.x = (cel.x / 2) * 2;
  cel.y = (cel.y / 2) * 2;

  const auto cell = m_decor[cel.x / 2][cel.y / 2];

  channel = cell.objectChannel;
  icon    = cell.objectIcon;

  canBurn = channel == CHOBJECT && ((icon >= 6 && icon <= 11)     // trees ?
                                    || (icon >= 65 && icon <= 71) // palissade ?
                                    || icon == 61                 // cabane ?
                                    || icon == 36                 // planches ?
                                    || icon == 60                 // tomatoes ?
                                    || icon == 63                 // eggs ?
                                    || icon == 113                // house ?
                                    || icon == 121 // mine de fer ?
                                    || icon == 122 // mine de fer ?
                                   );
  if (canBurn)
    return true;

  // If there is an other object, then no fire.
  if (channel >= 0)
    return false;

  channel = cell.floorChannel;
  icon    = cell.floorIcon;

  canBurn = channel == CHFLOOR && (icon == 20 || // herbe foncée ?
                                   (icon >= 59 && icon <= 64));
  return canBurn;
}

/**
 * \brief Start the fire on a cell.
 *
 * \param[in] cel - Position.
 * \returns true if possible.
 */
bool
CDecor::MoveStartFire (Point cel)
{
  if (this->CanBurn (cel))
  {
    cel.x = (cel.x / 2) * 2;
    cel.y = (cel.y / 2) * 2;

    if (!MoveCreate (cel, -1, false, CHOBJECT, -1, -1, -1, 9999, 1, 0, true))
      return false;

    MoveAddIcons (cel, 1, true); // small fire
    m_decor[cel.x / 2][cel.y / 2].fire = 2;
    return true;
  }

  return false;
}

// Démarre le feu si c'est possible par proximité.

void
CDecor::MoveProxiFire (Point cel)
{
  Sint32 cx, cy, xx, yy, x, y, channel, icon;

  static Sint16 tableInd[5] = {2, 1, 3, 0, 4};
  // clang-format off
  static Sint16 tablePos[5 * 5 * 2] =
  {
     0,  0, -1, -2,  0, -2, +1, -2,  0,  0,
    -2, -1, -1, -1,  0, -1, +1, -1, +2, -1,
    -2,  0, -1,  0,  0,  0, +1,  0, +2,  0,
    -2, +1, -1, +1,  0, +1, +1, +1, +2, +1,
     0,  0, -1, +2,  0, +2, +1, +2,  0,  0,
  };
  // clang-format on

  for (cx = 0; cx < 5; cx++)
  {
    for (cy = 0; cy < 5; cy++)
    {
      xx = tablePos[(tableInd[cx] + tableInd[cy] * 5) * 2 + 0];
      yy = tablePos[(tableInd[cx] + tableInd[cy] * 5) * 2 + 1];

      if (xx == 0 && yy == 0)
        continue;

      x = cel.x + xx * 2;
      y = cel.y + yy * 2;

      if (x < 0 || x >= MAXCELX || y < 0 || y >= MAXCELX)
        continue;

      if (m_decor[x / 2][y / 2].fire != 0)
        continue; // brule déjà ?

      x = ((cel.x + xx) / 2) * 2;
      y = ((cel.y + yy) / 2) * 2;

      // Mur ou rochers entre le feu et l'objet ?
      channel = m_decor[x / 2][y / 2].objectChannel;
      icon    = m_decor[x / 2][y / 2].objectIcon;
      if (
        channel == CHOBJECT &&
        ((icon >= 20 && icon <= 26) || (icon >= 37 && icon <= 43)))
        continue;

      x = cel.x + xx * 2;
      y = cel.y + yy * 2;

      // Mur ou rochers entre le feu et l'objet ?
      channel = m_decor[x / 2][y / 2].objectChannel;
      icon    = m_decor[x / 2][y / 2].objectIcon;
      if (
        channel == CHOBJECT &&
        ((icon >= 20 && icon <= 26) || (icon >= 37 && icon <= 43)))
        continue;

      // Démarre éventuellement un seul foyer.
      if (MoveStartFire (GetCel (x, y)))
        return;
    }
  }
}

// Fait évoluer le feu.

void
CDecor::MoveFire (Sint32 rank)
{
  Sint32 x, y, icon, newIcon;
  Point  pos;

  x = (m_move[rank].cel.x / 2) * 2;
  y = (m_move[rank].cel.y / 2) * 2;

  if (
    m_decor[x / 2][y / 2].fire == 0 ||
    m_decor[x / 2][y / 2].fire >= MoveMaxFire ())
    return;

  m_decor[x / 2][y / 2].fire++;

  pos = ConvCelToPos (GetCel (x, y));
  m_pSound->PlayImage (SOUND_FIRE, pos);

  if (m_decor[x / 2][y / 2].objectIcon >= 0) // objet qui brule ?
  {
    if (
      m_decor[x / 2][y / 2].fire > MoveMaxFire () / 2 &&
      m_decor[x / 2][y / 2].fire % 50 == 0) // pas trop souvent !
    {
      MoveProxiFire (GetCel (x, y)); // boutte le feu
    }

    // Début petites flammes.
    if (m_decor[x / 2][y / 2].fire == 2)
      MoveStartFire (GetCel (x, y));

    // Début grandes flammes.
    if (m_decor[x / 2][y / 2].fire == (MoveMaxFire () - DIMOBJY * 2) / 2)
    {
      MoveAddIcons (GetCel (x, y), 2, true); // grandes flammes
    }

    // Début objet squelette.
    if (m_decor[x / 2][y / 2].fire == MoveMaxFire () - DIMOBJY * 2)
    {
      icon = m_decor[x / 2][y / 2].floorIcon;
      if (icon == 20) // herbe foncée ?
      {
        PutFloor (GetCel (x, y), CHFLOOR, 19); // herbe brulée
      }

      icon    = m_decor[x / 2][y / 2].objectIcon;
      newIcon = -1;
      if (icon >= 6 && icon <= 11)
        newIcon = icon - 6 + 30; // arbres ?
      if (icon == 61)
        newIcon = 62; // cabane ?
      if (icon == 113)
        newIcon = 15; // maison ?
      if (icon == 121)
        newIcon = 126; // mine de fer ?
      if (icon == 122)
        newIcon = 126; // mine de fer ?
      MoveCreate (
        GetCel (x, y), -1, false, CHOBJECT, newIcon, -1, -1, DIMOBJY * 2, 1,
        -1 * 50);
      MoveAddIcons (GetCel (x, y), 2, true); // grandes flammes
    }

    // Fin grandes flammes.
    if (m_decor[x / 2][y / 2].fire == MoveMaxFire () - DIMOBJY)
    {
      MoveAddIcons (GetCel (x, y), 1, true); // petites flammes
    }

    // Fin feu.
    if (m_decor[x / 2][y / 2].fire == MoveMaxFire () - 1)
    {
      MoveFinish (GetCel (x, y));
      icon = m_decor[x / 2][y / 2].objectIcon;
      if (
        icon == 36 ||               // planches ?
        icon == 60 ||               // tomates ?
        icon == 63 ||               // oeufs ?
        icon == 113 ||              // maison ?
        (icon >= 65 && icon <= 71)) // palissade ?
      {
        m_decor[x / 2][y / 2].objectChannel = -1;
        m_decor[x / 2][y / 2].objectIcon    = -1;
      }
      m_decor[x / 2][y / 2].fire = MoveMaxFire (); // déjà brulé
    }
  }
  else // sol qui brule ?
  {
    if (
      m_decor[x / 2][y / 2].fire > DIMOBJY &&
      m_decor[x / 2][y / 2].fire % 50 == 0) // pas trop souvent !
    {
      MoveProxiFire (GetCel (x, y)); // boutte le feu
    }

    // Début petites flammes.
    if (m_decor[x / 2][y / 2].fire == 2)
      MoveStartFire (GetCel (x, y));

    // Milieu feu.
    if (m_decor[x / 2][y / 2].fire == DIMOBJY)
    {
      icon = m_decor[x / 2][y / 2].floorIcon;
      switch (icon)
      {
      case 20:                                 // herbe foncée ?
        PutFloor (GetCel (x, y), CHFLOOR, 19); // herbe brulée
        break;
      case 59:                                // pont ?
        PutFloor (GetCel (x, y), CHFLOOR, 2); // rivage
        break;
      case 61:                                // pont ?
        PutFloor (GetCel (x, y), CHFLOOR, 4); // rivage
        break;
      case 62:                                // pont ?
        PutFloor (GetCel (x, y), CHFLOOR, 3); // rivage
        break;
      case 64:                                // pont ?
        PutFloor (GetCel (x, y), CHFLOOR, 5); // rivage
        break;
      case 60:
      case 63:                                 // pont ?
        PutFloor (GetCel (x, y), CHFLOOR, 14); // eau
        break;
      }
    }

    // Fin feu.
    if (m_decor[x / 2][y / 2].fire == DIMOBJY * 2 - 1)
    {
      MoveFinish (GetCel (x, y));
      m_decor[x / 2][y / 2].objectChannel = -1;
      m_decor[x / 2][y / 2].objectIcon    = -1;
      m_decor[x / 2][y / 2].fire          = MoveMaxFire (); // déjà brulé
    }
  }
}

// Fait évoluer tous les décors animés.

void
CDecor::MoveStep (bool bFirst)
{
  Sint32 rank, rankBlupi;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (m_move[rank].bExist)
    {
      if (
        !bFirst && (m_move[rank].rankIcons == 4 || // éclair entre tours ?
                    m_move[rank].rankIcons == 5))
        continue;

      MoveFire (rank);

      // Si le blupi travaillant ici est stoppé, on
      // arrête aussi la construction.
      rankBlupi = m_move[rank].rankBlupi;
      if (rankBlupi != -1)
      {
        if (
          m_blupi[rankBlupi].action == ACTION_STOP ||
          m_blupi[rankBlupi].action == ACTION_STOPTIRED)
          continue;
      }

      m_move[rank].phase++;
      if (m_move[rank].phase > 32000)
        m_move[rank].phase = 0;

      m_move[rank].cDelai++;
      if (m_move[rank].cDelai >= m_move[rank].delai) // délai écoulé ?
      {
        m_move[rank].cDelai = 0;

        m_move[rank].cTotal++;
        if (m_move[rank].cTotal > 32000)
          m_move[rank].cTotal = 0;

        if (
          m_move[rank].total != 9999 &&
          m_move[rank].cTotal >= m_move[rank].total)
          MoveFinish (m_move[rank].cel);
      }
    }
  }
}

// Termine un mouvement pour une cellule donnée.

void
CDecor::MoveFinish (Point cel)
{
  Sint32 rank;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (
      m_move[rank].bExist && m_move[rank].cel.x == cel.x &&
      m_move[rank].cel.y == cel.y)
    {
      if (m_move[rank].channel >= 0 && m_move[rank].icon >= 0)
      {
        if (m_move[rank].bFloor)
          PutFloor (cel, m_move[rank].channel, m_move[rank].icon);
        else
          PutObject (cel, m_move[rank].channel, m_move[rank].icon);
      }

      m_decor[cel.x / 2][cel.y / 2].rankMove = -1;
      m_move[rank].bExist                    = false;
    }
  }
}

// Termine un mouvement, d'après le rang du blupi.

void
CDecor::MoveFinish (Sint32 rankBlupi)
{
  Sint32 rank;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (m_move[rank].bExist && m_move[rank].rankBlupi == rankBlupi)
      MoveFinish (m_move[rank].cel);
  }
}

// Vérifie si une cellule est déjà utilisée.

bool
CDecor::MoveIsUsed (Point cel)
{
  Sint32 rank;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (
      m_move[rank].bExist && m_move[rank].cel.x == cel.x &&
      m_move[rank].cel.y == cel.y)
      return true;
  }

  return false;
}

// Retourne l'objet en construction à un endroit donné.

bool
CDecor::MoveGetObject (Point cel, Sint32 & channel, Sint32 & icon)
{
  Sint32 rank;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (
      m_move[rank].bExist && m_move[rank].cel.x == cel.x &&
      m_move[rank].cel.y == cel.y && m_move[rank].bFloor == false)
    {
      channel = m_move[rank].channel;
      icon    = m_move[rank].icon;
      return true;
    }
  }

  return false;
}

// Modifie un objet en construction à un endroit donné.

bool
CDecor::MovePutObject (Point cel, Sint32 channel, Sint32 icon)
{
  Sint32 rank;

  for (rank = 0; rank < MAXMOVE; rank++)
  {
    if (
      m_move[rank].bExist && m_move[rank].cel.x == cel.x &&
      m_move[rank].cel.y == cel.y && m_move[rank].bFloor == false)
    {
      m_move[rank].channel = channel;
      m_move[rank].icon    = icon;
      return true;
    }
  }

  return false;
}
