/**************************************************************
 * FFTacticsMUD : move.cpp                                    *
 **************************************************************
 * (c) 2002 Damien Dailidenas (Trenton). All rights reserved. *
 **************************************************************/

#include "main.h"
#include <strstream>

short dir_id(const string str) {
  return find(str, "north") ? DIR_N : find(str, "south") ? DIR_S : find(str, "east") ? DIR_E : find(str, "west") ? DIR_W : find(str, "ne") ? DIR_NE : find(str, "nw") ? DIR_NW : find(str, "se") ? DIR_SE : find(str, "sw") ? DIR_SW : find(str, "up") ? DIR_UP : DIR_DOWN;
}

bool CH::can_move(const ROOM *room) {
  if(!room)
    return false;

  return true;
}

void do_move(CH *ch, string argument="") {
  if(!ch->battle_room) {
    if(argument.empty() || !find(argument, "n e s w", true)) {
      ch->printf("Usage: move [n | e | s | w]\n\r");
      return;
    }

    if(ch->room != ch->area->room[ROOM_OUTSIDE]) {
      ch->room->echo(ch->name + " leaves.\n\r", ch);
      ch->to(ch->area->room[ROOM_OUTSIDE]);
      do_look(ch);
      ch->room->echo(ch->name + " arrives.\n\r", ch);
      return;
    }

    short dir = dir_id(argument);

    if(!ch->can_move(ch->area->exits[dir])) {
      ch->printf("ERROR: Can't move " + (string)dir_name[dir] + ".\n\r");
      return;
    }

    if(ch->group && ch->group->leader != ch && !ch->following)
      ch->leave_group();

    ch->room->echo(ch->name + " leaves " + dir_name[dir] + ".\n\r", ch);
    ch->to(ch->to_room);
    ch->following = NULL;
    do_look(ch);
    ch->room->echo(ch->name + " arrives from the " + dir_name[rev_dir[dir]] + ".\n\r", ch);

    if(ch->group && ch->group->leader == ch) {
      for(CH *pers = ch->group->people; pers; pers = pers->next_in_group) {
        if(pers != ch) {
          pers->following = ch;
          do_move(pers, argument);
        }
      }
    }

//    if((!ch->group || ch->group->leader == ch) && ch->area->battle_area && num_percent() < 50)
  //    ch->load_battle();

    return;
  }

  if(ch->battle->turn == ch && !ch->orig_room) {
    if(!ch->temp_action.action)
      ch->temp_action.action = do_move;

    ch->temp_action.range = (ch->temp_action.action == do_act ? ch->get_act_range(ch->action.id) : ch->get_move_range());
    ch->orig_room = ch->battle_room;
    do_look(ch);
    ch->printf("Move to a location within the %s circle.\n\rType 'ok' when done or 'cancel' to go back.\n\r", ch->temp_action.action == do_act ? "red" : "blue");
    ch->commands = "move ok cancel";
    return;
  }

  string arg;
  split(argument, arg);

  if(arg.empty() || !find(arg, "n e s w", true)) {
    ch->printf("Usage: move [direction] -[distance]\n\r");
    return;
  }

  short distance = argument.empty() || !is_num(argument) ? 1 : atoi(argument.c_str());
  ch->to_room = ch->battle_room;
  short dir = dir_id(arg);

  for(short x = 0; x < distance; ++x) {
    if(!ch->move(dir)) {
      ch->printf("move: ERROR: Can't move %d %s.\n\r", distance, arg.c_str());
      return;
    }
  }

  ch->to(ch->to_room);
  do_look(ch);
  ch->check_battle();
  return;  
}  

const char *dir_name[] = {
  "north", "east", "south", "west", "northeast", "northwest", "southeast", "southwest"
};

const char *sh_dir_name[] = {
  "n", "e", "s", "w", "ne", "nw", "se", "sw", "u", "d"
};

const short rev_dir[] = {
  2, 3, 0, 1, 7, 6, 5, 4
};

