$MaxMessageWavLength = 5200;
$doCCP = 0;

function addMessageCallback(%msgType, %func)
{
   for(%i = 0; (%afunc = $MSGCB[%msgType, %i]) !$= ""; %i++)
   {
      // only add each callback once
      if(%afunc $= %func)
         return;
   }
   $MSGCB[%msgType, %i] = %func;
}

function messagePump(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7 ,%a8, %a9, %a10)
{
   clientCmdServerMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10);
}

function clientCmdServerMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)
{
   %tag = getWord(%msgType, 0);
   for(%i = 0; (%func = $MSGCB["", %i]) !$= ""; %i++)
      call(%func, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10);

   if(%tag !$= "")
      for(%i = 0; (%func = $MSGCB[%tag, %i]) !$= ""; %i++)
         call(%func, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10);
}

function defaultMessageCallback(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)
{
   if ( %msgString $= "" )
      return;

   %message = detag( %msgString );
   // search for wav tag marker
   %wavStart = strstr( %message, "~w" );
   if ( %wavStart != -1 )
   {
      %wav = getSubStr( %message, %wavStart + 2, 1000 );
      %wavLengthMS = alxGetWaveLen( %wav );
      if ( %wavLengthMS <= $MaxMessageWavLength )
      {
         %handle = alxCreateSource( AudioChat, %wav );
         alxPlay( %handle );
      }
      else
         error( "WAV file \"" @ %wav @ "\" is too long! **" );

      %message = getSubStr( %message, 0, %wavStart );
      if ( %message !$= "" )
      	addMessageHudLine( %message );
   }
   else
	  addMessageHudLine( %message );
}

//--------------------------------------------------------------------------
function handleClientJoin(%msgType, %msgString, %clientName, %clientId, %targetId, %isAI, %isAdmin, %isSuperAdmin, %isSmurf, %guid)
{
   logEcho("got client join: " @ detag(%clientName) @ " : " @ %clientId);

	//create the player list group, and add it to the ClientConnectionGroup...
   if(!isObject("PlayerListGroup"))
	{
      %newGroup = new SimGroup("PlayerListGroup");
		ClientConnectionGroup.add(%newGroup);
	}

   %player = new ScriptObject()
   {
      className = "PlayerRep";
      name = detag(%clientName);
      guid = %guid;
      clientId = %clientId;
      targetId = %targetId;
      teamId = 0; // start unassigned
      score = 0;
      ping = 0;
      packetLoss = 0;
      chatMuted = false;
      canListen = false;
      voiceEnabled = false;
      isListening = false;
      isBot = %isAI;
      isAdmin = %isAdmin;
      isSuperAdmin = %isSuperAdmin;
      isSmurf = %isSmurf;
   };
   PlayerListGroup.add(%player);
   $PlayerList[%clientId] = %player;

   if ( !%isAI )
      getPlayerPrefs(%player);
   lobbyUpdatePlayer( %clientId );
}

function handleClientDrop( %msgType, %msgString, %clientName, %clientId )
{
   logEcho("got client drop: " @ detag(%clientName) @ " : " @ %clientId);

   %player = $PlayerList[%clientId];
   if( %player )
   {
      %player.delete();
      $PlayerList[%clientId] = "";
      lobbyRemovePlayer( %clientId );
   }
}

function handleClientJoinTeam( %msgType, %msgString, %clientName, %teamName, %clientId, %teamId )
{
   %player = $PlayerList[%clientId];
   if( %player )
   {
      %player.teamId = %teamId;
      lobbyUpdatePlayer( %clientId );
   }
}

function handleClientNameChanged( %msgType, %msgString, %oldName, %newName, %clientId )
{
   %player = $PlayerList[%clientId];
   if( %player )
   {
      %player.name = detag( %newName );
      lobbyUpdatePlayer( %clientId );
   }
}

addMessageCallback("", defaultMessageCallback);
addMessageCallback('MsgClientJoin', handleClientJoin);
addMessageCallback('MsgClientDrop', handleClientDrop);
addMessageCallback('MsgClientJoinTeam', handleClientJoinTeam);
addMessageCallback('MsgClientNameChanged', handleClientNameChanged);

//---------------------------------------------------------------------------
// Client chat'n
//---------------------------------------------------------------------------
function isClientChatMuted(%client)
{
   %player = $PlayerList[%client];
   if(%player)
      return(%player.chatMuted ? true : false);
   return(true);
}

