Jump to content

Fix Target Info System


Intel

Recommended Posts

  • Premium

Premise: I don't know whether you have this system from a free release elsewhere or you bought it and I have no idea if they are the same or not (even though the same version can be found in the first server files of Rubinum).

There are two issues I was concerned with:

-creating an item when you request the infos

-putting the mob data dictionary in constinfo

The first is if you have, in CreateDropItemVector function something like this:

if (item) vec_item.push_back(item);

 

 

So, we create an item and we do nothing with it, staying in memory for no purpose. Instead, we can use a vector of pairs.

Open item_manager.h

Replace 

bool			CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item);

with:

bool			CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item);

 

Now, open item_manager.cpp

Replace

bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item)

With

bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item)

and remove 

LPITEM item = NULL;

and edit the function accordingly.

EX for common_drop_item, replace:

std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin();

	while (it != g_vec_pkCommonDropItem[bRank].end())
	{
		const CItemDropInfo & c_rInfo = *(it++);

		if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd)
			continue;

		TItemTable * table = GetTable(c_rInfo.m_dwVnum);

		if (!table)
			continue;

		item = NULL;

		if (table->bType == ITEM_POLYMORPH)
		{
			if (c_rInfo.m_dwVnum == pkChr->GetPolymorphItemVnum())
			{
				item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true);

				if (item)
					item->SetSocket(0, pkChr->GetRaceNum());
			}
		}
		else
			item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true);

		if (item) vec_item.push_back(item);
	}

with

	std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin();

	while (it != g_vec_pkCommonDropItem[bRank].end())
	{
		const CItemDropInfo & c_rInfo = *(it++);

		if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd)
			continue;

		TItemTable * table = GetTable(c_rInfo.m_dwVnum);

		if (!table)
			continue;

		if(c_rInfo.m_dwVnum > 70103 && c_rInfo.m_dwVnum < 70108)
		{	
			
			if (c_rInfo.m_dwVnum != pkChr->GetPolymorphItemVnum())
			{
				continue;
			}
		}

		vec_item.push_back(std::make_pair(c_rInfo.m_dwVnum, 1));
	}

(edit 70103 and 70108 accordingly with your Polymorph Marble vnums

 

The whole function should look like this:

bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item)
{
	if (pkChr->IsPolymorphed() || pkChr->IsPC())
	{
		return false;
	}

	int iLevel = pkKiller->GetLevel();

	BYTE bRank = pkChr->GetMobRank();

	std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin();

	while (it != g_vec_pkCommonDropItem[bRank].end())
	{
		const CItemDropInfo & c_rInfo = *(it++);

		if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd)
			continue;

		TItemTable * table = GetTable(c_rInfo.m_dwVnum);

		if (!table)
			continue;

		if(c_rInfo.m_dwVnum > 70103 && c_rInfo.m_dwVnum < 70108)
		{	
			
			if (c_rInfo.m_dwVnum != pkChr->GetPolymorphItemVnum())
			{
				continue;
			}
		}

		vec_item.push_back(std::make_pair(c_rInfo.m_dwVnum, 1));
	}

	// Drop Item Group
	{
		itertype(m_map_pkDropItemGroup) it;
		it = m_map_pkDropItemGroup.find(pkChr->GetRaceNum());

		if (it != m_map_pkDropItemGroup.end())
		{
			typeof(it->second->GetVector()) v = it->second->GetVector();

			for (DWORD i = 0; i < v.size(); ++i)
			{
				vec_item.push_back(std::make_pair(v[i].dwVnum, v[i].iCount));
			}
		}
	}

	// MobDropItem Group
	{
		itertype(m_map_pkMobItemGroup) it;
		it = m_map_pkMobItemGroup.find(pkChr->GetRaceNum());

		if ( it != m_map_pkMobItemGroup.end() )
		{
			CMobItemGroup* pGroup = it->second;

			if (pGroup && !pGroup->IsEmpty())
			{
				const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne();
				vec_item.push_back(std::make_pair(info.dwItemVnum, info.iCount));
			}
		}
	}

	// Level Item Group
	{
		itertype(m_map_pkLevelItemGroup) it;
		it = m_map_pkLevelItemGroup.find(pkChr->GetRaceNum());

		if ( it != m_map_pkLevelItemGroup.end() )
		{
			if ( it->second->IsInLevelRange((DWORD)iLevel) )
			{
				typeof(it->second->GetVector()) v = it->second->GetVector();

				for ( DWORD i=0; i < v.size(); i++ )
				{
					DWORD dwVnum = v[i].dwVNum;
					vec_item.push_back(std::make_pair(dwVnum, v[i].iCount));
				}
			}
		}
	}

	// ETC DropItem
	if (pkChr->GetMobDropItemVnum())
	{
		itertype(m_map_dwEtcItemDropProb) it = m_map_dwEtcItemDropProb.find(pkChr->GetMobDropItemVnum());

		if (it != m_map_dwEtcItemDropProb.end())
		{		
			vec_item.push_back(std::make_pair(pkChr->GetMobDropItemVnum(), 1));
		}
	}

	//Metin
	if (pkChr->IsStone())
	{
		if (pkChr->GetDropMetinStoneVnum())
		{
			vec_item.push_back(std::make_pair(pkChr->GetDropMetinStoneVnum(), 1));
		}
	}


	return vec_item.size();
}

Note: level item group and drop item group might be different because in my files i have a level_limit_max token and, for drop_item_group, I don't expect to put a Polymorph Marble. Also it's missing the buyertheifgloves item group, but you can clearly see the pattern and modify accordingly.

 

 Edit : I found a bug in the mob_drop_item infos. If you don't know, with the type "kill", you drop only one of the items you put in the list, so the info drop, well, will show only one, and then another, and another one, every time you reopen it.

 

To fix this issue, go to item_manager.h and search for:

		const SMobItemGroupInfo& GetOne() const
		{
			return m_vecItems[GetOneIndex()];
		}

Add after:

		std::vector<std::pair<int,int>> GetVector()
		{
			std::vector<std::pair<int,int>> item_list;
			for(auto &x : m_vecItems)
				item_list.emplace_back(std::make_pair(x.dwItemVnum,x.iCount));

			return item_list;			
		}

then, in item_manager.cpp, instead of:

			if (pGroup && !pGroup->IsEmpty())
			{
				const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne();
				vec_item.push_back(std::make_pair(info.dwItemVnum, info.iCount));
			}

