/******************************************************************************
 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        : commands.c
 ******************************************************************************
 Description      : All commands used by the players.
 ******************************************************************************/

/******************************************************************************
 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 "string.h"
#include "commands.h"
#include "combat.h"
#include "glad.h"
#include "text_io.h"
#include "file_io.h"
#include "help.h"

/******************************************************************************
 Required global variables.
 ******************************************************************************/

/* Create the command table */
const cmd_table_t kstCmdTable[] =
{
   { "l?",        CmdLeftHand,      FIGHTING },
   { "r?",        CmdRightHand,     FIGHTING },
   { "f?",        CmdFeet,          FIGHTING },
   { "e?",        CmdEyes,          FIGHTING },
   { "quit",      CmdQuit,          ALL },
   { "who",       CmdWho,           ALL },
   { "commands",  CmdCommands,      ALL },
   { "shutdown",  CmdShutdown,     ~CROWD },
   { "say",       CmdSay,           ALL },
   { "chat",      CmdChat,         ~CROWD },
   { "emote",     CmdEmote,         ALL },
   { "prompt",    CmdPrompt,       ~CROWD },
   { "create",    CmdCreate,        CROWD },
   { "score",     CmdScore,        ~CROWD },
   { "train",     CmdTrain,         CITIZEN },
   { "str",       CmdStr,           TRAINING },
   { "dex",       CmdDex,           TRAINING },
   { "sta",       CmdSta,           TRAINING },
   { "siz",       CmdSiz,           TRAINING },
   { "wit",       CmdWit,           TRAINING },
   { "leave",     CmdLeave,         TRAINING },
   { "challenge", CmdChallenge,     GLADIATOR|CHALLENGER },
   { "accept",    CmdAccept,        GLADIATOR },
   { "help",      CmdHelp,          ALL },
   { "credits",   CmdCredits,       ALL },
   { NULL }
};

/******************************************************************************
 Required operations.
 ******************************************************************************/

CMD(Quit)
{
   /* Must free their body, if they've created one */
   if ( pstConn->pstBody != NULL )
   {
      body_t* pstBody = pstConn->pstBody;

      /* Log the fact that they've quit */
      Log("%s has quit.\n\r",pstConn->pstBody->a_chName);

      /* If they are fighting someone, that person has to stop */
      if ( pstConn->pstBody->pstOpponent != NULL )
      {
         body_t* pstBody2 = pstConn->pstBody->pstOpponent;

         /* Always clear their opponent (in case of an unaccepted challenge) */
         pstBody2->pstOpponent = NULL;

         /* Check whether you quit while actually fighting (a surrender) */
         if ( GetStatus(pstConn) == FIGHTING )
         {
            /* Adjusts wins and losses... */
            pstConn->pstBody->iLoss++;
            pstBody2->iWin++;

            /* ...stop the fighting... */
            pstConn->pstBody->pstOpponent->eStatus = GLADIATOR;

            /* ...return them to the stadium... */
            pstBody2->iRoom = 0;

            /* ...and save both you and them! */
            SaveBody(pstBody2);
            SaveBody(pstConn->pstBody);
         }
      }

      if ( pstFirstBody == pstBody )
      {
         pstFirstBody = pstBody->pstNext;
      }

      /* Remove the body from the list */
      if ( pstBody->pstPrev )
      {
         pstBody->pstPrev->pstNext = pstBody->pstNext;
      }

      if ( pstBody->pstNext )
      {
         pstBody->pstNext->pstPrev = pstBody->pstPrev;
      }

      PutOutput(pstConn,TO_USER,"You vanish into the crowd.\n\r");
      SendToBody(pstConn->pstBody,TO_ROOM,"{name} vanishes into the crowd.\n\r");
      FreeBody(&(pstConn->pstBody));
      pstConn->pstBody = NULL;
   }
   else /* They quit before creating a body */
   {
      /* Log the fact that they've quit */
      Log("Player quit without creating a body.\n\r");
   }

   /* Close down the socket and tidy up */
   CloseConnection(pstConn);
}

