/*   Copyright 2005,2006 Paweł Niegowski
*
*    This file is part of Fenrir.
*
*    Fenrir 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.
*
*    Fenrir 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 Fenrir; if not, write to the Free Software
*    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "player.h"
#include "main.h"
#include "map.h"
#include "palette.h"
#include "network.h"
#include "login.h"
#include "damage.h"
#include "play.h"
#include "combat.h"
#include "console.h"
#include "ghost.h"
#include "lookup.h"
#include "particle.h"
#include "skill.h"
#include <stdio.h>
#include <string.h>
#include <math.h>

#define ATTACK_DELAY 350
#define ATTACK_FRAME_DURATION 15
#define HIT_FRAME_DURATION 30
#define MOVEMENT_PACKET_DELAY 4
#define ANIM_SPEED 7
#define ANIM_MAX 16
#define ANIM_WALK_MAX 6
#define COL_MAX 20

#define STUNLOCK_MOD 10

//just a helper variable
float rot;

//client-side attack target (for debug purposes)
int last_attack_target = 0;

// helper function - replaces col1 with col2 on a bitmap
void _replace_pixel(BITMAP *bmp, int col1, int col2)
{
 for(int i = 0; i < bmp->h; i++)
  for(int j = 0; j < bmp->w; j++)
   if(bmp->line[i][j] == col1)
    bmp->line[i][j] = col2;
}


struct PlayerPalette
{
 int l;
 int c1[COL_MAX],c2[COL_MAX];
 void read(FILE *f)
 {
  fscanf(f,"%d",&l);
  for(int i = 0; i < l; i++)
   fscanf(f,"%d %d",&(c1[i]),&(c2[i]));
 }
 void apply(BITMAP *bmp)
 {
  for(int i = 0; i < l; i++)
   _replace_pixel(bmp,c1[i],c2[i]);
 }
};

PlayerPalette player_palettes[CLASS_MAX][16];
int head_palettes[HEAD_MAX][16][4];


bool pvp_mode = false;
bool fencing_expert_mode = false;
unsigned int free_statpoints = 0;
char aura_current[3],aura_target[3];
char energy;
long regen_timer;
Player *player = 0;
int keymap[KEYMAP_NUM];

DATAFILE *player_sprites[CLASS_MAX];
DATAFILE *head_sprites[HEAD_MAX];
int player_sprites_headx[CLASS_MAX][SPRITE_MAX];
int player_sprites_heady[CLASS_MAX][SPRITE_MAX];

PlayerBase::PlayerBase()
: move_anim_delay(0), moved(false), die_blur(false), last_frame_moved(false),
  fx(0), fy(0), walk_anim_counter(0), walk_anim_frame(0), anim_current_frame(0), job(0)
{
 colorize_color = 0;
 die_blur = false;
 eva_1 = eva_2 = 0;
 temp_sprite = create_bitmap(32,32);
}


Player::Player(unsigned char *data, int length)
: hard_delay(0), soft_delay(0), range(40*40), send_packet_timer(MOVEMENT_PACKET_DELAY)
{
 regen_timer = REGEN_DELAY;
 attack_a_pressed = attack_x_pressed = false;
 stamina_regen_counter = 0;
 strcpy(name,login);
 aura_current[0] = aura_target[0] = aura_current[1] = aura_target[1] = 33;
 aura_current[2] = aura_target[2] = 34;
 energy = 100;
 if(!data) { load_sprites(0,0,0, 0,0,0); }
 else
 {
  level = data[4];
  fx = net_x = x = data[21]*16;
  fy = net_y = y = data[22]*16;
  speed = data[23] / 60.0 * 16.0;
  hp = mhp = mp = mmp = 0;
  memcpy(&hp,data+10,2);
  memcpy(&mhp,data+12,2);
  memcpy(&mp,data+14,2);
  memcpy(&mmp,data+16,2);
  st = data[18];
  free_statpoints = 0;
  energy = data[24];
  memcpy(&free_statpoints,data+25,2);
  memcpy(stat,data+27,12);
  load_sprites(data[8],data[6]&15,data[7], ((unsigned char*)data)[5]&15,((unsigned char*)data)[6]>>4,((unsigned char*)data)[5]>>4);
  for(int i = 0; i < 16; i++)
  {
   //two-byte bitfield indicating initial state of hotkeys
   hotkeys[i].orig_delay = 60;
   hotkeys[i].delay = ((data[39+i/8])&(1<<(i%8))) ? 1:0;
  }

 }
 //loading the keymap
 keymap[KEYMAP_UP] = get_config_int("keymap","up",KEY_UP);
 keymap[KEYMAP_DOWN] = get_config_int("keymap","down",KEY_DOWN);
 keymap[KEYMAP_LEFT] = get_config_int("keymap","left",KEY_LEFT);
 keymap[KEYMAP_RIGHT] = get_config_int("keymap","right",KEY_RIGHT);

 keymap[KEYMAP_A] = get_config_int("keymap","a",KEY_A);
 keymap[KEYMAP_B] = get_config_int("keymap","b",KEY_S);

 keymap[KEYMAP_X] = get_config_int("keymap","x",KEY_Z);
 keymap[KEYMAP_Y] = get_config_int("keymap","y",KEY_X);

 keymap[KEYMAP_L] = get_config_int("keymap","l",KEY_Q);
 keymap[KEYMAP_R] = get_config_int("keymap","r",KEY_W);
}

OtherPlayer::OtherPlayer(unsigned char *data, int length)
{
 if(length < 11) return;
 obj_num = data[0];
 objects[obj_num] = this;
 load_sprites(data[1],data[3]&15,data[4], ((unsigned char*)data)[2]&15,((unsigned char*)data)[3]>>4,((unsigned char*)data)[2]>>4);
 speed = data[5] / 60.0 * 16.0;
 unsigned int _x = data[8]&255, _y = data[9]&255, _detail = data[10]&255;

 fx = x = net_x = (_x<<4) + (_detail&15) - 8;
 fy = y = net_y = (_y<<4) + (_detail>>4) - 8;
 net_updated = true;

 memcpy(name,data+11,length-11);
 name[length-11] = 0;
}


PlayerBase::~PlayerBase()
{
 for(int i=0; i<SPRITE_MAX; i++)
  destroy_bitmap(sprites[i]);
 destroy_bitmap(head_sprite);
 destroy_bitmap(temp_sprite);
 if(player == this)
  player = 0;
}

void Player::set_cam()
{
 cam_target_x = (int)fx - buf->w/2 + 8;
 cam_target_y = (int)fy - buf->h/2 + 8;
 cam_x = cam_target_x;
 cam_y = cam_target_y;
}

//a helper function to test whether an object is in front of the player
bool PlayerBase::_is_in_front(Object *o)
{
 if(direction == DIR_UP) return o->y < y+16;
 if(direction == DIR_DOWN) return o->y > y-16;
 if(direction == DIR_LEFT) return o->x < x+16;
 if(direction == DIR_RIGHT) return o->x > x-16;
 return false;
}

void PlayerBase::_turn_to_target(Object *o)
{
 int difx, dify;
 difx = x - o->x;
 dify = y - o->y;
 if(ABS(difx) > ABS(dify))
  if(difx > 0)
   direction = DIR_LEFT;
  else
   direction = DIR_RIGHT;
 else
  if(dify > 0)
   direction = DIR_UP;
  else
   direction = DIR_DOWN;

}

void PlayerBase::think()
{
 if(evade_counter)
 {
  evade_counter--;
  eva_1->fx = sin(evade_counter/30.0*3.14) * 24.0;
  eva_2->fx = -sin(evade_counter/30.0*3.14) * 24.0;
 } else
 {
  if(eva_1) { eva_1->remove = true; eva_1 = 0; }
  if(eva_2) { eva_2->remove = true; eva_2 = 0; }
 }
 if(dead && !die_blur)
 {
  die_blur = true;
  for(int i = 0; i < 4; i++)
  {
   new Ghost(this,i*0.5,0,6,5+i,true,false);
   new Ghost(this,i*-0.5,0,6,5+i,true,false);
  }
 }
 if(!dead) { die_blur = false; }
 if(move_anim_delay) move_anim_delay--;
 if(stunlock)
 {
  stunlock--;
  if(!(stunlock%3)/* && !network_disabled*/)
   new Ghost(this,0,0,5,2,false,false);
  fx += (net_x-fx)/STUNLOCK_MOD;
  fy += (net_y-fy)/STUNLOCK_MOD;
 }
 anim_select();
  if(colorize_color)
 {
  colorize_timer++;
  if(colorize_timer > colorize_speed)
  { colorize_phase++; colorize_timer = 0;
    if(colorize_phase >= TRANS_LEVELS*2) colorize_color = 0; return; }
 }

}

