// ####################################
// #                                  #
// #       Ping of Death - Bot        #
// #                by                #
// #    Markus Klinge aka Count Floyd #
// #                                  #
// ####################################
//
// Started from the HPB-Bot Alpha Source
// by Botman so Credits for a lot of the basic
// HL Server/Client Stuff goes to him
//
// engine.cpp
//
// Does the major work of calling the original Engine Functions

#include "bot_globals.h"


void pfnEmitSound (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch)
{
   SoundAttachToThreat (entity, sample, volume);

   RETURN_META (MRES_IGNORED);
}


edict_t *pfnFindEntityByString (edict_t *pEdictStartSearchAfter, const char *pszField, const char *pszValue)
{
   // new round in CS 1.5
   if (strcmp ("info_map_parameters", pszValue) == 0)
      UTIL_RoundStart (); // the round has restarted

   RETURN_META_VALUE (MRES_IGNORED, 0);
}


void pfnClientCommand (edict_t *pEdict, char *szFmt, ...)
{
   va_list argptr;
   static char string[1024];

   va_start (argptr, szFmt);
   vsprintf (string, szFmt, argptr);
   va_end (argptr);

   // is the target entity an official bot, a third party bot or a real player ?
   if (pEdict->v.flags & FL_FAKECLIENT)
      RETURN_META (MRES_SUPERCEDE); // prevent bots to be forced to issue client commands

   RETURN_META (MRES_IGNORED);
}


void pfnMessageBegin (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed)
{
   // Called each Time a Message is about to sent

   int index;
   int tab_index;

   botMsgFunction = NULL; // no msg function until known otherwise
   state = 0;

   // Bot involved ?
   if (!FNullEnt (ed))
   {
      index = ENTINDEX (ed) - 1;

      // is this message for a bot?
      if ((index >= 0) && (index < gpGlobals->maxClients) && (bots[index].pEdict == ed))
      {
         botMsgIndex = index; // index of bot receiving message

         // Message handling is done in bot_client.cpp
         if (msg_type == GET_USER_MSG_ID (PLID, "VGUIMenu", NULL))
            botMsgFunction = BotClient_CS_VGUI;
         else if (msg_type == GET_USER_MSG_ID (PLID, "ShowMenu", NULL))
            botMsgFunction = BotClient_CS_ShowMenu;
         else if (msg_type == GET_USER_MSG_ID (PLID, "StatusIcon", NULL))
            botMsgFunction = BotClient_CS_StatusIcon;
         else if (msg_type == GET_USER_MSG_ID (PLID, "WeaponList", NULL))
            botMsgFunction = BotClient_CS_WeaponList;
         else if (msg_type == GET_USER_MSG_ID (PLID, "CurWeapon", NULL))
            botMsgFunction = BotClient_CS_CurrentWeapon;
         else if (msg_type == GET_USER_MSG_ID (PLID, "AmmoX", NULL))
            botMsgFunction = BotClient_CS_AmmoX;
         else if (msg_type == GET_USER_MSG_ID (PLID, "AmmoPickup", NULL))
            botMsgFunction = BotClient_CS_AmmoPickup;
         else if (msg_type == GET_USER_MSG_ID (PLID, "Damage", NULL))
            botMsgFunction = BotClient_CS_Damage;
         else if (msg_type == GET_USER_MSG_ID (PLID, "Money", NULL))
            botMsgFunction = BotClient_CS_Money;
         else if (msg_type == GET_USER_MSG_ID (PLID, "ScreenFade", NULL))
            botMsgFunction = BotClient_CS_ScreenFade;
         else if (msg_type == GET_USER_MSG_ID (PLID, "BombDrop", NULL))
            botMsgFunction = BotClient_CS_BombDrop;
         else if (msg_type == GET_USER_MSG_ID (PLID, "BombPickup", NULL))
            botMsgFunction = BotClient_CS_BombPickup;
         else if (msg_type == GET_USER_MSG_ID (PLID, "SayText", NULL))
            botMsgFunction = BotClient_CS_SayText;
      }
   }

   // round restart in CS 1.6
   if (!g_bIsOldCS15 && (msg_dest == MSG_SPEC) && (msg_type == GET_USER_MSG_ID (PLID, "HLTV", NULL)))
      botMsgFunction = BotClient_CS_HLTV;

   else if (msg_dest == MSG_ALL)
   {
      botMsgIndex = -1; // index of bot receiving message (none)

      if (msg_type == GET_USER_MSG_ID (PLID, "DeathMsg", NULL))
         botMsgFunction = BotClient_CS_DeathMsg;
      else if (msg_type == GET_USER_MSG_ID (PLID, "WeaponList", NULL))
         botMsgFunction = BotClient_CS_WeaponList;
      else if (msg_type == GET_USER_MSG_ID (PLID, "TextMsg", NULL))
         botMsgFunction = BotClient_CS_TextMsgAll;
      else if (msg_type == SVC_INTERMISSION)
      {
         tab_index = 0;
         for (index = 0; index < gpGlobals->maxClients; index++)
            if (bots[index].is_used)
            {
               BotCreateTab[tab_index].bNeedsCreation = TRUE;
               strcpy (BotCreateTab[tab_index].bot_name, bots[index].name);
               BotCreateTab[tab_index].bot_skill = bots[index].bot_skill;
               BotCreateTab[tab_index].bot_personality = bots[index].bot_personality;
               BotCreateTab[tab_index].bot_team = bots[index].bot_team;
               BotCreateTab[tab_index].bot_class = bots[index].bot_class;
               tab_index++;
            }

         // Save collected Experience on Map Change
         SaveExperienceTab ();
         SaveVisTab ();
      }
   }

   RETURN_META (MRES_IGNORED);
}


