/******************************************************************************
 Copyright (c) 2000-2001 Richard Woolcock

 Permission is hereby granted, free of charge, to any person obtaining a copy 
 of this software and associated documentation files (the "Software"), to deal 
 in the Software without restriction, including without limitation the rights 
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 copies of the Software, and to permit persons to whom the Software is 
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all 
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 SOFTWARE.
 ******************************************************************************/

/******************************************************************************
 File Name        : combat_hands.c
 ******************************************************************************
 Description      : The "hands" combat table and associated commands.
 ******************************************************************************/

/******************************************************************************
 Required library files.
 ******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "sockets.h"
#include "combat.h"
#include "combat_hands.h"
#include "glad.h"

/******************************************************************************
 Required local variables.
 ******************************************************************************/

/* Create the combat table */
const cmbt_table_t kstCmbtTableHands[] =
{ /* Requirements Function        AP   ATT  DEF  DAM  Block message */
   { " ",         CmbtNull,       1,   +0,  +0,  +0,  "misses" },
   { "dfswswe",   CmbtKnockout,   8,   -10, +0,  +999,"misses" },
   { "wfpfp",     CmbtShift,      6,   +0,  +0,  +0,  "misses" },
   { "fdds",      CmbtBackfist,   3,   +5,  +25, +0,  "is blocked by" },
   { "wfs",       CmbtStun,       4,   +10, +0,  +0,  "misses" },
   { "fse",       CmbtSmash,      7,   +0,  +0,  +0,  "misses" },
   { "fdd",       CmbtHardBlock,  1,   +0,  +50, +0,  "is blocked by" },
   { "pdd",       CmbtSoftBlock,  1,   +0,  +50, +0,  "is blocked by" },
   { "fsssssds",  CmbtBackfist,   3,   +5,  +25, +0,  "is blocked by" },
   { "fsssssd",   CmbtHardBlock,  1,   +0,  +50, +0,  "is blocked by" },
   { "fsssss",    CmbtPunch,      5,   +5,  +0,  +0,  "misses" },
   { "fssss",     CmbtPunch,      5,   +5,  +0,  +0,  "misses" },
   { "fsss",      CmbtUppercut,   4,  +15,  +0,  +10, "misses" },
   { "fss",       CmbtHook,       4,  +10,  +0,  +5,  "misses" },
   { "fs",        CmbtJab,        4,   +5,  +0,  +0,  "misses" },
   { "ps",        CmbtPalmStrike, 3,   +0,  +5,  +0,  "misses" },
   { "d",         CmbtGuard,      2,   +0,  +25, +0,  "is deflected by" },
   { "e",         CmbtElbow,      6,   -5,  +0,  +5,  "misses" },
   { "f",         CmbtFist,       3,   +0,  +0,  +0,  "misses" },
   { "p",         CmbtPalm,       3,   +0,  +0,  +0,  "misses" },
   { "s",         CmbtStrike,     3,   -20, +0,  -5,  "misses" },
   { "w",         CmbtWithdraw,   6,   +0,  +0,  +0,  "misses" },

   /* Commands (an '@' followed by one letter) */
   { "@d",        CmbtCmdDefence, 0,   +0,  +0,  +0,  "misses" },
   { "@e",        CmbtCmdElbow,   0,   +0,  +0,  +0,  "misses" },
   { "@f",        CmbtCmdFist,    0,   +0,  +0,  +0,  "misses" },
   { "@p",        CmbtCmdPalm,    0,   +0,  +0,  +0,  "misses" },
   { "@s",        CmbtCmdStrike,  0,   +0,  +0,  +0,  "misses" },
   { "@w",        CmbtCmdWithdraw,0,   +0,  +0,  +0,  "misses" },

   { NULL }
};

/******************************************************************************
 Commands specific to this table.
 ******************************************************************************/

CMBT(CmdDefence)
{
   const int kiCost = 50; /* The cost of this particular fighting move */

   if ( pstBody->bBusy[eLocation] )
   {
      SendToBody( pstBody, TO_USER, "Your {location} is already in action!\n\r" );
   }
   else if ( pstBody->iActions[eLocation] < kiCost )
   {
      /* Inform the player that they cannot attack */
      SendToBody( pstBody, TO_USER, "You need %d action points to assume a defensive position, you have %d.\n\r", 
         kiCost, pstBody->iActions[eLocation] );
   }
   else /* The character is able to make the attack */
   {
      /* Inform the user that the move is taking place */
      SendToBody( pstBody, TO_USER, "Ok.\n\r" );

      pstBody->iActions[eLocation] -= kiCost;         /* Subtract the action points */
      pstBody->bBusy[eLocation] = TRUE;               /* Set the hand to "busy"     */
      Combat( pstBody, eLocation, 'd', TABLE_HANDS ); /* Call the combat function   */
   }
}

