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

#include "main.h"
#include <strstream>
#include <iomanip>
#include <mysql/mysql.h>
#include <cstdio>

const struct skill_type skill_table[] = {
  {},
  { "Attack", SK_ACTION, do_attack, 0, 1, 0, 0, false },
  
  /*****************
   * SQUIRE SKILLS *
   *****************/
  { "Accumulate",     SK_ACTION,   do_accumulate,    JOB_SQUIRE, 0, 1, 0,  300, true    },
  { "Dash",           SK_ACTION,   do_dash,          JOB_SQUIRE, 1, 1, 0,  80,  false   },
  { "Throw Stone",    SK_ACTION,   do_throwstone,    JOB_SQUIRE, 4, 1, 0,  90,  false   },
  { "Heal",           SK_ACTION,   do_heal,          JOB_SQUIRE, 1, 1, 0,  150, true    },
  { "Counter Tackle", SK_REACTION, do_countertackle, JOB_SQUIRE, 0, 0, 0,  180, 0,      TRIG_HPDAMAGE },
  { "Equip Axe",      SK_SUPPORT,  NULL,             JOB_SQUIRE, 0, 0, 0,  170, 0       },
  { "Monster Skill",  SK_SUPPORT,  do_monsterskill,  JOB_SQUIRE, 0, 0, 0,  200, false   },
  { "Defend",         SK_SUPPORT,  do_defend,        JOB_SQUIRE, 0, 0, 0,  50,  0       },
  { "Gained Jp UP",   SK_SUPPORT,  NULL,             JOB_SQUIRE, 0, 0, 0,  200, 0       },
  { "Move +1",        SK_MOVE,     NULL,             JOB_SQUIRE, 0, 0, 0,  200, 0       },
  
  /******************
   * CHEMIST SKILLS *
   ******************/
  { "Potion",         SK_ACTION,   do_potion,       JOB_CHEMIST, 1, 1, 0, 30,  true },
  { "Hi-Potion",      SK_ACTION,   do_hipotion,     JOB_CHEMIST, 1, 1, 0, 200, true },
  { "X-Potion",       SK_ACTION,   do_xpotion,      JOB_CHEMIST, 1, 1, 0, 300, true },
  { "Ether",          SK_ACTION,   do_ether,        JOB_CHEMIST, 1, 1, 0, 300, true },
  { "Hi-Ether",       SK_ACTION,   do_hiether,      JOB_CHEMIST, 1, 1, 0, 400, true },
  { "Elixir",         SK_ACTION,   do_elixir,       JOB_CHEMIST, 1, 1, 0, 900, true },
  { "Antidote",       SK_ACTION,   do_antidote,     JOB_CHEMIST, 1, 1, 0, 70,  true },
  { "Eye Drop",       SK_ACTION,   do_eyedrop,      JOB_CHEMIST, 1, 1, 0, 80,  true },
  { "Echo Grass",     SK_ACTION,   do_echograss,    JOB_CHEMIST, 1, 1, 0, 120, true },
  { "Maiden's Kiss",  SK_ACTION,   do_maidenskiss,  JOB_CHEMIST, 1, 1, 0, 200, true },
  { "Soft",           SK_ACTION,   do_soft,         JOB_CHEMIST, 1, 1, 0, 250, true },
  { "Holy Water",     SK_ACTION,   do_holywater,    JOB_CHEMIST, 1, 1, 0, 400, true },
  { "Remedy",         SK_ACTION,   do_remedy,       JOB_CHEMIST, 1, 1, 0, 700, true },
  { "Phoenix Down",   SK_ACTION,   do_phoenixdown,  JOB_CHEMIST, 1, 1, 0, 90,  true },
  { "Auto Potion",    SK_REACTION, do_autopotion,   JOB_CHEMIST, 0, 0, 0, 400, 0,       TRIG_HPDAMAGE },
  { "Throw Item",     SK_SUPPORT,  NULL,            JOB_CHEMIST, 0, 0, 0, 350, true },
  { "Maintenance",    SK_SUPPORT,  do_maintenance,  JOB_CHEMIST, 0, 0, 0, 250, 0 },
  { "Equip Change",   SK_SUPPORT,  do_equipchange,  JOB_CHEMIST, 0, 0, 0, 0,   0 },
  { "Move-Find Item", SK_MOVE,     do_movefinditem, JOB_CHEMIST, 0, 0, 0, 100, 0 },
  
  /*****************
   * KNIGHT SKILLS *
   *****************/
  { "Head Break",   SK_ACTION,   do_headbreak,   JOB_KNIGHT, 1, 1, 0, 300, false },
  { "Armor Break",  SK_ACTION,   do_armorbreak,  JOB_KNIGHT, 1, 1, 0, 400, false },
  { "Shield Break", SK_ACTION,   do_shieldbreak, JOB_KNIGHT, 1, 1, 0, 300, false },
  { "Weapon Break", SK_ACTION,   do_weaponbreak, JOB_KNIGHT, 1, 1, 0, 400, false },
  { "Magic Break",  SK_ACTION,   do_magicbreak,  JOB_KNIGHT, 1, 1, 0, 250, false },
  { "Speed Break",  SK_ACTION,   do_speedbreak,  JOB_KNIGHT, 1, 1, 0, 250, false },
  { "Power Break",  SK_ACTION,   do_powerbreak,  JOB_KNIGHT, 1, 1, 0, 250, false },
  { "Mind Break",   SK_ACTION,   do_mindbreak,   JOB_KNIGHT, 1, 1, 0, 250, false },
  { "Weapon Guard", SK_REACTION, do_weaponguard, JOB_KNIGHT, 0, 0, 0, 200, 0,   TRIG_ATTACK },
  { "Equip Armor",  SK_SUPPORT,  NULL,           JOB_KNIGHT, 0, 0, 0, 500, 0 },
  { "Equip Shield", SK_SUPPORT,  NULL,           JOB_KNIGHT, 0, 0, 0, 250, 0 },
  { "Equip Sword",  SK_SUPPORT,  NULL,           JOB_KNIGHT, 0, 0, 0, 400, 0 },
   
  /*****************
   * ARCHER SKILLS *
   *****************/
  { "Charge +1",      SK_ACTION,   do_charge1,       JOB_ARCHER, 1, 1, 4,  100,  false }, 
  { "Charge +2",      SK_ACTION,   do_charge2,       JOB_ARCHER, 1, 1, 5,  150,  false },
  { "Charge +3",      SK_ACTION,   do_charge3,       JOB_ARCHER, 1, 1, 6,  200,  false }, 
  { "Charge +4",      SK_ACTION,   do_charge4,       JOB_ARCHER, 1, 1, 8,  250,  false }, 
  { "Charge +5",      SK_ACTION,   do_charge5,       JOB_ARCHER, 1, 1, 10, 300,  false }, 
  { "Charge +7",      SK_ACTION,   do_charge7,       JOB_ARCHER, 1, 1, 14, 400,  false }, 
  { "Charge +10",     SK_ACTION,   do_charge10,      JOB_ARCHER, 1, 1, 20, 600,  false }, 
  { "Charge +20",     SK_ACTION,   do_charge20,      JOB_ARCHER, 1, 1, 35, 1000, false },
  { "Speed Save",     SK_REACTION, do_speedsave,     JOB_ARCHER, 0, 0, 0,  800,  0,     TRIG_HPDAMAGE },
  { "Arrow Guard",    SK_REACTION, do_arrowguard,    JOB_ARCHER, 0, 0, 0,  450,  0 },
  { "Equip Crossbow", SK_SUPPORT,  NULL,             JOB_ARCHER, 0, 0, 0,  350,  0 },
  { "Concentrate",    SK_SUPPORT,  do_concentrate,   JOB_ARCHER, 0, 0, 0,  400,  0 }, 
  { "Jump +1",        SK_MOVE,     NULL,             JOB_ARCHER, 0, 0, 0,  200,  0 }, 
  
  /***************
   * MONK SKILLS *
   ***************/
  { "Spin Fist",      SK_ACTION, do_spinfist,      JOB_MONK, 0, 2, 0, 150, false },   
  { "Repeating Fist", SK_ACTION, do_repeatingfist, JOB_MONK, 1, 1, 0, 300, false },   
  { "Wave Fist",      SK_ACTION, do_wavefist,      JOB_MONK, 3, 1, 0, 300, false },   
  { "Earth Slash",    SK_ACTION, do_earthslash,    JOB_MONK, 4, 1, 0, 600, false },   
  { "Secret Fist",    SK_ACTION, do_secretfist,    JOB_MONK, 1, 1, 0, 300, false },   
  { "Stigma Magic",   SK_ACTION, do_stigmamagic,   JOB_MONK, 0, 2, 0, 200, false },   
  { "Chakra",         SK_ACTION, do_chakra,        JOB_MONK, 0, 2, 0, 350, false },   
  { "Revive",         SK_ACTION, do_revive,        JOB_MONK, 1, 1, 0, 500, false },   
  
  /******************
   * CHOCOBO SKILLS *
   ******************/
  { "Choco Attack", SK_ACTION, do_chocoattack, JOB_NPC, 1, 1, 0, 0, 0 },
  { "Choco Ball",   SK_ACTION, do_chocoball,   JOB_NPC, 4, 1, 0, 0, 0 },
  { "Choco Cure",   SK_ACTION, do_chococure,   JOB_NPC, 1, 2, 0, 0, false },
  { "Choco Esuna",  SK_ACTION, do_chocoesuna,  JOB_NPC, 1, 2, 0, 0, false },
  { "Choco Meteor", SK_ACTION, do_chocometeor, JOB_NPC, 5, 1, 0, 0, 0 },
  
  { "Scratch",      SK_ACTION, do_scratch,     JOB_NPC, 1, 1, 0, 0, 0 },
  { "Cat Kick",     SK_ACTION, do_catkick,     JOB_NPC, 1, 1, 0, 0, 0 },
  { "Poison Nail",  SK_ACTION, do_poisonnail,  JOB_NPC, 1, 1, 0, 0, 0 },
  { "Blaster",      SK_ACTION, do_blaster,     JOB_NPC, 3, 1, 0, 0, 0 },
  { "Blood Suck",   SK_ACTION, do_bloodsuck,   JOB_NPC, 1, 1, 0, 0, 0 },
  
  {}
};