bool CH::move(const short dir) {
  BATTLE_ROOM *to_room = NULL, *room = this->to_room;
  long id = 0, size = battle->area->size();
    
  if(dir == DIR_N)       	id = room->id - battle->area->width;
  else if(dir == DIR_S)  	id = room->id + battle->area->width;
  else if(dir == DIR_E)   	id = room->id + 1;
  else if(dir == DIR_W)		id = room->id - 1;
  else if(dir == DIR_NW)     	id = room->id - battle->area->width - 1;
  else if(dir == DIR_NE)     	id = room->id - battle->area->width + 1;
  else if(dir == DIR_SW)     	id = room->id + battle->area->width - 1;
  else if(dir == DIR_SE)     	id = room->id + battle->area->width + 1;
  
  if(id > size)  
    id -= size;
  else if(id < 1)
    id += size;
  
  to_room = battle->area->room[id];
  
  if(to_room && ((abs)(to_room->x - room->x) > 22 || (abs)(to_room->y - room->y) > 22))
    to_room = NULL;
  
  if(!to_room)
    return false;
  
  this->to_room = to_room;
  return true;
} 

void CH::retreat() {
  CH *victim = get_nearest_target(false);

  if(victim) {
    short distance = this->distance(victim) + Mv + 1, x;
          
    while(distance--) {
      for(x = 1; battle->area->room[x]; ++x) {
        if(!battle->area->room[x]->people && battle->area->room[x]->distance(victim->battle_room) == distance && battle->area->room[x]->distance(battle_room) <= Mv) {
          to(battle->area->room[x]);
          return;
        }
      }
    }
  }

  return;
}
  
bool CH::move_within_range(CH *victim) {
  for(short x = 1; battle->area->room[x]; ++x) {
    if(!battle->area->room[x]->people && battle->area->room[x]->distance(victim->battle_room) <= temp_action.range) {
      battle->echo("{B" + name + " moved.{0\n\r");
      to(battle->area->room[x]);
      return true;
    }
  } 
    
  return false;
}

void CH::advance() {
  CH *victim = get_nearest_target(false);

  if(victim) {
    short distance, x, pdist = this->distance(victim), rdist;
      
    for(distance = MIN(pdist - Mv, 0); distance < pdist; ++distance) {
      for(x = 1; battle->area->room[x]; ++x) {
        if(!battle->area->room[x]->people && (rdist = battle->area->room[x]->distance(victim->battle_room)) == distance && rdist < pdist && battle->area->room[x]->distance(battle_room) <= Mv) {
          to(battle->area->room[x]);
          return;
        }
      }   
    }     
  }       
  
  return;
}

void CH::to(ROOM *room) {
  from_battle_room();
  from_room();
  this->room = room;
  next_in_room = room->people;
  room->people = this;
  area = room->area;
  return;
}

void CH::from_room() {
  if(!room)
    return;
  
  if(this == room->people)
    room->people = next_in_room;
  else {
    for(CH *prev = room->people; prev; prev = prev->next_in_room) {
      if(prev->next_in_room == this) {
        prev->next_in_room = next_in_room;
        break;
      }
    }
  }

  room = NULL; 
  next_in_room = NULL;
  return;
}

void CH::to(BATTLE_ROOM *room) {
  from_room();
  from_battle_room();
  battle_room = room;
  next_in_room = room->people;
  room->people = this;
  return;
}

void CH::from_battle_room() {
  if(!battle_room)
    return;

  if(this == battle_room->people)
    battle_room->people = next_in_room;
  else {
    for(CH *prev = battle_room->people; prev; prev = prev->next_in_room) {
      if(prev->next_in_room == this) {
        prev->next_in_room = next_in_room;
        break;
      }
    }
  }
  
  battle_room = NULL;
  next_in_room = NULL;
  return;
}

short CH::get_act_range(const short id) {
  if(skill_table[id].job == JOB_CHEMIST && skill_table[id].type == SK_ACTION)
    if(cjob == JOB_CHEMIST || has_skill("Throw Item", true))
      return 4;

  if(skill_table[id].fun == do_attack || (skill_table[id].job == JOB_ARCHER && skill_table[id].type == SK_ACTION))
    return (eq[LOC_RHAND] ? obj_table[eq[LOC_RHAND]->id].range : 1);

  return skill_table[id].range;
}

short CH::get_move_range() {
  short range = Mv;

  if(has_skill("Move +1", true))
    ++range;

  return range;
}

void CH::check_battle() {
  if(room->area->battle())
    room->area->battle()(this);

  return;
}