void OtherPlayer::think()
{
// if(ABS(net_x - x) > speed * 60) fx = net_x;
// if(ABS(net_y - y) > speed * 60) fy = net_y;
 //freshly "killed"
 if(kill_counter == KILL_DELAY) { new Ghost(this,0,0,7,2,false,false); }
 if(kill_counter > 0) { kill_counter--; targetable = false; }
 if(!kill_counter)  { remove = true; }
 if(pvp_mode) targetable = true; else targetable = false;
 moved = false;

 if(net_x - x > speed) { fx += speed; direction = DIR_RIGHT; moved = true; move_anim_delay = 5; } else
 if(x - net_x > speed) { fx -= speed; direction = DIR_LEFT; moved = true; move_anim_delay = 5; } else
  if(last_frame_moved) fx = net_x;

 if(net_y - y > speed) { fy += speed; direction = DIR_DOWN; moved = true; move_anim_delay = 5; } else
 if(y - net_y > speed) { fy -= speed; direction = DIR_UP; moved = true; move_anim_delay = 5; } else
  if(last_frame_moved) fy = net_y;

 last_frame_moved = moved;
 x = (int)fx;
 sort_y = y = (int)fy;

 PlayerBase::think();
}


void Player::think()
{
 if(regen_timer)
 {
  regen_timer--;
  if(regen_timer == 1)  new DelayEnd(100);
 }
 if(kill_counter) kill_counter = -1;
 static NLtime t;
 nlTime(&t);
 if(hard_delay && time_diff(&t,&hard_time) > hard_delay) hard_delay = 0;
 if(soft_delay && time_diff(&t,&soft_time) > soft_delay)  { soft_delay = 0; new DelayEnd(); }
 if(!stunlock && !hard_delay && !dead && !menu_active && !sitting)
 {
  if(key[keymap[KEYMAP_LEFT]] && x-speed > 0 &&
     map_check((int)(x-speed),y+1) && map_check((int)(x-speed),y+15))
  {  fx -= speed; direction = DIR_LEFT;  moved = true; move_anim_delay = 5; }
  if(key[keymap[KEYMAP_RIGHT]] && x+speed+16 < (map_w << 4) &&
     map_check((int)(x+16+speed),y+1) && map_check((int)(x+16+speed),y+15))
  { fx += speed; direction = DIR_RIGHT; moved = true; move_anim_delay = 5; }
  if(key[keymap[KEYMAP_UP]] && y-speed > 0 &&
     map_check(x+1,(int)(y-speed)) && map_check(x+15,(int)(y-speed)))
  { fy -= speed; direction = DIR_UP; moved = true; move_anim_delay = 5; }
  if(key[keymap[KEYMAP_DOWN]] && y+16+speed < (map_h << 4) &&
     map_check(x+1,(int)(y+16+speed)) && map_check(x+15,(int)(y+16+speed)))
  { fy += speed; direction = DIR_DOWN; moved = true; move_anim_delay = 5; }
 }

 x = (int)fx;
 sort_y = y = (int)fy;

 if(net_updated && !stunlock && !network_disabled)
 {
  if(ABS(x - net_x) > 64) fx = x =  net_x;
  if(ABS(y - net_y) > 64) fy = y = net_y;
  net_updated = false;
 }

 cam_target_x = x - buf->w/2 + 8;
 cam_target_y = y - buf->h/2 + 8;

 //stamina regeneration
 if(dead) st = 100;
 if(st < 100)
 { stamina_regen_counter++; if(stamina_regen_counter >= 30) { stamina_regen_counter = 0; st++; }}

 //attack
 if(!hard_delay && !soft_delay && !console_active && !menu_active && !dead && !sitting)
 {
  if(fencing_expert_mode)
  {
   if(!key[keymap[KEYMAP_X]] && attack_x_pressed)
     input_attack(keymap[KEYMAP_X]); else
   if(!key[keymap[KEYMAP_A]] && attack_a_pressed)
    input_attack(keymap[KEYMAP_A]);
  } else
  {
   if(key[keymap[KEYMAP_X]] && !key[keymap[KEYMAP_B]] ) input_attack(keymap[KEYMAP_X]); else
   if(key[keymap[KEYMAP_A]] && !key[keymap[KEYMAP_B]] ) input_attack(keymap[KEYMAP_A]);
  }
 }
 if(key[keymap[KEYMAP_X]]) attack_x_pressed = true; else attack_x_pressed = false;
 if(key[keymap[KEYMAP_A]]) attack_a_pressed = true; else attack_a_pressed = false;

 if(moved || aura_updated)
 {
  send_packet_timer++;
  if(send_packet_timer > MOVEMENT_PACKET_DELAY)
  {
   send_packet_timer = 0;
   network_send_position(x,y);
   moved = false;
   aura_updated = false;
  }
 }

 PlayerBase::think();
}