CMBT(CmdElbow)
{
   const int kiCost = 125; /* The cost of this particular fighting move */

   if ( pstBody->bBusy[eLocation] )
   {
      SendToBody( pstBody, TO_USER, "Your {hand} arm is already in action!\n\r" );
   }
   else if ( pstBody->iActions[eLocation] < kiCost )
   {
      /* Inform the player that they cannot attack */
      SendToBody( pstBody, TO_USER, "You need %d action points to perform an elbow chop, you have %d.\n\r", 
         kiCost, pstBody->iActions[eLocation] );
   }
   else /* The character is able to make the attack */
   {
      /* Inform the user that the move is taking place */
      SendToBody( pstBody, TO_USER, "Ok.\n\r" );

      pstBody->iActions[eLocation] -= kiCost;         /* Subtract the action points */
      pstBody->bBusy[eLocation] = TRUE;               /* Set the hand to "busy"     */
      Combat( pstBody, eLocation, 'e', TABLE_HANDS ); /* Call the combat function   */
   }
}

CMBT(CmdFist)
{
   const int kiCost = 100; /* The cost of this particular fighting move */

   if ( pstBody->bBusy[eLocation] )
   {
      SendToBody( pstBody, TO_USER, "Your {location} is already in action!\n\r" );
   }
   else if ( pstBody->iActions[eLocation] < kiCost )
   {
      /* Inform the player that they cannot attack */
      SendToBody( pstBody, TO_USER, "You need %d action points to form a fist, you have %d.\n\r", 
         kiCost, pstBody->iActions[eLocation] );
   }
   else /* The character is able to make the move */
   {
      /* Inform the user that the move is taking place */
      SendToBody( pstBody, TO_USER, "Ok.\n\r" );

      pstBody->iActions[eLocation] -= kiCost;         /* Subtract the action points */
      pstBody->bBusy[eLocation] = TRUE;               /* Set the hand to "busy"     */
      Combat( pstBody, eLocation, 'f', TABLE_HANDS ); /* Call the combat function   */
   }
}

CMBT(CmdPalm)
{
   const int kiCost = 100; /* The cost of this particular fighting move */

   if ( pstBody->bBusy[eLocation] )
   {
      SendToBody( pstBody, TO_USER, "Your {location} is already in action!\n\r" );
   }
   else if ( pstBody->iActions[eLocation] < kiCost )
   {
      /* Inform the player that they cannot attack */
      SendToBody( pstBody, TO_USER, "You need %d action points to form a palm-hand position, you have %d.\n\r", 
         kiCost, pstBody->iActions[eLocation] );
   }
   else /* The character is able to make the move */
   {
      /* Inform the user that the move is taking place */
      SendToBody( pstBody, TO_USER, "Ok.\n\r" );

      pstBody->iActions[eLocation] -= kiCost;         /* Subtract the action points */
      pstBody->bBusy[eLocation] = TRUE;               /* Set the hand to "busy"     */
      Combat( pstBody, eLocation, 'p', TABLE_HANDS ); /* Call the combat function   */
   }
}

CMBT(CmdStrike)
{
   const int kiCost = 100; /* The cost of this particular fighting move */

   if ( pstBody->bBusy[eLocation] )
   {
      SendToBody( pstBody, TO_USER, "Your {location} is already in action!\n\r" );
   }
   else if ( pstBody->iActions[eLocation] < kiCost )
   {
      /* Inform the player that they cannot attack */
      SendToBody( pstBody, TO_USER, "You need %d action points to perform a strike, you have %d.\n\r", 
         kiCost, pstBody->iActions[eLocation] );
   }
   else /* The character is able to make the attack */
   {
      /* Inform the user that the move is taking place */
      SendToBody( pstBody, TO_USER, "Ok.\n\r" );

      pstBody->iActions[eLocation] -= kiCost;         /* Subtract the action points */
      pstBody->bBusy[eLocation] = TRUE;               /* Set the hand to "busy"     */
      Combat( pstBody, eLocation, 's', TABLE_HANDS ); /* Call the combat function   */
   }
}