replace it with:

			if (pGroup && !pGroup->IsEmpty())
			{
				auto vec_items = pGroup->GetVector();
				for(auto &x : vec_items)
					vec_item.push_back(std::make_pair(x.first,x.second));
			}

 

Side Note (how to add drop of the Stones from Metins server side):

Spoiler

In char.cpp there's this function:



void CHARACTER::DetermineDropMetinStone()

which will determine the drop of Stones from Metin. The percentages are in constants.cpp.

EX:



{8001, 60, {30, 30, 30, 9, 1, 0, 0}},	//liv 5
  • 8001: Metin vnum
  • 60: % of drop of a Stone
  • 30 - 30 - 30 - 9 - 1 - 0 - 0: respectively the % of what will be the grade of the stone. So, in this case, 30% for +0, +1 and +2, 9% for +3, 1% for +4.

You can clearly see the sum must be 100%.

How to actually set this drop? We should follow the mathematic calculation here:



int idx = (GetMobDropItemVnum() % 1000) - 1;

 If we put 1 in drop_item in mob_proto for the Metin of vnum 8001, idx is gonna be 0. And what's at the index 0? Exactly that line.

Before return vec_item.size(); you can also organize the vector as you want or create another one with a certain structure, like, first the common drop items, then the advance items or whatever, you can also clear it for what I care. 

Me, for example, I sort it to have all the vnums from the smallest to the largest. You can do that by:

std::sort(vec_item.begin(), vec_item.end(), std::less<std::pair<int,int> >());

Now, let's go to input_main.cpp

Remove:

LPITEM pkInfoItem;

We don't use an item anymore, remember?

So now replace:

static std::vector<LPITEM> s_vec_item;

with:

static std::vector<std::pair<int,int> > s_vec_item;

 

Then replace:

if (ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item) && (m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()))

with:

if ((m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()) && ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item))

This way also, if you target a player or a NPC, it will not trigger the CreateDropItemVector function.

Remove everything inside the function

if (s_vec_item.size() == 0); //this is useless

else if (s_vec_item.size() == 1) //even more useless

Just put:

		for(std::vector<std::pair<int,int> >::const_iterator iter = s_vec_item.begin(); iter != s_vec_item.end();++iter)
		{
			pInfo.dwVID	= m_pkChrTarget->GetVID();
			pInfo.race = m_pkChrTarget->GetRaceNum();
			pInfo.dwVnum = iter->first;
			pInfo.count = iter->second;
			ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo));
		}

 

That's it. Now the constinfo issue.

So, open constinfo.py and remove:

import app
if app.ENABLE_SEND_TARGET_INFO:
	MONSTER_INFO_DATA = {}

Open uitarget.py and after the imports add:

MONSTER_INFO_DATA = {}

and replace whatever declaration with constinfo.MONSTER_INFO_DATA with just MONSTER_INFO_DATA

 

Now open game.py. Even here, replace constinfo.MONSTER_INFO_DATA with uiTarget.MONSTER_INFO_DATA

 

Last little fix, open intrologin.py

Search for:

self.stream.SetLoginInfo(id, pwd)

the one in: def __OnClickLoginButton(self):

add before

uiTarget.MONSTER_INFO_DATA.clear()

This should fix the issue when you don't close the client and, if you change the drop, you still see the older drop.

 

Now you may ask, why all the other servers who may have this have never crashed or imploded? To be fair, I don't know and I don't care. Just know, if your system is similar to this, be aware of the issues and that this is one but definitely not the only fix possible.

 

 

Edited by Gurgarath
  • Metin2 Dev 4
  • Good 2
  • Love 1
  • Love 12
Link to comment
Share on other sites

Am 8.4.2019 um 18:43 schrieb xXIntelXx:

Premise: I don't know whether you have this system from a free release elsewhere or you bought it and I have no idea if they are the same or not (even though the same version can be found in the first server files of Rubinum).

There are two issues I was concerned with:

-creating an item when you request the infos

-putting the mob data dictionary in constinfo

The first is if you have, in CreateDropItemVector function something like this:


if (item) vec_item.push_back(item);

 

 

So, we create an item and we do nothing with it, staying in memory for no purpose. Instead, we can use a vector of pairs.

Open item_manager.h

Replace 


bool			CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item);

with:


bool			CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item);

 

Now, open item_manager.cpp

Replace


bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item)

With


bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item)

and remove 


LPITEM item = NULL;

and edit the function accordingly.

EX for common_drop_item, replace:


std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin();

	while (it != g_vec_pkCommonDropItem[bRank].end())
	{
		const CItemDropInfo & c_rInfo = *(it++);

		if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd)
			continue;

		TItemTable * table = GetTable(c_rInfo.m_dwVnum);

		if (!table)
			continue;

		item = NULL;

		if (table->bType == ITEM_POLYMORPH)
		{
			if (c_rInfo.m_dwVnum == pkChr->GetPolymorphItemVnum())
			{
				item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true);

				if (item)
					item->SetSocket(0, pkChr->GetRaceNum());
			}
		}
		else
			item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true);

		if (item) vec_item.push_back(item);
	}

with


	std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin();

	while (it != g_vec_pkCommonDropItem[bRank].end())
	{
		const CItemDropInfo & c_rInfo = *(it++);

		if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd)
			continue;

		TItemTable * table = GetTable(c_rInfo.m_dwVnum);

		if (!table)
			continue;

		if(c_rInfo.m_dwVnum > 70103 && c_rInfo.m_dwVnum < 70108)
		{	
			
			if (c_rInfo.m_dwVnum != pkChr->GetPolymorphItemVnum())
			{
				continue;
			}
		}

		vec_item.push_back(std::make_pair(c_rInfo.m_dwVnum, 1));
	}

