/*********************************************************
 *          Bugblatter Sound Extension - V3.3            *
 *********************************************************
 *                                                       *
 * This plug-in is linux & win32 friendly.               *
 *                                                       *
 * Version history                                       *
 *                                                       *
 * NOTE: Version numbers are for the plugins package     *
 *       this plugin may not change every version.       *
 *                                                       *
 * Version 3.3:                                          *
 *                                                       *
 *  - Updated to compile on Admin Mod V2.50.56           *
 *                                                       *
 * Version 3.2:                                          *
 *                                                       *
 *  - Updated for language plugin version 3.3            *
 *                                                       *
 * Version 3.1:                                          *
 *                                                       *
 *  -  No Changes                                        *
 *                                                       *
 * Version 2.6:                                          *
 *                                                       *
 *  -  Copes with plugin_info firing before              *
 *     plugin_connect                                    *
 *                                                       *
 * Version 2.5:                                          *
 *  -  Initial Version                                   *
 *                                                       *
 *                                                       *
 *********************************************************/
 
#pragma dynamic 4096
#include <core>
#include <console>
#include <string>
#include <admin>
#include <adminlib>
#include <plugin>
 
#include "settings"
#include "clientio"
#include "ip"
#include "language"
#include "dictionary"
#include "speech"
#include "connections"
 
new g_Version[] = "3.3";
 
/* IMPORTANT: Increase this is your playersounds.ini contains more than 100 lines */
#define MAX_PLAYER_SOUNDS 100
 
 
/* Maximum length of a line in playersounds.ini - 3 phrases + a type + a name/wonid/ip address/acess level */
#define MAX_SOUNDS_LENGTH 300
 
#define DEFAULT_ENABLED 1
#define MIN_ENABLED 0
#define MAX_ENABLED 1
 
#define DEFAULT_REPORT 1
#define MIN_REPORT 0
#define MAX_REPORT 1
 
 
#define PSTYPE_WONID 1
#define PSTYPE_NAME 2
#define PSTYPE_IP 3
#define PSTYPE_LEVEL 4
#define PSTYPE_CLAN 5
#define PSTYPE_ANY 6
 
forward RegisterPlugin();
forward BBSoundEnabled(HLCommand,HLData,HLUserName,UserIndex);
forward BBSoundReset(HLCommand,HLData,HLUserName,UserIndex);
forward BBSoundReport(HLCommand,HLData,HLUserName,UserIndex);
forward BBLogdTeamSelect(HLCommand,HLData,HLUserName,UserIndex);
forward ShowConfig(Force,UserIndex);
forward ReadPlayerSounds();
forward ReadSoundsLine(SoundsFile[],i,Line[]);
forward StartOfRound(Timer,Repeat,HLUserName,HLParam);
forward HandleConnect(Timer,Repeat,HLUserName,HLParam);
forward HandleConnectOther(Timer,Repeat,HLUserName,HLParam);
forward HandleDisconnect(UserIndex);
forward DoHandleConnect(UserIndex,Retries);
forward DoHandleConnectOther(UserIndex,Retries);
 
new g_MSG_REPORT_TITLE[]="Bugblatter Sound configuration:|Configuration du Son Bugblatter:|Bugblatter Sound Einstellungen:";
new g_MSG_RPT_ENABLED_ON[]=" - Sound is currently enabled (bbsound_enabled)| - Le son est actuellement actif (bbsound_enabled)| - Sound ist momentan aktiviert (bbsound_enabled)";
new g_MSG_RPT_ENABLED_OFF[]=" - Sound is currently disabled (bbsound_enabled)| - Le son est actuellement desative (bbsound_enabled)| - Sound ist momentan deaktiviert (bbsound_enabled)";
 
/* Configuration variables */
new g_Enabled=DEFAULT_ENABLED;                          /* Is this plugin enabled */
new g_Report=DEFAULT_REPORT;                            /* Should it report */
 
/* State Varaibles */
new g_Registered=0;
new g_PSType[MAX_PLAYER_SOUNDS];
new g_PSKey[MAX_PLAYER_SOUNDS];
new g_PSKeyString[MAX_PLAYER_SOUNDS][MAX_NAME_LENGTH];
new g_PSConnectSelf[MAX_PLAYER_SOUNDS][MAX_SOUND_LENGTH];
new g_PSConnectOther[MAX_PLAYER_SOUNDS][MAX_SOUND_LENGTH];
new g_PSDisconnectOther[MAX_PLAYER_SOUNDS][MAX_SOUND_LENGTH];
new g_PSSize=0;
new g_IP[MAX_PLAYERS];
new g_JustConnected[MAX_PLAYERS];
new g_StartOfRound=1;
new g_SelfTimers[MAX_PLAYERS];
new g_OtherTimers[MAX_PLAYERS];
new g_LogdDetected=0;                                       /* True if logd is installed */
 