CMBT(CmdWithdraw)
{
   const int kiCost = 100; /* The cost of this particular fighting move */

   if ( pstBody->bBusy[eLocation] )
   {
      SendToBody( pstBody, TO_USER, "Your {location} is already in action!\n\r" );
   }
   else if ( pstBody->iActions[eLocation] < kiCost )
   {
      /* Inform the player that they cannot attack */
      SendToBody( pstBody, TO_USER, "You need %d action points to perform a withdraw, you have %d.\n\r", 
         kiCost, pstBody->iActions[eLocation] );
   }
   else /* The character is able to make the attack */
   {
      /* Inform the user that the move is taking place */
      SendToBody( pstBody, TO_USER, "Ok.\n\r" );

      pstBody->iActions[eLocation] -= kiCost;         /* Subtract the action points */
      pstBody->bBusy[eLocation] = TRUE;               /* Set the hand to "busy"     */
      Combat( pstBody, eLocation, 'w', TABLE_HANDS ); /* Call the combat function   */
   }
}

/******************************************************************************
 Combat techniques.
 ******************************************************************************/

CMBT(Guard)
{
   SendToBody( pstBody, TO_USER, "You raise your guard with your {location}.\n\r" );
   SendToBody( pstBody, TO_ROOM, "{name} raises {his/her} guard with {his/her} {location}.\n\r" );
}

CMBT(Elbow)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iDamage = GetEnhancedDamage(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iDamage;
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "{hand} elbow strike", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You strike {opponent} with your {hand} elbow!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} strikes you with {opponent.his/her} {hand} elbow!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam += RollDice(iDamage);
   }
}

CMBT(Fist)
{
   SendToBody( pstBody, TO_USER, "You clench your {location} into a fist.\n\r" );
   SendToBody( pstBody, TO_ROOM, "{name} clenches {his/her} {location} into a fist.\n\r" );
}

CMBT(Palm)
{
   SendToBody( pstBody, TO_USER, "You open your {location}, palm facing outwards.\n\r" );
   SendToBody( pstBody, TO_ROOM, "{name} opens {his/her} {location}, palm facing outwards.\n\r" );
}

CMBT(Strike)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iDamage = GetEnhancedDamage(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iDamage;
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "clumsy {hand} punch", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You strike {opponent} with a clumsy {hand} punch!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} strikes you with a clumsy {hand} punch!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam += RollDice(iDamage);
   }
}

CMBT(Withdraw)
{
   SendToBody( pstBody, TO_USER, "You draw your {location} back to your side.\n\r" );
   SendToBody( pstBody, TO_ROOM, "{name} draws {his/her} {location} back to {his/her} side.\n\r" );
}

CMBT(PalmStrike)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iDamage = GetEnhancedDamage(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iDamage;
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "{hand} palm strike", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You strike {opponent} with the palm of your {location}!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} strikes you with the palm of {opponent.his/her} {location}!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam += RollDice(iDamage);
   }
}

CMBT(Punch)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iDamage = GetEnhancedDamage(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iDamage;
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "{hand} punch", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You strike {opponent} with a {hand} punch!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} strikes you with a {hand} punch!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam += RollDice(iDamage);
   }
}


CMBT(Jab)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iDamage = GetEnhancedDamage(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iDamage;
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "{hand} jab", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You strike {opponent} with a {hand} jab!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} strikes you with a {hand} jab!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam += RollDice(iDamage);
   }
}

CMBT(Hook)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iDamage = GetEnhancedDamage(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iDamage;
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "{hand} hook", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You strike {opponent} with a {hand} hook!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} strikes you with a {hand} hook!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam += RollDice(iDamage);
   }
}

CMBT(Uppercut)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iDamage = GetEnhancedDamage(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iDamage;
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "{hand} uppercut", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You strike {opponent} with a {hand} uppercut!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} strikes you with a {hand} uppercut!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam += RollDice(iDamage);
   }
}

CMBT(SoftBlock)
{
   SendToBody( pstBody, TO_USER, "You move your {location} into a soft blocking position.\n\r" );
   SendToBody( pstBody, TO_ROOM, "{name} moves {his/her} {location} into a soft blocking position.\n\r" );
}