void Player::input_attack(int attack_key)
{
  if(!combat_mode) return;
  static unsigned long int closest_target_distance, tmp;
  static int closest_target;
  closest_target = -1;
  closest_target_distance = range;
  for(int i=0; i<256; i++)
   if(objects[i] && objects[i]->targetable && _is_in_front(objects[i]))
   {
    //squared distance
    tmp = (objects[i]->x - x) * (objects[i]->x - x) + (objects[i]->y - y) * (objects[i]->y - y);
    if(tmp < closest_target_distance)
    {     closest_target_distance = tmp;  closest_target = i;  }
   }//if
  if(closest_target > -1)
  {
   last_attack_target = closest_target;
   hard_delay = ATTACK_DELAY;
   nlTime(&hard_time);
   unsigned char msg[3];
   msg[0] = ID_ATTACK;
   //000 - attack w/o stamina, 001 - attack with stamina
   if(attack_key == keymap[KEYMAP_A])
    msg[1] = 1;
   else
    msg[1] = 0;
   // 0 - no height (not used), 1 - low, 2 - mid, 3 - high
   int h = 2;
   if(fencing_expert_mode)
   {
    if(key[keymap[KEYMAP_B]]) h = 3;
    if(key[keymap[KEYMAP_Y]]) h = 1;
   } else h = 0;
   msg[1] |= h << 2;
   msg[2] = closest_target;
   network_send_packet(msg,3);
  }
  if(anim_queue.empty())
  {
   anim_queue.push_back(_anim(10,ATTACK_FRAME_DURATION));
   anim_queue.push_back(_anim(11,ATTACK_FRAME_DURATION));
  }
}

