Implement menu_create "ml" param to allow menu title/item to be looked up as multilingual key (#593)

* Clean up menu_create() internal code

No code changes. Just modernizing code a little and tidying up things.

* Implement menu_create() "ml" param

* Include newmenu.inc to VS project files

* Adjust documentation

* Put back the quote in the log error message.
This commit is contained in:
Vincent Herbet 2018-09-28 16:46:52 +02:00 committed by GitHub
parent b4768a3d1d
commit 8717ffe07c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 33 deletions

View File

@ -457,6 +457,7 @@ md -p JIT 2>NUL
<None Include="..\..\plugins\include\cvars.inc" /> <None Include="..\..\plugins\include\cvars.inc" />
<None Include="..\..\plugins\include\datapack.inc" /> <None Include="..\..\plugins\include\datapack.inc" />
<None Include="..\..\plugins\include\gameconfig.inc" /> <None Include="..\..\plugins\include\gameconfig.inc" />
<None Include="..\..\plugins\include\newmenus.inc" />
<None Include="..\..\plugins\include\string_const.inc" /> <None Include="..\..\plugins\include\string_const.inc" />
<None Include="..\..\plugins\include\string_stocks.inc" /> <None Include="..\..\plugins\include\string_stocks.inc" />
<None Include="..\..\plugins\include\textparse_ini.inc" /> <None Include="..\..\plugins\include\textparse_ini.inc" />

View File

@ -54,7 +54,7 @@
</Filter> </Filter>
<Filter Include="ReSDK\engine"> <Filter Include="ReSDK\engine">
<UniqueIdentifier>{04fab577-6f56-40d0-8f69-7ce1b8bf3bb9}</UniqueIdentifier> <UniqueIdentifier>{04fab577-6f56-40d0-8f69-7ce1b8bf3bb9}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Third Party\UTF8Rewind"> <Filter Include="Third Party\UTF8Rewind">
<UniqueIdentifier>{270f3524-564f-4154-bb35-242a6faac09e}</UniqueIdentifier> <UniqueIdentifier>{270f3524-564f-4154-bb35-242a6faac09e}</UniqueIdentifier>
</Filter> </Filter>
@ -693,10 +693,13 @@
<None Include="..\..\plugins\include\string_stocks.inc"> <None Include="..\..\plugins\include\string_stocks.inc">
<Filter>Pawn Includes</Filter> <Filter>Pawn Includes</Filter>
</None> </None>
<None Include="..\..\plugins\include\newmenus.inc">
<Filter>Pawn Includes</Filter>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Object Include="..\Jit\helpers-x86.obj"> <Object Include="..\Jit\helpers-x86.obj">
<Filter>Assembly\Builds</Filter> <Filter>Assembly\Builds</Filter>
</Object> </Object>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -10,6 +10,7 @@
#include "amxmodx.h" #include "amxmodx.h"
#include "CMenu.h" #include "CMenu.h"
#include "newmenus.h" #include "newmenus.h"
#include "format.h"
ke::Vector<Menu *> g_NewMenus; ke::Vector<Menu *> g_NewMenus;
CStack<int> g_MenuFreeStack; CStack<int> g_MenuFreeStack;
@ -90,9 +91,9 @@ bool CloseNewMenus(CPlayer *pPlayer)
return true; return true;
} }
Menu::Menu(const char *title, AMX *amx, int fid) : m_Title(title), m_ItemColor("\\r"), Menu::Menu(const char *title, AMX *amx, int fid, bool use_ml) : m_Title(title), m_ItemColor("\\r"),
m_NeverExit(false), m_AutoColors(g_coloredmenus), thisId(0), func(fid), m_NeverExit(false), m_AutoColors(g_coloredmenus), thisId(0), func(fid),
isDestroying(false), pageCallback(-1), showPageNumber(true), items_per_page(7) isDestroying(false), pageCallback(-1), showPageNumber(true), useMultilingual(use_ml), amx(amx), items_per_page(7)
{ {
CPluginMngr::CPlugin *pPlugin = g_plugins.findPluginFast(amx); CPluginMngr::CPlugin *pPlugin = g_plugins.findPluginFast(amx);
menuId = g_menucmds.registerMenuId(title, amx); menuId = g_menucmds.registerMenuId(title, amx);
@ -356,18 +357,32 @@ const char *Menu::GetTextString(int player, page_t page, int &keys)
m_Text = nullptr; m_Text = nullptr;
auto title = m_Title.chars();
if (this->useMultilingual)
{
const auto language = playerlang(player);
const auto definition = translate(this->amx, language, title);
if (definition)
{
title = definition;
}
}
char buffer[255]; char buffer[255];
if (showPageNumber && items_per_page && (pages != 1)) if (showPageNumber && items_per_page && (pages != 1))
{ {
if (m_AutoColors) if (m_AutoColors)
ke::SafeSprintf(buffer, sizeof(buffer), "\\y%s %d/%d\n\\w\n", m_Title.chars(), page + 1, pages); ke::SafeSprintf(buffer, sizeof(buffer), "\\y%s %d/%d\n\\w\n", title, page + 1, pages);
else else
ke::SafeSprintf(buffer, sizeof(buffer), "%s %d/%d\n\n", m_Title.chars(), page + 1, pages); ke::SafeSprintf(buffer, sizeof(buffer), "%s %d/%d\n\n", title, page + 1, pages);
} else { } else {
if (m_AutoColors) if (m_AutoColors)
ke::SafeSprintf(buffer, sizeof(buffer), "\\y%s\n\\w\n", m_Title.chars()); ke::SafeSprintf(buffer, sizeof(buffer), "\\y%s\n\\w\n", title);
else else
ke::SafeSprintf(buffer, sizeof(buffer), "%s\n\n", m_Title.chars()); ke::SafeSprintf(buffer, sizeof(buffer), "%s\n\n", title);
} }
m_Text = m_Text + buffer; m_Text = m_Text + buffer;
@ -462,24 +477,37 @@ const char *Menu::GetTextString(int player, page_t page, int &keys)
option_display = 0; option_display = 0;
} }
auto itemName = pItem->name.chars();
if (this->useMultilingual)
{
const auto language = playerlang(player);
const auto definition = translate(this->amx, language, itemName);
if (definition)
{
itemName = definition;
}
}
if (pItem->isBlank) if (pItem->isBlank)
{ {
ke::SafeSprintf(buffer, sizeof(buffer), "%s\n", pItem->name.chars()); ke::SafeSprintf(buffer, sizeof(buffer), "%s\n", itemName);
} }
else if (enabled) else if (enabled)
{ {
if (m_AutoColors) if (m_AutoColors)
{ {
ke::SafeSprintf(buffer, sizeof(buffer), "%s%d.\\w %s\n", m_ItemColor.chars(),option_display, pItem->name.chars()); ke::SafeSprintf(buffer, sizeof(buffer), "%s%d.\\w %s\n", m_ItemColor.chars(),option_display, itemName);
} else { } else {
ke::SafeSprintf(buffer, sizeof(buffer), "%d. %s\n", option_display, pItem->name.chars()); ke::SafeSprintf(buffer, sizeof(buffer), "%d. %s\n", option_display, itemName);
} }
} else { } else {
if (m_AutoColors) if (m_AutoColors)
{ {
ke::SafeSprintf(buffer, sizeof(buffer), "\\d%d. %s\n\\w", option_display, pItem->name.chars()); ke::SafeSprintf(buffer, sizeof(buffer), "\\d%d. %s\n\\w", option_display, itemName);
} else { } else {
ke::SafeSprintf(buffer, sizeof(buffer), "#. %s\n", pItem->name.chars()); ke::SafeSprintf(buffer, sizeof(buffer), "#. %s\n", itemName);
} }
} }
slots++; slots++;
@ -620,38 +648,45 @@ const char *Menu::GetTextString(int player, page_t page, int &keys)
LogError(amx, AMX_ERR_NATIVE, "Invalid menu id %d(%d)", p, g_NewMenus.length()); \ LogError(amx, AMX_ERR_NATIVE, "Invalid menu id %d(%d)", p, g_NewMenus.length()); \
return 0; } return 0; }
//Makes a new menu handle (-1 for failure) // native menu_create(const title[], const handler[], bool:ml = false);
//native csdm_makemenu(title[]);
static cell AMX_NATIVE_CALL menu_create(AMX *amx, cell *params) static cell AMX_NATIVE_CALL menu_create(AMX *amx, cell *params)
{ {
int len; enum args { arg_count, arg_title, arg_handler, arg_ml };
char *title = get_amxstring(amx, params[1], 0, len);
validate_menu_text(title);
char *handler = get_amxstring(amx, params[2], 1, len);
int func = registerSPForwardByName(amx, handler, FP_CELL, FP_CELL, FP_CELL, FP_DONE); int length;
const auto title = get_amxstring(amx, params[arg_title], 0, length);
if (func == -1) const auto handler = get_amxstring(amx, params[arg_handler], 1, length);
const auto callback = registerSPForwardByName(amx, handler, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
if (callback == -1)
{ {
LogError(amx, AMX_ERR_NOTFOUND, "Invalid function \"%s\"", handler); LogError(amx, AMX_ERR_NOTFOUND, R"(Invalid function "%s")", handler);
return 0; return 0;
} }
Menu *pMenu = new Menu(title, amx, func); validate_menu_text(title);
auto pMenu = new Menu(title, amx, callback, params[arg_ml] != 0);
if (g_MenuFreeStack.empty()) if (g_MenuFreeStack.empty())
{ {
g_NewMenus.append(pMenu); g_NewMenus.append(pMenu);
pMenu->thisId = (int)g_NewMenus.length() - 1;
} else { pMenu->thisId = static_cast<int>(g_NewMenus.length()) - 1;
int pos = g_MenuFreeStack.front(); }
else
{
const auto position = g_MenuFreeStack.front();
g_MenuFreeStack.pop(); g_MenuFreeStack.pop();
g_NewMenus[pos] = pMenu; g_NewMenus[position] = pMenu;
pMenu->thisId = pos;
pMenu->thisId = position;
} }
return pMenu->thisId; return pMenu->thisId;
} }
static cell AMX_NATIVE_CALL menu_addblank(AMX *amx, cell *params) static cell AMX_NATIVE_CALL menu_addblank(AMX *amx, cell *params)
{ {
GETMENU(params[1]); GETMENU(params[1]);

View File

@ -95,7 +95,7 @@ typedef unsigned int page_t;
class Menu class Menu
{ {
public: public:
Menu(const char *title, AMX *amx, int fid); Menu(const char *title, AMX *amx, int fid, bool use_ml);
~Menu(); ~Menu();
menuitem *GetMenuItem(item_t item); menuitem *GetMenuItem(item_t item);
@ -127,6 +127,8 @@ public:
bool isDestroying; bool isDestroying;
int pageCallback; int pageCallback;
bool showPageNumber; bool showPageNumber;
bool useMultilingual;
AMX *amx;
public: public:
unsigned int items_per_page; unsigned int items_per_page;
}; };

View File

@ -117,13 +117,14 @@
* @param title Title the menu should use. * @param title Title the menu should use.
* @param handler Name of the handler function. The function will be invoked * @param handler Name of the handler function. The function will be invoked
* once and only once to every menu_display() call. * once and only once to every menu_display() call.
* @param ml Unused (should be 0). * @param ml If true, the menu title and items will be looked up as multilingual keys
* when the menu displays.
* @return Menu resource identifier which must be destroyed via * @return Menu resource identifier which must be destroyed via
* menu_destroy(). All menus are destroyed when the plugin * menu_destroy(). All menus are destroyed when the plugin
* unloads. * unloads.
* @error Function name not found. * @error Function name not found.
*/ */
native menu_create(const title[], const handler[], ml=0); native menu_create(const title[], const handler[], bool:ml = false);
/** /**
* Creates a menu item callback handler. * Creates a menu item callback handler.