void CH::load_skills() {
  MYSQL mysql;
  MYSQL_RES *res;
  MYSQL_ROW row; 
  mysql_init(&mysql);
  
  if(!mysql_real_connect(&mysql, DB_LOC, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0))
    return;
    
  ostrstream ost;
  ost << "SELECT id FROM skills WHERE player='" << name << "'" << ends;
  mysql_query(&mysql, ost.str());
  res = mysql_store_result(&mysql);
  
  while((row = mysql_fetch_row(res)))
    skill[atoi(row[0])] = true;
    
  mysql_free_result(res);
  mysql_close(&mysql);   
  return;
}

string get_skill_type(const short type) {
  switch(type) {
  case SK_ACTION:
    return "Action";
  case SK_REACTION: 
    return "Reaction";
  case SK_SUPPORT:
    return "Support";
  case SK_MOVE:
    return "Move";
  }
  
  return "";
}

short CH::get_skill_id(const string name, const bool learned) {
  for(short x = 1; skill_table[x].name; ++x)
    if(find(name, skill_table[x].name) && ((learned && skill[x]) || !learned))
      return x;
   
  return 0;
}

void do_learn(CH *ch, string argument="") {
  short id;
    
  if(argument.empty()) {
    char buf_ctr[3], buf_mp[3], buf_jp[10];  
    ch->printf("Usage: learn [ability]\n\r");
    ch->printf("{3+--{0Ability{3----------------{0Type{3-----{0Mp{3--{0Speed{3----{0Jp{3----+\n\r");
    ch->printf("+-----------------------------------------------------+{0\n\r");
  
    for(short x = 1; skill_table[x].name; ++x) {   
      if(skill_table[x].job == ch->cjob) {
        sprintf(buf_ctr, "%02d", skill_table[x].ctr);
        sprintf(buf_mp, "%02d", 0);
        sprintf(buf_jp, "%04d ", skill_table[x].JP);
        ch->printf("   %s%-20s  %-8s  %2s  %3s  %8s{0\n\r", !ch->skill[x] && ch->job[ch->cjob].JP < skill_table[x].JP ? "{8" : "{0", skill_table[x].name, get_skill_type(skill_table[x].type).c_str(), skill_table[x].type == SK_ACTION ? buf_mp : "--", skill_table[x].type == SK_ACTION ? buf_ctr : "--", ch->skill[x] ? "  Learned" : buf_jp);
      }
    }
    
    return;
  }
  
  if(!(id = ch->get_skill_id(argument, false)) || skill_table[id].job != ch->cjob) {
    ch->printf("learn: ERROR: No such ability, " + argument + ".\n\r");
    return;
  }
  
  if(ch->skill[id]) {
    ch->printf("learn: ERROR: You already learned \"" + (string)skill_table[id].name + "\".\n\r");
    return;
  }
  
  if(ch->job[ch->cjob].JP < skill_table[id].JP) {   
    ch->printf("learn: ERROR: Not enough JP.\n\r");
    return;
  }
  
  ch->job[ch->cjob].JP -= skill_table[id].JP;
  ch->skill[id] = true;
  ostrstream ost;
  ost << "You learn \"" << skill_table[id].name << "\" for " << skill_table[id].JP << ".\n\r" << ends;
  ch->printf(ost.str());
  return;
}
      