void pfnMessageEnd (void)
{
   botMsgFunction = NULL;
   state = 0;

   RETURN_META (MRES_IGNORED);
}


void pfnWriteByte (int iValue)
{
   // if this message is for a bot, call the client message function...
   if (botMsgFunction)
      (*botMsgFunction) ((void *) &iValue, botMsgIndex);

   state++;

   RETURN_META (MRES_IGNORED);
}


void pfnWriteChar (int iValue)
{
   // if this message is for a bot, call the client message function...
   if (botMsgFunction)
      (*botMsgFunction) ((void *) &iValue, botMsgIndex);

   state++;

   RETURN_META (MRES_IGNORED);
}


void pfnWriteShort (int iValue)
{
   // if this message is for a bot, call the client message function...
   if (botMsgFunction)
      (*botMsgFunction) ((void *) &iValue, botMsgIndex);

   state++;

   RETURN_META (MRES_IGNORED);
}


void pfnWriteLong (int iValue)
{
   // if this message is for a bot, call the client message function...
   if (botMsgFunction)
      (*botMsgFunction) ((void *) &iValue, botMsgIndex);

   state++;

   RETURN_META (MRES_IGNORED);
}


void pfnWriteAngle (float flValue)
{
   // if this message is for a bot, call the client message function...
   if (botMsgFunction)
      (*botMsgFunction) ((void *) &flValue, botMsgIndex);

   state++;

   RETURN_META (MRES_IGNORED);
}


void pfnWriteCoord (float flValue)
{
   // if this message is for a bot, call the client message function...
   if (botMsgFunction)
      (*botMsgFunction) ((void *) &flValue, botMsgIndex);

   state++;

   RETURN_META (MRES_IGNORED);
}


void pfnWriteString (const char *sz)
{
   // if this message is for a bot, call the client message function...
   if (botMsgFunction)
      (*botMsgFunction) ((void *) sz, botMsgIndex);

   state++;

   RETURN_META (MRES_IGNORED);
}


void pfnWriteEntity (int iValue)
{
   // if this message is for a bot, call the client message function...
   if (botMsgFunction)
      (*botMsgFunction) ((void *) &iValue, botMsgIndex);

   state++;

   RETURN_META (MRES_IGNORED);
}


void pfnClientPrintf (edict_t *pEdict, PRINT_TYPE ptype, const char *szMsg)
{
   if (pEdict->v.flags & FL_FAKECLIENT)
      RETURN_META (MRES_SUPERCEDE); // disallow client printings for bots

   RETURN_META (MRES_IGNORED);
}


