mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-18 03:15:41 +00:00
feat(core\dbc): item.dbc, sItemStore, item_dbc, item enforcement conf, subclass fix (#14675)
feat (core\log\db): item.dbc and enforcement dbc enforcement partial pick from tc:0c44bd33eeCustom Item for testing by menevia16a (SPP DEV VEIL) feat (core\log\db): item.dbc and enforcement Update Item.sql Update DBCStores.cpp Update World.cpp Update ObjectMgr.cpp further replacement from template to dbc lookup further logging and implementation cherry pick tcfd26c3c87creplace with db lookup update (sql): Murder all the backticks line break fixit fix (item_template): fix incorrect subclass fix incorrect subclass matching with dbc enforcement update: log correction for sub class update log correction for sub class add subclass to dbc enforcement add subclass dbc enforcement since it is part of the item.dbc item_dbc update (log): additional log Co-authored-by: blub <trinity.michael_vincent@gmx.eu> Co-authored-by: Shocker <511388+shocker@users.noreply.github.com> Co-authored-by: Veil <1913466+menevia16a@users.noreply.github.com> Co-authored-by: Shocker <43253032+shockerqt@users.noreply.github.com>
This commit is contained in:
15
data/sql/updates/pending_db_world/Item.sql
Normal file
15
data/sql/updates/pending_db_world/Item.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- add item_dbc table
|
||||
DROP TABLE IF EXISTS `item_dbc`;
|
||||
CREATE TABLE `item_dbc` ( `ID` INT NOT NULL DEFAULT '0', `ClassID` INT NOT NULL DEFAULT '0', `SubclassID` INT NOT NULL DEFAULT '0', `Sound_Override_Subclassid` INT NOT NULL DEFAULT '0', `Material` INT NOT NULL DEFAULT '0', `DisplayInfoID` INT NOT NULL DEFAULT '0', `InventoryType` INT NOT NULL DEFAULT '0', `SheatheType` INT NOT NULL DEFAULT '0', PRIMARY KEY (`ID`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Corrects subclass error messages
|
||||
UPDATE `item_template` SET `subclass`=4 WHERE `entry`=17;
|
||||
UPDATE `item_template` SET `subclass`=6 WHERE `entry`=2556;
|
||||
UPDATE `item_template` SET `subclass`=0 WHERE `entry`=20221;
|
||||
UPDATE `item_template` SET `subclass`=13 WHERE `entry`=31802;
|
||||
UPDATE `item_template` SET `subclass`=3 WHERE `entry`=33080;
|
||||
UPDATE `item_template` SET `subclass`=3 WHERE `entry`=33604;
|
||||
UPDATE `item_template` SET `subclass`=8 WHERE `entry`=37445;
|
||||
UPDATE `item_template` SET `subclass`=12 WHERE `entry`=37677;
|
||||
UPDATE `item_template` SET `subclass`=7 WHERE `entry`=41749;
|
||||
UPDATE `item_template` SET `subclass`=1 WHERE `entry`=53048;
|
||||
@@ -1309,6 +1309,14 @@ DeletedCharacterTicketTrace = 0
|
||||
|
||||
DungeonFinder.OptionsMask = 5
|
||||
|
||||
#
|
||||
# DBC.EnforceItemAttributes
|
||||
# Disallow overriding item attributes stored in DBC files with values from the database
|
||||
# Default: 0 - Off, Use DB values
|
||||
# 1 - On, Enforce DBC Values (default)
|
||||
|
||||
DBC.EnforceItemAttributes = 1
|
||||
|
||||
#
|
||||
# AccountInstancesPerHour
|
||||
# Description: Controls the max amount of different instances player can enter within hour
|
||||
|
||||
@@ -2007,7 +2007,7 @@ private:
|
||||
|
||||
bool IsItemValid(SmartScriptHolder const& e, uint32 entry)
|
||||
{
|
||||
if (!sObjectMgr->GetItemTemplate(entry))
|
||||
if (!sItemStore.LookupEntry(entry))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} uses non-existent Item entry {}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
|
||||
return false;
|
||||
|
||||
@@ -98,6 +98,7 @@ DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore(GtRegenMPPerSptf
|
||||
|
||||
DBCStorage <HolidaysEntry> sHolidaysStore(Holidaysfmt);
|
||||
|
||||
DBCStorage <ItemEntry> sItemStore(Itemfmt);
|
||||
DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore(ItemBagFamilyfmt);
|
||||
//DBCStorage <ItemCondExtCostsEntry> sItemCondExtCostsStore(ItemCondExtCostsEntryfmt);
|
||||
DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt);
|
||||
@@ -317,6 +318,7 @@ void LoadDBCStores(const std::string& dataPath)
|
||||
LOAD_DBC(sGtRegenHPPerSptStore, "gtRegenHPPerSpt.dbc", "gtregenhpperspt_dbc");
|
||||
LOAD_DBC(sGtRegenMPPerSptStore, "gtRegenMPPerSpt.dbc", "gtregenmpperspt_dbc");
|
||||
LOAD_DBC(sHolidaysStore, "Holidays.dbc", "holidays_dbc");
|
||||
LOAD_DBC(sItemStore, "Item.dbc", "item_dbc");
|
||||
LOAD_DBC(sItemBagFamilyStore, "ItemBagFamily.dbc", "itembagfamily_dbc");
|
||||
LOAD_DBC(sItemDisplayInfoStore, "ItemDisplayInfo.dbc", "itemdisplayinfo_dbc");
|
||||
//LOAD_DBC(sItemCondExtCostsStore, "ItemCondExtCosts.dbc", "itemcondextcosts_dbc");
|
||||
@@ -631,9 +633,10 @@ void LoadDBCStores(const std::string& dataPath)
|
||||
}
|
||||
|
||||
// Check loaded DBC files proper version
|
||||
if (!sAreaTableStore.LookupEntry(4987) || // last area added in 3.3.5a
|
||||
if (!sAreaTableStore.LookupEntry(4987) || // last area added in 3.3.5a
|
||||
!sCharTitlesStore.LookupEntry(177) || // last char title added in 3.3.5a
|
||||
!sGemPropertiesStore.LookupEntry(1629) || // last added spell in 3.3.5a
|
||||
!sItemStore.LookupEntry(56806) || // last client known item added in 3.3.5a
|
||||
!sItemExtendedCostStore.LookupEntry(2997) || // last item extended cost added in 3.3.5a
|
||||
!sMapStore.LookupEntry(724) || // last map added in 3.3.5a
|
||||
!sSpellStore.LookupEntry(80864) ) // last client known item added in 3.3.5a
|
||||
|
||||
@@ -124,6 +124,7 @@ extern DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore;
|
||||
extern DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore;
|
||||
extern DBCStorage <HolidaysEntry> sHolidaysStore;
|
||||
extern DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore;
|
||||
extern DBCStorage <ItemEntry> sItemStore;
|
||||
extern DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore;
|
||||
extern DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore;
|
||||
extern DBCStorage <ItemLimitCategoryEntry> sItemLimitCategoryStore;
|
||||
|
||||
@@ -1595,9 +1595,9 @@ void ObjectMgr::LoadEquipmentTemplates()
|
||||
if (!equipmentInfo.ItemEntry[i])
|
||||
continue;
|
||||
|
||||
ItemTemplate const* item = GetItemTemplate(equipmentInfo.ItemEntry[i]);
|
||||
ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.ItemEntry[i]);
|
||||
|
||||
if (!item)
|
||||
if (!dbcItem)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Unknown item (ID={}) in creature_equip_template.ItemID{} for CreatureID = {} and ID = {}, forced to 0.",
|
||||
equipmentInfo.ItemEntry[i], i + 1, entry, id);
|
||||
@@ -1605,15 +1605,15 @@ void ObjectMgr::LoadEquipmentTemplates()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->InventoryType != INVTYPE_WEAPON &&
|
||||
item->InventoryType != INVTYPE_SHIELD &&
|
||||
item->InventoryType != INVTYPE_RANGED &&
|
||||
item->InventoryType != INVTYPE_2HWEAPON &&
|
||||
item->InventoryType != INVTYPE_WEAPONMAINHAND &&
|
||||
item->InventoryType != INVTYPE_WEAPONOFFHAND &&
|
||||
item->InventoryType != INVTYPE_HOLDABLE &&
|
||||
item->InventoryType != INVTYPE_THROWN &&
|
||||
item->InventoryType != INVTYPE_RANGEDRIGHT)
|
||||
if (dbcItem->InventoryType != INVTYPE_WEAPON &&
|
||||
dbcItem->InventoryType != INVTYPE_SHIELD &&
|
||||
dbcItem->InventoryType != INVTYPE_RANGED &&
|
||||
dbcItem->InventoryType != INVTYPE_2HWEAPON &&
|
||||
dbcItem->InventoryType != INVTYPE_WEAPONMAINHAND &&
|
||||
dbcItem->InventoryType != INVTYPE_WEAPONOFFHAND &&
|
||||
dbcItem->InventoryType != INVTYPE_HOLDABLE &&
|
||||
dbcItem->InventoryType != INVTYPE_THROWN &&
|
||||
dbcItem->InventoryType != INVTYPE_RANGEDRIGHT)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (ID={}) in creature_equip_template.ItemID{} for CreatureID = {} and ID = {} is not equipable in a hand, forced to 0.",
|
||||
equipmentInfo.ItemEntry[i], i + 1, entry, id);
|
||||
@@ -2741,8 +2741,10 @@ void ObjectMgr::LoadItemTemplates()
|
||||
return;
|
||||
}
|
||||
|
||||
_itemTemplateStore.rehash(result->GetRowCount());
|
||||
_itemTemplateStore.reserve(result->GetRowCount());
|
||||
uint32 count = 0;
|
||||
// original inspiration https://github.com/TrinityCore/TrinityCore/commit/0c44bd33ee7b42c924859139a9f4b04cf2b91261
|
||||
bool enforceDBCAttributes = sWorld->getBoolConfig(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES);
|
||||
|
||||
do
|
||||
{
|
||||
@@ -2859,17 +2861,55 @@ void ObjectMgr::LoadItemTemplates()
|
||||
itemTemplate.FlagsCu = fields[137].Get<uint32>();
|
||||
|
||||
// Checks
|
||||
if (itemTemplate.Class >= MAX_ITEM_CLASS)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Class value ({})", entry, itemTemplate.Class);
|
||||
itemTemplate.Class = ITEM_CLASS_MISC;
|
||||
}
|
||||
ItemEntry const* dbcitem = sItemStore.LookupEntry(entry);
|
||||
|
||||
if (itemTemplate.SubClass >= MaxItemSubclassValues[itemTemplate.Class])
|
||||
if (dbcitem)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Subclass value ({}) for class {}", entry, itemTemplate.SubClass, itemTemplate.Class);
|
||||
itemTemplate.SubClass = 0;// exist for all item classes
|
||||
if (itemTemplate.Class != dbcitem->ClassID)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Class value ({}), must be ({}).", entry, itemTemplate.Class, dbcitem->ClassID);
|
||||
if (enforceDBCAttributes)
|
||||
itemTemplate.Class = dbcitem->ClassID;
|
||||
}
|
||||
if (itemTemplate.SubClass != dbcitem->SubclassID)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Subclass value ({}) for class {}, must be ({}).", entry, itemTemplate.SubClass, itemTemplate.Class, dbcitem->SubclassID);
|
||||
if (enforceDBCAttributes)
|
||||
itemTemplate.SubClass = dbcitem->SubclassID;
|
||||
}
|
||||
if (itemTemplate.SoundOverrideSubclass != dbcitem->SoundOverrideSubclassID)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct SoundOverrideSubclass ({}), must be {}.", entry, itemTemplate.SoundOverrideSubclass);
|
||||
if (enforceDBCAttributes)
|
||||
itemTemplate.SoundOverrideSubclass = dbcitem->SoundOverrideSubclassID;
|
||||
}
|
||||
if (itemTemplate.Material != dbcitem->Material)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {%u}}) does not have a correct material ({}), must be {}.", entry, itemTemplate.Material, dbcitem->Material);
|
||||
if (enforceDBCAttributes)
|
||||
itemTemplate.Material = dbcitem->Material;
|
||||
}
|
||||
if (itemTemplate.InventoryType != dbcitem->InventoryType)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong InventoryType value ({}), must be {}.", entry, itemTemplate.InventoryType, dbcitem->InventoryType);
|
||||
if (enforceDBCAttributes)
|
||||
itemTemplate.InventoryType = dbcitem->InventoryType;
|
||||
}
|
||||
if (itemTemplate.DisplayInfoID != dbcitem->DisplayInfoID)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {%u}}) does not have a correct display id ({}), must be {}.", entry, itemTemplate.DisplayInfoID, dbcitem->DisplayInfoID);
|
||||
if (enforceDBCAttributes)
|
||||
itemTemplate.DisplayInfoID = dbcitem->DisplayInfoID;
|
||||
}
|
||||
if (itemTemplate.Sheath != dbcitem->SheatheType)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Sheath ({}), must be {}.", entry, itemTemplate.Sheath, dbcitem->SheatheType);
|
||||
if (enforceDBCAttributes)
|
||||
itemTemplate.Sheath = dbcitem->SheatheType;
|
||||
}
|
||||
}
|
||||
else
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) does not exist in item.dbc! (not correct id?).", entry);
|
||||
|
||||
if (itemTemplate.Quality >= MAX_ITEM_QUALITY)
|
||||
{
|
||||
@@ -2902,12 +2942,6 @@ void ObjectMgr::LoadItemTemplates()
|
||||
itemTemplate.BuyCount = 1;
|
||||
}
|
||||
|
||||
if (itemTemplate.InventoryType >= MAX_INVTYPE)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong InventoryType value ({})", entry, itemTemplate.InventoryType);
|
||||
itemTemplate.InventoryType = INVTYPE_NON_EQUIP;
|
||||
}
|
||||
|
||||
if (itemTemplate.RequiredSkill >= MAX_SKILL_TYPE)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong RequiredSkill value ({})", entry, itemTemplate.RequiredSkill);
|
||||
@@ -3118,12 +3152,6 @@ void ObjectMgr::LoadItemTemplates()
|
||||
if (itemTemplate.LockID && !sLockStore.LookupEntry(itemTemplate.LockID))
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong LockID ({})", entry, itemTemplate.LockID);
|
||||
|
||||
if (itemTemplate.Sheath >= MAX_SHEATHETYPE)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Sheath ({})", entry, itemTemplate.Sheath);
|
||||
itemTemplate.Sheath = SHEATHETYPE_NONE;
|
||||
}
|
||||
|
||||
if (itemTemplate.RandomProperty)
|
||||
{
|
||||
// To be implemented later
|
||||
@@ -9995,8 +10023,8 @@ void ObjectMgr::LoadGameObjectQuestItems()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1
|
||||
QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId FROM gameobject_questitem ORDER BY Idx ASC");
|
||||
// 0 1 2
|
||||
QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId, Idx FROM gameobject_questitem ORDER BY Idx ASC");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -10011,6 +10039,21 @@ void ObjectMgr::LoadGameObjectQuestItems()
|
||||
|
||||
uint32 entry = fields[0].Get<uint32>();
|
||||
uint32 item = fields[1].Get<uint32>();
|
||||
uint32 idx = fields[2].Get<uint32>();
|
||||
|
||||
GameObjectTemplate const* goInfo = GetGameObjectTemplate(entry);
|
||||
if (!goInfo)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject_questitem` has data for nonexistent gameobject (entry: {}, idx: {}), skipped", entry, idx);
|
||||
continue;
|
||||
};
|
||||
|
||||
ItemEntry const* dbcData = sItemStore.LookupEntry(item);
|
||||
if (!dbcData)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject_questitem` has nonexistent item (ID: {}) in gameobject (entry: {}, idx: {}), skipped", item, entry, idx);
|
||||
continue;
|
||||
};
|
||||
|
||||
_gameObjectQuestItemStore[entry].push_back(item);
|
||||
|
||||
@@ -10025,8 +10068,8 @@ void ObjectMgr::LoadCreatureQuestItems()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1
|
||||
QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, ItemId FROM creature_questitem ORDER BY Idx ASC");
|
||||
// 0 1 2
|
||||
QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -10041,6 +10084,21 @@ void ObjectMgr::LoadCreatureQuestItems()
|
||||
|
||||
uint32 entry = fields[0].Get<uint32>();
|
||||
uint32 item = fields[1].Get<uint32>();
|
||||
uint32 idx = fields[2].Get<uint32>();
|
||||
|
||||
CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry);
|
||||
if (!creatureInfo)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, idx: {}), skipped", entry, idx);
|
||||
continue;
|
||||
};
|
||||
|
||||
ItemEntry const* dbcData = sItemStore.LookupEntry(item);
|
||||
if (!dbcData)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, idx: {}), skipped", item, entry, idx);
|
||||
continue;
|
||||
};
|
||||
|
||||
_creatureQuestItemStore[entry].push_back(item);
|
||||
|
||||
|
||||
@@ -4928,11 +4928,11 @@ void Spell::WriteAmmoToPacket(WorldPacket* data)
|
||||
{
|
||||
if (uint32 item_id = m_caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i))
|
||||
{
|
||||
if (ItemTemplate const* itemEntry = sObjectMgr->GetItemTemplate(item_id))
|
||||
if (ItemEntry const* itemEntry = sItemStore.LookupEntry(item_id))
|
||||
{
|
||||
if (itemEntry->Class == ITEM_CLASS_WEAPON)
|
||||
if (itemEntry->ClassID == ITEM_CLASS_WEAPON)
|
||||
{
|
||||
switch (itemEntry->SubClass)
|
||||
switch (itemEntry->SubclassID)
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_THROWN:
|
||||
ammoDisplayID = itemEntry->DisplayInfoID;
|
||||
|
||||
@@ -138,6 +138,7 @@ enum WorldBoolConfigs
|
||||
CONFIG_AUTOBROADCAST,
|
||||
CONFIG_ALLOW_TICKETS,
|
||||
CONFIG_DELETE_CHARACTER_TICKET_TRACE,
|
||||
CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES,
|
||||
CONFIG_PRESERVE_CUSTOM_CHANNELS,
|
||||
CONFIG_PDUMP_NO_PATHS,
|
||||
CONFIG_PDUMP_NO_OVERWRITE,
|
||||
|
||||
@@ -1356,6 +1356,9 @@ void World::LoadConfigSettings(bool reload)
|
||||
// Dungeon finder
|
||||
_int_configs[CONFIG_LFG_OPTIONSMASK] = sConfigMgr->GetOption<int32>("DungeonFinder.OptionsMask", 5);
|
||||
|
||||
// DBC_ItemAttributes
|
||||
_bool_configs[CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES] = sConfigMgr->GetOption<bool>("DBC.EnforceItemAttributes", true);
|
||||
|
||||
// Max instances per hour
|
||||
_int_configs[CONFIG_MAX_INSTANCES_PER_HOUR] = sConfigMgr->GetOption<int32>("AccountInstancesPerHour", 5);
|
||||
|
||||
|
||||
@@ -1127,6 +1127,18 @@ struct HolidaysEntry
|
||||
//uint32 flags; // 54 m_flags (0 = Darkmoon Faire, Fishing Contest and Wotlk Launch, rest is 1)
|
||||
};
|
||||
|
||||
struct ItemEntry
|
||||
{
|
||||
uint32 ID; // 0
|
||||
uint32 ClassID; // 1
|
||||
uint32 SubclassID; // 2
|
||||
int32 SoundOverrideSubclassID; // 3
|
||||
int32 Material; // 4
|
||||
uint32 DisplayInfoID; // 5
|
||||
uint32 InventoryType; // 6
|
||||
uint32 SheatheType; // 7
|
||||
};
|
||||
|
||||
struct ItemBagFamilyEntry
|
||||
{
|
||||
uint32 ID; // 0
|
||||
|
||||
@@ -68,6 +68,7 @@ char constexpr GtOCTRegenHPfmt[] = "df";
|
||||
char constexpr GtRegenHPPerSptfmt[] = "df";
|
||||
char constexpr GtRegenMPPerSptfmt[] = "df";
|
||||
char constexpr Holidaysfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix";
|
||||
char constexpr Itemfmt[] = "niiiiiii";
|
||||
char constexpr ItemBagFamilyfmt[] = "nxxxxxxxxxxxxxxxxx";
|
||||
char constexpr ItemDisplayTemplateEntryfmt[] = "nxxxxsxxxxxxxxxxxxxxxxxxx";
|
||||
//char constexpr ItemCondExtCostsEntryfmt[] = "xiii";
|
||||
|
||||
Reference in New Issue
Block a user