Jump to content

Separated table for costume bonuses


Recommended Posts

Costume bonuses are taken from item_attr table but this is quite a limit.

We are going to create a second table item_attr_costume which will contain only costume bonuses.

Transform costume and Enchant costume will take bonus from the new table item_attr_costume.

 

 

Let's start.

 

Open ClientManagerBoot.cpp and add at the end


bool CClientManager::InitializeCostumeAttrTable()
{
	char query[4096];
	snprintf(query, sizeof(query),
		"SELECT apply, apply+0, prob, lv1, lv2, lv3, lv4, lv5, weapon, body, wrist, foots, neck, head, shield, ear FROM item_attr_costume%s ORDER BY apply",
		GetTablePostfix());

	std::unique_ptr<SQLMsg> pkMsg(CDBManager::instance().DirectQuery(query));
	SQLResult* pRes = pkMsg->Get();

	if (!pRes->uiNumRows)
	{
		sys_err("no result from item_attr_costume");
		return false;
	}

	if (!m_vec_costumeAttrTable.empty())
	{
		sys_log(0, "RELOAD: item_attr_costume");
		m_vec_costumeAttrTable.clear();
	}

	m_vec_costumeAttrTable.reserve(pRes->uiNumRows);

	MYSQL_ROW	data;

	while ((data = mysql_fetch_row(pRes->pSQLResult)))
	{
		TItemAttrTable t{};

		int col = 0;

		strlcpy(t.szApply, data[col++], sizeof(t.szApply));
		str_to_number(t.dwApplyIndex, data[col++]);
		str_to_number(t.dwProb, data[col++]);
		str_to_number(t.lValues[0], data[col++]);
		str_to_number(t.lValues[1], data[col++]);
		str_to_number(t.lValues[2], data[col++]);
		str_to_number(t.lValues[3], data[col++]);
		str_to_number(t.lValues[4], data[col++]);
		str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WEAPON], data[col++]);
		str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_BODY], data[col++]);
		str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_WRIST], data[col++]);
		str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_FOOTS], data[col++]);
		str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_NECK], data[col++]);
		str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_HEAD], data[col++]);
		str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_SHIELD], data[col++]);
		str_to_number(t.bMaxLevelBySet[ATTRIBUTE_SET_EAR], data[col++]);

		m_vec_costumeAttrTable.push_back(t);
	}

	return true;
}

 

Search

	if (!InitializeItemRareTable())
	{
		sys_err("InitializeItemRareTable FAILED");
		return false;
	}

 

add under

 

	if (!InitializeCostumeAttrTable())
	{
		sys_err("InitializeCostumeAttrTable FAILED");
		return false;
	}

 

open ClientManager.h and look for

	bool		InitializeMonarch();

add under

	bool        InitializeCostumeAttrTable();

 

look for

	std::vector<TItemAttrTable>		m_vec_itemRareTable;

add under

	std::vector<TItemAttrTable>     m_vec_costumeAttrTable;

open ClientManager.cpp and look for

		tmp->EncodeWORD(m_vec_itemRareTable.size());
		tmp->Encode(&m_vec_itemRareTable[0], sizeof(TItemAttrTable) * m_vec_itemRareTable.size());

add under

		tmp->EncodeWORD(m_vec_costumeAttrTable.size());
		tmp->Encode(&m_vec_costumeAttrTable[0], sizeof(TItemAttrTable) * m_vec_costumeAttrTable.size());

 

look for

		sizeof(WORD) + sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemRareTable.size() +

 

add under

		sizeof(WORD) + sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_costumeAttrTable.size() +

 