/*********************************************************/
/* Standard event handlers for adminmod                  */
/*********************************************************/
 
public plugin_init() {
  plugin_registerinfo("Bugblatter's Sound plugin","Provide various sound releated features",g_Version);
 
  /* Check server is configured properly */
  new fOK = checkFileAccessRead();
  fOK = checkVaultOK() && fOK;
  if (fOK == 0) {
    return PLUGIN_CONTINUE;
  }
 
  if (ReadPlayerSounds() == 0) {
    return PLUGIN_CONTINUE;
  }
 
  /* No need to abort if this one fails */
  checkLanguage();
  readvaultnum("bbsound_enabled",g_Enabled,DEFAULT_ENABLED,MIN_ENABLED,MAX_ENABLED);
  RegisterPlugin();
 
  set_timer("StartOfRound",30,1,"");
 
  plugin_registercmd("bbsound_logdts","BBLogdTeamSelect",131072,"");
  exec("logd_reg 54 admin_command bbsound_logdts",0);
 
  return PLUGIN_CONTINUE;
}
 
 
RegisterPlugin() {
  /* Register commands with adminmod */
  language_init();
  plugin_registercmd("bbsound_speak","BBSoundSpeak",ACCESS_SAY,
                     "bbsound_speak <message>: Speaks a message to all players.");
  plugin_registercmd("bbsound_enabled","BBSoundEnabled",ACCESS_CONFIG,
                     "bbsound_enabled < ^"on^" | ^"off^" >: Enables / Disables player connection sounds.");
  plugin_registercmd("bbsound_report","BBSoundReport",ACCESS_CONFIG,
                     "bbsound_report [ ^"on^" | ^"off^" ]: shows the sound config, and enables/disable report after every command");
  plugin_registercmd("bbsound_reset","BBSoundReset",ACCESS_CONFIG,
                     "bbsound_reset: resets all sound configuraiton settings to the compiled defaults");
 
  g_Registered=1;
}
 
 
 
public plugin_connect(HLUserName, HLIP, UserIndex) {
  if (g_Registered ==0) {
    return PLUGIN_FAILURE;
  }
  PlayerConnected(UserIndex);
 
  new Data[MAX_DATA_LENGTH];
  safe_convert(HLIP,Data,MAX_DATA_LENGTH);
 
  strtoip(Data,g_IP[UserIndex]);
  return PLUGIN_CONTINUE;
}
 
 
 
public plugin_info(HLOldName, HLNewName, UserIndex) {
  if (g_Registered ==0) {
    return PLUGIN_FAILURE;
  }
 
  if (IsPlayerConnected(UserIndex)==0) {
    return 0;
  }
 
  /* Prevent sounds in the first 20 seconds as it will
   * just be a mess */
 
  if (g_StartOfRound == 1) {
    return PLUGIN_CONTINUE;
  }
 
  new OldName[MAX_NAME_LENGTH];
  safe_convert(HLOldName, OldName, MAX_NAME_LENGTH);
  new NewName[MAX_NAME_LENGTH];
  safe_convert(HLNewName, NewName, MAX_NAME_LENGTH);
 
  new userid = 0;
  new wonid=0;
  new team=0;
  new dead=0;
  new AuthID[MAX_AUTHID_LENGTH];
  new UserName[MAX_NAME_LENGTH];
 
  /* this fails for humans, works for bots, but we don't really care
   * as AuthID is only needed for bots*/
  playerinfo(UserIndex, UserName, MAX_NAME_LENGTH, userid, wonid,team,dead,AuthID);
 
  if (((OldName[0] == NULL_CHAR) || isbot(AuthID)) && (NewName[0] != NULL_CHAR)) {
    new UserData[3];
    UserData[0]=UserIndex;
    UserData[1]=10;
    UserData[2]=NULL_CHAR;
 
    if (g_LogdDetected) {
      g_JustConnected[UserIndex] = 1;
    }
    else {
      if (g_SelfTimers[UserIndex]) {
        kill_timer(g_SelfTimers[UserIndex]);
      }
      g_SelfTimers[UserIndex] = set_timer("HandleConnect",30,1,UserData);
      if (g_OtherTimers[UserIndex]) {
        kill_timer(g_OtherTimers[UserIndex]);
      }
      g_OtherTimers[UserIndex] = set_timer("HandleConnectOther",15,1,UserData);
    }
  }
 
  return PLUGIN_CONTINUE;
}
 
 
 
