/********************************************************************/
/*                                                                  */
/*            L   I  QQ  U U I DD    W   W  A  RR    555            */
/*            L   I Q  Q U U I D D   W   W A A R R   5              */
/*            L   I Q  Q U U I D D   W W W AAA RR    55             */
/*            L   I Q Q  U U I D D   WW WW A A R R     5            */
/*            LLL I  Q Q  U  I DD    W   W A A R R   55             */
/*                                                                  */
/*                             b                                    */
/*                             bb  y y                              */
/*                             b b yyy                              */
/*                             bb    y                              */
/*                                 yy                               */
/*                                                                  */
/*                     U U       FFF  O   O  TTT                    */
/*                     U U       F   O O O O  T                     */
/*                     U U TIRET FF  O O O O  T                     */
/*                     U U       F   O O O O  T                     */
/*                      U        F    O   O   T                     */
/*                                                                  */
/********************************************************************/

/*****************************************************************************/
/* Liquid War is a multiplayer wargame                                       */
/* Copyright (C) 1998-2004 Christian Mauduit                                 */
/*                                                                           */
/* 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 2 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               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
/*                                                                           */
/* Liquid War homepage : http://www.ufoot.org/liquidwar                      */
/* Contact author      : ufoot@ufoot.org                                     */
/*****************************************************************************/

/********************************************************************/
/* nom           : fighter.c                                        */
/* contenu       : deplacement des mechants                         */
/* date de modif : 3 mai 98                                         */
/********************************************************************/

/*==================================================================*/
/* includes                                                         */
/*==================================================================*/

#include <allegro.h>

#include "army.h"
#include "config.h"
#include "cursor.h"
#include "decal.h"
#include "grad.h"
#include "mesh.h"
#include "fighter.h"
#include "monster.h"
#include "palette.h"
#include "startup.h"
#include "time.h"

/*==================================================================*/
/* variables globales                                               */
/*==================================================================*/

#define SIDE_ATTACK_FACTOR 4
/* on divise l'attaque par 2^4 = 16 quand elle se fait sur le cote */

#define NB_SENS_MOVE 2
#define NB_TRY_MOVE 5
#define NB_LOCAL_DIRS 16

static int LOCAL_DIR[NB_LOCAL_DIRS * 2];

static int FIGHTER_MOVE_DIR[NB_SENS_MOVE][NB_DIRS][NB_TRY_MOVE] =
  { {{DIR_NNE, DIR_NE, DIR_NW, DIR_ENE, DIR_WNW},
     {DIR_NE, DIR_ENE, DIR_NNE, DIR_SE, DIR_NW},
     {DIR_ENE, DIR_NE, DIR_SE, DIR_NNE, DIR_SSE},
     {DIR_ESE, DIR_SE, DIR_NE, DIR_SSE, DIR_NNE},
     {DIR_SE, DIR_SSE, DIR_ESE, DIR_SW, DIR_NE},
     {DIR_SSE, DIR_SE, DIR_SW, DIR_ESE, DIR_WSW},
     {DIR_SSW, DIR_SW, DIR_SE, DIR_WSW, DIR_ESE},
     {DIR_SW, DIR_WSW, DIR_SSW, DIR_NW, DIR_SE},
     {DIR_WSW, DIR_SW, DIR_NW, DIR_SSW, DIR_NNW},
     {DIR_WNW, DIR_NW, DIR_SW, DIR_NNW, DIR_SSE},
     {DIR_NW, DIR_NNW, DIR_WNW, DIR_NE, DIR_SW},
     {DIR_NNW, DIR_NW, DIR_NE, DIR_WNW, DIR_ENE}},

{{DIR_NNE, DIR_NE, DIR_NW, DIR_ENE, DIR_WNW},
 {DIR_NE, DIR_NNE, DIR_ENE, DIR_NW, DIR_SE},
 {DIR_ENE, DIR_NE, DIR_SE, DIR_NNE, DIR_SSE},
 {DIR_ESE, DIR_SE, DIR_NE, DIR_SSE, DIR_NNE},
 {DIR_SE, DIR_ESE, DIR_SSE, DIR_NE, DIR_SW},
 {DIR_SSE, DIR_SE, DIR_SW, DIR_ESE, DIR_WSW},
 {DIR_SSW, DIR_SW, DIR_SE, DIR_WSW, DIR_ESE},
 {DIR_SW, DIR_SSW, DIR_WSW, DIR_SE, DIR_NW},
 {DIR_WSW, DIR_SW, DIR_NW, DIR_SSW, DIR_NNW},
 {DIR_WNW, DIR_NW, DIR_SW, DIR_NNW, DIR_SSE},
 {DIR_NW, DIR_WNW, DIR_NNW, DIR_SW, DIR_NE},
 {DIR_NNW, DIR_NW, DIR_NE, DIR_WNW, DIR_ENE}}
};