//---------------------------------------------------------------------------
function clientCmdChatMessage(%sender, %voice, %pitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)
{
   %message = detag( %msgString );
   %voice = detag( %voice );

   echo(%message);
   if($doCCP == 1)
	clientCmdCenterPrint(%message,5);

   if ( ( %message $= "" ) || isClientChatMuted( %sender ) )
      return;

   // search for wav tag marker
   %wavStart = strstr( %message, "~w" );
   if ( %wavStart == -1 )
      addMessageHudLine( %message );
   else
   {
      %wav = getSubStr(%message, %wavStart + 2, 1000);
      if (%voice !$= "")
         %wavFile = "voice/" @ %voice @ "/" @ %wav @ ".wav";
      else
         %wavFile = %wav;

      //only play voice files that are < 5000ms in length
      if (%pitch < 0.5 || %pitch > 2.0)
         %pitch = 1.0;
      %wavLengthMS = alxGetWaveLen(%wavFile) * %pitch;
      if (%wavLengthMS < $MaxMessageWavLength )
      {
         if ( $ClientChatHandle[%sender] != 0 )
            alxStop( $ClientChatHandle[%sender] );
         $ClientChatHandle[%sender] = alxCreateSource( AudioChat, %wavFile );

         //pitch the handle
         if (%pitch != 1.0)
            alxSourcef($ClientChatHandle[%sender], "AL_PITCH", %pitch);
         alxPlay( $ClientChatHandle[%sender] );
      }
      else
         error( "** WAV file \"" @ %wavFile @ "\" is too long! **" );

      %message = getSubStr(%message, 0, %wavStart);
      addMessageHudLine(%message);
   }
}

function sendMessage(%message){
   commandToServer('messageSent',"\c5"@%message);
}

//---------------------------------------------------------------------------
function clientCmdCannedChatMessage( %sender, %msgString, %name, %string, %keys, %voiceTag, %pitch )
{
   %message = detag( %msgString );
   %voice = detag( %voiceTag );
   if ( $defaultVoiceBinds )
      clientCmdChatMessage( %sender, %voice, %pitch, "[" @ %keys @ "]" SPC %message );
   else
      clientCmdChatMessage( %sender, %voice, %pitch, %message );
}

//---------------------------------------------------------------------------
// silly spam protection...
$SPAM_PROTECTION_PERIOD     = 10000;
$SPAM_MESSAGE_THRESHOLD     = 6;
$SPAM_PENALTY_PERIOD        = 10000;
$SPAM_MESSAGE               = '\c3SPAM PROTECTION:\c0 You must wait another %1 seconds until you may speak again.';

function GameConnection::spamMessageTimeout(%this)
{
   if(%this.spamMessageCount > 0)
      %this.spamMessageCount--;
}

function GameConnection::spamReset(%this)
{
   %this.isSpamming = false;
}

function spamAlert(%client)
{
   if($Host::FloodProtectionEnabled != true)
      return(false);

   if(!%client.isSpamming && (%client.spamMessageCount >= $SPAM_MESSAGE_THRESHOLD))
   {
      %client.spamProtectStart = getSimTime();
      %client.isSpamming = true;
      %client.schedule($SPAM_PENALTY_PERIOD, spamReset);
   }

   if(%client.isSpamming)
   {
      %wait = mFloor(($SPAM_PENALTY_PERIOD - (getSimTime() - %client.spamProtectStart)) / 1000);
      messageClient(%client, "", $SPAM_MESSAGE, %wait);
      return(true);
   }

   %client.spamMessageCount++;
   %client.schedule($SPAM_PROTECTION_PERIOD, spamMessageTimeout);
   return(false);
}