CMD(Shutdown)
{
   /* If there is no input with the command, send a message and return */
   if ( szTxt[0] == '\0' )
   {
      PutOutput(pstConn,TO_USER,"Syntax: shutdown <password>\n\r");
      return;
   }

   /* Replace the password with something that only you know! */
   if ( strcmp( szTxt, "topsecret" ) )
   {
      PutOutput(pstConn,TO_USER,"Incorrect password.\n\r");
   }
   else
   {
      /* Shutdown message */
      PutOutput(pstConn,TO_ALL,"[Shutdown by %s]\n\r",
         pstConn->pstBody->a_chName);
      Log("Shutdown by %s.\n\r",pstConn->pstBody->a_chName);

      /* Toggle the flag used within the GameLoop function */
      bShutdown=TRUE;
   }
}

CMD(Create)
{
   int i = 0; /* Loop counter */
   int j = 0; /* Loop counter */
   body_t* pstBody;
   char  * szName     = ChopToken( &szTxt );
   char  * szPassword = ChopToken( &szTxt );

   if ( szName[0] == '\0' || szPassword[0] == '\0' )
   {
      PutOutput(pstConn,TO_USER,"Syntax: create <name> <password> - PASSWORDS ARE NOT ENCRYPTED.\n\r");
   }
   else if ( strlen(szName) < 3 || strlen(szName) > 15 )
   {
      PutOutput(pstConn,TO_USER,"Names must be 3-15 letters long.\n\r");
   }
   else if ( strlen(szPassword) < 5 || strlen(szPassword) > 15 )
   {
      PutOutput(pstConn,TO_USER,"Passwords must be 5-15 letters long - AND ARE NOT ENCRYPTED!\n\r");
   }
   else if ( ( pstBody = FindBody(szName) ) != NULL )
   {
      if ( pstBody->pstConn != NULL )
      {
         PutOutput(pstConn,TO_USER,"That name is in use.\n\r");
      }
      else if ( strcmp( pstBody->a_chPassword, szPassword ) )
      {
         PutOutput(pstConn,TO_USER,"Incorrect password.\n\r");

         /* Log the failed reconnection attempt */
         Log("%s tried to reconnect but got the wrong password.\n",szName);
      }
      else /* Reconnect them */
      {
         pstConn->pstBody = pstBody;
         pstBody->pstConn = pstConn;
         PutOutput(pstConn,TO_USER,"Reconnected.\n\r");
         SendToBody(pstBody,TO_ROOM,"{name} has reconnected.\n\r");
      }
   }
   else if ( !(pstConn->pstBody = (body_t*)malloc(sizeof(body_t))) )
   {
      Log("BUG: Cannot malloc.\n");
   }
   else /* The name was acceptable and a body was created */
   {
      /* Clear the body structure */
      *(pstConn->pstBody)=stBodyEmpty;

      do /* loop through the name */
      {
         /* Make sure that the name only contains letters */
         if (!isalpha(szName[i]))
         {
            PutOutput(pstConn,TO_USER,"Name must only contain letters.\n\r");
            return;
         }

         /* Convert the letters to lower case, if they're not already */
         szName[i]|=32;
      }
      while(szName[++i]);

      /* Make the first letter of the name uppercase */
      szName[0]-=32;

      /* Look for an existing player file */
      if ( LoadBody( szName, pstConn->pstBody ) == TRUE )
      {
         /* Make sure they got the password right */
         if ( strcmp( pstConn->pstBody->a_chPassword, szPassword ) )
         {
            /* Inform them that the password is wrong */
            PutOutput(pstConn,TO_USER,"Incorrect password.\n\r");

            /* Destroy the loaded body */
            FreeBody(&(pstConn->pstBody));
            pstConn->pstBody = NULL;

            /* Log the failed connection attempt and return */
            Log("%s tried to connect but got the wrong password.\n",szName);
            return;
         }

         /* Clear their pointer variable */
         pstConn->pstBody->pstOpponent = NULL;

         /* Set the connection to GLADIATOR status */
         pstConn->pstBody->eStatus = GLADIATOR;

         /* Start them in the stadium */
         pstConn->pstBody->iRoom = 0;

         /* Log the load */
         Log("%s has rejoined.\n",szName);
      }
      else if ( pstConn->pstBody->eStatus == CORRUPTED_PFILE )
      {
         /* Inform them that their player file is corrupted */
         PutOutput(pstConn,TO_USER,"Sorry, but your player file has been corrupted.\n\r");

         /* Destroy the loaded body */
         FreeBody(&(pstConn->pstBody));
         pstConn->pstBody = NULL;
         return;
      }
      else/* No player file exists yet for that name */
      {
         /* The connection is now a CITIZEN */
         pstConn->pstBody->eStatus=CITIZEN;

         /* Store the password */
         strcpy( pstConn->pstBody->a_chPassword, szPassword );

         /* Initialise the body's stats to 1 */
         for(i=STR;i<=WIT;i++)
         {
            pstConn->pstBody->iStats[i]=1;
         }

         /* Store the name of the body */
         strcpy(pstConn->pstBody->a_chName,szName);

         /* Log the new body's creation */
         Log("New person '%s' created.\n",szName);
      }

      /* Reset speed counters and action points */
      for ( i = AP_LEFT_HAND; i < AP_MAX; i++ )
      {
         /* Clear the techniques */
         for ( j = 0; j < MAX_TECHNIQUES; j++ )
         {
            pstConn->pstBody->a_chTechniques[i][j] = ' ';
         }
         pstConn->pstBody->a_chTechniques[i][MAX_TECHNIQUES] = '\0';
      }

      /* Display the message to the user and others in the room */
      PutOutput(pstConn,TO_USER,"You step from the crowd.\n\r");
      PutOutput(pstConn,TO_ROOM,"%s steps from the crowd.\n\r",szName);

      /* Initialise the table */
      pstConn->pstBody->eTable[AP_LEFT_HAND] = TABLE_HANDS;
      pstConn->pstBody->eTable[AP_RIGHT_HAND] = TABLE_HANDS;
      pstConn->pstBody->eTable[AP_LEGS] = TABLE_LEGS;
      pstConn->pstBody->eTable[AP_EYES] = TABLE_EYES;

      /* Link the body back to it's connection */
      pstConn->pstBody->pstConn = pstConn;

      /* Insert the new body into the body list */
      if ( pstFirstBody )
      {
         pstFirstBody->pstPrev = pstConn->pstBody;
         pstConn->pstBody->pstNext = pstFirstBody;
      }

      /* The new connection goes on the front of the list */
      pstFirstBody = pstConn->pstBody;
   }
}