void CH::restore_hp(const short x) {
  ostrstream ost;
  ost << "{6[" << name << "] Hp +" << x << "{0\n\r" << ends;
  battle->echo(ost.str());
  HP[0] = MAX(HP[0] + x, HP[1]);
  return;
}  

void do_act(CH *ch, string argument="") {
  ch->temp_action.action = do_act;
  ch->cancel(status_charging, false);
  ch->printf("Select an action from the following list:\n\r");
  ch->printf("{3+--{0Ability{3------------------------+\n\r");
  ch->printf("{3+---------------------------------+{0\n\r");
    
  for(short x = 1; skill_table[x].name; ++x)
    if(((!skill_table[x].JP && !skill_table[x].job) || (ch->skill[x] && (skill_table[x].job == ch->cjob || skill_table[x].job == ch->job[ch->cjob].subjob))) && (skill_table[x].type == SK_ACTION || skill_table[x].type == SK_SUPPORT) && skill_table[x].fun)
      ch->printf("   %s\n\r", skill_table[x].name);
  
  return;
}       

void CH::announce_action() {
  battle->echo("{&" + name + "> " + skill_table[action.id].name + "...{0\n\r");
  return;
}   

/*******************
 * SKILL FUNCTIONS *
 *******************/
bool do_accumulate(CH *ch, const bool check=false) {
  ch->battle->echo("{6[" + ch->name + "] Physical AT +1!{0\n\r");
  ch->add(status_accumulate, 0, 0, false);
  return true;
}   