look for

		tmp->EncodeHeader(HEADER_DG_RELOAD_PROTO, 0,
			sizeof(WORD) + sizeof(TSkillTable) * m_vec_skillTable.size() +
			sizeof(WORD) + sizeof(TBanwordTable) * m_vec_banwordTable.size() +
			sizeof(WORD) + sizeof(TItemTable) * m_vec_itemTable.size() +
			sizeof(WORD) + sizeof(TMobTable) * m_vec_mobTable.size() +
			sizeof(WORD) + sizeof(TShopTable) * m_iShopTableSize +
			sizeof(WORD) + sizeof(TRefineTable) * m_iRefineTableSize +
			sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemAttrTable.size() +
			sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemRareTable.size();

 

and change it like this

		tmp->EncodeHeader(HEADER_DG_RELOAD_PROTO, 0,
			sizeof(WORD) + sizeof(TSkillTable) * m_vec_skillTable.size() +
			sizeof(WORD) + sizeof(TBanwordTable) * m_vec_banwordTable.size() +
			sizeof(WORD) + sizeof(TItemTable) * m_vec_itemTable.size() +
			sizeof(WORD) + sizeof(TMobTable) * m_vec_mobTable.size() +
			sizeof(WORD) + sizeof(TShopTable) * m_iShopTableSize +
			sizeof(WORD) + sizeof(TRefineTable) * m_iRefineTableSize +
			sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemAttrTable.size() +
			sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_itemRareTable.size() +
			sizeof(WORD) + sizeof(TItemAttrTable) * m_vec_costumeAttrTable.size());

 

look for

	peer->EncodeWORD(sizeof(TItemAttrTable));
	peer->EncodeWORD(m_vec_itemRareTable.size());
	peer->Encode(&m_vec_itemRareTable[0], sizeof(TItemAttrTable) * m_vec_itemRareTable.size());

add under

	peer->EncodeWORD(sizeof(TItemAttrTable));
	peer->EncodeWORD(m_vec_costumeAttrTable.size());
	peer->Encode(&m_vec_costumeAttrTable[0], sizeof(TItemAttrTable) * m_vec_costumeAttrTable.size());

 

open constants.cpp and look for

TItemAttrMap g_map_itemRare;

add under

TItemAttrMap g_map_itemAttrCostume;

 

open constants.h and look for

extern TItemAttrMap g_map_itemRare;

add under

extern TItemAttrMap g_map_itemAttrCostume;

 

open input_db.cpp and look for

	/*
     * ITEM RARE
     */
	if (decode_2bytes(data) != sizeof(TItemAttrTable))
	{
		sys_err("item rare table size error");
		thecore_shutdown();
		return;
	}
	data += 2;

	size = decode_2bytes(data);
	data += 2;
	sys_log(0, "BOOT: ITEM_RARE: %d", size);

	if (size)
	{
		TItemAttrTable * p = (TItemAttrTable *) data;

		for (int i = 0; i < size; ++i, ++p)
		{
			if (p->dwApplyIndex >= MAX_APPLY_NUM)
				continue;

			g_map_itemRare[p->dwApplyIndex] = *p;
			sys_log(0, "ITEM_RARE[%d]: %s %u", p->dwApplyIndex, p->szApply, p->dwProb);
		}
	}

	data += size * sizeof(TItemAttrTable);

 

add under


	/*
	 * ITEM ATTR COSTUME
	 */
	if (decode_2bytes(data) != sizeof(TItemAttrTable))
	{
		sys_err("item attr costume table size error");
		thecore_shutdown();
		return;
	}
	data += 2;

	size = decode_2bytes(data);
	data += 2;
	sys_log(0, "BOOT: ITEM_ATTR_COSTUME: %d", size);

	if (size)
	{
		TItemAttrTable* p = (TItemAttrTable*)data;

		for (int i = 0; i < size; ++i, ++p)
		{
			if (p->dwApplyIndex >= MAX_APPLY_NUM)
				continue;

			g_map_itemAttrCostume[p->dwApplyIndex] = *p;
			sys_log(0, "ITEM_ATTR_COSTUME[%d]: %s %u", p->dwApplyIndex, p->szApply, p->dwProb);
		}
	}

	data += size * sizeof(TItemAttrTable);

 

open item_attribute.cpp and add these functions at the end (I don't remember why I rewrote this part, just replace your functions if you already have them)

 

bool CItem::AddCostumeAttribute()
{
	int count = GetAttributeCount();

	if (count >= COSTUME_ATTRIBUTE_MAX_LEVEL)
		return false;

	int pos = count;
	TPlayerItemAttribute& attr = m_aAttr[pos];

	int nAttrSet = GetAttributeSetIndex();
	std::vector<int> avail;

	for (int i = 0; i < MAX_APPLY_NUM; ++i)
	{
		const TItemAttrTable& r = g_map_itemAttrCostume[i];

		if (r.dwApplyIndex != 0 && r.bMaxLevelBySet[nAttrSet] > 0 && HasAttr(i) != true)
		{
			avail.push_back(i);
		}
	}

	if (avail.size() == 0) {
		return false;
	}

	const TItemAttrTable& r = g_map_itemAttrCostume[avail[number(0, avail.size() - 1)]];

	int nAttrLevel = number(1, 5);

	if (nAttrLevel > r.bMaxLevelBySet[nAttrSet])
		nAttrLevel = r.bMaxLevelBySet[nAttrSet];

	attr.bType = r.dwApplyIndex;
	attr.sValue = r.lValues[nAttrLevel - 1];

	UpdatePacket();

	Save();

	return true;
}

bool CItem::AddRandomNumberOfCostumeAttributes()
{
	int cnt = GetAttributeCount();
  	int new_bonus_cnt = number(1, 3);

	for (int i = 0; i < cnt; ++i)
	{
		m_aAttr[i].bType = 0;
		m_aAttr[i].sValue = 0;
	}
  
  	for (int i = 0; i < new_bonus_cnt ; ++i)
	{
		AddCostumeAttribute();
	}

	return true;
}

bool CItem::ChangeCostumeAttribute()
{
	int cnt = GetAttributeCount();

	for (int i = 0; i < cnt; ++i)
	{
		m_aAttr[i].bType = 0;
		m_aAttr[i].sValue = 0;
	}

	for (int i = 0; i < cnt; ++i)
	{
		AddCostumeAttribute();
	}

	return true;
}

 

 

open item_length.h and look for

	ITEM_ATTRIBUTE_MAX_LEVEL = 5,

add under

	COSTUME_ATTRIBUTE_MAX_LEVEL = 5,

 

open item.h and look for

		int			GetRareAttrCount();
		bool		AddRareAttribute();

add under

		bool		AddCostumeAttribute();
		bool		ChangeCostumeAttribute();
		bool		AddRandomNumberOfCostumeAttributes();

 

 

open char_item.cpp and look for

 

							switch (item->GetSubType())
							{
								case USE_CHANGE_COSTUME_ATTR:
									item2->ChangeAttribute();
									{
										char buf[21];
										snprintf(buf, sizeof(buf), "%u", item2->GetID());
										LogManager::instance().ItemLog(this, item, "CHANGE_COSTUME_ATTR", buf);
									}
									break;
								case USE_RESET_COSTUME_ATTR:
									item2->ClearAttribute();
									item2->AlterToMagicItem();
									{
										char buf[21];
										snprintf(buf, sizeof(buf), "%u", item2->GetID());
										LogManager::instance().ItemLog(this, item, "RESET_COSTUME_ATTR", buf);
									}
									break;
							}

							ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¼Ó¼ºÀ» º¯°æÇÏ¿´½À´Ï´Ù."));

							item->SetCount(item->GetCount() - 1);
							break;

 

replace with

							if (item->GetSubType() == USE_RESET_COSTUME_ATTR) {
								if (item2->GetAttributeCount() < 3) {
									if (item2->AddCostumeAttribute()) {
										item->SetCount(item->GetCount() - 1);
									}
								}
							
							}
							else {
								if (item2->GetAttributeCount() == 0)
								{
									ChatPacket(CHAT_TYPE_INFO, "You must add some bonuses first.");
									return false;
								}

								if (item2->ChangeCostumeAttribute()) {
									item->SetCount(item->GetCount() - 1);
								}
							}
							break;

 

 

run this query in your database player:

 

DROP TABLE IF EXISTS `item_attr_costume`;
CREATE TABLE `item_attr_costume` (
  `apply` enum('MAX_HP','MAX_SP','CON','INT','STR','DEX','ATT_SPEED','MOV_SPEED','CAST_SPEED','HP_REGEN','SP_REGEN','POISON_PCT','STUN_PCT','SLOW_PCT','CRITICAL_PCT','PENETRATE_PCT','ATTBONUS_HUMAN','ATTBONUS_ANIMAL','ATTBONUS_ORC','ATTBONUS_MILGYO','ATTBONUS_UNDEAD','ATTBONUS_DEVIL','STEAL_HP','STEAL_SP','MANA_BURN_PCT','DAMAGE_SP_RECOVER','BLOCK','DODGE','RESIST_SWORD','RESIST_TWOHAND','RESIST_DAGGER','RESIST_BELL','RESIST_FAN','RESIST_BOW','RESIST_FIRE','RESIST_ELEC','RESIST_MAGIC','RESIST_WIND','REFLECT_MELEE','REFLECT_CURSE','POISON_REDUCE','KILL_SP_RECOVER','EXP_DOUBLE_BONUS','GOLD_DOUBLE_BONUS','ITEM_DROP_BONUS','POTION_BONUS','KILL_HP_RECOVER','IMMUNE_STUN','IMMUNE_SLOW','IMMUNE_FALL','SKILL','BOW_DISTANCE','ATT_GRADE_BONUS','DEF_GRADE_BONUS','MAGIC_ATT_GRADE_BONUS','MAGIC_DEF_GRADE_BONUS','CURSE_PCT','MAX_STAMINA','ATT_BONUS_TO_WARRIOR','ATT_BONUS_TO_ASSASSIN','ATT_BONUS_TO_SURA','ATT_BONUS_TO_SHAMAN','ATT_BONUS_TO_MONSTER','ATT_BONUS','MALL_DEFBONUS','MALL_EXPBONUS','MALL_ITEMBONUS','MALL_GOLDBONUS','MAX_HP_PCT','MAX_SP_PCT','SKILL_DAMAGE_BONUS','NORMAL_HIT_DAMAGE_BONUS','SKILL_DEFEND_BONUS','NORMAL_HIT_DEFEND_BONUS','PC_BANG_EXP_BONUS','PC_BANG_DROP_BONUS','EXTRACT_HP_PCT','RESIST_WARRIOR','RESIST_ASSASSIN','RESIST_SURA','RESIST_SHAMAN','ENERGY','DEF_GRADE','COSTUME_ATTR_BONUS','MAGIC_ATT_BONUS_PER','MELEE_MAGIC_ATT_BONUS_PER','RESIST_ICE','RESIST_EARTH','RESIST_DARK','RESIST_CRITICAL','RESIST_PENETRATE','BLEEDING_REDUCE','BLEEDING_PCT','ATT_BONUS_TO_WOLFMAN','RESIST_WOLFMAN','RESIST_CLAW') NOT NULL DEFAULT 'MAX_HP',
  `prob` int unsigned NOT NULL DEFAULT '0',
  `lv1` int unsigned NOT NULL DEFAULT '0',
  `lv2` int unsigned NOT NULL DEFAULT '0',
  `lv3` int unsigned NOT NULL DEFAULT '0',
  `lv4` int unsigned NOT NULL DEFAULT '0',
  `lv5` int unsigned NOT NULL DEFAULT '0',
  `weapon` int unsigned NOT NULL DEFAULT '0',
  `body` int unsigned NOT NULL DEFAULT '0',
  `wrist` int unsigned NOT NULL DEFAULT '0',
  `foots` int unsigned NOT NULL DEFAULT '0',
  `neck` int unsigned NOT NULL DEFAULT '0',
  `head` int unsigned NOT NULL DEFAULT '0',
  `shield` int unsigned NOT NULL DEFAULT '0',
  `ear` int unsigned NOT NULL DEFAULT '0',
  `costume_body` int unsigned NOT NULL DEFAULT '0',
  `costume_hair` int unsigned NOT NULL DEFAULT '0',
  `costume_weapon` int unsigned NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
SET FOREIGN_KEY_CHECKS=1;

 

and you can add bonuses you want for costumes in it :)

 

that's it :) 

  • Metin2 Dev 2
  • Love 2
Link to comment
Share on other sites

  • Developer
21 hours ago, Cappuccino said:

Costume bonuses are taken from item_attr table but this is quite a limit.

May I ask why is this limited?

You could as well just create separate fields for costume_body, costume_head, costume_weapon (or even costume_acce, if wanted), in the common item_attr table, and you'd break the limitation, if any.

when you return 0 and server doesn't boot:

unknown.png

Link to comment
Share on other sites

2 hours ago, PACI said:

May I ask why is this limited?

You could as well just create separate fields for costume_body, costume_head, costume_weapon (or even costume_acce, if wanted), in the common item_attr table, and you'd break the limitation, if any.

Yes you can, but the bonus values will be shared with normal items.

For example, you might want to set a different bonus value for "chance of critical damage" for costumes.

Link to comment
Share on other sites

  • 9 months later...

Before you share your C++ edit, please test it on your own, this code cant work. with your Edit I get compile error because of the char_item.cpp if I change my values item2->AddAttribute(); & ChangeAttribute to AddCostumeAttribute & ChangeCostumeAttribute() it only removes my item and dont add / change bonus.

Edited by crewfire1337
Link to comment
Share on other sites

  • 1 year later...

 I tested and that works.

But he is reading the item_attr_rare table bonuses instead of   `item_attr_costume`  everything is fine same the tutorial.

that is my char_item.cpp

Spoiler

                            case 79998 : // ADDBONUS TRAJE
                                {
                                    LPITEM item2;

                                    if (!IsValidItemPosition(DestCell) || !(item2 = GetInventoryItem(wDestCell)))
                                        return false;

                                    if (item2->IsExchanging() == true)
                                        return false;
                                    
                                    if (item2->IsEquipped() == true) // Fix
                                        return false;
                                    
                                    // if (item2->GetType() == ITEM_COSTUME) // Fix
                                        // return false;

                                    if (item2->GetAttributeSetIndex() == -1)
                                    {
                                        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Nao foi possivel."));
                                        return false;
                                    }

                                    if (item2->GetType() == ITEM_COSTUME) // Fix
                                    {
                                        if (item2->GetAttributeCount() < 3)
                                        {
                                            if (item2->AddCostumeAttribute())
                                            {
                                                item->SetCount(item->GetCount() - 1);
                                            }
                                        }

                                        else
                                        {
                                            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Nao e possivel adicionar mais bonus"));
                                        }
                                    }
                                }
                                break;


and my item_atribrute.cpp

Spoiler

same tutorial using 

const TItemAttrTable& r = g_map_itemAttrCostume[avail[number(0, avail.size() - 1)]];


i make clientemanager and clientmanagerboot.cpp same as in the tutorial.

that dont make sense =/

Edited by garofolo
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.