static int FIGHTER_MOVE_X_REF[NB_DIRS] =
  { 0, 1, 1, 1, 1, 0, 0, -1, -1, -1, -1, 0 };

static int FIGHTER_MOVE_Y_REF[NB_DIRS] =
  { -1, -1, 0, 0, 1, 1, 1, 1, 0, 0, -1, -1 };

static int FIGHTER_MOVE_OFFSET_ASM[NB_SENS_MOVE][NB_DIRS * NB_TRY_MOVE];
static int FIGHTER_MOVE_XY_ASM[NB_SENS_MOVE][NB_DIRS * NB_TRY_MOVE];

static int FIGHTER_MOVE_OFFSET[NB_SENS_MOVE][NB_DIRS][NB_TRY_MOVE];
int FIGHTER_MOVE_X[NB_SENS_MOVE][NB_DIRS][NB_TRY_MOVE];
int FIGHTER_MOVE_Y[NB_SENS_MOVE][NB_DIRS][NB_TRY_MOVE];

/*==================================================================*/
/* fonctions                                                        */
/*==================================================================*/

/*------------------------------------------------------------------*/
static void
erase_fighter (FIGHTER * f)
{
  putpixel (CURRENT_AREA_DISP, f->x, f->y,
	    getpixel (CURRENT_AREA_BACK, f->x, f->y));
}

/*------------------------------------------------------------------*/
static void
disp_fighter (FIGHTER * f)
{
  putpixel (CURRENT_AREA_DISP, f->x, f->y,
	    COLOR_FIRST_ENTRY[(int) (f->team)]
	    + (f->health * COLORS_PER_TEAM) / MAX_FIGHTER_HEALTH);
}