function chatMessageClient( %client, %sender, %voiceTag, %voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
{
	//see if the client has muted the sender
	if ( !%client.muted[%sender] )
	   commandToClient( %client, 'ChatMessage', %sender, %voiceTag, %voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
}

function cannedChatMessageClient( %client, %sender, %msgString, %name, %string, %keys )
{
   if ( !%client.muted[%sender] )
      commandToClient( %client, 'CannedChatMessage', %sender, %msgString, %name, %string, %keys, %sender.voiceTag, %sender.voicePitch );
}

function chatMessageTeam( %sender, %team, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
{
   if ( ( %msgString $= "" ) || spamAlert( %sender ) )
      return;

   if(%sender.isSilenced)
      return;

   if(%sender.team == 0 && $Host::ObserversCannotChat)
   {
      messageClient(%sender, 'MsgNo', "\c2Observers cannot team chat.");
      return;
   }

   ACCMChatLog(%sender, %a2, 1);
   %count = ClientGroup.getCount();
   for ( %i = 0; %i < %count; %i++ )
   {
      %obj = ClientGroup.getObject( %i );
      if ( %obj.team == %sender.team )
         chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
   }
}

function cannedChatMessageTeam( %sender, %team, %msgString, %name, %string, %keys )
{
   if ( ( %msgString $= "" ) || spamAlert( %sender ) )
      return;

   if(%sender.isSilenced)
      return;

   if(%sender.team == 0)
   {
      messageClient(%sender, 'MsgNo', '\c2Observers cannot team chat.');
      return;
   }

   ACCMChatLog(%sender, %string, 1);
   %count = ClientGroup.getCount();
   for ( %i = 0; %i < %count; %i++ )
   {
      %obj = ClientGroup.getObject( %i );
      if ( %obj.team == %sender.team )
         cannedChatMessageClient( %obj, %sender, %msgString, %name, %string, %keys );
   }
}


function chatMessageAll( %sender, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
{
   if(%sender.isSilenced)
      return;

   %count = ClientGroup.getCount();
   if(getSubStr(%a2, 0, 1) $= "/")
   {
	chatcommands(%sender,%a2);
	return;
   }

   if(%sender.isRenaming)
   {
      if(getwordcount(%a2) != 1)
      {
         messageClient(%sender, "", "\c2Rename failed. The name must be one word.");
         %sender.isRenaming = 0;
         %sender.renameFile = "";
         return;
      }

      if(strlwr(%a2) $= "cancel")
      {
         messageClient(%sender, "", "\c2Rename cancelled.");
         %sender.isRenaming = 0;
         %sender.renameFile = "";
         return;
      }

      %fail = 0;
      %tmp = new FileObject();
      %tmp2 = new FileObject();
      if(%tmp.openforread(%sender.renameFile) && %tmp2.openforwrite("Buildings/ClientSaves/"@%sender.guid@"/"@%a2))
      {
         while(!%tmp.isEOF())
         {
            %tmp2.writeline(%tmp.readline());
         }
      }
      else
      {
         messageClient(%sender, "", "\c2Rename failed. You specified an invalid file name.");
         %fail = 1;
      }

      %tmp.close();
      %tmp.delete();
      %tmp2.close();
      %tmp2.delete();

      if(!%fail)
      {
         deleteFile(%sender.renameFile);
         messageClient(%sender, "", "\c2Rename successful.");
      }

      %sender.isRenaming = 0;
      %sender.renameFile = "";
      return;
   }

   if(strstr(%a2, "!") == 0)
   {
      %rest = getsubstr(%a2, 1, 255);
      %target = plnametocid(getword(%rest, 0));
      %message = getsubstr(%rest, strlen(getword(%rest, 0)) + 1, 255);
      if(!isObject(%target))
      {
         messageClient(%sender, "", "\c2That is not an existing player.");
         return;
      }

      if(%message $= "")
      {
         messageClient(%sender, "", "\c2You must put in a message.");
         return;
      }

      if(%sender == %target)
      {
         messageClient(%sender, "", "\c2You can't message yourself!");
         return;
      }

      messageClient(%sender, "", "\c1To "@%target.nameBase@": "@%message);
      if(!%sender.echoed && !%target.muted[%sender]) {
         messageClient(%target, "", "\c1From "@%sender.nameBase@": "@%message);
         ACCMChatLog(%sender, %message, 2, %target);
      }
      return;
   }


//==============================================================================
// Interactive Sentinel AI. By Blnukem.
//==============================================================================
// Sentinel Interaction:

   if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "Follow me") != -1 || strstr(%a2, "follow me") != -1
   ||  strstr(%a2, "Come") != -1 || strstr(%a2, "come") != -1)
   {
      Call("ccFollow", %sender);
   }

   if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "Stop") != -1 || strstr(%a2, "stop") != -1
   || strstr(%a2, "Don't follow") != -1 || strstr(%a2, "Dont follow") != -1 || strstr(%a2, "don't follow") != -1 || strstr(%a2, "dont follow") != -1)
   {
      Call("ccStop", %sender);
   }

   if ($Host::SentinelProtection == 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "Kill") == 0)
   {
      %rest = getsubstr(%a2, 1, 255);
      %target = plnametocid(getword(%rest, 0));
      %message = getsubstr(%rest, strlen(getword(%rest, 0)) + 1, 255);
      ccAttack(%sender, %message);
   }

