Code:
#include <amxmodx>
#include <amxmisc>
#define MAX_COLOR_MESSAGE 190
new const g_coloredChatPrefix[] = "^4[^1AMXX^4] "
/**
* Defines the maximum players number, when it is not specified for olders AMXX versions.
*/
#if !defined MAX_PLAYERS
#define MAX_PLAYERS 32
#endif
/**
* Remove the log messages.
*/
#define LOG(%1)
// #define LOG(%1) debugMesssageLogger( %1 );
new g_debug_level = 1023
/**
* Write debug messages to server's console accordantly to the global variable g_debug_level.
*
* @param mode the debug mode level, see the variable 'g_debug_level' for the levels.
* @param message[] the text formatting rules to display.
* @param any the variable number of formatting parameters.
*/
stock debugMesssageLogger( mode, message[], any: ... )
{
if( mode & g_debug_level )
{
static formated_message[ LONG_STRING ];
vformat( formated_message, charsmax( formated_message ), message, 3 );
server_print( "%s", formated_message );
}
}
/**
* Common strings sizes used around the plugin.
*/
#define MAX_LONG_STRING 256
#define MAX_COLOR_MESSAGE 190
#define MAX_SHORT_STRING 64
#define MAX_BIG_BOSS_STRING 512
#define MAX_NOMINATION_TRIE_KEY_SIZE 48
#define MAX_MAPNAME_LENGHT 64
#define MAX_FILE_PATH_LENGHT 128
#define MAX_PLAYER_NAME_LENGHT 48
/**
* This is holder to allow pass fake "variadic" arguments to the macro COLOR().
*
* @param variadic_args a variable number of arguments
*/
#define CHAT(%1) %1
/**
* Properly switch the colored chat implementation accordantly to the AMXX's compilers version.
*/
#if AMXX_VERSION_NUM < 183
/**
* Same as the functions COLOR(CHAT) just bellow, but uses color_print_only() for AMXX 182.
*
* @see <a href="https://www.amxmodx.org/api/amxmodx/client_print">client_print</a>
*/
#define COLOR(%1,%2) \
{ \
LOG( 128, "" ) \
LOG( 128, "I AM ENTERING ON COLOR(CHAT) player_id: %d", %1 ) \
\
if( IS_COLORED_CHAT_ENABLED() ) \
{ \
color_print_only( %1, %2 ); \
} \
else \
{ \
client_print( %1, print_chat, %2 ); \
} \
}
new g_user_msgid;
#else
/**
* Dynamically switch on runtime the client colored print function supported by the current game
* mod running. This is necessary because the colored print function client_print_color() is
* only supported by a very few game modes. Therefore we cannot know at compile time whether the
* game mod will supported the colored print function client_print_color().
*
* The usage is: COLOR( 0, CHAT( "Message: %d", argument ) )
*
* @param player_id the player id
* @param variadic_args a variable number of arguments
*
* @see <a href="https://www.amxmodx.org/api/amxmodx/client_print_color">client_print_color</a>
*/
#define COLOR(%1,%2) \
{ \
LOG( 128, "" ) \
LOG( 128, "I AM ENTERING ON COLOR(CHAT) player_id: %d", %1 ) \
LOG( 128, "( COLOR(CHAT) ) IS_COLORED_CHAT_ENABLED: %d, constant: %s", IS_COLORED_CHAT_ENABLED(), %2 ) \
\
if( IS_COLORED_CHAT_ENABLED() ) \
{ \
client_print_color( %1, print_team_default, %2 ); \
} \
else \
{ \
client_print( %1, print_chat, %2 ); \
} \
}
#endif
/**
* Switch between the AMXX 182 and 183 deprecated/bugged functions.
*/
#if AMXX_VERSION_NUM < 183
#define str_token strtok
#define str_break strbreak
#else
#define str_token strtok2
#define str_break argbreak
#endif
#define IS_COLORED_CHAT_ENABLED() ( is_running( "czero" ) || is_running( "cstrike" ) )
/**
* Print to the users chat, a colored chat message.
*
* @param player_id a player id from 1 to MAX_PLAYERS
* @param message a colored formatted string message. At the AMXX 182 it must start within
* one color code as found on REMOVE_CODE_COLOR_TAGS(1) above macro. Example:
* "^1Hi! I am a ^3 colored message".
*/
#define PRINT_COLORED_MESSAGE(%1,%2) \
{ \
message_begin( MSG_ONE_UNRELIABLE, g_user_msgid, _, %1 ); \
write_byte( %1 ); \
write_string( %2 ); \
message_end(); \
}
/**
* Convert colored strings codes '!g for green', '!y for yellow', '!t for team'.
*
* @param string[] a string pointer to be converted.
*/
#define INSERT_COLOR_TAGS(%1) \
{ \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "!g", "^4" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "!t", "^3" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "!n", "^1" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "!y", "^1" ); \
}
/**
* Remove the colored strings codes '^4 for green', '^1 for yellow', '^3 for team' and
* '^2 for unknown'.
*
* @param string[] a string pointer to be formatted.
*/
#define REMOVE_CODE_COLOR_TAGS(%1) \
{ \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "^4", "" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "^3", "" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "^2", "" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "^1", "" ); \
}
/**
* Remove the colored strings codes '!g for green', '!y for yellow', '!t for team' and
* '!n for unknown'.
*
* @param string[] a string pointer to be formatted.
*/
#define REMOVE_LETTER_COLOR_TAGS(%1) \
{ \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "!g", "" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "!t", "" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "!n", "" ); \
replace_all( %1, MAX_COLOR_MESSAGE - 1, "!y", "" ); \
}
public plugin_init()
{
register_plugin( "New Plug-In", "1.0", "Author" );
register_clcmd( "say test", "test" );
register_dictionary_colored( "cmdmenu.txt" )
}
public plugin_cfg()
{
#if AMXX_VERSION_NUM < 183
g_user_msgid = get_user_msgid( "SayText" );
#endif
}
public test()
{
COLOR( 0, CHAT( "COLOR ^4%s", "CHAT" ) )
// client_print( 0, print_chat, "message %s", "bbbbbb" );
// client_print_color( 0, print_team_default, "Message: ^4%L", LANG_PLAYER, "CMD_MENU" );
}
/**
* Call the AMXX logger log_amx() and the internal debugger using the same logging message.
*
* @param text the debug message, if omitted its default value is ""
* @param any the variable number of formatting parameters
*/
stock doAmxxLog( const message[] = "", any:... )
{
static formatted_message[ MAX_LONG_STRING ];
vformat( formatted_message, charsmax( formatted_message ), message, 2 );
LOG( 1, formatted_message )
log_amx( formatted_message );
}
/**
* ConnorMcLeod's [Dyn Native] ColorChat v0.3.2 (04 jul 2013) register_dictionary_colored function:
* <a href="https://forums.alliedmods.net/showthread.php?p=851160">ColorChat v0.3.2</a>
*
* @param dictionaryFile the dictionary file name including its file extension.
*/
stock register_dictionary_colored( const dictionaryFile[] )
{
LOG( 128, "I AM ENTERING ON register_dictionary_colored(1) dictionaryFile: %s", dictionaryFile )
if( !register_dictionary( dictionaryFile ) )
{
LOG( 1, " Returning 0 on if( !register_dictionary(%s) )", dictionaryFile )
return 0;
}
new dictionaryFilePath[ MAX_FILE_PATH_LENGHT ];
get_localinfo( "amxx_datadir", dictionaryFilePath, charsmax( dictionaryFilePath ) );
formatex( dictionaryFilePath, charsmax( dictionaryFilePath ), "%s/lang/%s", dictionaryFilePath, dictionaryFile );
// DO not SEPARE/SPLIT THIS DECLARATION in: new var; var = fopen... or it can crash some servers.
new dictionaryFile = fopen( dictionaryFilePath, "rt" );
if( !dictionaryFile )
{
doAmxxLog( "ERROR, register_dictionary_colored: Failed to open ^"%s^"", dictionaryFilePath );
LOG( 1, " Returning 0 on if( !dictionaryFile ), Failed to open ^"%s^"", dictionaryFilePath )
return 0;
}
new TransKey:translationKeyId;
new langTypeAcronym [ 3 ];
new currentReadLine [ MAX_BIG_BOSS_STRING ];
new langConstantName [ MAX_SHORT_STRING ];
new langTranslationText[ MAX_LONG_STRING ];
while( !feof( dictionaryFile ) )
{
fgets( dictionaryFile, currentReadLine, charsmax( currentReadLine ) );
trim( currentReadLine );
if( currentReadLine[ 0 ] == '[' )
{
str_token( currentReadLine[ 1 ], langTypeAcronym, charsmax( langTypeAcronym ), currentReadLine, 1, ']' );
}
else if( currentReadLine[ 0 ] )
{
str_break( currentReadLine, langConstantName, charsmax( langConstantName ), langTranslationText, charsmax( langTranslationText ) );
translationKeyId = GetLangTransKey( langConstantName );
if( translationKeyId != TransKey_Bad )
{
if( IS_COLORED_CHAT_ENABLED() )
{
INSERT_COLOR_TAGS( langTranslationText )
}
else
{
REMOVE_LETTER_COLOR_TAGS( langTranslationText )
}
// LOG( 256, "lang: %s, Id: %d, Text: %s", langTypeAcronym, translationKeyId, langTranslationText )
AddTranslation( langTypeAcronym, translationKeyId, langTranslationText[ 2 ] );
}
}
}
fclose( dictionaryFile );
return 1;
}
/**
* For documentation and a general printer which accepts colored messages, but remove the color tags
* when colored chat is disabled, see general_chat_print() just below.
*
* This only accepts messages when the colored chat is enabled by IS_COLORED_CHAT_ENABLED().
*/
stock color_print_only( const player_id, const lang_formatting[], any:... )
{
LOG( 128, "I AM ENTERING ON color_print_only()" )
LOG( 64, "( color_print_only ) IS_COLORED_CHAT_ENABLED(): %d", IS_COLORED_CHAT_ENABLED() )
LOG( 64, "( color_print_only ) player_id: %d, lang_formatting: `%s`", player_id, lang_formatting )
const first_lang_parameter_position = 3;
new formatted_message[ MAX_COLOR_MESSAGE ];
if( player_id )
{
// Here all the colored messaged must to start within a color.
formatted_message[ 0 ] = '^1';
vformat( formatted_message[ 1 ], charsmax( formatted_message ) - 1, lang_formatting, first_lang_parameter_position );
PRINT_COLORED_MESSAGE( player_id, formatted_message )
}
else
{
new playersCount;
new players[ MAX_PLAYERS ];
// Get the server players skipping the bots
get_players( players, playersCount, "c" );
// Figure out if at least 1 player is connected, so we don't execute useless code
if( !playersCount )
{
LOG( 64, " ( color_print_only ) Returning on playersCount: %d...", playersCount )
return;
}
new player_id;
new string_index;
new argument_index;
new multi_lingual_constants_number;
new params_number;
new Array:multi_lingual_indexes_array;
multi_lingual_indexes_array = ArrayCreate();
params_number = numargs();
multi_lingual_constants_number = 0;
LOG( 64, "( color_print_only ) playersCount: %d, params_number: %d...", playersCount, params_number )
// ML can be used
if( params_number > first_lang_parameter_position )
{
for( argument_index = first_lang_parameter_position - 1; argument_index < params_number; argument_index++ )
{
LOG( 64, "( color_print_only ) getarg(%d): %d", argument_index, getarg( argument_index, 0 ) )
// retrieve original param value and check if it's LANG_PLAYER value
if( getarg( argument_index ) == LANG_PLAYER )
{
string_index = 0;
// as LANG_PLAYER == -1, check if next param string is a registered language translation
while( ( formatted_message[ string_index ] =
getarg( argument_index + 1, string_index++ ) ) )
{
}
// Closes the open string
formatted_message[ string_index ] = '^0';
LOG( 64, "( color_print_only ) LANG name: %s, GetLangTransKey(): %d, TransKey_Bad: %d", \
formatted_message, GetLangTransKey( formatted_message ), TransKey_Bad )
if( GetLangTransKey( formatted_message ) != TransKey_Bad )
{
// Store that argument as LANG_PLAYER so we can alter it later
LOG( 64, "( color_print_only ) Pushing argument_index: %d", argument_index )
ArrayPushCell( multi_lingual_indexes_array, argument_index++ );
// Update ML array, so we'll know 1st if ML is used, 2nd how many arguments we have to change
multi_lingual_constants_number++;
}
}
}
}
LOG( 64, "( color_print_only ) multi_lingual_constants_number: %d...", multi_lingual_constants_number )
for( --playersCount; playersCount >= 0; --playersCount )
{
player_id = players[ playersCount ];
if( multi_lingual_constants_number )
{
for( argument_index = 0; argument_index < multi_lingual_constants_number; argument_index++ )
{
LOG( 64, "( color_print_only ) player_id: %d, argument_index: %d, ArrayGetCell(%d,%d): %d", \
player_id, argument_index, \
multi_lingual_indexes_array, argument_index, ArrayGetCell( multi_lingual_indexes_array, argument_index ) )
// Set all LANG_PLAYER args to player index ( = player_id ), so we can format the text for that specific player
setarg( ArrayGetCell( multi_lingual_indexes_array, argument_index ), _, player_id );
}
}
// Here all the colored messaged must to start within a color.
formatted_message[ 0 ] = '^1';
vformat( formatted_message[ 1 ], charsmax( formatted_message ) - 1, lang_formatting, first_lang_parameter_position );
LOG( 64, "( color_print_only ) 2. [out] colored message: `%s`", formatted_message )
PRINT_COLORED_MESSAGE( player_id, formatted_message )
}
ArrayDestroy( multi_lingual_indexes_array );
}
}
/**
* Print colored text to a given player_id. It has to be called to each player using its player_id
* instead of 'LANG_PLAYER' constant. Just use the 'LANG_PLAYER' constant when using this function
* to display to all players.
*
* This includes the code:
* ConnorMcLeod's [Dyn Native] ColorChat v0.3.2 (04 jul 2013) register_dictionary_colored function:
* <a href="https://forums.alliedmods.net/showthread.php?p=851160">ColorChat v0.3.2</a>
*
* If you run this function on a Game Mod that do not support colored messages, they will be
* displayed as normal messages without any errors or bad formats.
*
* This allow you to use '!g for green', '!y for yellow', '!t for team' color with LANGs at a
* register_dictionary_colored file. Otherwise use '^1', '^2', '^3' and '^4'.
*
* @param player_id the player id.
* @param lang_formatting the text formatting rules to display.
* @param any the variable number of formatting parameters.
*
* @see <a href="https://forums.alliedmods.net/showthread.php?t=297484">vformat() ignoring user language</a>
*/
stock general_chat_print( const player_id, const lang_formatting[], any:... )
{
LOG( 128, "I AM ENTERING ON general_chat_print()" )
LOG( 64, "( general_chat_print ) IS_COLORED_CHAT_ENABLED(): %d", IS_COLORED_CHAT_ENABLED() )
LOG( 64, "( general_chat_print ) player_id: %d, lang_formatting: `%s`", player_id, lang_formatting )
const first_lang_parameter_position = 3;
new formatted_message[ MAX_COLOR_MESSAGE ];
if( player_id )
{
if( IS_COLORED_CHAT_ENABLED() )
{
// Here all the colored messaged must to start within a color.
formatted_message[ 0 ] = '^1';
vformat( formatted_message[ 1 ], charsmax( formatted_message ) - 1, lang_formatting, first_lang_parameter_position );
if( g_coloredChatPrefix[ 0 ] )
{
new message[ MAX_COLOR_MESSAGE ];
formatex( message, charsmax( message ), "^1%s^1%s", g_coloredChatPrefix, formatted_message[ 1 ] );
LOG( 64, "( general_chat_print ) [in] player_id: %d, Chat printed: `%s`", player_id, message )
PRINT_COLORED_MESSAGE( player_id, message )
}
else
{
PRINT_COLORED_MESSAGE( player_id, formatted_message )
}
}
else
{
vformat( formatted_message, charsmax( formatted_message ), lang_formatting, first_lang_parameter_position );
REMOVE_CODE_COLOR_TAGS( formatted_message )
client_print( player_id, print_chat, "%s%s", g_coloredChatPrefix, formatted_message );
}
}
else
{
new playersCount;
new players[ MAX_PLAYERS ];
// Get the server players skipping the bots
get_players( players, playersCount, "c" );
// Figure out if at least 1 player is connected, so we don't execute useless code
if( !playersCount )
{
LOG( 64, " ( general_chat_print ) Returning on playersCount: %d...", playersCount )
return;
}
new player_id;
new string_index;
new argument_index;
new multi_lingual_constants_number;
new params_number;
new Array:multi_lingual_indexes_array;
multi_lingual_indexes_array = ArrayCreate();
params_number = numargs();
multi_lingual_constants_number = 0;
LOG( 64, "( general_chat_print ) playersCount: %d, params_number: %d...", playersCount, params_number )
// ML can be used
if( params_number > first_lang_parameter_position )
{
for( argument_index = first_lang_parameter_position - 1; argument_index < params_number; argument_index++ )
{
LOG( 64, "( general_chat_print ) getarg(%d): %d", argument_index, getarg( argument_index, 0 ) )
// retrieve original param value and check if it's LANG_PLAYER value
if( getarg( argument_index ) == LANG_PLAYER )
{
string_index = 0;
// as LANG_PLAYER == -1, check if next param string is a registered language translation
while( ( formatted_message[ string_index ] =
getarg( argument_index + 1, string_index++ ) ) )
{
}
// Closes the open string
formatted_message[ string_index ] = '^0';
LOG( 64, "( general_chat_print ) LANG name: %s, GetLangTransKey(): %d, TransKey_Bad: %d", \
formatted_message, GetLangTransKey( formatted_message ), TransKey_Bad )
if( GetLangTransKey( formatted_message ) != TransKey_Bad )
{
// Store that argument as LANG_PLAYER so we can alter it later
LOG( 64, "( general_chat_print ) Pushing argument_index: %d", argument_index )
ArrayPushCell( multi_lingual_indexes_array, argument_index++ );
// Update ML array, so we'll know 1st if ML is used, 2nd how many arguments we have to change
multi_lingual_constants_number++;
}
}
}
}
LOG( 64, "( general_chat_print ) multi_lingual_constants_number: %d...", multi_lingual_constants_number )
for( --playersCount; playersCount >= 0; --playersCount )
{
player_id = players[ playersCount ];
if( multi_lingual_constants_number )
{
for( argument_index = 0; argument_index < multi_lingual_constants_number; argument_index++ )
{
LOG( 64, "( general_chat_print ) player_id: %d, argument_index: %d, ArrayGetCell(%d,%d): %d", \
player_id, argument_index, \
multi_lingual_indexes_array, argument_index, ArrayGetCell( multi_lingual_indexes_array, argument_index ) )
// Set all LANG_PLAYER args to player index ( = player_id ), so we can format the text for that specific player
setarg( ArrayGetCell( multi_lingual_indexes_array, argument_index ), _, player_id );
}
}
if( IS_COLORED_CHAT_ENABLED() )
{
// Here all the colored messaged must to start within a color.
formatted_message[ 0 ] = '^1';
vformat( formatted_message[ 1 ], charsmax( formatted_message ) - 1, lang_formatting, first_lang_parameter_position );
if( g_coloredChatPrefix[ 0 ] )
{
new message[ MAX_COLOR_MESSAGE ];
formatex( message, charsmax( message ), "^1%s^1%s", g_coloredChatPrefix, formatted_message[ 1 ] );
LOG( 64, "( general_chat_print ) 1. [out] colored message: `%s`", message )
PRINT_COLORED_MESSAGE( player_id, message )
}
else
{
LOG( 64, "( general_chat_print ) 2. [out] colored message: `%s`", formatted_message )
PRINT_COLORED_MESSAGE( player_id, formatted_message )
}
}
else
{
vformat( formatted_message, charsmax( formatted_message ), lang_formatting, first_lang_parameter_position );
REMOVE_CODE_COLOR_TAGS( formatted_message )
LOG( 64, "( general_chat_print ) 3. [out] uncolored message: `%s`", formatted_message )
client_print( player_id, print_chat, "%s%s", g_coloredChatPrefix, formatted_message );
}
}
ArrayDestroy( multi_lingual_indexes_array );
}
}