bool do_dash(CH *ch, const bool check=false) {
  CH *victim;
  
  if(!(victim = ch->action.target->people))
    return false;
  
  return ch->physical_attack(victim, num_range(1, 4) * ch->PA);
}   

bool do_throwstone(CH *ch, const bool check=false) {
  CH *victim; 
 
  if(!(victim = ch->action.target->people))
    return false;
  
  return ch->physical_attack(victim, num_range(1, 2) * ch->PA);
}

bool do_heal(CH *ch, const bool check=false) {
  CH *victim;
  
  if(!(victim = ch->action.target->people))
    return false;
  
  victim->cancel(status_darkness);
  victim->cancel(status_silence);
  victim->cancel(status_poison);
  return true;
}
 
bool do_countertackle(CH *ch, const bool check=false) {
  ch->announce_action();
  do_dash(ch);
  return false;
} 

bool do_bloodsuck(CH *ch, const bool check=false) {
  return true;
} 

bool do_ice2(CH *ch, const bool check=false) {
  return true;
}
   
bool do_monsterskill(CH *ch, const bool check=false) { 
  return true;
}
  
bool do_defend(CH *ch, const bool check=false) {
  ch->battle->echo(ch->name + " takes a defensive stance.\n\r");
  ch->action.action = do_defend;  
  return false;
} 
  
bool do_movefinditem(CH *ch, const bool check=false) {
  return true;
}
 
bool do_equipchange(CH *ch, const bool check=false) {
  return true;
}
 
bool do_maintenance(CH *ch, const bool check=false) {  
  return true;   
} 
  
bool do_autopotion(CH *ch, const bool check=false) {
  return true;
}
  
bool do_phoenixdown(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
  
  if(!(obj = ch->obj("Phoenix Down", 0))) {
    ch->printf("You have no \"Phoenix Down\".\n\r");
    return false;
  }
 
  zap(obj);

  if(!(victim = ch->action.target->people))
    return false;
  
  if(victim->status(status_dead)) {
    victim->cancel(status_dead);
    victim->restore_hp(num_range(5, 10));
  }
  
  return true;
}
  
bool do_remedy(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
  
  if(!(obj = ch->obj("Remedy", 0))) {
    ch->printf("You have no \"Remedy\".\n\r");
    return false;
  }
  
  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;
  
  victim->cancel(status_petrify);
  victim->cancel(status_darkness);
  victim->cancel(status_confusion);
  victim->cancel(status_silence);
  victim->cancel(status_oil);
  victim->cancel(status_frog);
  victim->cancel(status_poison);    
  victim->cancel(status_sleep);
  return true;
} 

bool do_holywater(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;

  if(!(obj = ch->obj("Holy Water", 0))) {
    ch->printf("You have no \"Holy Water\".\n\r");
    return false;
  }
    
  zap(obj);
   
  if(!(victim = ch->action.target->people))
    return false;  

  victim->cancel(status_undead);
  victim->cancel(status_bloodsuck);
  return true;
}

bool do_soft(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
  
  if(!(obj = ch->obj("Soft", 0))) {
    ch->printf("You have no \"Soft\".\n\r");
    return false;
  }
  
  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;
   
  victim->cancel(status_petrify);
  return true;
} 
  
bool do_maidenskiss(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
  
  if(!(obj = ch->obj("Maiden's Kiss", 0))) {
    ch->printf("You have no \"Maiden's Kiss\".\n\r");
    return false;
  }
  
  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;

  victim->cancel(status_frog);
  return true;
}
  
bool do_echograss(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
    
  if(!(obj = ch->obj("Echo Grass", 0))) {
    ch->printf("You have no \"Echo Grass\".\n\r");
    return false;
  }

  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;

  victim->cancel(status_silence);
  return true;
}
  