/*------------------------------------------------------------------*/
void
init_move_fighters (void)
{
  int i, j, k, dir;

  for (k = 0; k < NB_SENS_MOVE; ++k)
    for (i = 0; i < NB_DIRS; ++i)
      for (j = 0; j < NB_TRY_MOVE; ++j)
	{
	  dir = FIGHTER_MOVE_DIR[k][i][j];
	  FIGHTER_MOVE_X[k][i][j] = FIGHTER_MOVE_X_REF[dir];
	  FIGHTER_MOVE_Y[k][i][j] = FIGHTER_MOVE_Y_REF[dir];

	  FIGHTER_MOVE_OFFSET[k][i][j] = 0;
	  if (FIGHTER_MOVE_X[k][i][j] == 1)
	    ++(FIGHTER_MOVE_OFFSET[k][i][j]);
	  if (FIGHTER_MOVE_X[k][i][j] == -1)
	    --(FIGHTER_MOVE_OFFSET[k][i][j]);
	  if (FIGHTER_MOVE_Y[k][i][j] == 1)
	    FIGHTER_MOVE_OFFSET[k][i][j] += CURRENT_AREA_W;
	  if (FIGHTER_MOVE_Y[k][i][j] == -1)
	    FIGHTER_MOVE_OFFSET[k][i][j] -= CURRENT_AREA_W;

	  FIGHTER_MOVE_OFFSET_ASM[k][i * NB_TRY_MOVE + j] =
	    FIGHTER_MOVE_OFFSET[k][i][j];
	  FIGHTER_MOVE_XY_ASM[k][i * NB_TRY_MOVE + j] =
	    FIGHTER_MOVE_Y[k][i][j] * 65536 + FIGHTER_MOVE_X[k][i][j];
	}

  for (i = 0; i < CURRENT_ARMY_SIZE; ++i)
    disp_fighter (CURRENT_ARMY + i);

  /*
   * note:
   * NB_LOCAL_DIRS is 16 but it's true that 12 is enough.
   * However, I find it safer to reserve 16 slots, since
   * this way all the combinations of 1,2,4 and 8 can
   * be handled correctly - even if some are impossible.
   * And it has the advantage to avoid confusion with
   * the 12 directions represented by NB_DIRS.
   */
  for (i = 1; i <= NB_LOCAL_DIRS; ++i)
    {
      for (j = 0; j < 2; ++j)
	{
	  k = -1;
	  switch (i)
	    {
	    case 1:
	      k = j ? DIR_NNW : DIR_NNE;
	      break;
	    case 3:
	      k = DIR_NE;
	      break;
	    case 2:
	      k = j ? DIR_ENE : DIR_ESE;
	      break;
	    case 6:
	      k = DIR_SE;
	      break;
	    case 4:
	      k = j ? DIR_SSE : DIR_SSW;
	      break;
	    case 12:
	      k = DIR_SW;
	      break;
	    case 8:
	      k = j ? DIR_WSW : DIR_WNW;
	      break;
	    case 9:
	      k = DIR_NW;
	      break;
	    }
	  LOCAL_DIR[(i - 1) * 2 + j] = k;
	}
    }
}

/*------------------------------------------------------------------*/
int
get_main_dir (MESH * mesh, int team, int sens, int start)
{
  int i, dir, dist;
  MESH *mesh2;

  dist = AREA_START_GRADIENT;
  dir = -1;
  i = start;

  if (sens)
    {
      do
	{
	  if ((mesh2 = mesh->link[i]))
	    if (mesh2->info[team].state.grad < dist)
	      {
		dir = i;
		dist = mesh2->info[team].state.grad;
	      }
	  i = (i < NB_DIRS - 1) ? i + 1 : 0;
	}
      while (i != start);
    }
  else
    {
      do
	{
	  if ((mesh2 = mesh->link[i]))
	    if (mesh2->info[team].state.grad < dist)
	      {
		dir = i;
		dist = mesh2->info[team].state.grad;
	      }
	  i = (i > 0) ? i - 1 : NB_DIRS - 1;
	}
      while (i != start);
    }

  if (dir >= 0)
    return dir;
  else
    return (GLOBAL_CLOCK % NB_TEAMS);
}

/*------------------------------------------------------------------*/
static int
get_close_dir (MESH * mesh, FIGHTER * f, int team, int sens, int start)
{
  int cursor_x, cursor_y, fighter_x, fighter_y;
  int code_dir = 0, dir;

  fighter_x = f->x;
  fighter_y = f->y;
  cursor_x = mesh->info[team].update.cursor.x;
  cursor_y = mesh->info[team].update.cursor.y;

  if (cursor_y < fighter_y)
    code_dir += 1;
  if (cursor_x > fighter_x)
    code_dir += 2;
  if (cursor_y > fighter_y)
    code_dir += 4;
  if (cursor_x < fighter_x)
    code_dir += 8;

  if (code_dir)
    dir = LOCAL_DIR[(code_dir - 1) * 2 + (sens ? 1 : 0)];
  else
    dir = start;

  return dir;
}