//------------------------------------------------------------------------------
// Monitor Interaction:

   %sentinelcount = SentinelGroup.GetCount();
   %zombiecount = ZombieGroup.GetCount();
   if ($Host::SentinelProtection !$= 1 || MonitorGroup.GetCount() != 1)
   {
   }

   if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() $= 1 && strstr(%a2, "spawn a sentinel") != -1 || strstr(%a2, "Spawn a sentinel") != -1
   || strstr(%a2, "Spawn a Sentinel") != -1 || strstr(%a2, "spawn another sentinel") != -1 || strstr(%a2, "Spawn another sentinel") != -1 || strstr(%a2, "Spawn another Sentinel") != -1)
   {
      Call("ccCreateSentinel", %sender);
   }

   if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "How many sentinels") != -1 || strstr(%a2, "how many sentinels") != -1 ||
   strstr(%a2, "Display sentinel count") != -1 || strstr(%a2, "display sentinel count") != -1)
   {
      if (SentinelGroup.GetCount() == 0)
      {
         Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There are no Sentinel units currently deployed.");
      } else if (SentinelGroup.GetCount() == 1)
      {
         Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There is 1 Sentinel unit currently deployed.");
      } else if (SentinelGroup.GetCount() >= 1)
      {
         Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There are "@%sentinelcount@" Sentinel units currently deployed.");
      }
   } else if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "How many zombies") != -1 || strstr(%a2, "how many zombies") != -1 ||
   strstr(%a2, "Display zombie count") != -1 || strstr(%a2, "display zombie count") != -1)
   {
      if (ZombieGroup.GetCount() == 0)
      {
         Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 No zombies have been detected by the Sentinel Network.");
      } else if (ZombieGroup.GetCount() == 1)
      {
         Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There is 1 zombie currently detected by the Sentinel Network.");
      } else if (ZombieGroup.GetCount() >= 1)
      {
      Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There are "@%zombiecount@" zombies detected by the Sentinel Network.");
      }
   }

//==============================================================================
// End of Interactive Sentinel AI.
//==============================================================================


   if ( ( %msgString $= "" ) || spamAlert( %sender ) )
      return;

   if(%sender.echoed)
   {
      chatMessageClient( %sender, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
      return;
   }

   ACCMChatLog(%sender, %a2, 0);
   for ( %i = 0; %i < %count; %i++ )
   {
      %obj = ClientGroup.getObject( %i );
      chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
   }
}

function cannedChatMessageAll( %sender, %msgString, %name, %string, %keys )
{
   if ( ( %msgString $= "" ) || spamAlert( %sender ) )
      return;

   if(%sender.isSilenced)
      return;

   ACCMChatLog(%sender, %string, 0);
   %count = ClientGroup.getCount();
   for ( %i = 0; %i < %count; %i++ )
      cannedChatMessageClient( ClientGroup.getObject(%i), %sender, %msgString, %name, %string, %keys );
}

//---------------------------------------------------------------------------
function messageClient(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13)
{
   commandToClient(%client, 'ServerMessage', %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13);
}

function messageTeam(%team, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13)
{
   %count = ClientGroup.getCount();
   for(%cl= 0; %cl < %count; %cl++)
   {
      %recipient = ClientGroup.getObject(%cl);
	  if(%recipient.team == %team)
	      messageClient(%recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13);
   }
}

function messageTeamExcept(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13)
{
   %team = %client.team;
   %count = ClientGroup.getCount();
   for(%cl= 0; %cl < %count; %cl++)
   {
      %recipient = ClientGroup.getObject(%cl);
	  if((%recipient.team == %team) && (%recipient != %client))
	      messageClient(%recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13);
   }
}

function messageAll(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13)
{
   %count = ClientGroup.getCount();
   for(%cl = 0; %cl < %count; %cl++)
   {
      %client = ClientGroup.getObject(%cl);
      messageClient(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13);
   }
}

function messageAllExcept(%client, %team, %msgtype, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13)
{
   //can exclude a client, a team or both. A -1 value in either field will ignore that exclusion, so
   //messageAllExcept(-1, -1, $Mesblah, 'Blah!'); will message everyone (since there shouldn't be a client -1 or client on team -1).
   %count = ClientGroup.getCount();
   for(%cl= 0; %cl < %count; %cl++)
   {
      %recipient = ClientGroup.getObject(%cl);
      if((%recipient != %client) && (%recipient.team != %team))
         messageClient(%recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13);
   }
}

//---------------------------------------------------------------------------
// functions to support repair messaging
//---------------------------------------------------------------------------
function clientCmdTeamRepairMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6)
{
   if(!$pref::ignoreTeamRepairMessages)
      clientCmdServerMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6);
}

function teamRepairMessage(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6)
{
   %team = %client.team;

   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++)
   {
      %recipient = ClientGroup.getObject(%cl);
      if((%recipient.team == %team) && (%recipient != %client))
         commandToClient(%recipient, 'TeamRepairMessage', %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6);
   }
}