public plugin_disconnect(HLUserName, UserIndex) {
  if (g_Registered ==0) {
    return PLUGIN_FAILURE;
  }
  PlayerDisconnected(UserIndex);
 
  g_IP[UserIndex] = -1;
  if (g_SelfTimers[UserIndex]) {
    kill_timer(g_SelfTimers[UserIndex]);
    g_SelfTimers[UserIndex]=0;
  }
  if (g_OtherTimers[UserIndex]) {
    kill_timer(g_OtherTimers[UserIndex]);
    g_OtherTimers[UserIndex]=0;
  }
 
  HandleDisconnect(UserIndex);
  return PLUGIN_CONTINUE;
}
 
 
public BBLogdTeamSelect(HLCommand,HLData,HLUserName,UserIndex) {
  new Data[MAX_DATA_LENGTH];
  safe_convert(HLData, Data, MAX_DATA_LENGTH);
  new i = strtonum(Data);
 
  g_LogdDetected=1;
 
  if ((i>0) && (i<=MAX_PLAYERS)) {
    if (g_JustConnected[i]) {
      g_JustConnected[i]=0;
      DoHandleConnect(i,10);
      DoHandleConnectOther(i,10);
    }
  }
 
  return PLUGIN_HANDLED;
}
 
 
/*********************************************************/
/* Command handlers for admin commands                   */
/*********************************************************/
 
 
public BBSoundSpeak(HLCommand,HLData,HLUserName,UserIndex) {
  new Message[MAX_DATA_LENGTH];
  safe_convert(HLData,Message,MAX_DATA_LENGTH);
  strstripquotes(Message);
  preparespeech(Message);
  SpeakOther("",Message);
  return PLUGIN_HANDLED;
}
 
public BBSoundEnabled(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLonoff(HLData,g_Enabled,DEFAULT_ENABLED,MIN_ENABLED,MAX_ENABLED)) {
  writevaultnum("bbsound_enabled",g_Enabled);
  }
  return ShowConfig(0,UserIndex);
}
 
public BBSoundReset(HLCommand,HLData,HLUserName,UserIndex) {
  g_Enabled=DEFAULT_ENABLED;
  writevaultnum("bbsound_report",g_Enabled);
  return ShowConfig(0,UserIndex);
}
 
 
public BBSoundReport(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLonoff(HLData,g_Report,DEFAULT_REPORT,MIN_REPORT,MAX_REPORT)) {
    writevaultnum("bbsound_report",g_Report);
    ShowConfig(0,UserIndex);
  }
  else {
    ShowConfig(1,UserIndex);
  }
  return PLUGIN_HANDLED;
}
 
 
ShowConfig(Force,UserIndex) {
  if ((Force==0) && (g_Report==0)) {
    return PLUGIN_HANDLED;
  }
 
  language_say(UserIndex,g_MSG_REPORT_TITLE,print_type:print_console);
  if (g_Enabled) {
    language_say(UserIndex,g_MSG_RPT_ENABLED_ON,print_type:print_console);
  }
  else {
    language_say(UserIndex,g_MSG_RPT_ENABLED_OFF,print_type:print_console);
  }
 
 
  return PLUGIN_HANDLED;
}
 
 
/*********************************************************/
/* Sound file handling                                   */
/*********************************************************/
 