bool do_eyedrop(CH *ch, const bool check=false) {  
  CH *victim;
  OBJ *obj;
   
  if(!(obj = ch->obj("Eye Drop", 0))) {
    ch->printf("You have no \"Eye Drop\".\n\r");
    return false;
  }
    
  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;
  
  victim->cancel(status_darkness);
  return true;
}
  
bool do_antidote(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
   
  if(!(obj = ch->obj("Antidote", 0))) {
    ch->printf("You have no \"Antidote\".\n\r");
    return false;
  }
    
  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;
  
  victim->cancel(status_poison);
  return true;
}
    
bool do_elixir(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
   
  if(!(obj = ch->obj("Elixir", 0))) {
    ch->printf("You have no \"Elixir\".\n\r");
    return false;
  }

  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;
  
  ch->battle->echo(ch, "[%s] Hp +%d\n\r[%s] Mp +%d\n\r", victim->name.c_str(), victim->HP[1] - victim->HP[0], victim->name.c_str(), victim->MP[1] - victim->MP[0]);
  victim->restore();
  return true;
}  
  
bool do_hiether(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
    
  if(!(obj = ch->obj("Hi-Ether", 0))) {
    ch->printf("You have no \"Hi-Ether\".\n\r");
    return false;
  }
  
  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;

  ch->battle->echo("[" + victim->name + "] Mp +50\n\r");
  victim->MP[0] = MAX(victim->MP[0] + 50, victim->MP[1]);
  return true;
}
    
bool do_ether(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
  
  if(!(obj = ch->obj("Ether", 0))) {
    ch->printf("You have no \"Ether\".\n\r");
    return false;
  }
  
  zap(obj);
 
  if(!(victim = ch->action.target->people))
    return false;
  
  ch->battle->echo("[" + victim->name + "] Mp +20\n\r");
  victim->MP[0] = MAX(victim->MP[0] + 20, victim->MP[1]);
  return true;
}
    
bool do_hipotion(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
  
  if(!(obj = ch->obj("Hi-Potion", 0))) {
    ch->printf("You have no \"Hi-Potion\".\n\r");
    return false;
  }
  
  zap(obj);
   
  if(!(victim = ch->action.target->people))
    return false;
  
  victim->restore_hp(70);
  return true;
}

bool do_potion(CH *ch, const bool check=false) {
  CH *victim = ch;
  OBJ *obj;
  
  if(!(obj = ch->obj("Potion", 0))) {
    ch->printf("You have no \"Potion\".\n\r");
    return false;
  }
  
  zap(obj);
  
  if(ch->battle && !(victim = ch->action.target->people))
    return false;

  victim->restore_hp(30);
  return true;
} 
  
bool do_xpotion(CH *ch, const bool check=false) {
  CH *victim;
  OBJ *obj;
  
  if(!(obj = ch->obj("X-Potion", 0))) {
    ch->printf("You have no \"X-Potion\".\n\r");
    return false;
  }
  
  zap(obj);
  
  if(!(victim = ch->action.target->people))
    return false;
    
  victim->restore_hp(150);
  return true;
}
  
bool do_charge1(CH *ch, const bool check=false) {
  CH *victim;
    
  if(!(victim = ch->action.target->people))
    return false;
  
  return ch->physical_attack(victim, ch->calculate_dam(1, ch->get_weap_loc()));
}
    
bool do_charge2(CH *ch, const bool check=false) {
  CH *victim;
  
  if(!(victim = ch->action.target->people))
    return false;

  return ch->physical_attack(victim, ch->calculate_dam(2, ch->get_weap_loc()));
}
  
bool do_charge3(CH *ch, const bool check=false) {
  CH *victim;
    
  if(!(victim = ch->action.target->people))
    return false;
  
  return ch->physical_attack(victim, ch->calculate_dam(3, ch->get_weap_loc()));
}
    
bool do_charge4(CH *ch, const bool check=false) {
  CH *victim;
  
  if(!(victim = ch->action.target->people))
    return false;

  return ch->physical_attack(victim, ch->calculate_dam(4, ch->get_weap_loc()));
}
  
bool do_charge5(CH *ch, const bool check=false) {
  CH *victim;
    
  if(!(victim = ch->action.target->people))
    return false;
  
  return ch->physical_attack(victim, ch->calculate_dam(6, ch->get_weap_loc()));
}
    
bool do_charge7(CH *ch, const bool check=false) {
  CH *victim;
  
  if(!(victim = ch->action.target->people))
    return false;

  return ch->physical_attack(victim, ch->calculate_dam(7, ch->get_weap_loc()));
}   
  