(edit 70103 and 70108 accordingly with your Polymorph Marble vnums

 

The whole function should look like this:


bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item)
{
	if (pkChr->IsPolymorphed() || pkChr->IsPC())
	{
		return false;
	}

	int iLevel = pkKiller->GetLevel();

	BYTE bRank = pkChr->GetMobRank();

	std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin();

	while (it != g_vec_pkCommonDropItem[bRank].end())
	{
		const CItemDropInfo & c_rInfo = *(it++);

		if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd)
			continue;

		TItemTable * table = GetTable(c_rInfo.m_dwVnum);

		if (!table)
			continue;

		if(c_rInfo.m_dwVnum > 70103 && c_rInfo.m_dwVnum < 70108)
		{	
			
			if (c_rInfo.m_dwVnum != pkChr->GetPolymorphItemVnum())
			{
				continue;
			}
		}

		vec_item.push_back(std::make_pair(c_rInfo.m_dwVnum, 1));
	}

	// Drop Item Group
	{
		itertype(m_map_pkDropItemGroup) it;
		it = m_map_pkDropItemGroup.find(pkChr->GetRaceNum());

		if (it != m_map_pkDropItemGroup.end())
		{
			typeof(it->second->GetVector()) v = it->second->GetVector();

			for (DWORD i = 0; i < v.size(); ++i)
			{
				vec_item.push_back(std::make_pair(v[i].dwVnum, v[i].iCount));
			}
		}
	}

	// MobDropItem Group
	{
		itertype(m_map_pkMobItemGroup) it;
		it = m_map_pkMobItemGroup.find(pkChr->GetRaceNum());

		if ( it != m_map_pkMobItemGroup.end() )
		{
			CMobItemGroup* pGroup = it->second;

			if (pGroup && !pGroup->IsEmpty())
			{
				const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne();
				vec_item.push_back(std::make_pair(info.dwItemVnum, info.iCount));
			}
		}
	}

	// Level Item Group
	{
		itertype(m_map_pkLevelItemGroup) it;
		it = m_map_pkLevelItemGroup.find(pkChr->GetRaceNum());

		if ( it != m_map_pkLevelItemGroup.end() )
		{
			if ( it->second->IsInLevelRange((DWORD)iLevel) )
			{
				typeof(it->second->GetVector()) v = it->second->GetVector();

				for ( DWORD i=0; i < v.size(); i++ )
				{
					DWORD dwVnum = v[i].dwVNum;
					vec_item.push_back(std::make_pair(dwVnum, v[i].iCount));
				}
			}
		}
	}

	// ETC DropItem
	if (pkChr->GetMobDropItemVnum())
	{
		itertype(m_map_dwEtcItemDropProb) it = m_map_dwEtcItemDropProb.find(pkChr->GetMobDropItemVnum());

		if (it != m_map_dwEtcItemDropProb.end())
		{		
			vec_item.push_back(std::make_pair(pkChr->GetMobDropItemVnum(), 1));
		}
	}

	//Metin
	if (pkChr->IsStone())
	{
		if (pkChr->GetDropMetinStoneVnum())
		{
			vec_item.push_back(std::make_pair(pkChr->GetDropMetinStoneVnum(), 1));
		}
	}


	return vec_item.size();
}

Note: level item group and drop item group might be different because in my files i have a level_limit_max token and, for drop_item_group, I don't expect to put a Polymorph Marble. Also it's missing the buyertheifgloves item group, but you can clearly see the pattern and modify accordingly.

 

Side Note (how to add drop of the Stones from Metins server side):

  Unsichtbaren Inhalt anzeigen

In char.cpp there's this function:



void CHARACTER::DetermineDropMetinStone()

which will determine the drop of Stones from Metin. The percentages are in constants.cpp.

EX:



{8001, 60, {30, 30, 30, 9, 1, 0, 0}},	//liv 5
  • 8001: Metin vnum
  • 60: % of drop of a Stone
  • 30 - 30 - 30 - 9 - 1 - 0 - 0: respectively the % of what will be the grade of the stone. So, in this case, 30% for +0, +1 and +2, 9% for +3, 1% for +4.

You can clearly see the sum must be 100%.

How to actually set this drop? We should follow the mathematic calculation here:



int idx = (GetMobDropItemVnum() % 1000) - 1;

 If we put 1 in drop_item in mob_proto for the Metin of vnum 8001, idx is gonna be 0. And what's at the index 0? Exactly that line.

Before return vec_item.size(); you can also organize the vector as you want or create another one with a certain structure, like, first the common drop items, then the advance items or whatever, you can also clear it for what I care. 

Me, for example, I sort it to have all the vnums from the smallest to the largest. You can do that by:


std::sort(vec_item.begin(), vec_item.end(), std::less<std::pair<int,int> >());

Now, let's go to input_main.cpp

Remove:


LPITEM pkInfoItem;

We don't use an item anymore, remember?

So now replace:


static std::vector<LPITEM> s_vec_item;

with:


static std::vector<std::pair<int,int> > s_vec_item;

 

Then replace:


if (ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item) && (m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()))

with:


if ((m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()) && ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item))

This way also, if you target a player or a NPC, it will not trigger the CreateDropItemVector function.

Remove everything inside the function

if (s_vec_item.size() == 0); //this is useless

else if (s_vec_item.size() == 1) //even more useless

Just put:


		for(std::vector<std::pair<int,int> >::const_iterator iter = s_vec_item.begin(); iter != s_vec_item.end();++iter)
		{
			pInfo.dwVID	= m_pkChrTarget->GetVID();
			pInfo.race = m_pkChrTarget->GetRaceNum();
			pInfo.dwVnum = iter->first;
			pInfo.count = iter->second;
			ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo));
		}

 

That's it. Now the constinfo issue.

So, open constinfo.py and remove:


import app
if app.ENABLE_SEND_TARGET_INFO:
	MONSTER_INFO_DATA = {}

Open uitarget.py and after the imports add:


MONSTER_INFO_DATA = {}

and replace whatever declaration with constinfo.MONSTER_INFO_DATA with just MONSTER_INFO_DATA

 

Now open game.py. Even here, replace constinfo.MONSTER_INFO_DATA with uiTarget.MONSTER_INFO_DATA

 

Last little fix, open intrologin.py

Search for:


self.stream.SetLoginInfo(id, pwd)

the one in: def __OnClickLoginButton(self):

add before


uiTarget.MONSTER_INFO_DATA.clear()

This should fix the issue when you don't close the client and, if you change the drop, you still see the older drop.

 

Now you may ask, why all the other servers who may have this have never crashed or imploded? To be fair, I don't know and I don't care. Just know, if your system is similar to this, be aware of the issues and that this is one but definitely not the only fix possible.

 

 

 Thank you

 

Link to comment
Share on other sites

  • 2 weeks later...
  • Premium

I am just curious but this would purge the items from memory, right?

If added at the end of the function at input_main.cpp?

	for (auto const& item : s_vec_item)
	{
		M2_DESTROY_ITEM(item);
	}
Spoiler

