• You've discovered RedGuides, an EverQuest multi-boxing and scripting community 🧙‍♀️⚙️. We want you to play several EQ characters at once, come join us and say hello! 👋

  • A TLP without truebox has thawed (Very Vanilla ready)
    Frostreaver

Plugin: MQ2ChatEvents

Persnickety

Active member
Joined
Dec 28, 2005
RedCents
This is a combination of Kuv's MQ2CustomSound and my own MQ2CustomPopup, and now has the ability to display most chat that would otherwise be missed while zoning. This plugin will read all incomming chat and search for matches that the user has specified in the INI file. If a match is found, it will either display an overlay popup on the screen, or play a sound, or both.

Reasons to use this for custom sounds instead of EQ's built-in Audio Triggers:
1. You can hear tell sounds even while zoning (if you set it to look for tells). This works for both normal tells and cross-server tells.
2. Tells from NPCs can be automatically filtered out.
3. It will not play tell sounds unless the text is the user-defined color for tells, or tell-echoes. This means the sound wont trigger if someone says "/gu blahblah tells you blahblah".

In game commands:
/togglepopups : Toggles displaying of custom popups
/togglesounds : Toggles playing of custom sounds
/toggleNPCtellfilter : Toggles the automatic filtering out of tells from an NPC
/missedChatEcho : Toggles echoing of missed chat to your MQ window.
/missedChatPopup : Toggles popup notification after missing chat while zoning.
/sound <key> : Plays the sound from the specified key in the INI file
/chatevents : Displays the in game commands and shows the current settings

MQ2ChatEvents.cpp
Rich (BB code):
/* 
MQ2ChatEvents.cpp
by Persnickety
1-30-2006
Credit to Kuv for his MQ2CustomSound plugin on which this was based
-----------------
Usage:
/togglepopups          : Toggles displaying of custom popups
/togglesounds          : Toggles playing of custom sounds
/toggleNPCtellfilter   : Toggles the automatic filtering out of tells from an NPC
/MissedChatEcho        : Toggles echoing of missed chat to MQ window
/MissedChatPopup       : Toggles popup notification after missing chat while zoning
/sound <key>           : Plays the sound from the specified key in the INI file
/chatevents            : Displays the in game commands and shows the current settings
*/

#include "../MQ2Plugin.h"
#include <mmsystem.h>
#include <vector>

#pragma comment(lib,"winmm.lib")

vector<string> events;
vector<string> matchStrings;
vector<string> sounds;
vector<string> popups;
vector<string> textColor;
vector<string> displayDuration;

CHAR soundPath[MAX_STRING] = {0};
CHAR popupText[MAX_STRING] = {0};

DWORD popupColor = 0;
DWORD popupTransparency = 100;
DWORD popupFadeIn = 500;
DWORD popupFadeOut = 500;
DWORD popupHold = 3000;
bool popupsEnabled = true;
bool soundsEnabled = true;
bool npcTellFilter = true;
bool missedChatPopup = true;
bool missedChatEcho = true;
bool tellFlag = false;

PreSetup("MQ2ChatEvents");