void PlayerBase::_draw()
{
 if(colorize_color)
 {
  clear_to_color(temp_sprite,colorize_color);
  //multiply
  color_map = trans_map+TRANS_LEVELS;
  draw_trans_sprite(temp_sprite,sprites[anim_current_frame],0,0);
  for(int i = 0; i < temp_sprite->w; i++)
   for(int j = 0; j < temp_sprite->h; j++)
    if(!sprites[anim_current_frame]->line[j][i])
     temp_sprite->line[j][i] = 0;
  if(direction == DIR_LEFT)
   draw_sprite_h_flip(buf,temp_sprite,x-cam_x-8,y-cam_y-16);
  else
   draw_sprite(buf,temp_sprite,x-cam_x-8,y-cam_y-16);
  color_map = trans_map + ABS(colorize_phase-8);
  if(direction == DIR_LEFT)
  {
   clear(temp_sprite);
   draw_sprite_h_flip(temp_sprite,sprites[anim_current_frame],0,0);
   draw_trans_sprite(buf,temp_sprite,x-cam_x-8,y-cam_y-16);
  }
  else
   draw_trans_sprite(buf,sprites[anim_current_frame],x-cam_x-8,y-cam_y-16);
 }
 else
 {
  if(direction == DIR_LEFT)
   draw_sprite_h_flip(buf,sprites[anim_current_frame],x-cam_x-8,y-cam_y-16);
  else
   draw_sprite(buf,sprites[anim_current_frame],x-cam_x-8,y-cam_y-16);
 }

}