ReadPlayerSounds() {
  g_PSSize=0;
 
  new SoundsFile[MAX_DATA_LENGTH];
  if (getfilelocation(SoundsFile,"playersounds.ini")==0) {
    return 0;
  }
 
  new ReadCount = filesize(SoundsFile)+1;
  if (ReadCount >= MAX_PLAYER_SOUNDS) {
    new msg[MAX_TEXT_LENGTH];
    snprintf(msg,MAX_TEXT_LENGTH,"ERROR: You have more than %i player sounds in %s",MAX_PLAYER_SOUNDS,SoundsFile);
    log(msg);
    log("You must recompile plugin_blatt_sounds with a higher value for MAX_PLAYER_SOUNDS");
    return 0;
  }
 
  new i;
  new args;
  new Line[MAX_SOUNDS_LENGTH];
  new Type[MAX_DATA_LENGTH];
  new Key[MAX_DATA_LENGTH];
  new ConnectSelf[MAX_SOUND_LENGTH];
  new ConnectOther[MAX_SOUND_LENGTH];
  new DisconnectOther[MAX_SOUND_LENGTH];
  new msg[MAX_TEXT_LENGTH];
 
  for(i=0;i<ReadCount;i++) {
    /* File line numbers are indexed from 1, not 0, hence the +1 */
    readline(SoundsFile,i,Line);
 
    if (strlen(Line)>0) {
      args=strsplitx(Line,':','\',Type,MAX_DATA_LENGTH,Key,MAX_DATA_LENGTH,ConnectSelf,MAX_SOUND_LENGTH,ConnectOther,MAX_SOUND_LENGTH,DisconnectOther,MAX_SOUND_LENGTH);
      if (args > 3) {
        strtrim(Type," ^t",2);
        strtrim(Key," ^t",2);
        strtrim(ConnectSelf," ^t",2);
        strtrim(ConnectOther," ^t",2);
        strtrim(DisconnectOther," ^t",2);
 
        if (strcasecmp(Type,"wonid")==0) {
          g_PSType[g_PSSize] = PSTYPE_WONID;
          strcpy(g_PSKeyString[g_PSSize],Key,MAX_DATA_LENGTH);
        }
        else if (strcasecmp(Type,"name")==0) {
          g_PSType[g_PSSize] = PSTYPE_NAME;
          strcpy(g_PSKeyString[g_PSSize],Key,MAX_DATA_LENGTH);
        }
        else if (strcasecmp(Type,"ip")==0) {
          g_PSType[g_PSSize] = PSTYPE_IP;
          if (strtoip(Key,g_PSKey[g_PSSize])==0) {
            snprintf(msg,MAX_TEXT_LENGTH,"Line %i of %s contains an invalid IP address %s",i,SoundsFile,Key);
            configError(msg);
            continue;
          }
        }
        else if (strcasecmp(Type,"level")==0) {
          g_PSType[g_PSSize] = PSTYPE_LEVEL;
          g_PSKey[g_PSSize] = strtonum(Key);
        }
        else if (strcasecmp(Type,"clan")==0) {
          g_PSType[g_PSSize] = PSTYPE_CLAN;
          strcpy(g_PSKeyString[g_PSSize],Key,MAX_DATA_LENGTH);
        }
        else if (strcasecmp(Type,"any")==0) {
          g_PSType[g_PSSize] = PSTYPE_ANY;
          /* key is irrelevent in this case */
        }
        else {
          snprintf(msg,MAX_TEXT_LENGTH,"Line %i of %s contains unknown type %s",i,SoundsFile,Type);
          configError(msg);
          continue;
        }
 
        preparespeech(ConnectSelf);
        preparespeech(ConnectOther);
        preparespeech(DisconnectOther);
 
        strcpy(g_PSKeyString[g_PSSize],Key,MAX_DATA_LENGTH);
        strcpy(g_PSConnectSelf[g_PSSize],ConnectSelf,MAX_DATA_LENGTH);
        strcpy(g_PSConnectOther[g_PSSize],ConnectOther,MAX_DATA_LENGTH);
        strcpy(g_PSDisconnectOther[g_PSSize],DisconnectOther,MAX_DATA_LENGTH);
 
        g_PSSize++;
 
      }
      else {
        snprintf(msg,MAX_TEXT_LENGTH,"Line %i of %s is not formatted correctly",i,SoundsFile);
        configError(msg);
      }
    }
  }
  return 1;
}
 
public StartOfRound(Timer,Repeat,HLUserName,HLParam) {
  g_StartOfRound=0;
}
 
public HandleConnect(Timer,Repeat,HLUserName,HLParam) {
  new UserData[2];
  convert_string(HLParam,UserData,2);
  DoHandleConnect(UserData[0],UserData[1]);
}
 
DoHandleConnect(UserIndex,Retries) {
  new i;
  new userid = 0;
  new wonid=0;
  new team=0;
  new dead=0;
  new AuthID[MAX_AUTHID_LENGTH];
  new UserName[MAX_NAME_LENGTH];
 
  Retries=Retries-1;
  g_SelfTimers[UserIndex] = 0;
 
  if(playerinfo(UserIndex, UserName, MAX_NAME_LENGTH, userid, wonid,team,dead,AuthID)==0) {
    if (Retries > 0) {
      new UserData[3];
      UserData[0]=UserIndex;
      UserData[1]=Retries;
      UserData[2]=0;
      g_SelfTimers[UserIndex] = set_timer("HandleConnect",5,1,UserData);
    }
    else {
      log("Player info failed in HandleConnect 10 times - giving up");
      return 0;
    }
  }
 
  for (i=0;i<g_PSSize;i++) {
    switch(g_PSType[i]) {
      case PSTYPE_WONID:
        if (streq(g_PSKeyString[i],AuthID)==0) {
          continue;
        }
 
      case PSTYPE_NAME:
        if (streq(g_PSKeyString[i],UserName)==0) {
          continue;
        }
 
      case PSTYPE_IP:
        if (g_PSKey[i] != g_IP[UserIndex]) {
          continue;
        }
 
      case PSTYPE_LEVEL:
        if (access(g_PSKey[i],UserName) ==0) {
          continue;
        }
 
      case PSTYPE_CLAN:
        if (strmatch(g_PSKeyString[i],UserName,strlen(g_PSKeyString[i]))==0) {
          continue;
        }
 
    }
 
    /* If it reaches this point, no continue above fired, so the line matches */
    if (strlen(g_PSConnectSelf[i]) > 0) {
      Speak(UserName,g_PSConnectSelf[i]);
      return 1;
    }
  }
 
  return 0;
}
 
public HandleConnectOther(Timer,Repeat,HLUserName,HLParam) {
  new UserData[2];
  convert_string(HLParam,UserData,2);
  DoHandleConnectOther(UserData[0],UserData[1]);
}
 
DoHandleConnectOther(UserIndex,Retries) {
  new i;
  new userid = 0;
  new wonid=0;
  new team=0;
  new dead=0;
  new AuthID[MAX_AUTHID_LENGTH];
  new UserName[MAX_NAME_LENGTH];
 
  Retries=Retries-1;
  g_SelfTimers[UserIndex] = 0;
 
  if(playerinfo(UserIndex, UserName, MAX_NAME_LENGTH, userid, wonid,team,dead,AuthID)==0) {
    if (Retries > 0) {
      new UserData[3];
      UserData[0]=UserIndex;
      UserData[1]=Retries;
      UserData[2]=0;
      g_SelfTimers[UserIndex] = set_timer("HandleConnectOther",5,1,UserData);
    }
    else {
      log("Player info failed in HandleConnectOther 10 times - giving up");
      return 0;
    }
  }
 
  for (i=0;i<g_PSSize;i++) {
    switch(g_PSType[i]) {
      case PSTYPE_WONID:
        if (streq(g_PSKeyString[i],AuthID)==0) {
          continue;
        }
 
      case PSTYPE_NAME:
        if (streq(g_PSKeyString[i],UserName)==0) {
          continue;
        }
 
      case PSTYPE_IP:
        if (g_PSKey[i] != g_IP[UserIndex]) {
          continue;
        }
 
      case PSTYPE_LEVEL:
        if (access(g_PSKey[i],UserName) ==0) {
          continue;
        }
 
      case PSTYPE_CLAN:
        if (strmatch(g_PSKeyString[i],UserName,strlen(g_PSKeyString[i]))==0) {
          continue;
        }
 
    }
 
    /* If it reaches this point, no continue above fired, so the line matches */
    if (strlen(g_PSConnectOther[i]) > 0) {
      SpeakOther(UserName,g_PSConnectOther[i]);
      return 1;
    }
  }
 
  return 0;
}
 
HandleDisconnect(UserIndex) {
  new i;
 
  new userid = 0;
  new wonid=0;
  new team=0;
  new dead=0;
  new AuthID[MAX_AUTHID_LENGTH];
  new UserName[MAX_NAME_LENGTH];
 
  if(playerinfo(UserIndex, UserName, MAX_NAME_LENGTH, userid, wonid,team,dead,AuthID)==0) {
    return 0;
  }
 
  for (i=0;i<g_PSSize;i++) {
    switch(g_PSType[i]) {
      case PSTYPE_WONID: {
        if (streq(g_PSKeyString[i],AuthID)==0) {
          continue;
        }
      }
      case PSTYPE_NAME: {
        if (streq(g_PSKeyString[i],UserName)==0) {
          continue;
        }
      }
      case PSTYPE_IP: {
        if (g_PSKey[i] != g_IP[UserIndex]) {
          continue;
        }
      }
      case PSTYPE_LEVEL: {
        if (access(g_PSKey[i],UserName) ==0) {
          continue;
        }
      }
      case PSTYPE_CLAN: {
        if (strmatch(g_PSKeyString[i],UserName,strlen(g_PSKeyString[i]))==0) {
          continue;
        }
      }
    }
 
    /* If it reaches this point, no continue above fired, so the line matches */
 
    if ((strlen(g_PSConnectOther[i]) > 0)) {
      SpeakOther(UserName,g_PSDisconnectOther[i]);
      return 1;
    }
  }
 
  return 0;
}