const char *pfnCmd_Args (void)
{
   // this function returns a pointer to the whole current client command string. Since bots
   // have no client DLL and we may want a bot to execute a client command, we had to implement
   // a g_argv string in the bot DLL for holding the bots' commands, and also keep track of the
   // argument count. Hence this hook not to let the engine ask an unexistent client DLL for a
   // command we are holding here. Of course, real clients commands are still retrieved the
   // normal way, by asking the engine.

   if (isFakeClientCommand)
   {
      // is it a "say" or "say_team" client command ?
      if (strncmp ("say ", g_argv, 4) == 0)
         RETURN_META_VALUE (MRES_SUPERCEDE, &g_argv[0] + 4); // skip the "say" bot client command (bug in HL engine)
      else if (strncmp ("say_team ", g_argv, 9) == 0)
         RETURN_META_VALUE (MRES_SUPERCEDE, &g_argv[0] + 9); // skip the "say_team" bot client command (bug in HL engine)

      RETURN_META_VALUE (MRES_SUPERCEDE, &g_argv[0]); // else return the whole bot client command string we know
   }

   RETURN_META_VALUE (MRES_IGNORED, 0);
}


const char *pfnCmd_Argv (int argc)
{
   // this function returns a pointer to a certain argument of the current client command. Since
   // bots have no client DLL and we may want a bot to execute a client command, we had to
   // implement a g_argv string in the bot DLL for holding the bots' commands, and also keep
   // track of the argument count. Hence this hook not to let the engine ask an unexistent client
   // DLL for a command we are holding here. Of course, real clients commands are still retrieved
   // the normal way, by asking the engine.

   if (isFakeClientCommand)
      RETURN_META_VALUE (MRES_SUPERCEDE, GetField (g_argv, argc)); // returns the wanted argument

   RETURN_META_VALUE (MRES_IGNORED, 0);
}


int pfnCmd_Argc (void)
{
   // this function returns the number of arguments the current client command string has. Since
   // bots have no client DLL and we may want a bot to execute a client command, we had to
   // implement a g_argv string in the bot DLL for holding the bots' commands, and also keep
   // track of the argument count. Hence this hook not to let the engine ask an unexistent client
   // DLL for a command we are holding here. Of course, real clients commands are still retrieved
   // the normal way, by asking the engine.

   if (isFakeClientCommand)
      RETURN_META_VALUE (MRES_SUPERCEDE, fake_arg_count); // return the argument count

   RETURN_META_VALUE (MRES_IGNORED, 0);
}


void pfnSetClientMaxspeed (const edict_t *pEdict, float fNewMaxspeed)
{
   bot_t *pBot = UTIL_GetBotPointer ((edict_t*) pEdict);
   if (pBot != NULL)
      pBot->pEdict->v.maxspeed = fNewMaxspeed;

   RETURN_META (MRES_IGNORED);
}


const char *pfnGetPlayerAuthId (edict_t *e)
{
   if (e->v.flags & FL_FAKECLIENT)
      RETURN_META_VALUE (MRES_SUPERCEDE, "0");

   RETURN_META_VALUE (MRES_IGNORED, NULL);
}


C_DLLEXPORT int GetEngineFunctions (enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion)
{
   meta_engfuncs.pfnEmitSound = pfnEmitSound;
   meta_engfuncs.pfnFindEntityByString = pfnFindEntityByString;
   meta_engfuncs.pfnClientCommand = pfnClientCommand;
   meta_engfuncs.pfnMessageBegin = pfnMessageBegin;
   meta_engfuncs.pfnMessageEnd = pfnMessageEnd;
   meta_engfuncs.pfnWriteByte = pfnWriteByte;
   meta_engfuncs.pfnWriteChar = pfnWriteChar;
   meta_engfuncs.pfnWriteShort = pfnWriteShort;
   meta_engfuncs.pfnWriteLong = pfnWriteLong;
   meta_engfuncs.pfnWriteAngle = pfnWriteAngle;
   meta_engfuncs.pfnWriteCoord = pfnWriteCoord;
   meta_engfuncs.pfnWriteString = pfnWriteString;
   meta_engfuncs.pfnWriteEntity = pfnWriteEntity;
   meta_engfuncs.pfnClientPrintf = pfnClientPrintf;
   meta_engfuncs.pfnCmd_Args = pfnCmd_Args;
   meta_engfuncs.pfnCmd_Argv = pfnCmd_Argv;
   meta_engfuncs.pfnCmd_Argc = pfnCmd_Argc;
   meta_engfuncs.pfnSetClientMaxspeed = pfnSetClientMaxspeed;
   meta_engfuncs.pfnGetPlayerAuthId = pfnGetPlayerAuthId;

   memcpy (pengfuncsFromEngine, &meta_engfuncs, sizeof (enginefuncs_t));
   return (TRUE);
}