void PlayerBase::draw_upper()
{
 if(evade_counter || kill_counter > 0) return;
 set_clip_rect(buf,0,y-cam_y-16,buf->w-1,y-cam_y);
 _draw();
 set_clip_rect(buf,0,0,buf->w-1,buf->h-1);
 //hline(buf,net_x-30-cam_x,net_y-cam_y,net_x+30-cam_x,255);
 //vline(buf,net_x-cam_x,net_y-30-cam_y,net_y+30-cam_y,255);
// textprintf_ex(buf,font,0,12*(1+obj_num),255,-1,"%d (%d) / %d (%d)",x,y,net_x,net_y);
}

void PlayerBase::draw_lower()
{
 if(evade_counter || kill_counter > 0) return;
 set_clip_rect(buf,0,y-cam_y,buf->w-1,y-cam_y+16);
 _draw();
 set_clip_rect(buf,0,0,buf->w-1,buf->h-1);
// textprintf_ex(buf,font,0,12*(1+obj_num),255,-1,"%d (%d) / %d (%d)",x,y,net_x,net_y);
}

void PlayerBase::display_name()
{
 textout_centre_ex(buf,font,name,x-cam_x+8,y-cam_y+16,255,-1);
}

void PlayerBase::reload_sprites(unsigned char *data)
{
 for(int i=0; i<SPRITE_MAX; i++)
  destroy_bitmap(sprites[i]);
 destroy_bitmap(head_sprite);
 load_sprites(data[0],data[2]&15,data[3], ((unsigned char*)data)[1]&15,((unsigned char*)data)[2]>>4,((unsigned char*)data)[1]>>4);
}

void PlayerBase::attack(int target, Packet *attack_data)
{
// console_add_line("bar",3,255);
 rot = (rand()%100 / 100.0)*6.28;
 _attack(target,attack_data);

 int mx = 0,my = 0;
 if(direction == DIR_UP) mx=0,my=-1; else
 if(direction == DIR_DOWN) mx=0,my=1; else
 if(direction == DIR_LEFT) mx=-1,my=0; else
 if(direction == DIR_RIGHT) mx=1,my=0;
 new Ghost(this,mx*3,my*3,6,3,true,true);
 new Ghost(this,mx*2,my*2,6,3,true,true);
 new Ghost(this,mx*1,my*1,6,3,true,true);
// new Ghost(this,mx*-3,my*-3,6,3,true,true);
// new Ghost(this,mx*-2,my*-2,6,3,true,true);
// new Ghost(this,mx*-1,my*-1,6,3,true,true);
}

void Player::attack(int target, Packet *attack_data)
{
 //stamina attack
 if(attack_data->data[3]&1) { if(st < 10) st = 0; else st -= 10; }
 PlayerBase::attack(target,attack_data);
}

void OtherPlayer::attack(int target, Packet *attack_data)
{
 anim_queue.push_back(_anim(10,ATTACK_FRAME_DURATION));
 anim_queue.push_back(_anim(11,ATTACK_FRAME_DURATION));
 PlayerBase::attack(target,attack_data);
}