CMBT(HardBlock)
{
   SendToBody( pstBody, TO_USER, "You move your {location} into a hard blocking position.\n\r" );
   SendToBody( pstBody, TO_ROOM, "{name} moves {his/her} {location} into a hard blocking position.\n\r" );
}

CMBT(Backfist)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iDamage = GetEnhancedDamage(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iDamage;
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "vicious {hand} backfist", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You strike {opponent} with a vicious {hand} backfist!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} strikes you with a vicious {hand} backfist!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam += RollDice(iDamage);
   }
}

CMBT(Knockout)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;

   if ( !IsBlocked( pstBody, "devastating {hand} elbow", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You knock {opponent} out with a devastating {hand} elbow!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} knocks you out with a devastating {hand} elbow!\n\r" );

      /* Inflict damage */
      pstOpponent->iDam = GetHealth(pstOpponent)+1;
   }
}

CMBT(Stun)
{
   int iCmbtIndex = pstBody->iCmbtIndex[eLocation];
   table_t eTable = pstBody->eTable[eLocation];
   int iAttack = GetAttack(pstBody) + kstCmbtTable[eTable][iCmbtIndex].iAttack;
   body_t* pstOpponent = pstBody->pstOpponent;
   int i; /* Loop counter */

   if ( !IsBlocked( pstBody, "stunning {hand} blow", iAttack ) )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You stun {opponent} with a well-placed {hand} punch!\n\r" );
      SendToBody( pstOpponent, TO_USER, "{opponent} stuns you with a well-placed {hand} punch!\n\r" );

      /* No damage, but screws up their moves */
      for ( i = AP_LEFT_HAND; i <= AP_RIGHT_HAND; i++ )
      {
         PushMove(pstOpponent->a_chTechniques[i], ' ');
         pstOpponent->iCmbtIndex[i] = 0;
         pstOpponent->iSpeed[i] = 0;
         pstOpponent->iActions[i] = 0;
         pstOpponent->bBusy[i] = FALSE;
         pstOpponent->pfnAttack[i] = CmbtNull;
      }
   }
}

CMBT(Shift)
{
   /* Transfer remaining action points into other hand */
   SendToBody( pstBody, TO_USER, "You focus all your energy into your {location}!\n\r" );
   SendToBody( pstBody, TO_ROOM, "{name} focuses all of {his/her} energy into {his/her} {location}!\n\r" );

   pstBody->iActions[!eLocation] += pstBody->iActions[eLocation];
   pstBody->iActions[eLocation] = 0;
}

CMBT(Smash)
{
   body_t* pstOpponent = pstBody->pstOpponent;
   ap_t    eOppLoc;

   /* Find their best defensive hand */
   if ( kstCmbtTable[pstOpponent->eTable[AP_LEFT_HAND]][pstOpponent->iCmbtIndex[AP_LEFT_HAND]].iDefence > 
      kstCmbtTable[pstOpponent->eTable[AP_RIGHT_HAND]][pstOpponent->iCmbtIndex[AP_RIGHT_HAND]].iDefence )
   {
      eOppLoc = AP_LEFT_HAND;
   }
   else /* Right hand has the best defence */
   {
      eOppLoc = AP_RIGHT_HAND;
   }

   if ( kstCmbtTable[pstOpponent->eTable[eOppLoc]][pstOpponent->iCmbtIndex[eOppLoc]].iDefence > 0 )
   {
      /* Send the message */
      SendToBody( pstBody, TO_USER, "You smash {opponent}'s %s hand from it's defensive position!\n\r", 
         (eOppLoc == AP_LEFT_HAND ? "left" : "right") );
      SendToBody( pstOpponent, TO_USER, "{opponent} smashes your %s hand from it's defensive position!\n\r", 
         (eOppLoc == AP_LEFT_HAND ? "left" : "right") );

      /* No damage, but screws up their moves */
      PushMove(pstOpponent->a_chTechniques[eOppLoc], ' ');

      /* Also ruins their defence */
      pstOpponent->iCmbtIndex[eOppLoc] = 0;
   }
   else /* They don't have any defences! */
   {
      SendToBody( pstBody, TO_USER, "You try to smash down {his/her} defences, but {opponent} doesn't have any!\n\r" );
   }
}