CMD(Say)
{
   /* If there is no input with the command, send a message and return */
   if ( szTxt[0] == '\0' )
   {
      PutOutput(pstConn,TO_USER,"Syntax: say <sentence>\n\r");
      return;
   }

   /* Display the message to the user and others in the room */
   PutOutput(pstConn,TO_USER,"You say '%s'\n\r",szTxt);
   PutOutput(pstConn,TO_ROOM,"%s says '%s'\n\r",GetName(pstConn),szTxt);
}

CMD(Chat)
{
   /* If there is no input with the command, send a message and return */
   if ( szTxt[0] == '\0' )
   {
      PutOutput(pstConn,TO_USER,"Syntax: chat <sentence>\n\r");
      return;
   }

   /* Display the message to the user and others in the room */
   PutOutput(pstConn,TO_USER,"You chat '%s'\n\r",szTxt);
   PutOutput(pstConn,TO_WORLD,"%s chats '%s'\n\r",GetName(pstConn),szTxt);
}

CMD(Emote)
{
   /* If there is no input with the command, send a message and return */
   if ( szTxt[0] == '\0' )
   {
      PutOutput(pstConn,TO_USER,"Syntax: emote <action>\n\r");
      return;
   }

   /* Display the message to the user and others in the room */
   PutOutput(pstConn,TO_USER,"%s %s\n\r",GetName(pstConn),szTxt);
   PutOutput(pstConn,TO_ROOM,"%s %s\n\r",GetName(pstConn),szTxt);
}