bool do_charge10(CH *ch, const bool check=false) {
  CH *victim;
  
  if(!(victim = ch->action.target->people))
    return false;

  return ch->physical_attack(victim, ch->calculate_dam(10, ch->get_weap_loc()));
} 
  
bool do_charge20(CH *ch, const bool check=false) {
  CH *victim;
  
  if(!(victim = ch->action.target->people))
    return false;

  return ch->physical_attack(victim, ch->calculate_dam(20, ch->get_weap_loc()));
}   
  
bool do_speedsave(CH *ch, const bool check=false) {
  return true;
}
 
bool do_arrowguard(CH *ch, const bool check=false) {
  return true;
}
  
bool do_concentrate(CH *ch, const bool check=false) {  
  return true;   
}
  
bool do_headbreak(CH *ch, const bool check=false) {
  CH *victim;

  if(!(victim = ch->action.target->people))
    return false;
  
  return ch->physical_attack(victim, ch->calculate_dam(0, ch->get_weap_loc()));
} 
  
bool do_armorbreak(CH *ch, const bool check=false) {
  return true;
}
  
bool do_shieldbreak(CH *ch, const bool check=false) {
  return true;
}

bool do_weaponbreak(CH *ch, const bool check=false) {
  return true;
} 

bool do_magicbreak(CH *ch, const bool check=false) {
  return true;
}
    
bool do_speedbreak(CH *ch, const bool check=false) {
  return true;
} 
  
bool do_powerbreak(CH *ch, const bool check=false) {
  return true;
} 
  
bool do_mindbreak(CH *ch, const bool check=false) {
  return true;
}
    
bool do_weaponguard(CH *ch, const bool check=false) {
  short loc;
  
  if(!ch->eq[(loc = ch->get_weap_loc())] || obj_table[ch->eq[loc]->id].pEv < num_percent())
    return false;

  ch->announce_action();
  ch->battle->echo(ch->name + " guards with his weapon!\n\r");
  return true;
}
  
bool do_fire(CH *ch, const bool check=false) {
  return true;
}
  
bool do_fire2(CH *ch, const bool check=false) {
  return true;
}
  
bool do_ice(CH *ch, const bool check=false) {
  return true;
} 

bool do_bolt(CH *ch, const bool check=false) {
  return true;
} 

bool do_bolt2(CH *ch, const bool check=false) {
  return true;
}

bool do_spinfist(CH *ch, const bool check=false) {
  return true;
}

bool do_repeatingfist(CH *ch, const bool check=false) {
  return true;
}   

bool do_wavefist(CH *ch, const bool check=false) {
  return true;
} 

bool do_earthslash(CH *ch, const bool check=false) {
  return true;
} 

bool do_secretfist(CH *ch, const bool check=false) {
  return true;
}   

bool do_stigmamagic(CH *ch, const bool check=false) {
  return true;
}
    
bool do_chakra(CH *ch, const bool check=false) {
  return true;
}
  
bool do_revive(CH *ch, const bool check=false) {
  return true;
}
  
/**************
 * NPC skills *
 **************/
bool do_chocoattack(CH *ch, const bool check=false) {
  CH *victim;
  
  if(check) {
    victim = ch->get_nearest_target(false);
    short distance = victim->distance(ch);
    ch->action.target = victim->battle_room;
  
    if(distance <= ch->temp_action.range)
      return true;
    else if(distance <= (ch->Mv + ch->temp_action.range) && ch->move_within_range(victim)) {
      ch->moved = true;
      return true;
    }
  
    ch->action.target = NULL;
    return false;
  }
  
  if(!(victim = ch->action.target->people))
    return false;

  return ch->physical_attack(victim, ch->calculate_dam(0, 0));
} 

bool do_chococure(CH *ch, const bool check=false) { 
  CH *victim; 
  
  if(check) {
    victim = ch->get_nearest_target(true);

    if(!victim || victim->HP[0] > victim->HP[1] / 2)
      return false;
 
    short distance = victim->distance(ch);
    ch->action.target = victim->battle_room;
  
    if(distance <= ch->temp_action.range)
      return true;
    else if(distance <= (ch->Mv + ch->temp_action.range) && ch->move_within_range(victim)) {
      ch->moved = true;
      return true;
    }

    ch->action.target = NULL;
    return false;
  }
  
  if(!(victim = ch->action.target->people))
    return false;
    
  victim->restore_hp((ch->MA * 3));
  return true;
}
    
bool do_chocoesuna(CH *ch, const bool check=false) {
  CH *victim;
      
  if(check) {
    victim = ch->get_nearest_target(true);
  
    if(!victim || !victim->status(status_poison))
      return false;
   
    short distance = victim->distance(ch);
    ch->action.target = victim->battle_room;
    
    if(distance <= ch->temp_action.range)
      return true;
    else if(distance <= (ch->Mv + ch->temp_action.range) && ch->move_within_range(victim)) {
      ch->moved = true;
      return true;
    }
  
    ch->action.target = NULL;
    return false;  
  }
    
  if(!(victim = ch->action.target->people) || num_percent() > ch->PA * 4)
    return false;
      
  victim->cancel(status_poison);
  return true;
}
  