void CInputMain::TargetInfoLoad(LPCHARACTER ch, const char* c_pData)
{
	TPacketCGTargetInfoLoad* p = (TPacketCGTargetInfoLoad*)c_pData;
	TPacketGCTargetInfo pInfo;
	pInfo.header = HEADER_GC_TARGET_INFO;
	static std::vector<LPITEM> s_vec_item;
	s_vec_item.clear();
	LPITEM pkInfoItem;
	LPCHARACTER m_pkChrTarget = CHARACTER_MANAGER::instance().Find(p->dwVID);
	
	if (!ch || !m_pkChrTarget)
		return;

	if (ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item) && (m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()))
	{
		if (s_vec_item.size() == 0);
		else if (s_vec_item.size() == 1)
		{
			pkInfoItem = s_vec_item[0];
			pInfo.dwVID	= m_pkChrTarget->GetVID();
			pInfo.race = m_pkChrTarget->GetRaceNum();
			pInfo.dwVnum = pkInfoItem->GetVnum();
			pInfo.count = pkInfoItem->GetCount();
			ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo));
		}
		else
		{
			int iItemIdx = s_vec_item.size() - 1;
			while (iItemIdx >= 0)
			{
				pkInfoItem = s_vec_item[iItemIdx--];

				if (!pkInfoItem)
				{
					sys_err("pkInfoItem null in vector idx %d", iItemIdx + 1);
					continue;
				}

					pInfo.dwVID	= m_pkChrTarget->GetVID();
					pInfo.race = m_pkChrTarget->GetRaceNum();
					pInfo.dwVnum = pkInfoItem->GetVnum();
					pInfo.count = pkInfoItem->GetCount();
					ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo));
			}
		}
	}

	for (auto const& value : s_vec_item)
	{
		M2_DESTROY_ITEM(value);
	}

}

 

 

Link to comment
Share on other sites

  • Premium
On 4/20/2019 at 10:19 PM, Sonitex said:

I am just curious but this would purge the items from memory, right?

If added at the end of the function at input_main.cpp?


	for (auto const& item : s_vec_item)
	{
		M2_DESTROY_ITEM(item);
	}
  Reveal hidden contents


void CInputMain::TargetInfoLoad(LPCHARACTER ch, const char* c_pData)
{
	TPacketCGTargetInfoLoad* p = (TPacketCGTargetInfoLoad*)c_pData;
	TPacketGCTargetInfo pInfo;
	pInfo.header = HEADER_GC_TARGET_INFO;
	static std::vector<LPITEM> s_vec_item;
	s_vec_item.clear();
	LPITEM pkInfoItem;
	LPCHARACTER m_pkChrTarget = CHARACTER_MANAGER::instance().Find(p->dwVID);
	
	if (!ch || !m_pkChrTarget)
		return;

	if (ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item) && (m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()))
	{
		if (s_vec_item.size() == 0);
		else if (s_vec_item.size() == 1)
		{
			pkInfoItem = s_vec_item[0];
			pInfo.dwVID	= m_pkChrTarget->GetVID();
			pInfo.race = m_pkChrTarget->GetRaceNum();
			pInfo.dwVnum = pkInfoItem->GetVnum();
			pInfo.count = pkInfoItem->GetCount();
			ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo));
		}
		else
		{
			int iItemIdx = s_vec_item.size() - 1;
			while (iItemIdx >= 0)
			{
				pkInfoItem = s_vec_item[iItemIdx--];

				if (!pkInfoItem)
				{
					sys_err("pkInfoItem null in vector idx %d", iItemIdx + 1);
					continue;
				}

					pInfo.dwVID	= m_pkChrTarget->GetVID();
					pInfo.race = m_pkChrTarget->GetRaceNum();
					pInfo.dwVnum = pkInfoItem->GetVnum();
					pInfo.count = pkInfoItem->GetCount();
					ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo));
			}
		}
	}

	for (auto const& value : s_vec_item)
	{
		M2_DESTROY_ITEM(value);
	}

}

 

 

It would but, you could also state that creating an item in the first place, is pointless (if you wanna be hyper-conscious about constant CPU/RAM usage). At the end we just want to send "vnum" and "count". Or you can just create a script that creates the dictionary  and just not use this at all (I use this method for the chests drop), but needs "more" maintenance. 

  • Love 1
Link to comment
Share on other sites

  • 10 months later...

Thanks for this.

 

how can i use this :

std::sort(vec_item.begin(), vec_item.end(), std::less<std::pair<int,int> >());

To set the drop of metinstone at the top :

 

https://metin2.download/picture/r3F9504x09DVmF8K988YXgTtGTP6KMUj/.gif

 

kind regards.

Edited by Metin2 Dev
Core X - External 2 Internal
  • Metin2 Dev 3
  • Good 3
  • Love 1
  • Love 3
Link to comment
Share on other sites

  • Honorable Member
On 2/26/2020 at 7:12 PM, Chief said:

how can i use this :

To set the drop of metinstone at the top :

not tested

item_manager.h(public):

Spoiler

		bool			IsItemMetin(const DWORD& vnum);

 

item_manager.cpp:

Spoiler

bool ITEM_MANAGER::IsItemMetin(const DWORD& vnum)
{
	const TItemTable* p = GetTable(vnum);
	return (p && p->bType == ITEM_METIN);
}

 

sort:

Spoiler

 


	std::sort(vec_item.begin(), vec_item.end()); // vnum1 < vnum2
	std::sort(vec_item.begin(), vec_item.end(), [](const std::pair<int, int>& f, const std::pair<int, int>& s) { return ITEM_MANAGER::instance().IsItemMetin(f.first) > ITEM_MANAGER::instance().IsItemMetin(s.first); });

 

 

Link to comment
Share on other sites

14 hours ago, Mali61 said:
Spoiler


std::sort(vec_item.begin(), vec_item.end()); // vnum1 < vnum2
	std::sort(vec_item.begin(), vec_item.end(), [](const std::pair<int, int>& f, const std::pair<int, int>& s) { return ITEM_MANAGER::instance().IsItemMetin(f.first) > ITEM_MANAGER::instance().IsItemMetin(s.first); });

 

Where adding this ? i tried before 

 

return vec_item.size();

 

in CreateDropItemVector func

Link to comment
Share on other sites

  • Premium
On 2/28/2020 at 11:37 PM, Chief said:

Where adding this ? i tried before 

 

return vec_item.size();

 

in CreateDropItemVector func

Literally:

FUCKY_FUNCTION(ARGUMENTS I DONT CARE ABOUT)
{
  	[Creating vector of pair(item vnum/quantity)]
  
	std::sort(vec_item.begin(), vec_item.end(), std::less<std::pair<int,int> >()); //here my sort or w/e initial sort you wanna use
	std::sort(vec_item.begin(), vec_item.end(), [](const std::pair<int, int>& f, const std::pair<int, int>& s) { return ITEM_MANAGER::instance().IsItemMetin(f.first) > ITEM_MANAGER::instance().IsItemMetin(s.first); }); //this the metins first sort
	
	return vec_item.size();
}