string GetOnOffLabel( bool val )
{
   string s;

   if( val ) s = "\agON\ax";
   else s = "\auOFF\ax";

   return s;
}
// IsNpc - checks for a spawn in the current zone by name and returns TRUE if it is an NPC
bool IsNpc( string strSpawnName )
{ 
   // NPC check: cycle through spawn list
    PSPAWNINFO pSpawn = (PSPAWNINFO)pSpawnList;

   while (pSpawn){
      string tmpName (pSpawn->Name);
      if ( tmpName.find(strSpawnName) != string::npos ){
         // Found a name match - check if it's an NPC
         if ( pSpawn->Type != 0 )
            return true;
      }
      pSpawn = pSpawn->pNext;
   }

   return false;
}
// InitEvents - loads custom events and match strings from INI file
void InitEvents()
{
	CHAR	tempSetting[MAX_STRING];		
	GetPrivateProfileString("Settings","PopupsEnabled","TRUE",tempSetting,MAX_STRING,INIFileName);
	if(!strnicmp(tempSetting,"true",4) || !strnicmp(tempSetting,"on",2) || !strnicmp(tempSetting,"1",1)) popupsEnabled = true;
	else popupsEnabled = false;
	GetPrivateProfileString("Settings","SoundsEnabled","TRUE",tempSetting,MAX_STRING,INIFileName);
	if(!strnicmp(tempSetting,"true",4) || !strnicmp(tempSetting,"on",2) || !strnicmp(tempSetting,"1",1)) soundsEnabled = true;
	else soundsEnabled = false;
	GetPrivateProfileString("Settings","NPCTellFilter","TRUE",tempSetting,MAX_STRING,INIFileName);
	if(!strnicmp(tempSetting,"true",4) || !strnicmp(tempSetting,"on",2) || !strnicmp(tempSetting,"1",1)) npcTellFilter = true;
	else npcTellFilter = false;
	GetPrivateProfileString("Settings","MissedChatEcho","TRUE",tempSetting,MAX_STRING,INIFileName);
	if(!strnicmp(tempSetting,"true",4) || !strnicmp(tempSetting,"on",2) || !strnicmp(tempSetting,"1",1)) missedChatEcho = true;
	else missedChatEcho = false;
	GetPrivateProfileString("Settings","MissedChatPopup","TRUE",tempSetting,MAX_STRING,INIFileName);
	if(!strnicmp(tempSetting,"true",4) || !strnicmp(tempSetting,"on",2) || !strnicmp(tempSetting,"1",1)) missedChatPopup = true;
	else missedChatPopup = false;

	CHAR szAllKeys[MAX_STRING] = {0};
	GetPrivateProfileString("Events","keys","MQ2CustomSound_Error",szAllKeys,MAX_STRING,INIFileName);
	string strAllKeys( szAllKeys );	
		
	string::size_type pos = strAllKeys.find("|",0);
	while (pos != string::npos )
	{
		// Add event name to events vector
		string strTempEvent = strAllKeys.substr(0, pos);
		events.push_back( strTempEvent );
	
		// Get event match string from INI
		CHAR szMatchString[MAX_STRING] = {0};
		GetPrivateProfileString("MatchStrings",strTempEvent.c_str(),"MQ2ChatEvents_Error",szMatchString,MAX_STRING,INIFileName);		    
		string strTempMatch ( szMatchString );
		matchStrings.push_back( strTempMatch );

		// Get sound file from INI
		CHAR szSound[MAX_STRING] = {0};
		GetPrivateProfileString("Sounds",strTempEvent.c_str(),"MQ2ChatEvents_Error",szSound,MAX_STRING,INIFileName);
		string strTempSound ( szSound );
		sounds.push_back( strTempSound );

		// Get popup message from INI
		CHAR szPopup[MAX_STRING] = {0};
		GetPrivateProfileString("Popups",strTempEvent.c_str(),"MQ2ChatEvents_Error",szPopup,MAX_STRING,INIFileName);
		string strTempPopup ( szPopup );
		popups.push_back( strTempPopup );

		// Get popup text color from INI
		CHAR szTextColor[MAX_STRING] = {0};
		GetPrivateProfileString("TextColor",strTempEvent.c_str(), "15",szTextColor,MAX_STRING,INIFileName);
		string strTempTextColor ( szTextColor );
		textColor.push_back( strTempTextColor );

		// Get popup display duration from INI
		CHAR szDisplayDuration[MAX_STRING] = {0};
		GetPrivateProfileString("DisplayDuration",strTempEvent.c_str(),"3000",szDisplayDuration,MAX_STRING,INIFileName);
		string strTempDisplayDuration ( szDisplayDuration );
		displayDuration.push_back( strTempDisplayDuration );

		// Erase command from AllKeys variable (so we process the next one on next loop)
		strAllKeys.erase(0, pos + 1);
		pos = strAllKeys.find("|");
	}
}

