Using print color function with 'LANG_PLAYER' on
Amx Mod X '1.8.2' or superior
Contents' Table
Introduction
Points
Snippets
Global variables and functions needed
The function 'color_print'
The function 'register_dictionary_colored'
Trying to use macros fail
A more elaborated failure with macros
Full Example
Introduction | go to top
The problem happens because the constant 'LANG_PLAYER' is used to send a message to all players, but when do we use the color print function on Amx Mod X 1.8.2, we have to send that message to every player, then we cannot use the constant 'LANG_PLAYER'. As we need to send the colored text to every player, we need need to use the player_id instead of the constant 'LANG_PLAYER'.
The sad part part about that is, that we cannot create a macro function that easly print colored text to all uses respecting the 'LANG_PLAYER' constant limitation on Amx Mod X '1.8.2'. Latter on, will be show a unsucessful atempt to create a general way to print colored functions on Amx Mod X '1.8.2'.
Here are present 2 new implementations for Amx Mod X '1.8.2' that are compatible simultaniasly with the Amx Mod X '1.8.3' or superior. One implementation is using a remastered code, and another just using preprocessor directives to improve speed. And both implementations are compatible with each other, which means that you can use both at your code.
If you run this functions on a Game Mod that do not support colored messages, they will be displayed as normal messages without any errors or bad formats.
To this works, we need to register the LANG file dictionary as colored dictionary, then we can use '!t', '!g', and '!y' for colors at the lang file. And this allow you to use '!g for green', '!y for yellow' and '!t for team' color at the colored registred dictionary file and '^1', '^3' and '^4' inside de plugin. To register the dictionary as a colored dictionary, simple call 'register_dictionary_colored' provided here bellow, instead of 'register_dictionary' when you register your LANG dictionary.
Points
If the player id is 0 we need to repeat this code that follow or equivalent every time we need to print a chat color, because this is the implementation based on preprocessor directives. So, if you want to use the optimized way at the Amx Mod X '1.8.2', you have to use the code as follows bellow. If you do not want to use the optimized way, just simply/normally call the function 'color_print' using the 'LANG_PLAYER' constant and do not use this preprocessor based way every time you want to print colored messages to all players.
Code: Select all
#if AMXX_VERSION_NUM < 183
new g_colored_player_id
new g_colored_players_number
new g_colored_current_index
new g_colored_players_ids[ 32 ]
#endif
some_function()
{
... some code
#if AMXX_VERSION_NUM < 183
get_players( g_colored_players_ids, g_colored_players_number, "c" );
for( g_colored_current_index = 0; g_colored_current_index < g_colored_players_number;
g_colored_current_index++ )
{
g_colored_player_id = g_colored_players_ids[ g_colored_current_index ]
color_print( g_colored_player_id, "^1%L %L %L", g_colored_player_id, "LANG_A", any_variable_used_on_LANG_A )
}
#else
color_print( 0, "^1%L %L %L", LANG_PLAYER, "LANG_A", any_variable_used_on_LANG_A );
#endif
... some code
}Code: Select all
color_print( player_id, "^1%L %L %L", player_id, "LANG_A", any_variable_used_on_LANG_A )Code: Select all
// Every formatted message must begin with a color.
color_print( player_id, "^1Here is everyting yellow.^3Now it is team color.^4\
Now everything is green and my poor string variable is^1 %s yellow again.", poor_string_variable )This is when you know that it is just for only one player: (You can use this to show a colored message to all player on Amx Mod X '1.8.2' with player_id as 0 and using LANG_PLAYER instead of player_id, but it will be not optimized. But if you are at the Amx Mod X '1.8.3' or superior, it will be optimized)
Code: Select all
color_print( player_id, "^1%L", player_id, "MY_LANG_CONSTANT" ) // here, use player_id instead of LANG_PLAYERCode: Select all
#if AMXX_VERSION_NUM < 183
get_players( g_colored_players_ids, g_colored_players_number, "ch" );
for( g_colored_current_index = 0; g_colored_current_index < g_colored_players_number;
g_colored_current_index++ )
{
g_colored_player_id = g_colored_players_ids[ g_colored_current_index ]
color_print( g_colored_player_id, "^1%L", g_colored_player_id, "MY_LANG_CONSTANT" ) // here, use g_colored_player_id instead of LANG_PLAYER
}
#else
client_print_color( 0, print_team_default, "^1%L", LANG_PLAYER, "MY_LANG_CONSTANT" ) // here, use LANG_PLAYER
#endifCode: Select all
#include <amxmodx>
#include <amxmisc>
#define MAX_COLOR_MESSAGE 192
#if AMXX_VERSION_NUM < 183
new g_user_msgid
new g_colored_player_id
new g_colored_players_number
new g_colored_current_index
new g_colored_players_ids[ 32 ]
#endif
new g_coloredChatPrefix[ 16 ] = "^4[^1AMXX^4]"
new bool:g_is_color_chat_supported
/**
* Convert colored strings codes '!g for green', '!y for yellow', '!t for team'.
*/
#define INSERT_COLOR_TAGS(%1) \
{ \
replace_all( %1, charsmax( %1 ), "!g", "^4" ); \
replace_all( %1, charsmax( %1 ), "!t", "^3" ); \
replace_all( %1, charsmax( %1 ), "!n", "^1" ); \
replace_all( %1, charsmax( %1 ), "!y", "^1" ); \
}
#define REMOVE_COLOR_TAGS(%1) \
{ \
replace_all( %1, charsmax( %1 ), "^1", "" ); \
replace_all( %1, charsmax( %1 ), "^2", "" ); \
replace_all( %1, charsmax( %1 ), "^3", "" ); \
replace_all( %1, charsmax( %1 ), "^4", "" ); \
}
#define PRINT_COLORED_MESSAGE(%1,%2) \
{ \
message_begin( MSG_ONE_UNRELIABLE, g_user_msgid, _, %1 ); \
write_byte( %1 ); \
write_string( %2 ); \
message_end(); \
}
public plugin_cfg()
{
g_is_color_chat_supported = ( is_running( "czero" ) || is_running( "cstrike" ) )
#if AMXX_VERSION_NUM < 183
g_user_msgid = get_user_msgid( "SayText" )
#endif
register_dictionary_colored( "your_dictionary_colored.txt" );
}Code: Select all
/**
* 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://www.amxmodx.org/api/amxmodx/client_print_color">client_print_color</a>
* @see <a href="https://forums.alliedmods.net/showthread.php?t=297484">vformat() ignoring user language</a>
*/
stock color_print( const player_id, const lang_formatting[], any:... )
{
new formatted_message[ MAX_COLOR_MESSAGE ];
if( player_id )
{
if( g_is_color_chat_supported )
{
// 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, 3 );
if( g_coloredChatPrefix[ 0 ] )
{
new message[ MAX_COLOR_MESSAGE ];
formatex( message, charsmax( message ), "^1%s^1%s", g_coloredChatPrefix, formatted_message[ 1 ] );
PRINT_COLORED_MESSAGE( player_id, message )
}
else
{
PRINT_COLORED_MESSAGE( player_id, formatted_message )
}
}
else
{
vformat( formatted_message, charsmax( formatted_message ), lang_formatting, 3 );
REMOVE_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 )
{
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;
// ML can be used
if( params_number > 3 )
{
for( argument_index = 2; argument_index < params_number; argument_index++ )
{
// 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++ ) ) )
{
}
formatted_message[ string_index ] = '^0';
if( GetLangTransKey( formatted_message ) != TransKey_Bad )
{
// Store that argument as LANG_PLAYER so we can alter it later
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++;
}
}
}
}
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++ )
{
// 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( g_is_color_chat_supported )
{
// 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, 3 );
if( g_coloredChatPrefix[ 0 ] )
{
new message[ MAX_COLOR_MESSAGE ];
formatex( message, charsmax( message ), "^1%s^1%s", g_coloredChatPrefix, formatted_message[ 1 ] );
PRINT_COLORED_MESSAGE( player_id, message )
}
else
{
PRINT_COLORED_MESSAGE( player_id, formatted_message )
}
}
else
{
vformat( formatted_message, charsmax( formatted_message ), lang_formatting, 3 );
REMOVE_COLOR_TAGS( formatted_message )
client_print( player_id, print_chat, "%s%s", g_coloredChatPrefix, formatted_message );
}
}
ArrayDestroy( multi_lingual_indexes_array );
}
}Code: Select all
/**
* 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 filename the dictionary file name including its file extension.
*/
stock register_dictionary_colored( const filename[] )
{
if( !register_dictionary( filename ) )
{
return 0;
}
new szFileName[ 256 ];
get_localinfo( "amxx_datadir", szFileName, charsmax( szFileName ) );
format( szFileName, charsmax( szFileName ), "%s/lang/%s", szFileName, filename );
new fp = fopen( szFileName, "rt" );
if( !fp )
{
log_amx( "Failed to open %s", szFileName );
return 0;
}
new szBuffer[ 512 ], szLang[ 3 ], szKey[ 64 ], szTranslation[ 256 ], TransKey:iKey;
while( !feof( fp ) )
{
fgets( fp, szBuffer, charsmax( szBuffer ) );
trim( szBuffer );
if( szBuffer[ 0 ] == '[' )
{
strtok( szBuffer[ 1 ], szLang, charsmax( szLang ), szBuffer, 1, ']' );
}
else if( szBuffer[ 0 ] )
{
#if AMXX_VERSION_NUM < 183
strbreak( szBuffer, szKey, charsmax( szKey ), szTranslation, charsmax( szTranslation ) );
#else
argbreak( szBuffer, szKey, charsmax( szKey ), szTranslation, charsmax( szTranslation ) );
#endif
iKey = GetLangTransKey( szKey );
if( iKey != TransKey_Bad )
{
INSERT_COLOR_TAGS( szTranslation )
AddTranslation( szLang, iKey, szTranslation[ 2 ] );
}
}
}
fclose( fp );
return 1;
}This is how was tried to create a macro function to let us to just use a simple and normal function call as in the Amx Mod X 1.8.3. But looks like that the key Variadic Macros are not supported on Pawn, because every time it was used, the preprocessor does not recognize/associate the ellipsis arguments during the macro expansion:
Code: Select all
#if AMXX_VERSION_NUM < 183 \
#define LANG_PLAYER_COLORED players_ids[ i ]
#define PRINT_COLORED_TEXT(%1,...) \
{ \
new i = 0; \
new players_ids[ 32 ]; \
players_ids[i] = %1; \
\
if( players_ids[i] ) \
{ \
color_print( %1, __VA_ARGS__ ); \
} \
else \
{ \
new playerIndex_idsCounter; \
\
get_players( players_ids, playerIndex_idsCounter, "ch" ); \
\
for( ; i < playerIndex_idsCounter; i++ ) \
{ \
color_print( %1, __VA_ARGS__ ); \
} \
} \
}
#else
#define LANG_PLAYER_COLORED LANG_PLAYER
#define PRINT_COLORED_TEXT(%1,...) \
color_print( %1, __VA_ARGS__ );
#endifA more elaborated Full Working Example
Code: Select all
#include <amxmodx>
#include <amxmisc>
#define MAX_COLOR_MESSAGE 192
#if AMXX_VERSION_NUM < 183
new g_user_msgid
new g_colored_player_id
new g_colored_players_number
new g_colored_current_index
new g_colored_players_ids[ 32 ]
#endif
new g_coloredChatPrefix[ 16 ] = "^4[^1AMXX^4]"
new bool:g_is_color_chat_supported
/**
* Convert colored strings codes '!g for green', '!y for yellow', '!t for team'.
*/
#define INSERT_COLOR_TAGS(%1) \
{ \
replace_all( %1, charsmax( %1 ), "!g", "^4" ); \
replace_all( %1, charsmax( %1 ), "!t", "^3" ); \
replace_all( %1, charsmax( %1 ), "!n", "^1" ); \
replace_all( %1, charsmax( %1 ), "!y", "^1" ); \
}
#define REMOVE_COLOR_TAGS(%1) \
{ \
replace_all( %1, charsmax( %1 ), "^1", "" ); \
replace_all( %1, charsmax( %1 ), "^2", "" ); \
replace_all( %1, charsmax( %1 ), "^3", "" ); \
replace_all( %1, charsmax( %1 ), "^4", "" ); \
}
#define PRINT_COLORED_MESSAGE(%1,%2) \
{ \
message_begin( MSG_ONE_UNRELIABLE, g_user_msgid, _, %1 ); \
write_byte( %1 ); \
write_string( %2 ); \
message_end(); \
}
public plugin_init()
{
register_plugin( "New Plug-In", "1.0", "Author" );
register_clcmd( "say test", "test" );
}
public plugin_cfg()
{
g_is_color_chat_supported = ( is_running( "czero" ) || is_running( "cstrike" ) )
#if AMXX_VERSION_NUM < 183
g_user_msgid = get_user_msgid( "SayText" )
#endif
register_dictionary_colored( "your_dictionary_colored.txt" );
}
public test()
{
#if AMXX_VERSION_NUM < 183
get_players( g_colored_players_ids, g_colored_players_number, "ch" );
for( g_colored_current_index = 0; g_colored_current_index < g_colored_players_number;
g_colored_current_index++ )
{
g_colored_player_id = g_colored_players_ids[ g_colored_current_index ]
color_print( g_colored_player_id, "^1%L", g_colored_player_id, "CMD_MENU" ) // here, use g_colored_player_id instead of LANG_PLAYER
}
#else
client_print_color( 0, print_team_default, "^1%L", LANG_PLAYER, "CMD_MENU" ) // here, use LANG_PLAYER
#endif
#if AMXX_VERSION_NUM < 183
color_print( 0, "Message: ^4%L", LANG_PLAYER, "CMD_MENU" );
#else
client_print_color( 0, print_team_default, "Message: ^4%L", LANG_PLAYER, "CMD_MENU" ); // here, use LANG_PLAYER
#endif
}
/**
* 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://www.amxmodx.org/api/amxmodx/client_print_color">client_print_color</a>
* @see <a href="https://forums.alliedmods.net/showthread.php?t=297484">vformat() ignoring user language</a>
*/
stock color_print( const player_id, const lang_formatting[], any:... )
{
new formatted_message[ MAX_COLOR_MESSAGE ];
if( player_id )
{
if( g_is_color_chat_supported )
{
// 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, 3 );
if( g_coloredChatPrefix[ 0 ] )
{
new message[ MAX_COLOR_MESSAGE ];
formatex( message, charsmax( message ), "^1%s^1%s", g_coloredChatPrefix, formatted_message[ 1 ] );
PRINT_COLORED_MESSAGE( player_id, message )
}
else
{
PRINT_COLORED_MESSAGE( player_id, formatted_message )
}
}
else
{
vformat( formatted_message, charsmax( formatted_message ), lang_formatting, 3 );
REMOVE_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 )
{
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;
// ML can be used
if( params_number > 3 )
{
for( argument_index = 2; argument_index < params_number; argument_index++ )
{
// 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++ ) ) )
{
}
formatted_message[ string_index ] = '^0';
if( GetLangTransKey( formatted_message ) != TransKey_Bad )
{
// Store that argument as LANG_PLAYER so we can alter it later
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++;
}
}
}
}
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++ )
{
// 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( g_is_color_chat_supported )
{
// 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, 3 );
if( g_coloredChatPrefix[ 0 ] )
{
new message[ MAX_COLOR_MESSAGE ];
formatex( message, charsmax( message ), "^1%s^1%s", g_coloredChatPrefix, formatted_message[ 1 ] );
PRINT_COLORED_MESSAGE( player_id, message )
}
else
{
PRINT_COLORED_MESSAGE( player_id, formatted_message )
}
}
else
{
vformat( formatted_message, charsmax( formatted_message ), lang_formatting, 3 );
REMOVE_COLOR_TAGS( formatted_message )
client_print( player_id, print_chat, "%s%s", g_coloredChatPrefix, formatted_message );
}
}
ArrayDestroy( multi_lingual_indexes_array );
}
}
/**
* 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 filename the dictionary file name including its file extension.
*/
stock register_dictionary_colored( const filename[] )
{
if( !register_dictionary( filename ) )
{
return 0;
}
new szFileName[ 256 ];
get_localinfo( "amxx_datadir", szFileName, charsmax( szFileName ) );
format( szFileName, charsmax( szFileName ), "%s/lang/%s", szFileName, filename );
new fp = fopen( szFileName, "rt" );
if( !fp )
{
log_amx( "Failed to open %s", szFileName );
return 0;
}
new szBuffer[ 512 ], szLang[ 3 ], szKey[ 64 ], szTranslation[ 256 ], TransKey:iKey;
while( !feof( fp ) )
{
fgets( fp, szBuffer, charsmax( szBuffer ) );
trim( szBuffer );
if( szBuffer[ 0 ] == '[' )
{
strtok( szBuffer[ 1 ], szLang, charsmax( szLang ), szBuffer, 1, ']' );
}
else if( szBuffer[ 0 ] )
{
#if AMXX_VERSION_NUM < 183
strbreak( szBuffer, szKey, charsmax( szKey ), szTranslation, charsmax( szTranslation ) );
#else
argbreak( szBuffer, szKey, charsmax( szKey ), szTranslation, charsmax( szTranslation ) );
#endif
iKey = GetLangTransKey( szKey );
if( iKey != TransKey_Bad )
{
INSERT_COLOR_TAGS( szTranslation )
AddTranslation( szLang, iKey, szTranslation[ 2 ] );
}
}
}
fclose( fp );
return 1;
}