Though, I am pretty sure the python part of the system already handles this with metin stones.

Link to comment
Share on other sites

  • Premium
2 hours ago, Tallywa said:

compatible with drop item in db?

If originally the target system info wasn't compatible, this won't be either. I just changed how the infos are sent to the client (vector of pairs(int,int) instead of creating a new LPITEM object).

 

But, in a few seconds checking: 

This is the hidden content, please

Ikarus recalls this instance. Basically what you might wanna do is: 

#ifdef ENABLE_DROP_FROM_TABLE
	//MOB_DROP_MANAGER::instance().MakeDropInfoItems(pkChr, pkKiller, vec_item); //old drop info
	MOB_DROP_MANAGER::instance().NewMakeDropInfoItems(pkChr, pkKiller, vec_item); //NEW FUNCTION

#endif

and in mob_drop_manager.cpp the new function would be very similar (obviously you need to change the parameters), but, instead of creating an item, you use rDropInfo.dwItemVnum and rDropInfo.iCount

 

like, common drop would be:

	//common drop
	{
		BYTE bRank = pkChar->GetMobRank();
		COMMON_DROP_MAP::iterator it = m_commonDropMap.find(bRank);

		if (it != m_commonDropMap.end())
		{
			COMMON_DROP_VEC& rVec = it->second;
			COMMON_DROP_VEC::iterator itVec = rVec.begin();

			for ( ; itVec != rVec.end() ; itVec++)
			{
				CommonDropInfo& rDropInfo = *itVec;

				if (rDropInfo.iLevelStart > pkKiller->GetLevel())
					continue;

				if (rDropInfo.iLevelEnd < pkKiller->GetLevel())
					continue;

				TItemTable * table = rItemManager.GetTable(rDropInfo.dwItemVnum);

				if (!table)
					continue;

				if(rDropInfo.dwItemVnum > 70103 && rDropInfo.dwItemVnum < 70108)//your polymorph marbles vnums
				{
					if (rDropInfo.dwItemVnum != pkChr->GetPolymorphItemVnum())
						continue;
				}
				vec_item.push_back(std::make_pair(rDropInfo.dwItemVnum, 1));
			}
		}
	}

When I have more time I can give you the full edited code (with a different define and bla bla bla). If you understand a bit what I did though, you can try changing it by yourself.

 