PLUGIN_API DWORD OnIncomingChat(PCHAR Line, DWORD Color)
{   
//	DebugSpewAlways("MQ2ChatEvents::OnIncomingChat(%s)",Line);
	
   // Only process events if in-game
   if (MQ2Globals::gGameState != GAMESTATE_INGAME)
      return 0;
   
   string strLine ( Line );
   string::size_type pos;	  

   // Process all popup events for matches   
   for( unsigned int i = 0; i < events.size(); i++ )
   {
	   pos = strLine.find(matchStrings,0);
	   if ( pos != string::npos ) {
		//process popups
		sprintf( popupText, "%s", popups.c_str() );
		if( popupsEnabled && strcmp( popupText, "MQ2ChatEvents_Error") != 0 ){
			popupColor = 0x00 + atoi( textColor.c_str() ) ;
			popupHold = (DWORD) atoi( displayDuration.c_str() ); //convert string to int, cast as dword
	
			if( strcmp( popupText, "FULLTEXT") == 0 ){  //if default is defined, display full chat line
				strcpy( popupText, Line);
			}
			DisplayOverlayText(popupText, popupColor, popupTransparency, popupFadeIn, popupFadeOut,popupHold);   
		}
		sprintf( soundPath, "%s", sounds.c_str() );
		//process sounds
		if( soundsEnabled && strcmp( soundPath, "MQ2ChatEvents_Error") != 0 ){
			// Special handling for "tells"
			// Should only play sounds if the tell came from a player
			if( (	matchStrings.find("tells you") != string::npos) 
					|| (matchStrings.find("told you") != string::npos)
					|| (matchStrings.find("You tell") != string::npos)
					|| (matchStrings.find("You told") != string::npos) ) {  
				// Only need to check if the source is a player if color is USERCOLOR_TELL
				if(Color == USERCOLOR_TELL || Color == USERCOLOR_ECHO_TELL) {
					if(npcTellFilter){						
						string strName = strLine.substr(0, pos - 1); // Get tell source
						if( IsNpc(strName) ) return 0; // NPC check	
					}
					// Special handling for suspend minion: unsuspend
					// Because the pet is not actually in the spawn list when the tell from it
					// is received, IsNpc does not catch it - so just filter based on the
					// unsuspend message here.
					if( strLine.find("tells you, 'I live again...'") != string::npos )	return 0;					
				} 
				else return 0;
			} // END special 'tells' handling
			PlaySound(sounds.c_str(),NULL,SND_FILENAME | SND_ASYNC);        
		}
	  }	
	}
	
	if (!gbInZone && missedChatEcho) {
		pos = strLine.find(" tells ",0);
		if ( pos != string::npos ) {
			WriteChatColor( Line,Color);
			tellFlag = true;
		}
		pos = strLine.find(" told ",0);
		if ( pos != string::npos ) {
			WriteChatColor( Line,Color);
			tellFlag = true;			
		}		
	} 
	
	//Check for entered a new zone
	pos = strLine.find("You have entered ",0);
    if ( pos != string::npos && Color == 273) {
		if(tellFlag && missedChatPopup) {
			DisplayOverlayText(	"You missed some chat while zoning!  Check MQ window.", 
							CONCOLOR_YELLOW, 100, 500, 500, 10000);
			tellFlag = false;
		}
	}	

   return 0;
} 

// PlaySound - plays sound events when using the /sound command
// Inspired by the MQ2PlaySound plug-in by Digitalxero
void PlaySoundCmd(PSPAWNINFO pChar, PCHAR szLine)
{
   CHAR szSoundName[MAX_STRING] = {0};
   CHAR szSoundFile[MAX_STRING] = {0};
   
   // Get sound file from INI
   GetArg(szSoundName,szLine,1);
   GetPrivateProfileString("Sounds",szSoundName,"default",szSoundFile,MAX_STRING,INIFileName);
	
   if ( !stricmp( szSoundName, "MQ2ChatEvents_Error" )) return;
   if ( stricmp( szSoundName, "stop" ) == 0 ) {
      PlaySound(NULL,NULL,SND_ASYNC);
   } else {
      PlaySound(szSoundFile,NULL,SND_FILENAME | SND_ASYNC);      
   }
}


