Jump to content

Official Client Locale String[REVERSED]


Recommended Posts

  • Honorable Member

Q: Do they really use boost::format?

A: No, They wrote a function that checks the arguments one by one. I preferred boost::format which makes this job more professional and clean.

Here is real function:

Spoiler
bool sub_27D1C0(std::string& sMessage, std::vector<std::string>& args)
{
	std::stringstream ss;

	size_t _c_ = 0;
	size_t arg_i = 1;

	const size_t sMessage_size = sMessage.size();

	while (_c_ < sMessage_size)
	{
		char current_character = sMessage[_c_];
		if (current_character == '%')
		{
			if (++_c_ < sMessage_size)
			{
				current_character = sMessage[_c_];
				switch (current_character)
				{
					case '%':
					{
						ss << '%';
						break;
					}
					case '*':
					{
						if (++_c_ < sMessage_size)
						{
							current_character = sMessage[_c_];
							if (current_character == 's' && arg_i < args.size())
							{
								std::string& _arg = args[arg_i++];
								ss << _arg;
							}
						}
						break;
					}
					case '.':
					{
						if (++_c_ < sMessage_size)
						{
							current_character = sMessage[_c_];
							if (current_character >= '1' && current_character <= '9' && ++_c_ < sMessage_size)
							{
								current_character = sMessage[_c_];
								if (current_character == 'f' && arg_i < args.size())
								{
									std::string& _arg = args[arg_i++];
									ss << _arg;
								}
							}
						}
						break;
					}
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
					{
						if (++_c_ < sMessage_size)
						{
							current_character = sMessage[_c_];
							if (current_character == 'd' && arg_i < args.size())
							{
								std::string& _arg = args[arg_i++];
								ss << _arg;
							}
						}
						break;
					}
					case 'd':
					{
						if (arg_i < args.size())
						{
							std::string& _arg = args[arg_i++];
							ss << boost::lexical_cast<int>(_arg);
						}
						break;
					}
					case 'l':
					{
						if (++_c_ < sMessage_size)
						{
							current_character = sMessage[_c_];
							if (current_character == 'l')
							{
								if (++_c_ < sMessage_size)
								{
									current_character = sMessage[_c_];
									if (current_character == 'd' && arg_i < args.size())
									{
										std::string& _arg = args[arg_i++];
										ss << boost::lexical_cast<long long>(_arg);
									}
								}
							}
							else if (current_character == 'u' && arg_i < args.size())
							{
								std::string& _arg = args[arg_i++];
								ss << boost::lexical_cast<unsigned long>(_arg);
							}
						}
						break;
					}
					case 's':
					{
						if (arg_i < args.size())
						{
							std::string& _arg = args[arg_i++];
							ss << _arg;
						}
						break;
					}
					case 'u':
					{
						if (arg_i < args.size())
						{
							std::string& _arg = args[arg_i++];
							ss << boost::lexical_cast<unsigned int>(_arg);
						}
						break;
					}
					default:
						return false;
				}
			}
		}
		else
		{
			ss << current_character;
		}
		++_c_;
	}

	sMessage = ss.str();
	return true;
}

 

pseudo:

char __stdcall sub_27D1C0(_DWORD *sMessage, int args)
{
  char *v3; // [esp+10h] [ebp-1F8h]
  _DWORD *v4; // [esp+18h] [ebp-1F0h]
  _DWORD *v5; // [esp+20h] [ebp-1E8h]
  char *v6; // [esp+28h] [ebp-1E0h]
  _DWORD *v7; // [esp+30h] [ebp-1D8h]
  _DWORD *v8; // [esp+3Ch] [ebp-1CCh]
  _DWORD *v9; // [esp+44h] [ebp-1C4h]
  char *v10; // [esp+4Ch] [ebp-1BCh]
  char *v11; // [esp+54h] [ebp-1B4h]
  _DWORD *v12; // [esp+5Ch] [ebp-1ACh]
  _DWORD *v13; // [esp+64h] [ebp-1A4h]
  _DWORD *v14; // [esp+68h] [ebp-1A0h]
  _DWORD *v15; // [esp+9Ch] [ebp-16Ch]
  int v16; // [esp+C0h] [ebp-148h]
  unsigned int v17; // [esp+C4h] [ebp-144h]
  int v18; // [esp+C8h] [ebp-140h]
  unsigned int v19; // [esp+CCh] [ebp-13Ch]
  int v20; // [esp+D0h] [ebp-138h]
  char v21[4]; // [esp+D4h] [ebp-134h] BYREF
  int v22; // [esp+D8h] [ebp-130h]
  unsigned int v23; // [esp+DCh] [ebp-12Ch]
  _DWORD *v24; // [esp+E0h] [ebp-128h]
  char v25[4]; // [esp+E4h] [ebp-124h] BYREF
  int v26; // [esp+E8h] [ebp-120h]
  unsigned int v27; // [esp+ECh] [ebp-11Ch]
  unsigned int v28; // [esp+F0h] [ebp-118h]
  char v29[4]; // [esp+F4h] [ebp-114h] BYREF
  int v30; // [esp+F8h] [ebp-110h]
  unsigned int v31; // [esp+FCh] [ebp-10Ch]
  unsigned int v32; // [esp+100h] [ebp-108h]
  char v33[4]; // [esp+104h] [ebp-104h] BYREF
  int v34; // [esp+108h] [ebp-100h]
  unsigned int v35; // [esp+10Ch] [ebp-FCh]
  unsigned int v36; // [esp+110h] [ebp-F8h]
  unsigned int v37; // [esp+114h] [ebp-F4h]
  unsigned int v38; // [esp+118h] [ebp-F0h]
  unsigned int v39; // [esp+11Ch] [ebp-ECh]
  int v40; // [esp+120h] [ebp-E8h]
  int v41; // [esp+124h] [ebp-E4h]
  char v42; // [esp+12Bh] [ebp-DDh]
  int v43[7]; // [esp+12Ch] [ebp-DCh] BYREF
  char v44; // [esp+14Bh] [ebp-BDh]
  __int64 v45; // [esp+14Ch] [ebp-BCh]
  int v46; // [esp+158h] [ebp-B0h]
  int v47; // [esp+15Ch] [ebp-ACh]
  int v48; // [esp+160h] [ebp-A8h]
  char v49[8]; // [esp+164h] [ebp-A4h] BYREF
  char v50[76]; // [esp+16Ch] [ebp-9Ch] BYREF
  int v51[13]; // [esp+1B8h] [ebp-50h] BYREF
  unsigned __int8 current_character; // [esp+1EFh] [ebp-19h]
  int sMessage_size; // [esp+1F0h] [ebp-18h]
  unsigned int _c_; // [esp+1F4h] [ebp-14h]
  unsigned int arg_i; // [esp+1F8h] [ebp-10h]
  int v56; // [esp+204h] [ebp-4h]

  v41 = 0;
  sub_2845D0(3, 1);
  v56 = 0;
  v40 = sMessage[5];
  sMessage_size = v40;
  current_character = 0;
  _c_ = 0;
  arg_i = 1;
  while ( _c_ < sMessage_size )
  {
    if ( sMessage[5] <= _c_ )
      sub_56B930();
    if ( sMessage[6] < 0x10u )
      v14 = sMessage + 1;
    else
      v14 = sMessage[1];
    current_character = *(v14 + _c_);
    if ( current_character == '%' )
    {
      if ( ++_c_ < sMessage_size )
      {
        if ( sMessage[5] <= _c_ )
          sub_56B930();
        if ( sMessage[6] < 0x10u )
          v13 = sMessage + 1;
        else
          v13 = sMessage[1];
        current_character = *(v13 + _c_);
        switch ( current_character )
        {
          case '%':
            if ( v49 )
              sub_1B7650(v50, '%');
            else
              sub_1B7650(0, '%');
            break;
          case '*':
            if ( ++_c_ < sMessage_size )
            {
              if ( sMessage[5] <= _c_ )
                sub_56B930();
              if ( sMessage[6] < 0x10u )
                v7 = sMessage + 1;
              else
                v7 = sMessage[1];
              current_character = *(v7 + _c_);
              if ( current_character == 's' && arg_i < (*(args + 16) - *(args + 12)) / 28 )
              {
                if ( v49 )
                  v6 = v50;
                else
                  v6 = 0;
                v19 = arg_i;
                if ( arg_i >= (*(args + 16) - *(args + 12)) / 28 )
                  fuck_it();
                v18 = *(args + 12) + 28 * v19;
                ++arg_i;
                if ( *(v18 + 24) < 0x10u )
                  sub_287790(v6, (v18 + 4));
                else
                  sub_287790(v6, *(v18 + 4));
              }
            }
            break;
          case '.':
            if ( ++_c_ < sMessage_size )
            {
              if ( sMessage[5] <= _c_ )
                sub_56B930();
              if ( sMessage[6] < 0x10u )
                v5 = sMessage + 1;
              else
                v5 = sMessage[1];
              current_character = *(v5 + _c_);
              if ( current_character >= '1' && current_character <= '9' && ++_c_ < sMessage_size )
              {
                if ( sMessage[5] <= _c_ )
                  sub_56B930();
                if ( sMessage[6] < 0x10u )
                  v4 = sMessage + 1;
                else
                  v4 = sMessage[1];
                current_character = *(v4 + _c_);
                if ( current_character == 'f' && arg_i < (*(args + 16) - *(args + 12)) / 28 )
                {
                  if ( v49 )
                    v3 = v50;
                  else
                    v3 = 0;
                  v17 = arg_i;
                  if ( arg_i >= (*(args + 16) - *(args + 12)) / 28 )
                    fuck_it();
                  v16 = *(args + 12) + 28 * v17;
                  ++arg_i;
                  if ( *(v16 + 24) < 0x10u )
                    sub_287790(v3, (v16 + 4));
                  else
                    sub_287790(v3, *(v16 + 4));
                }
              }
            }
            break;
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            if ( ++_c_ < sMessage_size )
            {
              if ( sMessage[5] <= _c_ )
                sub_56B930();
              if ( sMessage[6] < 0x10u )
                v12 = sMessage + 1;
              else
                v12 = sMessage[1];
              current_character = *(v12 + _c_);
              if ( current_character == 'd' && arg_i < (*(args + 16) - *(args + 12)) / 28 )
              {
                if ( v49 )
                  v11 = v50;
                else
                  v11 = 0;
                v39 = arg_i;
                if ( arg_i >= (*(args + 16) - *(args + 12)) / 28 )
                  fuck_it();
                v38 = *(args + 12) + 28 * v39;
                ++arg_i;
                if ( *(v38 + 24) < 0x10u )
                  sub_287790(v11, (v38 + 4));
                else
                  sub_287790(v11, *(v38 + 4));
              }
            }
            break;
          case 'd':
            if ( arg_i < (*(args + 16) - *(args + 12)) / 28 )
            {
              v35 = arg_i;
              if ( arg_i >= (*(args + 16) - *(args + 12)) / 28 )
                fuck_it();
              v32 = *(args + 12) + 28 * v35;
              v34 = 1;
              v48 = sub_28ACD0(v32, v33, 1);
              ++arg_i;
              (sub_283330)(v48);
            }
            break;
          case 'l':
            if ( ++_c_ < sMessage_size )
            {
              if ( sMessage[5] <= _c_ )
                sub_56B930();
              if ( sMessage[6] < 0x10u )
                v9 = sMessage + 1;
              else
                v9 = sMessage[1];
              current_character = *(v9 + _c_);
              if ( current_character == 'l' )
              {
                if ( ++_c_ < sMessage_size )
                {
                  if ( sMessage[5] <= _c_ )
                    sub_56B930();
                  if ( sMessage[6] < 0x10u )
                    v8 = sMessage + 1;
                  else
                    v8 = sMessage[1];
                  current_character = *(v8 + _c_);
                  if ( current_character == 'd' && arg_i < (*(args + 16) - *(args + 12)) / 28 )
                  {
                    v23 = arg_i;
                    if ( arg_i >= (*(args + 16) - *(args + 12)) / 28 )
                      fuck_it();
                    v20 = *(args + 12) + 28 * v23;
                    v22 = 1;
                    v45 = sub_28B0A0(v20, v21, 1);
                    ++arg_i;
                    sub_283C00(v45, HIDWORD(v45));
                  }
                }
              }
              else if ( current_character == 'u' && arg_i < (*(args + 16) - *(args + 12)) / 28 )
              {
                v27 = arg_i;
                if ( arg_i >= (*(args + 16) - *(args + 12)) / 28 )
                  fuck_it();
                v24 = (*(args + 12) + 28 * v27);
                v26 = 1;
                v46 = sub_28AF80(v24, v25, 1);
                ++arg_i;
                sub_283930(v46);
              }
            }
            break;
          case 's':
            if ( arg_i < (*(args + 16) - *(args + 12)) / 28 )
            {
              if ( v49 )
                v10 = v50;
              else
                v10 = 0;
              v37 = arg_i;
              if ( arg_i >= (*(args + 16) - *(args + 12)) / 28 )
                fuck_it();
              v36 = *(args + 12) + 28 * v37;
              ++arg_i;
              if ( *(v36 + 24) < 0x10u )
                sub_287790(v10, (v36 + 4));
              else
                sub_287790(v10, *(v36 + 4));
            }
            break;
          case 'u':
            if ( arg_i < (*(args + 16) - *(args + 12)) / 28 )
            {
              v31 = arg_i;
              if ( arg_i >= (*(args + 16) - *(args + 12)) / 28 )
                fuck_it();
              v28 = *(args + 12) + 28 * v31;
              v30 = 1;
              v47 = sub_28AE60(v28, v29, 1);
              ++arg_i;
              sub_283660(v47);
            }
            break;
          default:
            v44 = 0;
            v56 = -1;
            sub_2846D0();
            v51[0] = &std::ios_base::`vftable';
            sub_56BA2C(v51);
            return v44;
        }
      }
    }
    else if ( v49 )
    {
      sub_1B7650(v50, current_character);
    }
    else
    {
      sub_1B7650(0, current_character);
    }
    ++_c_;
  }
  v15 = sub_284BB0(v43);
  LOBYTE(v56) = 1;
  std::string::assign_0(sMessage, v15, 0, 0xFFFFFFFF);
  LOBYTE(v56) = 0;
  fuck_it_0(v43, 1, 0);
  v42 = 1;
  v56 = -1;
  sub_2846D0();
  v51[0] = &std::ios_base::`vftable';
  sub_56BA2C(v51);
  return v42;
}

 

 

  • Good 1
  • Love 1

 

Link to comment
Share on other sites

  • Honorable Member
13 minutes ago, filipw1 said:

Are you into this now or is this @ xP3NG3Rx contributing?

My work; after he get banned, someone needs to do this job🤣

13 minutes ago, filipw1 said:

BTW is this Saul as a GTA V character on your profile picture? 

yes it is xddd

Edited by Mali
  • kekw 4
  • Love 1

 

Link to comment
Share on other sites

  • Active+ Member

Very nice thread, thank you.

I think the functions are called wrong in PythonApplication. I mean, their names are differenet here and in PythonLocale according to github.

Also, for monster chat you can use something like that:

#if defined(__BL_CLIENT_LOCALE_STRING__)
                if (pkInstChatter->IsNPC() || pkInstChatter->IsEnemy())
                {
                    CPythonLocale::Instance().FormatString(buf, sizeof(buf));
                    _snprintf(line, sizeof(line), "%s", buf);
                    break;
                }
#endif

And monster chat example:

gameforge.locale.monster_chat_907_1 = "[LC;3403]"
gameforge.locale.monster_chat_907_2 = "[LC;3404]"
gameforge.locale.monster_chat_907_3 = "[LC;3405]"
gameforge.locale.monster_chat_907_4 = "[LC;3406]"
gameforge.locale.monster_chat_907_5 = "[LC;3407]"
gameforge.locale.monster_chat_907_6 = "[LC;3408]"

Have a good day

Edited by m2Ciaran
  • Good 1
Link to comment
Share on other sites

  • Honorable Member
2 hours ago, m2Ciaran said:

Very nice thread, thank you.

I think the functions are called wrong in PythonApplication. I mean, their names are differenet here and in PythonLocale according to github.

Thank you, fixed👍

2 hours ago, m2Ciaran said:

Also, for monster chat you can use something like that:

#if defined(__BL_CLIENT_LOCALE_STRING__)
                if (pkInstChatter->IsNPC() || pkInstChatter->IsEnemy())
                {
                    CPythonLocale::Instance().FormatString(buf, sizeof(buf));
                    _snprintf(line, sizeof(line), "%s", buf);
                    break;
                }
#endif

You don't have to add this.

Except for the messages written by the players, other messages are already formatting.

Edited by Mali

 

Link to comment
Share on other sites

hello and thank you for this beautiful system, it works great, but I have a question, atlas https://metin2.download/picture/s044hEzIY03aeF6337EU5AKOdZ27oCx8/.png are not translated monster or npc should that be?

Edited by Metin2 Dev
Core X - External 2 Internal
Link to comment
Share on other sites

  • Honorable Member
2 hours ago, Chris90909090909090 said:

hello and thank you for this beautiful system, it works great, but I have a question, atlas https://metin2.download/picture/s044hEzIY03aeF6337EU5AKOdZ27oCx8/.png are not translated monster or npc should that be?

 

Link to comment
Share on other sites

  • Honorable Member

Here is an easier way to write and read quest translations, a function that will filter and return the complete token.

This is the hidden content, please
 or 
This is the hidden content, please


Example:

quest black_mapinfo_test begin
    state start begin
        when letter begin
            send_letter(locale_quest(6457))
        end

        when info or button begin
            say_title(locale_quest(6457))
            say(locale_quest(6459, "Joan", 15, 20000))
            say(locale_quest(1534, "Joan"))

            local s = select(locale_quest(6555), locale_quest(6556))
            if s == 1 then
                say(locale_quest(6232))
            end
        end
    end
end

 

  • Metin2 Dev 62
  • Good 12
  • Love 21
Link to comment
Share on other sites

  • Honorable Member
1 hour ago, Owsap said:

Here is an easier way to write and read quest translations, a function that will filter and return the complete token.

 

nice idea, I was using sth like this on the server side in the first version:

enum class LOCALE_TYPE
{
    LOCALE_STRING,
    LOCALE_QUEST_STRING,
    LOCALE_OX_STRING,
};

template<LOCALE_TYPE type, int id, typename... Args>
std::string LOCALE_STRING(Args&& ... args)
{
    std::stringstream stream;
    stream << '[';
    
    switch (type)
    {
    case LOCALE_TYPE::LOCALE_STRING:
        stream << "LS;" << id;
        break;
    case LOCALE_TYPE::LOCALE_QUEST_STRING:
        stream << "LC;" << id;
        break;
    case LOCALE_TYPE::LOCALE_OX_STRING:
        stream << "LOX;" << id;
        break;
    default:
        return "";
    }
    
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
    ((stream << ';' << std::forward<Args>(args)), ...); // fold expression
#else
    int dummy[] = { 0, ((stream << ';' << std::forward<Args>(args)), 0)...};
    static_cast<void>(dummy); // Avoid warning for unused variable
#endif

    stream << ']';
    return stream.str();
}

 

LOCALE_STRING<LOCALE_TYPE::LOCALE_STRING, 3>();
LOCALE_STRING<LOCALE_TYPE::LOCALE_STRING, 16>("BLACK", "XD");

 

then I removed it because I saw official not do like that:

.png

Edited by Mali
  • Good 1
  • Love 1

 

Link to comment
Share on other sites

  • Management
8 hours ago, Sobolanescu said:

It's posible an alternative version, without the new Client: boost library ?

You can remove the boost library yourself, if needed. 🙂 

Link to comment
Share on other sites

  • Active Member
On 11/14/2022 at 1:22 AM, Sobolanescu said:

It's posible an alternative version, without the new Client: boost library ?

If you don't need boost, then just don't use it.

1588925913.jpg

Edited by Metin2 Dev
Core X - External 2 Internal
  • Scream 1
  • Lmao 1
Link to comment
Share on other sites

  • Honorable Member

For anyone interested in also translating the hyperlink item name, you can do the following, although it's not official.

Spoiler
/// 1. @ PythonLocale.h
// Search
	void FormatString(char* c, size_t size) const;

// Add below
	void FilterHyperlinkName(std::string& sMessage) const;

/// 2. @ PythonLocale.cpp
// Add anywhere
#include "../EterBase/Utils.h"
void CPythonLocale::FilterHyperlinkName(std::string& sMessage) const
{
	std::string sHyperItemLinkBegin = "|Hitem";

	std::string::size_type pos = 0;
	while ((pos = sMessage.find(sHyperItemLinkBegin, pos)) != std::string::npos)
	{
		pos += sHyperItemLinkBegin.length();

		std::string element = sMessage.substr(pos, sMessage.length());

		if (element.find("[") != std::string::npos && element.find("]") != std::string::npos)
		{
			size_t pos_begin = element.find(':');
			if (pos_begin == std::string::npos)
				break;

			size_t pos_vnum = element.find_first_of(':', ++pos_begin);
			if (pos_vnum == std::string::npos)
				break;

			DWORD dwItemVNum{};
			try
			{
				dwItemVNum = htoi(element.substr(pos_begin, pos_vnum - pos_begin).c_str());
			}
			catch (const std::exception& ex)
			{
				printf("CPythonLocale::FilterHyperlinkName: Error: %s", ex.what());
				break;
			}

			CItemData* pItemData;
			if (!CItemManager::Instance().GetItemDataPointer(dwItemVNum, &pItemData))
			{
				TraceError("CPythonLocale::FilterHyperlinkName: can't find item vnum: %lu", dwItemVNum);
				break;
			}

			size_t pos_name_begin = element.find('[');
			if (pos_name_begin == std::string::npos)
				break;

			size_t pos_name_end = element.find(']', ++pos_name_begin);
			if (pos_name_end == std::string::npos)
				break;

			sMessage.replace(pos + pos_name_begin, pos_name_end - pos_name_begin, pItemData->GetName());
		}
	}
}

/// 2. @ PythonLocale.cpp
// Search @ CPythonNetworkStream::RecvChatPacket
	buf[uChatSize] = '\0';

// Add below
#if defined(__BL_CLIENT_LOCALE_STRING__)
	TTokenVector vecMultiLine;

	std::string sFormat = { buf };
	if (kChat.bCanFormat && CHAT_TYPE_SHOUT != kChat.type)
	{
		CPythonLocale::Instance().FormatString(sFormat);
		CPythonLocale::Instance().MultiLineSplit(sFormat, vecMultiLine);
	}
	else
	{
		CPythonLocale::Instance().FilterHyperlinkName(sFormat);
	}

	strncpy(buf, sFormat.c_str(), sizeof(buf));
	buf[sizeof(buf) - 1] = '\0';
#endif

 

 

Edited by Owsap
  • Metin2 Dev 1
  • Confused 1
  • Love 3
Link to comment
Share on other sites

Hey, amazing system!

But can you explain me a bit how to use the quest functions correct?

I mean combinations, for example:

say -> use: IN, MN, count

Example: say("Test: Item: %s Mob: %s Count: %d", itemname[0], mobname[0], 30) - instead of string with number (item,mob)

Or

say -> use: IN, "String"

example: 

say("Test: Item: %s Stringname: %s, itemname[0], "Stringxxx")

 

is it always need to add string.format for it?

Because of this:...

say(format.string("[LC;99998;%s;MN;%d;%d]", "Schwert", 101, 30)) -- don't work too..

spacer.png

spacer.png

Thank you!

Edited by Metin2 Dev
Core X - External 2 Internal
Link to comment
Share on other sites

  • Honorable Member

@JeeX

Spoiler
20 minutes ago, JeeX said:

Hey, amazing system!

But can you explain me a bit how to use the quest functions correct?

I mean combinations, for example:

say -> use: IN, MN, count

Example: say("Test: Item: %s Mob: %s Count: %d", itemname[0], mobname[0], 30) - instead of string with number (item,mob)

Or

say -> use: IN, "String"

example: 

say("Test: Item: %s Stringname: %s, itemname[0], "Stringxxx")

 

is it always need to add string.format for it?

Because of this:...

say(format.string("[LC;99998;%s;MN;%d;%d]", "Schwert", 101, 30)) -- don't work too..

spacer.png

spacer.png

Thank you!

 

 

You forgot to wrap MN between squared brackets, like this:

say(format.string("[LC;99998;%s;[MN;%d];%d]", "Schwert", 101, 30))

Additional Information

Spoiler

 Note that, if you want to simplify this and make it like this, you can try to use my extension, 

This is the hidden content, please

say(locale_quest(99998, "Schwert", locale_mob(101), 30))
Edited by Metin2 Dev
Core X - External 2 Internal
  • Metin2 Dev 23
  • Good 5
  • Love 9
Link to comment
Share on other sites

2 hours ago, Owsap said:

@JeeX

  Reveal hidden contents

 

 

You forgot to wrap MN between squared brackets, like this:

say(format.string("[LC;99998;%s;[MN;%d];%d]", "Schwert", 101, 30))

Additional Information

  Reveal hidden contents

 Note that, if you want to simplify this and make it like this, you can try to use my extension, 

Hidden Content

  • Give reaction to this post to see the hidden content.

 

say(locale_quest(99998, "Schwert", locale_mob(101), 30))

Can you try locale.lua part ? For me the mob chat doesnt appear with this ML

Link to comment
Share on other sites

Nice idea!
Ive addet something like this (to request the lines inside of binary):
 

Spoiler

std::string CPythonLocale::GetLocaleString(uint16_t wIndex)
{
    const std::string idx = std::to_string(wIndex);

    auto it = m_LocaleStringMap[LOCALE_STRING].find(idx);
    if (it == m_LocaleStringMap[LOCALE_STRING].end())
    {
        TraceError("CPythonLocale::GetLocaleString: Cannot find string with index %u", wIndex);
        return "";
    }

    return it->second;
}

CPythonLocale::Instance().GetLocaleString(888); // "'s Horse"
Because i've deleted the Horse-Name from Source and made an exception on "RecvCharacterAppendPacket"
and addet an Request on "__Create_SetName"

  • Facepalm 1
  • Good 2
Link to comment
Share on other sites

  • 3 months later...
  • Honorable Member

Here is a little script that converts the translate.lua to the locale quest token including the placeholders for the string.format.

Spoiler
__author__ = "Owsap"
__copyright__ = "Copyright 2023, Owsap Development"

import re
from collections import OrderedDict

LOCALE_QUEST_FILE = "locale_quest.txt"
TRANSLATE_FILE = "translate.lua"
NEW_TRANSLATE_FILE = "new_translate.lua"

locale_quest = OrderedDict()
with open(LOCALE_QUEST_FILE, "r") as file:
	content = file.read()
	for line in content.split('\n'):
		try:
			key, value = line.split('\t')
			locale_quest[key] = value
		except:
			pass

translate = OrderedDict()
with open(TRANSLATE_FILE, "r") as file:
	content = file.read()
	for line in content.split('\n'):
		if not line or not re.match(r'^\s*gameforge\.[^=]+=\s*".*"', line):
			continue

		try:
			key = re.search(r'^\s*(gameforge\.[^=]+)=', line).group(1)
			value = re.search(r'^\s*gameforge\.[^=]+=\s*"(.*)"', line).group(1)
			translate[str.strip(key)] = str.strip(value)
		except:
			pass

used_translate_keys = []
with open(NEW_TRANSLATE_FILE, "w") as file:
	for locale_quest_key, locale_quest_value in locale_quest.items():
		for translate_key, translate_value in translate.items():
			if translate_value == locale_quest_value:
				if translate_key in used_translate_keys:
					continue

				num_s_placeholders = locale_quest_value.count("%s")
				num_d_placeholders = locale_quest_value.count("%d")
				num_placeholders = num_s_placeholders + num_d_placeholders

				if num_placeholders > 0:
					placeholder_list = ["%s"] * num_s_placeholders + ["%d"] * num_d_placeholders
					token = "[LC;{};".format(locale_quest_key) + ";".join(placeholder_list) + "]"
				else:
					token = "[LC;{}]".format(locale_quest_key)

				file.write('{} = "{}"\n'.format(translate_key, token))
				used_translate_keys.append(translate_key)
				break
		else:
			continue

 

Here is an example of the converstion.

Spoiler
gameforge.arena_manager._10_npcChat = "[LC;26]"
gameforge.arena_manager._100_say = "[LC;27]"
gameforge.arena_manager._110_say = "[LC;28;%s]"
gameforge.arena_manager._140_sayTitle = "[LC;31]"
gameforge.arena_manager._150_say = "[LC;32;%s]"
gameforge.arena_manager._160_say = "[LC;33]"
gameforge.arena_manager._170_say = "[LC;34]"
gameforge.arena_manager._180_say = "[LC;35]"
gameforge.arena_manager._190_npcChat = "[LC;36]"
gameforge.arena_manager._195_say = "[LC;37;%s;%s]"
gameforge.arena_manager._20_sayTitle = "[LC;38]"
gameforge.arena_manager._200_tableInsert = "[LC;39]"
gameforge.arena_manager._210_say = "[LC;40]"
gameforge.arena_manager._220_say = "[LC;41;%s]"
gameforge.arena_manager._30_say = "[LC;42]"
gameforge.arena_manager._40_say = "[LC;43]"
gameforge.arena_manager._50_say = "[LC;44;%s]"
gameforge.arena_manager._60_say = "[LC;45]"
gameforge.arena_manager._70_say = "[LC;46]"
gameforge.arena_manager._85_say = "[LC;48;%s;%s]"
gameforge.arena_manager._90_say = "[LC;50;%s]"
...

 

Keep in mind that this conversion is not 100% perfect and accurate so be aware that problems could occur. From what I've tested so far, all the individual checks I did where accurate.

 

You will need to run the script along with the translate.lua and the locale_quest.txt, the file new_translate.lua will be generated.

Edited by Owsap
  • Metin2 Dev 1
  • Love 1
Link to comment
Share on other sites

  • 2 months later...
1 hour ago, nicohare711 said:

@ Owsap@ Mali
Do you know how to do it for the part of the locale string and the mob and item name?

they left no example

thanks for the input and help

    ReplaceSkillName(sMessage);
    ReplacePetSkillName(sMessage);
    ReplaceMobName(sMessage);
    ReplaceItemName(sMessage);
    ReplaceLocaleString(sMessage);
    ReplaceQuestLocaleString(sMessage);
    ReplaceOXQuizLocaleString(sMessage);

SkillName = SN
PetSkillName = No information available.
MobName = MN
ItemName = IN
LocaleString = LS
QuestLocale = LC
OXQuizLocaleString = LOX

Link to comment
Share on other sites

1 hour ago, BadRomani said:

    ReplaceSkillName(sMessage);
    ReplacePetSkillName(sMessage);
    ReplaceMobName(sMessage);
    ReplaceItemName(sMessage);
    ReplaceLocaleString(sMessage);
    ReplaceQuestLocaleString(sMessage);
    ReplaceOXQuizLocaleString(sMessage);

SkillName = SN
PetSkillName = No information available.
MobName = MN
ItemName = IN
LocaleString = LS
QuestLocale = LC
OXQuizLocaleString = LOX

I don't understand where the item and mob name are placed, the Locale string, you can't leave 1-line photos as you have them?

Link to comment
Share on other sites

Announcements



×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.