EDIT: Ah, actually there's a small difference, because in item_manager he creates the vector from the maps in the server source, but then he does the same shit again in mob_drop_manager.cpp.. I must be missing something for sure (or German is destroying my brain). Imo, in this case, just use the CreateItemDropVector function in item_manager.cpp, edit it accordingly (parameters, some if statements and what gets pushed back into the vector, aka a pair of integers) and that's it (and I mean even the one already in the original system) . You are already doing the checks you need in item_manager.cpp. I understand why it is done for the normal drop (maybe can be avoided, but that's not the point here), but in item_manager.cpp you can already access the infos you need, aka: vnum, count and level limits. If this way doesn't work, then you might wanna do the changes in item_manager.cpp and do similar changes to the function in mob_manager.cpp 

I'll try to give you both, when I can.

Edited by xXIntelXx
  • Metin2 Dev 12
  • Good 6
  • Love 3
  • Love 2
Link to comment
Share on other sites

  • Premium

@Tallywa

Ok, so, for the drop with db, I think in char_battle.cpp, if you find:

if (ITEM_MANAGER::instance().CreateDropItem(this, pkAttacker, s_vec_item))
 
I would just change it with
if(MOB_DROP_MANAGER::instance().MakeDropItems(thus, pkAttacker, s_vec_item)
change the function in bool, return the vector size but also you should make some changes to know if GetDropPct returns you true or false, but w/e, it's not the point of the target info system, we don't need to know if we can drop the item but what are the items that we can drop.
 
So, what function do we wanna change? void MOB_DROP_MANAGER::MakeDropInfoItems(LPCHARACTER pkChar, LPCHARACTER pkKiller, std::vector<LPITEM>& items?
(
This is the hidden content, please
)
bool MOB_DROP_MANAGER::MakeDropInfoItems(LPCHARACTER pkChar, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item)
{

	if(!pkChar || !pkKiller)
		return;

	LPITEM item					= NULL;
	ITEM_MANAGER& rItemManager	= ITEM_MANAGER::instance();
	DWORD dwMobVnum				= pkChar->GetRaceNum();

	//common drop
	{
		BYTE bRank = pkChar->GetMobRank();
		COMMON_DROP_MAP::iterator it = m_commonDropMap.find(bRank);

		if (it != m_commonDropMap.end())
		{
			COMMON_DROP_VEC& rVec = it->second;
			COMMON_DROP_VEC::iterator itVec = rVec.begin();

			for ( ; itVec != rVec.end() ; itVec++)
			{
				CommonDropInfo& rDropInfo = *itVec;

				if (rDropInfo.iLevelStart > pkKiller->GetLevel())
					continue;

				if (rDropInfo.iLevelEnd < pkKiller->GetLevel())
					continue;

				TItemTable * table = rItemManager.GetTable(rDropInfo.dwItemVnum);

				if (!table)
					continue;


				if (rDropInfo.dwItemVnum)
				{
					if (rDropInfo.dwItemVnum > 70103 && rDropInfo.dwItemVnum < 70108)
					{
						if (rDropInfo.dwItemVnum != pkChr->GetPolymorphItemVnum())
						{
							continue;
						}		
					}
					vec_item.push_back(std::make_pair(rDropInfo.dwItemVnum, 1));
				}
			}
		}
	}





	//default drop
	{
		DEFAULT_DROP_MAP::iterator it = m_defaultDropMap.find(dwMobVnum);

		if (it != m_defaultDropMap.end())
		{
			DEFAULT_DROP_VEC& rVec = it->second;
			DEFAULT_DROP_VEC::iterator itVec = rVec.begin();

			for (; itVec != rVec.end(); itVec++)
			{
				DefaultDropInfo& rDropInfo = *itVec;

				vec_item.push_back(std::make_pair(rDropInfo.dwItemVnum, rDropInfo.iCount));
			}
		}
	}



	//mob drop kill
	{
		MOB_DROP_GROUP_KILL_MAP_VNUM::iterator it = m_dropGroupKillMap.find(dwMobVnum);

		if (it != m_dropGroupKillMap.end())
		{
			MOB_DROP_GROUP_KILL_MAP_ID& rGroupMap = it->second;
			MOB_DROP_GROUP_KILL_MAP_ID::iterator itGroup = rGroupMap.begin();

			for (; itGroup != rGroupMap.end(); itGroup++)
			{
				MobDropKillGroupInfo& rGroupInfo = itGroup->second;

				if(rGroupInfo.IsEmpty())
					continue;

				for (size_t i = 0; i < rGroupInfo.itemVector.size(); i++)
				{
					MobDropKillInfo& rDropInfo = rGroupInfo.itemVector[i];

					if (rDropInfo.dwItemVnum)
					{
					vec_item.push_back(std::make_pair(rDropInfo.dwItemVnum, rDropInfo.iCount));
					}
				}
			}
		}
	}




	//mob drop level
	{
		MOB_DROP_GROUP_LEVEL_MAP_VNUM::iterator it = m_dropGroupLevelMap.find(dwMobVnum);

		if (it != m_dropGroupLevelMap.end())
		{
			MOB_DROP_LEVEL_ID_MAP&			rMapID	= it->second;
			MOB_DROP_LEVEL_ID_MAP::iterator	itID	= rMapID.begin();

			for (; itID!=rMapID.end(); itID++)
			{
				MobDropLevelGroupInfo& rGroupInfo = itID->second;
				if(pkKiller->GetLevel() < rGroupInfo.iLevelStart)
					continue;

				if(pkKiller->GetLevel() > rGroupInfo.iLevelEnd)
					continue;

				MOB_DROP_LEVEL_INFO_VEC& rDropVec = rGroupInfo.itemVector;
				MOB_DROP_LEVEL_INFO_VEC::iterator itDrop = rDropVec.begin();

				for(; itDrop!=rDropVec.end();itDrop++)
				{
					MobDropLevelInfo& rDropInfo = *itDrop;
					if (rDropInfo.dwItemVnum)
					{
						vec_item.push_back(std::make_pair(rDropInfo.dwItemVnum, rDropInfo.iCount));
					}
				}
			}
		}
	}
	
	std::sort(vec_item.begin(), vec_item.end(), std::less<std::pair<int,int> >());
	return vec_item.size();
}

 

As you can see, I've done the same exact shit I've done for CreateItemVector function.
Now, you need to change the parameters in the declaration and the type of this function:
 
bool MOB_DROP_MANAGER::MakeDropInfoItems(LPCHARACTER pkChar, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item)

in mob_drop_manager.h

 

And now, in input_main.cpp, the line:

if ((m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()) && ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item))
    
needs to be changed with our new function:
if ((m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()) && MOB_DROP_MANAGER::instance().MakeDropInfoItems(m_pkChrTarget, ch, s_vec_item))

 

and I think that's it. 

 
Edited by xXIntelXx
  • Metin2 Dev 7
  • Good 4
  • Love 1
  • Love 3
Link to comment
Share on other sites

  • 2 weeks later...

//Hello.  I have some extra code and I'm not smart enough to fix it like you by myself. Could you please help me? Thank you in advance...

ok I modified the missing code by myself. I have no idea if it really works but thank you very much :)
 

 

Edited by nosacz
more alcohol required to write fixes
Link to comment
Share on other sites

@xXIntelXx

I think is good, but, in compile i have this error 


mob_drop_manager.cpp:478: error: return-statement with no value, in function returning 'int'

for this function


if(!pkChar || !pkKiller)
        return;

 

i have remplace with 


if(!pkChar || !pkKiller)
        return 0;

for try compile and this good, but i dont know when modif is good or bad

Link to comment
Share on other sites

  • Premium
On 3/20/2020 at 8:58 AM, Tallywa said:

@xXIntelXx

I think is good, but, in compile i have this error 

 


mob_drop_manager.cpp:478: error: return-statement with no value, in function returning 'int'

 

for this function

 


if(!pkChar || !pkKiller)
        return;

 

 

i have remplace with 

 


if(!pkChar || !pkKiller)
        return 0;

 

for try compile and this good, but i dont know when modif is good or bad

sorry for late reply, but yeah, 0 or false are ok. I forgot I changed it to a boolean.

 

P.S. Why on earth can I not modify my own old message? W/e..

Edited by xXIntelXx
Link to comment
Share on other sites

  • 8 months later...
  • Premium
On 11/25/2020 at 1:54 AM, ilovegreendays said:

In that post you say that this also fix if you target a player or a NPC, it will not trigger the CreateDropItemVector function

i followed step by step yout tutorial but i have this problem:

That.. is definitely not a problem I may have caused.

In the TargetInfoLoad or w/e in input_main.cpp you gotta check first if it's a monster or stone:

//here is checking if the target is a monster or a stone	
if ((m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone() /*|| m_pkChrTarget->IsBoss()) //stuff of my server*/&& ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item)) 
	{
		for(auto x : s_vec_item)
		{
			pInfo.dwVID	= m_pkChrTarget->GetVID();
			pInfo.race = m_pkChrTarget->GetRaceNum();
          //I use a tuple because I also send the rarity, doesn't matter to the problem you showed.
			pInfo.dwVnum = std::get<0>(x);
			pInfo.count = std::get<1>(x);
			pInfo.rarity = std::get<2>(x);
			buf.write(&pInfo, sizeof(pInfo));
			ch->GetDesc()->BufferedPacket(&pInfo, sizeof(TPacketGCTargetInfo));
			ch->GetDesc()->Packet(buf.read_peek(), buf.size());
		}
	}

and in game.py there is:

	if app.ENABLE_SEND_TARGET_INFO:
		def BINARY_AddTargetMonsterDropInfo(self, raceNum, itemVnum, itemCount, rarity):
			if not raceNum in uiTarget.MONSTER_INFO_DATA: ##no way here you send infos for the racenum of the lycan
				uiTarget.MONSTER_INFO_DATA.update({raceNum : {}})
				uiTarget.MONSTER_INFO_DATA[raceNum].update({"items" : []})
			curList = uiTarget.MONSTER_INFO_DATA[raceNum]["items"]
            [...]

plus, in uitarget.py

			def __LoadInformation_Drops(self, race):
				self.AppendSeperator()

				if race in MONSTER_INFO_DATA: ##this might be different, since I don't store the data in constinfo
          			##and still, race, aka lycan aka wtf it is, is it 8? It's impossible to be in the list because it's neither a monster or a stone
					if len(MONSTER_INFO_DATA[race]["items"]) == 0:

 

 

Spoiler

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

  • Premium

Ok, why can't I edit my own topic? I found a bug in the mob_drop_item infos. If you don't know, with the type "kill", you drop only one of the items you put in the list, so the info drop, well, will show only one, and then another, and another one, every time you reopen it.

 

To fix this issue, go to item_manager.h and search for:

		const SMobItemGroupInfo& GetOne() const
		{
			return m_vecItems[GetOneIndex()];
		}

Add after:

		std::vector<std::pair<int,int>> GetVector()
		{
			std::vector<std::pair<int,int>> item_list;
			for(auto &x : m_vecItems)
				item_list.emplace_back(std::make_pair(x.dwItemVnum,x.iCount));

			return item_list;			
		}

then, in item_manager.cpp, instead of:

			if (pGroup && !pGroup->IsEmpty())
			{
				const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne();
				vec_item.push_back(std::make_pair(info.dwItemVnum, info.iCount));
			}

replace it with:

			if (pGroup && !pGroup->IsEmpty())
			{
				auto vec_items = pGroup->GetVector();
				for(auto &x : vec_items)
					vec_item.push_back(std::make_pair(x.first,x.second));
			}

 

  • Love 1
Link to comment
Share on other sites

  • 2 weeks later...
  • Forum Moderator

Topic edited! Thank you for the updates and your code is really clean! This is the kind of tutorial that I like.

 

Also, I haven't tried yet but theorically it should work, if some of you ever need to add a thing to this system (i.e rarity), you can try to use std::make_tuple instead and access it through std::get (unless using more recent C++ versions that can actually iterate a tuple). Or simply stay stuck to C++98 and use more dirty (don't do it) nested pairs.

 