void PlayerBase::combat_hit(int target, Packet *attack_data, int num)
{
 unsigned char *data = attack_data->data+3;
 int hit_y = 1;
 if( ((data[0]>>2)&3) == 1 ) hit_y = 10; else
 if( ((data[0]>>2)&3) == 3 ) hit_y = -10;
 int scale = 10+5*(data[0]&1);
 Slash *s = new Slash(objects[target],rot+num*0.6,253,scale,hit_y);
 s->show_delay = 10*num;
 s->animators.push_back(new AttachAnimator(objects[target],true));
 if(data[1] & 1<<num)
 {
  ParticleSystem *ps = new ParticleSystem(300,136,8);
  ps->x = objects[target]->x+8;
  ps->y = objects[target]->y+hit_y;
  ps->lifetime = 5;
  ps->show_delay = 10 * num;
  ps->animators.push_back(new AttachAnimator(objects[target],true));
  ps->set_point_emitter(0, 0, 4.0, 2.0, -8.0, -4.0, 10*scale, 2*scale, 4*scale);
 }
}

void PlayerBase::evade(int effect_duration)
{
 regen_timer = REGEN_DELAY;
 if(evade_counter) return;
 eva_1 = new Ghost(this,0.0,0.0,6,effect_duration,true,true);
 eva_2 = new Ghost(this,0.0,0.0,6,effect_duration,true,true);
 evade_counter = effect_duration;
}

void PlayerBase::get_dmg(unsigned int dmg, int delay, float _vx, float _vy)
{
 anim_queue.push_back(_anim(7,HIT_FRAME_DURATION));
}

void Player::get_dmg(unsigned int dmg, int delay, float _vx, float _vy)
{
 PlayerBase::get_dmg(dmg,delay,_vx,_vy);
 if(hp > dmg)
  hp -= dmg;
 else
  hp = 0;
 Damage *d = new Damage(dmg,248,x+8,y,_vx,_vy);
 d->show_delay = delay;
 d->animators.push_back(new AttachAnimator(this, true));
}

void OtherPlayer::get_dmg(unsigned int dmg, int delay, float _vx, float _vy)
{
 PlayerBase::get_dmg(dmg,delay,_vx,_vy);
 Damage *d = new Damage(dmg,247,x+8,y,_vx,_vy);
 d->show_delay = delay;
 d->animators.push_back(new AttachAnimator(this, true));
}

void PlayerBase::effect_colorize(int color, int l)
{
 if(colorize_color)
 { colorize_color = color; return; }
 colorize_color = color;
 colorize_speed = l / 16;
 colorize_timer = 0;
 colorize_phase = 1;
}

BITMAP* PlayerBase::get_head()
{ return head_sprite;  }

BITMAP* PlayerBase::get_front_sprite()
{ return sprites[0]; }

BITMAP* PlayerBase::get_current_sprite()
{ return sprites[anim_current_frame]; }

void PlayerBase::load_sprites(int body, int head, int headgear, unsigned int body_pal, unsigned int head_pal, unsigned int headgear_pal)
{
 int head_num = 0, dir_num = 0, frame_num = 0;
 if(head_pal < 0) allegro_message("WTF?");
 if(!player_sprites[body]) body = 0;
 if(!head_sprites[head]) head = 0;
 for(int i=0; i<SPRITE_MAX; i++)
 {
  sprites[i] = create_bitmap(SPRITE_W,SPRITE_H);
  clear_bitmap(sprites[i]);
  blit((BITMAP*)player_sprites[body][i].dat,sprites[i],0,0,0,0,SPRITE_W,SPRITE_H);
  player_palettes[0][body_pal].apply(sprites[i]);
  dir_num = (int)((float)i/(SPRITE_MAX-3)*3);
  frame_num = i%((SPRITE_MAX-3)/3);
  if(i < 48)
   head_num = frame_num==7 ? 3+dir_num : dir_num;
  else
   head_num = 0;
  if(i == SPRITE_MAX-1) head_num = 6;
  masked_blit((BITMAP*)head_sprites[head][head_num].dat,sprites[i],0,0,player_sprites_headx[job][i],player_sprites_heady[job][i],HEAD_W,HEAD_H);
  for(int j = 0; j < 4; j++)
   _replace_pixel(sprites[i],j+1,head_palettes[0][head_pal][j]);
 }
 head_sprite = create_bitmap(HEAD_W,HEAD_H);
 blit((BITMAP*)head_sprites[head][0].dat,head_sprite,0,0,0,0,HEAD_W,HEAD_H);
 for(int j = 0; j < 4; j++)
  _replace_pixel(head_sprite,j+1,head_palettes[0][head_pal][j]);
}