CMD(Prompt)
{
   /* If there is no input with the command, send a message and return */
   if ( szTxt[0] == '\0' )
   {
      PutOutput(pstConn,TO_USER,"Syntax: prompt <text>\n\r");
      return;
   }

   if ( pstConn->pstBody == NULL )
   {
      /* Logs an error and returns, if pstConn has no body */
      Log("BUG[%s]: No body!\n",__FUNCTION__);
      return;
   }

   if ( strlen(szTxt) > 512 )
   {
      PutOutput( pstConn, TO_USER, "Prompt too big.\n\r" );
      return;
   }

   if ( pstConn->pstBody->szPrompt != NULL )
   {
      free( pstConn->pstBody->szPrompt );
   }

   pstConn->pstBody->szPrompt = strdup(szTxt);
}

CMD(Commands)
{
   int i;          /* Loop counter */
   int iCount = 0; /* Number of commands printed on the line */

   PutOutput( pstConn, TO_USER, GetBar("commands") );

   /* Loop through all the commands in the command table */
   for ( i = 0; kstCmdTable[i].szCommand != NULL; i++ )
   {
      /* Display the command if the connection has access to it */
      if ( ( GetStatus(pstConn) & kstCmdTable[i].eType ) != 0 )
      {
         PutOutput(pstConn,TO_USER,"%-19s%s",kstCmdTable[i].szCommand,
            (!(++iCount%4))?"\n\r":"");
      }
   }

   /* Add an extra newline if necessary, for formatting purposes */
   if ( (iCount % 4) != 0 )
   {
      PutOutput(pstConn,TO_USER,"\n\r");
   }

   PutOutput( pstConn, TO_USER, GetBar(NULL) );
}

CMD(Who)
{
   conn_t* pstUser;
   body_t* pstBody;
   int iCrowd = 0;
   int iCount = 0;

   /* Loop through all of the connections */
   for ( pstUser = pstFirstConn; pstUser; pstUser = pstUser->pstNext )
   {
      /* Check if the appropriate connection has a body */
      if ( ( pstBody = pstUser->pstBody ) != NULL )
      {
         /* If it DOES have a body, display it's name, wins, losses and kills */
         PutOutput(pstConn,TO_USER,"%-20s (Won:%d Lost:%d Kills:%d)\n\r", 
            pstBody->a_chName,pstBody->iWin,pstBody->iLoss,pstBody->iKill);

         /* Add one to the "people who have bodies" counter */
         iCount++;
      }
      else /* Add one to the "people who don't have bodies" counter */
      {
         iCrowd++;
      }
   }

   /* Display the total number of people with and without bodies */
   PutOutput(pstConn,TO_USER,"Total of %d visible %s, with %d other %s in the crowd.\n\r",
      iCount,iCount==1?"person":"people",iCrowd,iCrowd==1?"person":"people");
}