bool do_chocoball(CH *ch, const bool check=false) {  
  CH *victim;
    
  if(check) {
    victim = ch->get_nearest_target(false);
    short distance = victim->distance(ch);
    ch->action.target = victim->battle_room;
   
    if(distance <= ch->temp_action.range)
      return true;
    else if(distance <= (ch->Mv + ch->temp_action.range) && ch->move_within_range(victim)) {
      ch->moved = true;
      return true;
    }
 
    ch->action.target = NULL;
    return false;
  }
      
  if(!(victim = ch->action.target->people))
    return false;  
      
  return ch->physical_attack(victim, ch->PA * (ch->PA / 2));
} 
    
bool do_chocometeor(CH *ch, const bool check=false) {
  CH *victim;
    
  if(check) {
    victim = ch->get_nearest_target(false);
    short distance = victim->distance(ch);
    ch->action.target = victim->battle_room;
      
    if(distance <= ch->temp_action.range)
      return true;
    else if(distance <= (ch->Mv + ch->temp_action.range) && ch->move_within_range(victim)) {
      ch->moved = true;
      return true;
    }
  
    ch->action.target = NULL;
    return false;
  }
  
  if(!(victim = ch->action.target->people))
    return false;

  ch->damage(victim, ch->MA * 4);
  return true;
}
    
bool do_tackle(CH *ch, const bool check=false) {
  return false;
}
    
bool do_eyegouge(CH *ch, const bool check=false) {
  return false;
}
    
bool do_goblinpunch(CH *ch, const bool check=false) {
  return false;   
}
 
bool do_mutilate(CH *ch, const bool check=false) {
  return false;  
}  

bool do_bite(CH *ch, const bool check=false) {
  return false;    
}     
  
bool do_selfdestruct(CH *ch, const bool check=false) {
  return false;
}
  
bool do_smallbomb(CH *ch, const bool check=false) {
  return false;
}
    
bool do_turnpunch(CH *ch, const bool check=false) {
  return false;
}
      
bool do_spark(CH *ch, const bool check=false) {
  return false;   
}
      
bool do_scratch(CH *ch, const bool check=false) {
  CH *victim;
  
  if(check) {
    victim = ch->get_nearest_target(false);
    short distance = victim->distance(ch);
    ch->action.target = victim->battle_room;

    if(distance <= ch->temp_action.range)
      return true;
    else if(distance <= (ch->Mv + ch->temp_action.range) && ch->move_within_range(victim)) {
      ch->moved = true;
      return true;
    }
 
    ch->action.target = NULL;
    return false;
  }
 
  if(!(victim = ch->action.target->people))
    return false;
  
  return ch->physical_attack(victim, ch->calculate_dam(0, 0));
}

bool do_poisonnail(CH *ch, const bool check=false) {
  CH *victim;
      
  if(check) {
    if(num_percent() > 10)
      return false;
  
    if((victim = ch->get_nearest_target(false)) && victim->status(status_poison))
      return false;

    short distance = victim->distance(ch);
    ch->action.target = victim->battle_room;
    
    if(distance <= ch->temp_action.range)
      return true;
    else if(distance <= (ch->Mv + ch->temp_action.range) && ch->move_within_range(victim)) {
      ch->moved = true;
      return true;
    }
 
    ch->action.target = NULL;
    return false;
  }
  
  if(!(victim = ch->action.target->people))
    return false;  
   
  return victim->add(status_poison, status_table[get_status_id(status_poison)].ctr, (ch->MA + 40), true);
}
    
bool do_catkick(CH *ch, const bool check=false) {
  CH *victim;
      
  if(check) {
    if(num_percent() > 10)
      return false;
    
    victim = ch->get_nearest_target(false);
    short distance = victim->distance(ch);
    ch->action.target = victim->battle_room;
  
    if(distance <= ch->temp_action.range)
      return true;
    else if(distance <= (ch->Mv + ch->temp_action.range) && ch->move_within_range(victim)) {
      ch->moved = true;
      return true;
    }
  
    ch->action.target = NULL;
    return false;
  }
      
  if(!(victim = ch->action.target->people))
    return false;  
  
  return ch->physical_attack(victim, (ch->PA * num_range(1, 9)));
}

bool do_blaster(CH *ch, const bool check=false) {
  return false;
}   
    
bool do_tentacle(CH *ch, const bool check=false) {
  return false;
}
      
bool do_blackink(CH *ch, const bool check=false) {
  return false;
}
    