void TogglePopupsCmd(PSPAWNINFO pChar, PCHAR szLine)
{   
	CHAR tempString[MAX_STRING];
	
	popupsEnabled = !popupsEnabled;
	if(popupsEnabled) sprintf(tempString,"TRUE");
	else sprintf(tempString, "FALSE");
	WritePrivateProfileString("Settings","PopupsEnabled",tempString,INIFileName);
   
	string enabledTxt = GetOnOffLabel(popupsEnabled);
	sprintf(tempString,"Custom popups are now: %s", enabledTxt.c_str());
	WriteChatColor(tempString);

	if( popupsEnabled == 1) InitEvents();

}

void ToggleSoundsCmd(PSPAWNINFO pChar, PCHAR szLine)
{
	CHAR tempString[MAX_STRING];
	
	soundsEnabled = !soundsEnabled;
	if(soundsEnabled) sprintf(tempString,"TRUE");
	else sprintf(tempString, "FALSE");
	WritePrivateProfileString("Settings","SoundsEnabled",tempString,INIFileName);
	   
	string enabledTxt = GetOnOffLabel(soundsEnabled);
	sprintf(tempString,"Custom sound effects are now: %s", enabledTxt.c_str());
	WriteChatColor(tempString);

	if( soundsEnabled == 1) InitEvents();
}

// ToggleNpcTellFilter - toggles the NPC tell filter
// TRUE/ON = no tell sound events if source is an NPC
// FALSE/OFF = no special handling for tells - any match will fire sound events
void ToggleNpcTellFilterCmd(PSPAWNINFO pChar, PCHAR szLine)
{
	CHAR tempString[MAX_STRING];
	
	npcTellFilter = !npcTellFilter;
	if(npcTellFilter) sprintf(tempString,"TRUE");
	else sprintf(tempString, "FALSE");
	WritePrivateProfileString("Settings","NPCTellFilter",tempString,INIFileName);

	string enabledTxt = GetOnOffLabel(npcTellFilter);
	sprintf(tempString,"NPC tell sound filter is now: %s",enabledTxt.c_str());

	WriteChatColor("NPC tell sound filter suppresses tell events if the source is an NPC.");
	WriteChatColor(tempString);  

	if( npcTellFilter == 1) InitEvents();
}
void ToggleMissedChatEchoCmd(PSPAWNINFO pChar, PCHAR szLine)
{
	CHAR tempString[MAX_STRING];
	
	missedChatEcho = !missedChatEcho;
	if(missedChatEcho) sprintf(tempString,"TRUE");
	else sprintf(tempString, "FALSE");
	WritePrivateProfileString("Settings","MissedChatEcho",tempString,INIFileName);

	string enabledTxt = GetOnOffLabel(missedChatEcho);
	sprintf(tempString,"Missed Chat Echoing to MQ window is now: %s",enabledTxt.c_str());

	WriteChatColor(tempString);  

	if( missedChatEcho == 1) InitEvents();
}
void ToggleMissedChatPopupCmd(PSPAWNINFO pChar, PCHAR szLine)
{
	CHAR tempString[MAX_STRING];
	
	missedChatPopup = !missedChatPopup;
	if(missedChatPopup) sprintf(tempString,"TRUE");
	else sprintf(tempString, "FALSE");
	WritePrivateProfileString("Settings","MissedChatPopup",tempString,INIFileName);

	string enabledTxt = GetOnOffLabel(missedChatPopup);
	sprintf(tempString,"Missed Chat Popups are now: %s",enabledTxt.c_str());

	WriteChatColor(tempString);  

	if( missedChatPopup == 1) InitEvents();
}
void ChatEventsHelpCmd(PSPAWNINFO pChar, PCHAR szLine)
{
	char szTemp[MAX_STRING];
	string tempStr;

	WriteChatColor(" --------------------------------", CONCOLOR_LIGHTBLUE);
	WriteChatColor("Custom Chat Events    ", CONCOLOR_LIGHTBLUE);
	WriteChatColor("by Persnickety      ", CONCOLOR_LIGHTBLUE);
	WriteChatColor(" --------------------------------", CONCOLOR_LIGHTBLUE);
	tempStr = GetOnOffLabel(popupsEnabled);
	sprintf(szTemp,"/TogglePopups (%s)", tempStr.c_str() );
	WriteChatColor(szTemp);
	tempStr = GetOnOffLabel(soundsEnabled);
	sprintf(szTemp,"/ToggleSounds (%s)", tempStr.c_str() );
	WriteChatColor(szTemp);
	tempStr = GetOnOffLabel(npcTellFilter);
	sprintf(szTemp,"/ToggleNPCTellFilter (%s)", tempStr.c_str() );
	WriteChatColor(szTemp);
	tempStr = GetOnOffLabel(missedChatEcho);
	sprintf(szTemp,"/MissedChatEcho (%s)", tempStr.c_str() );
	WriteChatColor(szTemp);
	tempStr = GetOnOffLabel(missedChatPopup);
	sprintf(szTemp,"/MissedChatPopup (%s)", tempStr.c_str() );
	WriteChatColor(szTemp);
	WriteChatColor(" --------------------------------", CONCOLOR_LIGHTBLUE);
	
}