CMD(Challenge)
{
   conn_t* pstUser;
   body_t* pstBody;

   /* If there is no input with the command, send a message and return */
   if ( szTxt[0] == '\0' )
   {
      PutOutput(pstConn,TO_USER,"Syntax: challenge <gladiator>\n\r");
      return;
   }

   if ( pstConn->pstBody == NULL )
   {
      /* Logs an error and returns, if pstConn has no body */
      Log("BUG[%s]: No body!\n",__FUNCTION__);
      return;
   }

   if ( !( pstBody = FindBody(szTxt) ) )
   {
      PutOutput(pstConn,TO_USER,"No such gladiator as '%s'.\n\r",szTxt);
   }
   else if ( pstBody == pstConn->pstBody )
   {
      PutOutput(pstConn,TO_USER,"You cannot challenge yourself!\n\r");
   }
   else if ( (pstUser = pstBody->pstConn) == NULL )
   {
      PutOutput(pstConn,TO_USER,"You cannot challenge a link-dead gladiator.\n\r");
   }
   else if ( GetStatus(pstUser) != GLADIATOR && GetStatus(pstUser) != CHALLENGER )
   {
      PutOutput(pstConn,TO_USER,"They're no gladiator!\n\r");
   }
   else if ( GetRoom(pstConn) || GetRoom(pstUser) )
   {
      PutOutput(pstConn,TO_USER,"You must both be in the stadium.\n\r");
   }
   else
   {
      /* Send the challenge message */
      PutOutput(pstConn,TO_USER,"You challenge %s to a fight.\n\r",szTxt);
      PutOutput(pstUser,TO_USER,"%s challenges you to a fight.  To accept the challenge, type 'accept'.\n\r",
         pstConn->pstBody->a_chName);

      /* Indicate that you're now the challenger */
      pstConn->pstBody->eStatus = CHALLENGER;
      pstUser->pstBody->eStatus = GLADIATOR;

      /* If they've been challenged already, clear the old one */
      if ( pstUser->pstBody->pstOpponent != NULL )
      {
         pstUser->pstBody->pstOpponent->pstOpponent = NULL;
      }

      /* Set the two players as opponents */
      pstConn->pstBody->pstOpponent=pstUser->pstBody;
      pstUser->pstBody->pstOpponent=pstConn->pstBody;
   }
}

CMD(Accept)
{
   conn_t*    pstUser;
   static int s_iArena;
   ap_t       i; /* Loop counter */
   int        j; /* Loop counter */

   if ( pstConn->pstBody == NULL )
   {
      /* Logs an error and returns, if pstConn has no body */
      Log("BUG[%s]: No body!\n",__FUNCTION__);
      return;
   }

   if ( pstConn->pstBody->pstOpponent == NULL )
   {
      PutOutput(pstConn,TO_USER,"You've not been challenged.\n\r");
   }
   else if ( ( pstUser = pstConn->pstBody->pstOpponent->pstConn ) == NULL )
   {
      PutOutput(pstConn,TO_USER,"Wait until they reconnect!\n\r");
   }
   else
   {
      /* Send the acceptance message */
      PutOutput(pstConn,TO_USER,"Ok.\n\r");
      PutOutput(pstUser,TO_USER,"%s accepts your challenge!\n\r",
         pstConn->pstBody->a_chName);
      PutOutput(pstConn,TO_ALL,"[%s and %s have entered the arena]\n\r",
         pstConn->pstBody->a_chName,pstUser->pstBody->a_chName);

      /* Heal both gladiators */
      pstConn->pstBody->iDam = 0;
      pstUser->pstBody->iDam = 0;

      /* Reset speed counters and action points */
      for ( i = AP_LEFT_HAND; i < AP_MAX; i++ )
      {
         pstConn->pstBody->iSpeed[i] = 0;
         pstUser->pstBody->iSpeed[i] = 0;
         pstConn->pstBody->iActions[i] = 0;
         pstUser->pstBody->iActions[i] = 0;
         pstConn->pstBody->bBusy[i] = FALSE;
         pstUser->pstBody->bBusy[i] = FALSE;
         /* Clear the techniques */
         for ( j = 0; j < MAX_TECHNIQUES; j++ )
         {
            pstConn->pstBody->a_chTechniques[i][j] = ' ';
            pstUser->pstBody->a_chTechniques[i][j] = ' ';
         }
         pstConn->pstBody->a_chTechniques[i][MAX_TECHNIQUES] = '\0';
         pstUser->pstBody->a_chTechniques[i][MAX_TECHNIQUES] = '\0';
         pstConn->pstBody->iCmbtIndex[i] = 0;
         pstUser->pstBody->iCmbtIndex[i] = 0;
      }

      /* Move both gladiators into an arena */
      pstConn->pstBody->iRoom = pstUser->pstBody->iRoom= ++s_iArena;

      /* Set both gladiators to fighting */
      pstConn->pstBody->eStatus = pstUser->pstBody->eStatus = FIGHTING;
   }
}