bool do_darkwhisper(CH *ch, const bool check=false) {
  return false;
}
    
bool do_tripleflame(CH *ch, const bool check=false) {
  return false;
}
    
bool do_triplethunder(CH *ch, const bool check=false) {
  return false;
}     
  
bool do_triplebreath(CH *ch, const bool check=false) {
  return false;
}
      
bool do_tripleattack(CH *ch, const bool check=false) {
  return false;
}
    
bool do_firebreath(CH *ch, const bool check=false) {
  return false;
}
    
bool do_thunderbreath(CH *ch, const bool check=false) {
  return false;   
}
  
bool do_icebreath(CH *ch, const bool check=false) {
  return false;  
}  
      
bool do_tailswing(CH *ch, const bool check=false) {
  return false;    
} 
  
bool do_ulmaguest(CH *ch, const bool check=false) {
  return false;
}
  
bool do_suddencry(CH *ch, const bool check=false) {
  return false;
}
  
bool do_stabup(CH *ch, const bool check=false) {
  return false;
}
  
bool do_hurricane(CH *ch, const bool check=false) {
  return false;
}
  
bool do_gigaflare(CH *ch, const bool check=false) {
  return false;
}
  
bool do_moldballvirus(CH *ch, const bool check=false) {
  return false;
}
  
bool do_badbreath(CH *ch, const bool check=false) {
  return false;
}
  
bool do_lick(CH *ch, const bool check=false) {
  return false;
}
  
bool do_goo(CH *ch, const bool check=false) {
  return false;
}
  
bool do_blowfire(CH *ch, const bool check=false) {
  return false;
}
  
bool do_gatherpower(CH *ch, const bool check=false) {
  return false;
}
  
bool do_mimictitan(CH *ch, const bool check=false) {
  return false;
}
  
bool do_shakeoff(CH *ch, const bool check=false) {
  return false;
}
  
bool do_wavearound(CH *ch, const bool check=false) {
  return false;
}
  
bool do_magicspirit(CH *ch, const bool check=false) {
  return false;
}
  
bool do_calmspirit(CH *ch, const bool check=false) {
  return false;
}
  
bool do_protectspirit(CH *ch, const bool check=false) {
  return false;
}
  
bool do_leafdance(CH *ch, const bool check=false) {
  return false;
}
  
bool do_spiritoflife(CH *ch, const bool check=false) {
  return false;
}
  
bool do_pleaseeat(CH *ch, const bool check=false) {
  return false;
}
  
bool do_nosebreath(CH *ch, const bool check=false) {
  return false;
}
  
bool do_straightdash(CH *ch, const bool check=false) {
  return false;
}
  
bool do_pooh(CH *ch, const bool check=false) {
  return false;
}

bool do_oink(CH *ch, const bool check=false) {
  return false;
}
  
bool do_beaking(CH *ch, const bool check=false) {
  return false;
}
  
bool do_featherbomb(CH *ch, const bool check=false) {
  return false;
}
  
bool do_beak(CH *ch, const bool check=false) {
  return false;
}
  
bool do_scratchup(CH *ch, const bool check=false) {
  return false;
}
  
bool do_shinelover(CH *ch, const bool check=false) {
  return false;
}
  
bool do_circle(CH *ch, const bool check=false) {
  return false;
}
  
bool do_deathsentence(CH *ch, const bool check=false) {
  return false;
}
  
bool do_lookofdevil(CH *ch, const bool check=false) {
  return false;
}
  
bool do_wingattack(CH *ch, const bool check=false) {
  return false;
}
  
bool do_lookoffright(CH *ch, const bool check=false) {
  return false;
}
  
bool do_zombietouch(CH *ch, const bool check=false) {
  return false;
}
  
bool do_draintouch(CH *ch, const bool check=false) {
  return false;
}
  
bool do_throwspirit(CH *ch, const bool check=false) {
  return false;
}
  
bool do_greasetouch(CH *ch, const bool check=false) {
  return false;
}
  
bool do_sleeptouch(CH *ch, const bool check=false) {
  return false;
}
  
bool do_windsoul(CH *ch, const bool check=false) {
  return false;
}
  
bool do_icesoul(CH *ch, const bool check=false) {
  return false;
}
  
bool do_knifehand(CH *ch, const bool check=false) {
  return false;
}
  
bool do_aquasoul(CH *ch, const bool check=false) {
  return false;
}
  
bool do_thundersoul(CH *ch, const bool check=false) {
  return false;
}
  
bool do_levelblast(CH *ch, const bool check=false) {
  return false;
}
  
bool do_mindblast(CH *ch, const bool check=false) {
  return false;
}
  
bool do_oddsoundwave(CH *ch, const bool check=false) {
  return false;
}