PLUGIN_API VOID InitializePlugin(VOID)
{
   DebugSpewAlways("Initializing MQ2ChatEvents");
   AddCommand("/togglepopups",TogglePopupsCmd);
   AddCommand("/sound",PlaySoundCmd);
   AddCommand("/togglesounds",ToggleSoundsCmd);
   AddCommand("/npctellfilter",ToggleNpcTellFilterCmd);
   AddCommand("/MissedChatEcho",ToggleMissedChatEchoCmd);
   AddCommand("/MissedChatPopup",ToggleMissedChatPopupCmd);
   AddCommand("/chatevents",ChatEventsHelpCmd);
   InitEvents();
}

PLUGIN_API VOID ShutdownPlugin(VOID)
{
   DebugSpewAlways("Shutting down MQ2ChatEvents");
   RemoveCommand("/togglepopups");  
   RemoveCommand("/sound");
   RemoveCommand("/togglesounds");
   RemoveCommand("/npctellfilter");
   RemoveCommand("/MissedChatEcho");
   RemoveCommand("/MissedChatPopup");
   RemoveCommand("/chatevents");
}


Most of the stuff in the INI should be self-explanatory, but I'll describe some of it here anyway.

[Settings]
This stores your toggle settings for sounds, popups, and missed chat echoes/popups.

[Events]
This is the section to list keys to identify events. These key names are just labels for use in the other sections. Separate each key with a | .

[MatchStrings]
This section contains the actual text you want to look for.

[Sounds]
This section contains the path to the sound file you want to play when the text from MatchStrings is found. If nothing is listed for a key, no sound will play. Note that you have to include double backspaces in the path. Example: c:\\mq2\\sounds\\soundfile.wav