CMD(Train)
{
   if ( pstConn->pstBody == NULL )
   {
      /* Logs an error and returns, if pstConn has no body */
      Log("BUG[%s]: No body!\n",__FUNCTION__);
      return;
   }

   /* The connection is now in TRAINING */
   pstConn->pstBody->eStatus = TRAINING;

   /* Display the message to the user and others in the room */
   PutOutput(pstConn,TO_USER,"You walk to the training room.  To get back, type 'leave'.\n\r");
   PutOutput(pstConn,TO_ROOM,"%s walks to the training room.\n\r",
      pstConn->pstBody->a_chName);

   /* Indicate that the connection is in the training room (-1) */
   pstConn->pstBody->iRoom=-1;

   /* Display the message to the people already in the training room */
   PutOutput(pstConn,TO_ROOM,"%s enters the training room.\n\r", 
      pstConn->pstBody->a_chName);
}

CMD(Leave)
{
   int i,iCount=0;

   if ( pstConn->pstBody == NULL )
   {
      /* Logs an error and returns, if pstConn has no body */
      Log("BUG[%s]: No body!\n",__FUNCTION__);
      return;
   }

   /* Calculate their total number of points worth of stats */
   for ( i = STR; i <= WIT; i++ )
   {
      iCount += pstConn->pstBody->iStats[i];
   }

   /* Display the message to the user and others in the room */
   PutOutput(pstConn,TO_USER,"You leave the training room.\n\r");
   PutOutput(pstConn,TO_ROOM,"%s leaves the training room.\n\r", 
      pstConn->pstBody->a_chName);

   /* Indicate that the connection is now back at the stadium (0) */
   pstConn->pstBody->iRoom = 0;

   /* Display the message to the people already at the stadium */
   PutOutput(pstConn,TO_ROOM,"%s arrives from the training room.\n\r", 
      pstConn->pstBody->a_chName);

   /* If they've spent all their points, they become a gladiator */
   if ( iCount >= MAX_TRAIN )
   {
      pstConn->pstBody->eStatus = GLADIATOR;

      /* Gladiators can save! */
      SaveBody(pstConn->pstBody);
   }
   else /* Otherwise they're still a CITIZEN */
   {
      pstConn->pstBody->eStatus = CITIZEN;
   }
}

CMD(Str)
{
   int iNum = szTxt[0] ? TextCalculate( pstConn, szTxt ) : 1;

   /* Increase the connection's strength, if possible */
   TrainStat( pstConn, "strength", STR, iNum );
}

CMD(Dex)
{
   int iNum = szTxt[0] ? TextCalculate( pstConn, szTxt ) : 1;

   /* Increase the connection's dexterity, if possible */
   TrainStat( pstConn, "dexterity", DEX, iNum );
}

CMD(Sta)
{
   int iNum = szTxt[0] ? TextCalculate( pstConn, szTxt ) : 1;

   /* Increase the connection's stamina, if possible */
   TrainStat( pstConn, "stamina", STA, iNum );
}

CMD(Siz)
{
   int iNum = szTxt[0] ? TextCalculate( pstConn, szTxt ) : 1;

   /* Increase the connection's size, if possible */
   TrainStat( pstConn, "size", SIZ, iNum );
}

CMD(Wit)
{
   int iNum = szTxt[0] ? TextCalculate( pstConn, szTxt ) : 1;

   /* Increase the connection's wits, if possible */
   TrainStat( pstConn, "wits", WIT, iNum );
}

CMD(Score)
{
   PutHelpText( pstConn, "@score" );
}