void PlayerBase::anim_select()
{
 if(dead)
 { anim_current_frame = SPRITE_MAX-1; anim_queue.clear(); return; }
 if(sitting)
 { anim_current_frame = SPRITE_MAX-3; direction = DIR_DOWN;  anim_queue.clear();  return; }
 if(!anim_queue.empty())
 {
  list<_anim>::iterator it = anim_queue.begin();
  if((*it).frame > 47) anim_current_frame = (*it).frame; else
  if(direction == DIR_DOWN)
   anim_current_frame = (*it).frame; else
  if(direction == DIR_UP)
   anim_current_frame = ANIM_MAX*2+(*it).frame; else
  if(direction == DIR_LEFT || direction == DIR_RIGHT)
   anim_current_frame = ANIM_MAX+(*it).frame;

  (*it).duration -= anim_queue.size();
  if((*it).duration <= 0) anim_queue.erase(it);
  return;
 }
 if(moved || last_frame_moved || move_anim_delay)
 {
  walk_anim_counter++;
  if(walk_anim_counter > ANIM_SPEED)
  {
   walk_anim_counter = 0;
   walk_anim_frame++;
   if(walk_anim_frame > ANIM_WALK_MAX)
    walk_anim_frame = 1;
  }
  if(direction == DIR_DOWN)
   anim_current_frame = walk_anim_frame;
  if(direction == DIR_UP)
   anim_current_frame = ANIM_MAX*2+walk_anim_frame;
  if(direction == DIR_LEFT || direction == DIR_RIGHT)
   anim_current_frame = ANIM_MAX+walk_anim_frame;
  return;
 }
 anim_current_frame = (direction==DIR_DOWN?0:(direction==DIR_LEFT||direction==DIR_RIGHT?ANIM_MAX:ANIM_MAX*2));
}

void create_player(unsigned char *playerdata,int length)
{
 char path[100];
 if(!player_sprites[0]) player_sprites[0] = load_datafile("gfx/sprites/player_sprite.dat");
 for(int i=0; i < HEAD_MAX; i++)
 {
  if(head_sprites[i]) continue;
  sprintf(path,"gfx/sprites/head%d.dat",i);
  head_sprites[i] = load_datafile(path);
 }
 if(player) player->remove = true;
 player = new Player(playerdata,length);
}

void player_init()
{
 for(int i=0; i<CLASS_MAX; i++)
  player_sprites[i] = 0;
 for(int i=0; i<HEAD_MAX; i++)
  head_sprites[i] = 0;
 player_load_defs();
}

void player_load_defs()
{
 FILE *f;
 char path[100];
 for(int i=0; i<CLASS_MAX; i++)
 {
  sprintf(path,"gfx/sprites/%d.def",i);
  f = fopen(path,"r");
  if(!f) continue;
  for(int j=0; j<SPRITE_MAX; j++)
   fscanf(f,"%d %d",&(player_sprites_headx[i][j]),& (player_sprites_heady[i][j]));
  fclose(f);

  sprintf(path,"gfx/sprites/pal%d.def",i);
  f = fopen(path,"r");
  if(!f) continue;
  for(int j = 0; j < 16; j++)
   player_palettes[i][j].read(f);
  fclose(f);
 }
 for(int i=0; i<HEAD_MAX; i++)
 {
  sprintf(path,"gfx/sprites/head%d.def",i);
  f = fopen(path,"r");
  if(!f) continue;
  for(int j = 0; j < 16; j++)
   fscanf(f,"%d %d %d %d",&(head_palettes[i][j][0]),&(head_palettes[i][j][1]),&(head_palettes[i][j][2]),&(head_palettes[i][j][3]));
  fclose(f);
 }
}