[Popups]
This section contains the text that you want displayed as a popup when a match is found. If you set the text to FULLTEXT, it will popup the entire line of text that it finds (helpful when a mob is gating, so you can see the mob's name). If nothing is listed, nothing will popup.

[TextColor]
This section determines what color the popup text is. Included in the INI are examples of which numbers for which colors you can use. If nothing is listed, default color is 15 (yellow).

[DisplayDuration]
This section sets how long the popup will stay on your screen, in milliseconds. 1000 = 1 second. 10000 = 10 seconds, etc. If nothing is listed, default duration is 3000 (3 seconds).

MQ2ChatEvents.ini
Rich (BB code):
[Settings]
SoundsEnabled=TRUE
PopupsEnabled=TRUE
NPCTellFilter=TRUE
MissedChatEcho=TRUE
MissedChatPopup=TRUE

[Events]
keys=tells|told|youtell|youtold|OMMGaze|gate|Vish|GoM|Bloodeye|

[MatchStrings]
tells=tells you
told=told you
youtell=You tell
youtold=You told
OMMGaze=You feel a gaze of deadly power focusing on you.
gate=begins to cast the gate spell.
Vish=You sense your doom approaching
GoM=You have been granted a gift of mana!
Bloodeye=A dire disease creeps through your veins


[Sounds]
tells=c:\\mq2\\sounds\\message-inbound.wav
told=c:\\mq2\\sounds\\message-inbound.wav
youtell=c:\\mq2\\sounds\\message-outbound.wav
youtold=c:\\mq2\\sounds\\message-outbound.wav
kill=c:\\mq2\\sounds\\ding.wav
GoM=c:\\mq2\\sounds\\hoo-ah1.wav
OMMGaze=c:\\mq2\\sounds\\gaze.wav
Vish=c:\\mq2\\sounds\\deathtouch.wav
Bloodeye=c:\\mq2\\sounds\\deathtouch.wav

[Popups]
OMMGaze=!!! GAZE !!!  Click your mask now! ~~~
Vish=~~~~~~~~~~~!!! Creeping Doom !!!~~~~~~~~~~
GoM=~~ Gift of Mana! ~~
Bloodeye=~~ CURSED with DT ~~
gate=FULLTEXT


;--Examples for TextColors--
;DARKGREY       1
;DARKGREEN      2
;DARKBLUE       4
;PURPLE         5
;LIGHTGREY      6
;WHITE          7
;RED            13
;GREEN          14
;YELLOW         15
;BLUE           16
;LIGHTBLUE      18
;BLACK          20


[TextColor]
OMMGaze=14
Vish=13

[DisplayDuration]
OMMGaze=7000
Vish=10000


Some of the code in here might be a little redundant or unnecessary. I just smushed this together last night and today. I'm sure a lot of it can be cleaned up. I want to eventually add another option to do custom commands when certain chat text is found. Other than that I'm open to suggestions.
 
Last edited:
~Updated~

I cleaned up the code a tad, and added another feature. If you do a lot of cross-server tells, or even a lot of chatting in channels, you know that you can miss a lot of chat while zoning. MQ actually usually sees this missed chat, EQ just doesnt usually display it to the client. Sooo, this should help that quite a bit. The plugin will now (optionally) put missed tells / chat messages into the MQ window for you to see after you zone. It will also (optionally) display a popup telling you that you missed chat while zoning, just in case you had the MQ window minimized or something.

Hope a few people are getting some use out of this!
 
wow.

a must have for the passive MQ2 user during raids.

never miss those enrage, click mask, etc etc messages again.
 
Hmm whats the problem specifically? Just fails to load when you do /plugin mq2chatevents ?

Also make sure the INI has the new MissedChat settings (in case you didnt copy the new INI file).
 
Yep just unable to load. I'll check the INI once i can stop tasks =p
 
Last edited:
If MQ fails to load it, then it wouldnt be the INI, I think. Is anyone else using the compiled dll I posted and having problems loading it?
 
Before you updated it - It worked fine. I deleted the old one to put in the new one and now its having problems. It just says "mq2chatevents could not be loaded"
 
Same issue here. Using Codecaster's 1.5 compile in case that helps narrow anything down.

-SimpleMynd
 
Yeah I think thats the problem. I compiled it using the latest 20060129b MQ release, which added a global variable (gbInZone) that is used in this plugin. I think Codecaster's 1.5 is the 1-18 mq source.
 
Yah I am having the same problem. Any chance of the old version to come back if it is with the compile that Codecaster used?
 
Making it work with the older compile means rewriting part of it to not use the new global variable. BUT just for you guys, I'll try to get it done and posted in a little bit. =)
 
Ok I tested it with CodeCaster's 1.5 compile and it seems to be working. Let me know if there are any problems.
 
Not to sound like a complete idiot where did you get the sound files for like the ding and tells or did you just create some for yourself?
 
The inbound and outbound message sound files are what I use with Trillian (I think they come with Trillian, but cant remember for sure). For some of the other sounds like "Death Touch" I used a text-to-speech website.

http://www.research.att.com/projects/tts/demo.html

You can make them say some really great (and crude) things! My favorite was Audrey - UK English. =)
 
Plugin: MQ2ChatEvents

Users who are viewing this thread

Back
Top
Cart