CMD(Help)
{
   while ( *szTxt == '@' )
   {
      /* Get rid of any '@' symbols as those keywords be available via help */
      szTxt++;
   }

   if ( strlen(szTxt) > 40 )
   {
      /* Chop off excessively long help checks */
      szTxt[40] = '\0';
   }
   else if ( szTxt[0] == '\0' )
   {
      /* Help without arguments displays the index */
      PutOutput( pstConn, TO_USER, GetBar("index") );
      DisplayHelpIndex( pstConn );
      PutOutput( pstConn, TO_USER, GetBar(NULL) );
      return;
   }

   PutOutput( pstConn, TO_USER, GetBar(szTxt) );
   PutHelpText( pstConn, szTxt );
   PutOutput( pstConn, TO_USER, GetBar(NULL) );
}

CMD(Credits)
{
   PutOutput( pstConn, TO_USER, GetBar("credits") );
   PutHelpText( pstConn, "@credits" );
   PutOutput( pstConn, TO_USER, GetBar(NULL) );
}

CMD(LeftHand)
{
   void (*pfnCombatCmd)(body_t* pstBody, ap_t eLocation);

   /* The '@' character tells the combat table it's a command, not a move */
   szCmd[0] = '@';

   /* Initialise the pointer to the command in the combat table */
   pfnCombatCmd = CombatCmd( pstConn->pstBody, szCmd, AP_LEFT_HAND );

   if ( pfnCombatCmd != NULL )
   {
      /* Call the appropriate command */
      (*pfnCombatCmd)(pstConn->pstBody, AP_LEFT_HAND);
   }
   else /* There is no such command in the combat table */
   {
      PutOutput( pstConn, TO_USER, "Unrecognised left hand technique '%c'.\n\r", szCmd[1] );
   }
}

CMD(RightHand)
{
   void (*pfnCombatCmd)(body_t* pstBody, ap_t eLocation);

   /* The '@' character tells the combat table it's a command, not a move */
   szCmd[0] = '@';

   /* Initialise the pointer to the command in the combat table */
   pfnCombatCmd = CombatCmd( pstConn->pstBody, szCmd, AP_RIGHT_HAND );

   if ( pfnCombatCmd != NULL )
   {
      /* Call the appropriate command */
      (*pfnCombatCmd)(pstConn->pstBody, AP_RIGHT_HAND);
   }
   else /* There is no such command in the combat table */
   {
      PutOutput( pstConn, TO_USER, "Unrecognised right hand technique '%c'.\n\r", szCmd[1] );
   }
}

CMD(Feet)
{
   void (*pfnCombatCmd)(body_t* pstBody, ap_t eLocation);

   /* The '@' character tells the combat table it's a command, not a move */
   szCmd[0] = '@';

   /* Initialise the pointer to the command in the combat table */
   pfnCombatCmd = CombatCmd( pstConn->pstBody, szCmd, AP_LEGS );

   if ( pfnCombatCmd != NULL )
   {
      /* Call the appropriate command */
      (*pfnCombatCmd)(pstConn->pstBody, AP_LEGS);
   }
   else /* There is no such command in the combat table */
   {
      PutOutput( pstConn, TO_USER, "Unrecognised feet technique '%c'.\n\r", szCmd[1] );
   }
}

CMD(Eyes)
{
   void (*pfnCombatCmd)(body_t* pstBody, ap_t eLocation);

   /* The '@' character tells the combat table it's a command, not a move */
   szCmd[0] = '@';

   /* Initialise the pointer to the command in the combat table */
   pfnCombatCmd = CombatCmd( pstConn->pstBody, szCmd, AP_EYES );

   if ( pfnCombatCmd != NULL )
   {
      /* Call the appropriate command */
      (*pfnCombatCmd)(pstConn->pstBody, AP_EYES);
   }
   else /* There is no such command in the combat table */
   {
      PutOutput( pstConn, TO_USER, "Unrecognised eyes technique '%c'.\n\r", szCmd[1] );
   }
}