/*------------------------------------------------------------------*/
void
move_fighters (void)
{
  int attack[NB_TEAMS], defense[NB_TEAMS], new_health[NB_TEAMS];
  int i, dir, team, coef;
  FIGHTER *f;
  PLACE *p, *p0, *p1, *p2, *p3, *p4;
  int *move_offset, *move_x, *move_y;
  int sens, start, table;
  int cpu_influence[NB_TEAMS] = { 0, 0, 0, 0, 0, 0 };
  int temp = 0;

  sens = 0;

  for (i = 0; i < NB_TEAMS; ++i)
    if (CURRENT_CURSOR[i].control_type == CONFIG_CONTROL_TYPE_CPU
	&& CURRENT_CURSOR[i].active)
      cpu_influence[CURRENT_CURSOR[i].team] =
	LW_CONFIG_CURRENT_RULES.cpu_advantage;

  for (i = 0; i < PLAYING_TEAMS; ++i)
    {
      coef = ACTIVE_FIGHTERS[i] * PLAYING_TEAMS - CURRENT_ARMY_SIZE;
      coef *= 256;
      coef /= CURRENT_ARMY_SIZE;
      if (coef > 256)
	coef = 256;

      coef *=
	(LW_CONFIG_CURRENT_RULES.number_influence -
	 8) * (LW_CONFIG_CURRENT_RULES.number_influence - 8);
      coef /= 64;
      if (LW_CONFIG_CURRENT_RULES.number_influence < 8)
	coef = -coef;
      if (coef < 0)
	coef /= 2;
      coef += 256;

      attack[i] = (coef *
		   fsqrt (fsqrt (1 << (LW_CONFIG_CURRENT_RULES.fighter_attack
				       + cpu_influence[i])))) / (256 * 8);
      if (attack[i] >= MAX_FIGHTER_HEALTH)
	attack[i] = MAX_FIGHTER_HEALTH - 1;
      if (attack[i] < 1)
	attack[i] = 1;

      defense[i] = (coef *
		    fsqrt (fsqrt
			   (1 <<
			    (LW_CONFIG_CURRENT_RULES.fighter_defense +
			     cpu_influence[i])))) / (256 * 256);
      if (defense[i] >= MAX_FIGHTER_HEALTH)
	defense[i] = MAX_FIGHTER_HEALTH - 1;
      if (defense[i] < 1)
	defense[i] = 1;

      new_health[i] = (coef *
		       fsqrt (fsqrt
			      (1 <<
			       (LW_CONFIG_CURRENT_RULES.fighter_new_health +
				cpu_influence[i])))) / (256 * 4);
      if (new_health[i] >= MAX_FIGHTER_HEALTH)
	new_health[i] = MAX_FIGHTER_HEALTH - 1;
      if (new_health[i] < 1)
	new_health[i] = 1;

      ACTIVE_FIGHTERS[i] = 0;
    }
  start = (GLOBAL_CLOCK / 6) % NB_DIRS;
  table = (GLOBAL_CLOCK / 3) % 2;
  f = CURRENT_ARMY;

#ifdef ASM
  if (STARTUP_ASM && LW_CONFIG_CURRENT_RULES.asm_algorithm)
    boost_move_fighters (CURRENT_ARMY,
			 CURRENT_ARMY_SIZE,
			 CURRENT_AREA,
			 CURRENT_AREA_W,
			 ACTIVE_FIGHTERS,
			 attack,
			 defense,
			 new_health,
			 MAX_FIGHTER_HEALTH - 1,
			 sens,
			 start,
			 -GLOBAL_CLOCK,
			 FIGHTER_MOVE_XY_ASM[table],
			 FIGHTER_MOVE_OFFSET_ASM[table],
			 CURRENT_AREA_BACK->dat,
			 CURRENT_AREA_DISP->dat,
			 COLOR_FIRST_ENTRY,
			 COLORS_PER_TEAM,
			 LOCAL_DIR,
			 temp, temp, temp, temp, temp, temp, temp, temp);
  else
#else
  temp = 0;
#endif
  for (i = 0; i < CURRENT_ARMY_SIZE; ++i)
    {
      team = f->team;
      ACTIVE_FIGHTERS[team]++;
      start = (start < NB_DIRS - 1) ? start + 1 : 0;

      p = CURRENT_AREA + (f->y * CURRENT_AREA_W + f->x);

      if (p->mesh->info[team].update.time >= 0)
	{
	  p->mesh->info[team].state.dir =
	    get_close_dir (p->mesh, f, team, (sens++) % 2, start);
	}
      else if ((-p->mesh->info[team].update.time) < GLOBAL_CLOCK)
	{
	  p->mesh->info[team].state.dir =
	    get_main_dir (p->mesh, team, (sens++) % 2, start);
	  p->mesh->info[team].update.time = -GLOBAL_CLOCK;
	}

      dir = p->mesh->info[team].state.dir;

      move_offset = FIGHTER_MOVE_OFFSET[table][dir];
      move_x = FIGHTER_MOVE_X[table][dir];
      move_y = FIGHTER_MOVE_Y[table][dir];

      if (((p0 = p + move_offset[0])->mesh) && (!p0->fighter))
	{
	  erase_fighter (f);
	  p0->fighter = f;
	  p->fighter = NULL;
	  f->x += move_x[0];
	  f->y += move_y[0];
	  disp_fighter (f);
	}
      else
	{
	  if (((p1 = p + move_offset[1])->mesh) && (!p1->fighter))
	    {
	      erase_fighter (f);
	      p1->fighter = f;
	      p->fighter = NULL;
	      f->x += move_x[1];
	      f->y += move_y[1];
	      disp_fighter (f);
	    }
	  else
	    {
	      if (((p2 = p + move_offset[2])->mesh) && (!p2->fighter))
		{
		  erase_fighter (f);
		  p2->fighter = f;
		  p->fighter = NULL;
		  f->x += move_x[2];
		  f->y += move_y[2];
		  disp_fighter (f);
		}
	      else
		{
		  if (((p3 = p + move_offset[3])->mesh) && (!p3->fighter))
		    {
		      erase_fighter (f);
		      p3->fighter = f;
		      p->fighter = NULL;
		      f->x += move_x[3];
		      f->y += move_y[3];
		      disp_fighter (f);
		    }
		  else
		    {
		      if (((p4 = p + move_offset[4])->mesh) && (!p4->fighter))
			{
			  erase_fighter (f);
			  p4->fighter = f;
			  p->fighter = NULL;
			  f->x += move_x[4];
			  f->y += move_y[4];
			  disp_fighter (f);
			}
		      else
			{
			  if (p0->mesh
			      && p0->fighter && p0->fighter->team != team)
			    {
			      p0->fighter->health -= attack[team];
			      if (p0->fighter->health < 0)
				{
				  while (p0->fighter->health < 0)
				    p0->fighter->health += new_health[team];
				  p0->fighter->team = team;
				}
			      disp_fighter (p0->fighter);
			    }
			  else
			    {
			      if (p1->mesh
				  && p1->fighter && p1->fighter->team != team)
				{
				  p1->fighter->health -= attack[team]
				    >> SIDE_ATTACK_FACTOR;
				  if (p1->fighter->health < 0)
				    {
				      while (p1->fighter->health < 0)
					p1->fighter->health +=
					  new_health[team];
				      p1->fighter->team = team;
				    }
				  disp_fighter (p1->fighter);
				}
			      else
				{
				  if (p2->mesh
				      && p2->fighter
				      && p2->fighter->team != team)
				    {
				      p2->fighter->health -= attack[team]
					>> SIDE_ATTACK_FACTOR;
				      if (p2->fighter->health < 0)
					{
					  while (p2->fighter->health < 0)
					    p2->fighter->health +=
					      new_health[team];
					  p2->fighter->team = team;
					}
				      disp_fighter (p2->fighter);
				    }
				  else
				    {
				      if (p0->mesh
					  && p0->fighter
					  && p0->fighter->team == team)
					{
					  p0->fighter->health +=
					    defense[team];
					  if (p0->fighter->health >=
					      MAX_FIGHTER_HEALTH)
					    p0->fighter->health =
					      MAX_FIGHTER_HEALTH - 1;
					  disp_fighter (p0->fighter);
					}
				    }
				}
			    }
			}
		    }
		}
	    }
	}
      f++;
    }
}