std::vector<std::pair<int,std::pair<int, int>>

 

Edited by Gurgarath

Gurgarath
coming soon

Link to comment
Share on other sites

  • Premium
On 12/6/2020 at 4:53 PM, Gurgarath said:

Topic edited! Thank you for the updates and your code is really clean! This is the kind of tutorial that I like.

 

Also, I haven't tried yet but theorically it should work, if some of you ever need to add a thing to this system (i.e rarity), you can try to use std::make_tuple instead and access it through std::get (unless using more recent C++ versions that can actually iterate a tuple). Or simply stay stuck to C++98 and use more dirty nested pairs.

 


std::vector<std::pair<int,std::pair<int, int>>

 

Yep, I use c++2a:

spacer.png

 

I cannot even dream of using (please don't guys, just upgrade, even to c++11)

std::vector<std::pair<int,std::pair<int, int>>

 

And to calculate the drop, just take the drop_pct and make the currect calculations (it's different for every type of drop). An example:

spacer.png

A little nasty, but it does its job for now.

And then for ex, taking from the level item group:

	{
		auto it = m_map_pkLevelItemGroup.find(pkChr->GetRaceNum());

		if ( it != m_map_pkLevelItemGroup.end() )
		{
			if ( it->second->IsInLevelRange((DWORD)iLevel) ) //the drop doesn't show in my server if you are not at the right level
			{
				auto v = it->second->GetVector();

				for ( DWORD i=0; i < v.size(); i++ )
				{
					float rarity = CalculateDropRarityLevelItemGroup(v[i].dwPct);
					DWORD dwVnum = v[i].dwVNum;
					vec_item.emplace_back(std::make_tuple(dwVnum, v[i].iCount,rarity));
				}
			}
		}
	}

 

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

  • 4 months later...
  • Premium

Little lag fix when sending the packet:

 

//TARGET INFO
void CInputMain::TargetInfoLoad(LPCHARACTER ch, const char* c_pData)
{
	TPacketCGTargetInfoLoad* p = (TPacketCGTargetInfoLoad*)c_pData;
	TPacketGCTargetInfo pInfo;
	TEMP_BUFFER buf; //ADD THIS
	pInfo.header = HEADER_GC_TARGET_INFO;
	static std::vector<std::tuple<int,int,int>> s_vec_item; //IGNORE THIS LINE
	s_vec_item.clear();
	LPCHARACTER m_pkChrTarget = CHARACTER_MANAGER::instance().Find(p->dwVID);
	if (!ch || !m_pkChrTarget)
		return;

	if ((m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone() || m_pkChrTarget->IsBoss()) && ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item)) //IGNORE THE ISBOSS() FUNCTION
	{
		for(auto x : s_vec_item)  //IGNORE THIS LINE
		{
			pInfo.dwVID	= m_pkChrTarget->GetVID(); //IGNORE THIS LINE
			pInfo.race = m_pkChrTarget->GetRaceNum(); //IGNORE THIS LINE
			pInfo.dwVnum = std::get<0>(x); //IGNORE THIS LINE
			pInfo.count = std::get<1>(x); //IGNORE THIS LINE
			pInfo.rarity = std::get<2>(x); //IGNORE THIS LINE
			buf.write(&pInfo, sizeof(pInfo)); //ADD THIS
			//ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo)); //REMOVE THIS
			ch->GetDesc()->BufferedPacket(&pInfo, sizeof(TPacketGCTargetInfo));  //ADD THIS
		}
		ch->GetDesc()->Packet(buf.read_peek(), buf.size()); //ADD THIS AFTER THE FOR CYCLE
	}
}

 

 

If anyone is curious about my rarity calculations:

int CalculateDropRarityCommonDropItem(int x)
{
	int pct = x;
	int rarity = 0;
	if(pct >= 4000000)
		rarity = 1;
	else if(pct < 40000000 && pct >= 200000 )
		rarity = 2;
	else if(pct < 200000 && pct >= 10000 )
		rarity = 3;
	else if(pct < 10000 && pct >= 5000 )
		rarity = 4;
	else if(pct < 5000 && pct >= 1000 )
		rarity = 5;
	else if(pct < 1000 && pct >= 0 )
		rarity = 6;
	
	return rarity;
}

int CalculateDropRarityKillDrop(int x, BYTE bType)
{
	int pct = x;
	int rarity = 0;
	if(pct == 1)
		rarity = 1;
	else if(pct > 1 && pct <= 300 )
		rarity = 2;
	else if(pct > 300 && pct <= 500 )
		rarity = 3;
	else if(pct > 500 && pct <= 2000 )
		rarity = 4;
	else if(pct > 2000 && pct <= 5000 )
		rarity = 5;
	else if(pct > 5000 )
		rarity = 6;
	
	return rarity;
}

int CalculateDropRarityDropItemGroup(int x, BYTE bType)
{
	int pct = x;
	int rarity = 0;
	switch(bType)
	{
		case CHAR_TYPE_BOSS:
		{
			if(pct >= 4000000)
				rarity = 1;
			else if(pct < 4000000 && pct >= 2600000 ) //100~65
				rarity = 2;
			else if(pct < 2600000 && pct >= 1600000 ) //65~40
				rarity = 3;
			else if(pct < 1600000 && pct >= 1000000 ) //40~25
				rarity = 4;
			else if(pct < 1000000 && pct >= 600000 ) //25~15
				rarity = 5;
			else if(pct < 600000 )
				rarity = 6;
		}
		break;	
		case CHAR_TYPE_MONSTER:
		{
			if(pct >= 4000000)
				rarity = 1;
			else if(pct < 4000000 && pct >= 1600000 ) //100~40
				rarity = 2;
			else if(pct < 1600000 && pct >= 800000 ) //40~20
				rarity = 3;
			else if(pct < 800000 && pct >= 200000 ) //20~5
				rarity = 4;
			else if(pct < 200000 && pct >= 20000 ) //5~0.5
				rarity = 5;
			else if(pct < 20000 )
				rarity = 6;
		}
		break;		
		case CHAR_TYPE_STONE:
		{
			if(pct >= 4000000)
				rarity = 1;
			else if(pct < 4000000 && pct >= 2000000 ) //100~50
				rarity = 2;
			else if(pct < 2000000 && pct >= 1000000 ) //50~25
				rarity = 3;
			else if(pct < 1000000 && pct >= 600000 ) //25~15
				rarity = 4;
			else if(pct < 600000 && pct >= 200000 ) //15~5
				rarity = 5;
			else if(pct < 200000 )
				rarity = 6;
		}
		break;
		default:
		{
			if(pct >= 4000000)
				rarity = 1;
			else if(pct < 4000000 && pct >= 2000000 ) //100~50
				rarity = 2;
			else if(pct < 2000000 && pct >= 1000000 ) //50~25
				rarity = 3;
			else if(pct < 1000000 && pct >= 600000 ) //25~15
				rarity = 4;
			else if(pct < 600000 && pct >= 200000 ) //15~5
				rarity = 5;
			else if(pct < 200000 )
				rarity = 6;
		}
		break;	
	}
	return rarity;
}

int CalculateDropRarityLevelItemGroup(float x, BYTE bType)
{
	float pct = x / 10000.0000;
	int rarity = 0;
	switch(bType)
	{
		case CHAR_TYPE_BOSS:
		{
			if(pct >= 100.0000)
				rarity = 1;
			else if(pct < 100.0000 && pct >= 65.0000 )
				rarity = 2;
			else if(pct < 65.0000 && pct >= 40.0000 )
				rarity = 3;
			else if(pct < 40.0000 && pct >= 25.0000 )
				rarity = 4;
			else if(pct < 25.0000 && pct >= 15.0000 )
				rarity = 5;
			else if(pct < 15.0000 )
				rarity = 6;				
		}
		break;
		case CHAR_TYPE_STONE:
		{
			if(pct >= 100.0000)
				rarity = 1;
			else if(pct < 100.0000 && pct >= 50.0000 )
				rarity = 2;
			else if(pct < 50.0000 && pct >= 25.0000 )
				rarity = 3;
			else if(pct < 25.0000 && pct >= 15.0000 )
				rarity = 4;
			else if(pct < 15.0000 && pct >= 5.0000 )
				rarity = 5;
			else if(pct < 5.0000 )
				rarity = 6;
		}
		break;
		case CHAR_TYPE_MONSTER:
		{
			if(pct >= 100.0000)
				rarity = 1;
			else if(pct < 100.0000 && pct >= 30.0000 )
				rarity = 2;
			else if(pct < 30.0000 && pct >= 15.0000 )
				rarity = 3;
			else if(pct < 15.0000 && pct >= 1.0000 )
				rarity = 4;
			else if(pct < 1.0000 && pct >= 0.1000 )
				rarity = 5;
			else if(pct < 0.1000 )
				rarity = 6;			
		}
		break;
		default:
		{
			if(pct >= 100.0000)
				rarity = 1;
			else if(pct < 100.0000 && pct >= 65.0000 )
				rarity = 2;
			else if(pct < 65.0000 && pct >= 40.0000 )
				rarity = 3;
			else if(pct < 40.0000 && pct >= 25.0000 )
				rarity = 4;
			else if(pct < 25.0000 && pct >= 15.0000 )
				rarity = 5;
			else if(pct < 15.0000 )
				rarity = 6;				
		}	
	}	
	return rarity;
}

you can replace the switch statement with some ifs and use IsStone()/IsMonster()  (and check the rank of the mob).

Also if you wanna be even more precise on the rarity, there could be a check on the vnum of the monster, and decide also by the quantity of the monster (if there are 1kk of a monster  and pct is 0.5, it definitely it's easier than finding an item among 100 monsters but honestly cba about that LOL)

Edited by xXIntelXx
I am an idiot and fucked the vector sorting
  • Metin2 Dev 1
  • Love 1
Link to comment
Share on other sites

  • 7 months later...

 

It was giving me "IsInLevelRange" problems .. I edited it like this but I don't know if it's without problems.

        {
            if (it->second->IsInLevelRange((DWORD)iLevel))
            {

 

        {
            if (it->second->GetLevelLimit() <= (DWORD)iLevel)
            {

Link to comment
Share on other sites

  • Premium
On 12/13/2021 at 2:55 PM, Artix96it said:

 

It was giving me "IsInLevelRange" problems .. I edited it like this but I don't know if it's without problems.

        {
            if (it->second->IsInLevelRange((DWORD)iLevel))
            {

 

        {
            if (it->second->GetLevelLimit() <= (DWORD)iLevel)
            {

Ah yes, my bad. I do have:

	bool IsInLevelRange(DWORD dwLevel)
	{
		if(m_dwLevelMaxLimit == 0)
			return dwLevel >= m_dwLevelLimit;
		return dwLevel >= m_dwLevelLimit && dwLevel <= m_dwLevelMaxLimit;
	}

for the drop type limit.

Link to comment
Share on other sites

On 12/18/2021 at 9:45 PM, xXIntelXx said:

Ah yes, my bad. I do have:

	bool IsInLevelRange(DWORD dwLevel)
	{
		if(m_dwLevelMaxLimit == 0)
			return dwLevel >= m_dwLevelLimit;
		return dwLevel >= m_dwLevelLimit && dwLevel <= m_dwLevelMaxLimit;
	}

for the drop type limit.

Is there a way to contact you privately?

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.