Merge branch 'azerothcore:master' into Playerbot
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -108,6 +108,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);
|
||||
@@ -329,6 +330,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");
|
||||
@@ -650,9 +652,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
|
||||
@@ -875,9 +878,15 @@ CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType
|
||||
/// Returns LFGDungeonEntry for a specific map and difficulty. Will return first found entry if multiple dungeons use the same map (such as Scarlet Monastery)
|
||||
LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty)
|
||||
{
|
||||
for (LFGDungeonEntry const* dungeon : sLFGDungeonStore)
|
||||
if (dungeon->map == int32(mapId) && Difficulty(dungeon->difficulty) == difficulty)
|
||||
for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i)
|
||||
{
|
||||
LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i);
|
||||
if (!dungeon)
|
||||
continue;
|
||||
|
||||
if (dungeon->MapID == uint32(mapId) && Difficulty(dungeon->Difficulty) == difficulty)
|
||||
return dungeon;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -886,7 +895,7 @@ LFGDungeonEntry const* GetZoneLFGDungeonEntry(std::string const& zoneName, Local
|
||||
{
|
||||
for (LFGDungeonEntry const* dungeon : sLFGDungeonStore)
|
||||
{
|
||||
if (dungeon->type == lfg::LFG_TYPE_ZONE && zoneName.find(dungeon->name[locale]) != std::string::npos)
|
||||
if (dungeon->TypeID == lfg::LFG_TYPE_ZONE && zoneName.find(dungeon->Name[locale]) != std::string::npos)
|
||||
{
|
||||
return dungeon;
|
||||
}
|
||||
|
||||
@@ -130,6 +130,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;
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace lfg
|
||||
if (!dungeon)
|
||||
continue;
|
||||
|
||||
switch (dungeon->type)
|
||||
switch (dungeon->TypeID)
|
||||
{
|
||||
case LFG_TYPE_DUNGEON:
|
||||
case LFG_TYPE_HEROIC:
|
||||
|
||||
@@ -394,10 +394,10 @@ namespace lfg
|
||||
{
|
||||
LFGDungeonData(): name("")
|
||||
{ }
|
||||
LFGDungeonData(LFGDungeonEntry const* dbc): id(dbc->ID), name(dbc->name[0]), map(dbc->map),
|
||||
type(dbc->type), expansion(dbc->expansion), group(dbc->grouptype),
|
||||
minlevel(dbc->minlevel), maxlevel(dbc->maxlevel), difficulty(Difficulty(dbc->difficulty)),
|
||||
seasonal(dbc->flags & LFG_FLAG_SEASONAL), x(0.0f), y(0.0f), z(0.0f), o(0.0f)
|
||||
LFGDungeonData(LFGDungeonEntry const* dbc) : id(dbc->ID), name(dbc->Name[0]), map(dbc->MapID),
|
||||
type(dbc->TypeID), expansion(uint8(dbc->ExpansionLevel)), group(uint8(dbc->GroupID)),
|
||||
minlevel(uint8(dbc->MinLevel)), maxlevel(uint8(dbc->MaxLevel)), difficulty(Difficulty(dbc->Difficulty)),
|
||||
seasonal((dbc->Flags & LFG_FLAG_SEASONAL) != 0), x(0.0f), y(0.0f), z(0.0f), o(0.0f)
|
||||
{ }
|
||||
|
||||
uint32 id{0};
|
||||
|
||||
@@ -451,7 +451,7 @@ namespace lfg
|
||||
{
|
||||
if (Player* player = ObjectAccessor::FindConnectedPlayer(itRoles->first))
|
||||
{
|
||||
if (player->GetMapId() == static_cast<uint32>(dungeon->map))
|
||||
if (player->GetMapId() == static_cast<uint32>(dungeon->MapID))
|
||||
{
|
||||
if (InstanceScript* instance = player->GetInstanceScript())
|
||||
{
|
||||
|
||||
@@ -5879,7 +5879,7 @@ void Player::RewardReputation(Unit* victim, float rate)
|
||||
Map const* map = GetMap();
|
||||
if (map->IsNonRaidDungeon())
|
||||
if (LFGDungeonEntry const* dungeon = GetLFGDungeon(map->GetId(), map->GetDifficulty()))
|
||||
if (dungeon->reclevel == 80)
|
||||
if (dungeon->TargetLevel == 80)
|
||||
ChampioningFaction = GetChampioningFaction();
|
||||
}
|
||||
|
||||
|
||||
@@ -812,8 +812,14 @@ void Unit::DealDamageMods(Unit const* victim, uint32& damage, uint32* absorb)
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss, bool /*allowGM*/, Spell const* damageSpell /*= nullptr*/)
|
||||
uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss, bool /*allowGM*/, Spell const* damageSpell /*= nullptr*/, bool delayed)
|
||||
{
|
||||
if (delayed && attacker->GetTypeId() == TYPEID_PLAYER && attacker->GetGUID() != victim->GetGUID())
|
||||
{
|
||||
sWorld->AddDelayedDamage(attacker, victim, damage, cleanDamage, damagetype, damageSchoolMask, spellProto, durabilityLoss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Xinef: initialize damage done for rage calculations
|
||||
// Xinef: its rare to modify damage in hooks, however training dummy's sets damage to 0
|
||||
uint32 rage_damage = damage + ((cleanDamage != nullptr) ? cleanDamage->absorbed_damage : 0);
|
||||
@@ -1454,7 +1460,7 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss,
|
||||
|
||||
// Call default DealDamage
|
||||
CleanDamage cleanDamage(damageInfo->cleanDamage, damageInfo->absorb, BASE_ATTACK, MELEE_HIT_NORMAL);
|
||||
Unit::DealDamage(this, victim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss, false, spell);
|
||||
Unit::DealDamage(this, victim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss, false, spell, true);
|
||||
}
|
||||
|
||||
// @todo for melee need create structure as in
|
||||
@@ -20189,7 +20195,10 @@ void Unit::_ExitVehicle(Position const* exitPosition)
|
||||
}
|
||||
|
||||
if (player)
|
||||
{
|
||||
player->ResummonPetTemporaryUnSummonedIfAny();
|
||||
player->SetCanTeleport(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::BuildMovementPacket(ByteBuffer* data) const
|
||||
|
||||
@@ -757,6 +757,18 @@ struct CleanDamage
|
||||
struct CalcDamageInfo;
|
||||
struct SpellNonMeleeDamage;
|
||||
|
||||
struct DelayedDamage
|
||||
{
|
||||
Unit* attacker;
|
||||
Unit* victim;
|
||||
uint32 damage;
|
||||
CleanDamage const* cleanDamage;
|
||||
DamageEffectType damagetype;
|
||||
SpellSchoolMask damageSchoolMask;
|
||||
SpellInfo const* spellProto;
|
||||
bool durabilityLoss;
|
||||
};
|
||||
|
||||
class DamageInfo
|
||||
{
|
||||
private:
|
||||
@@ -1559,7 +1571,7 @@ public:
|
||||
|
||||
uint16 GetMaxSkillValueForLevel(Unit const* target = nullptr) const { return (target ? getLevelForTarget(target) : GetLevel()) * 5; }
|
||||
static void DealDamageMods(Unit const* victim, uint32& damage, uint32* absorb);
|
||||
static uint32 DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = nullptr, bool durabilityLoss = true, bool allowGM = false, Spell const* spell = nullptr);
|
||||
static uint32 DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = nullptr, bool durabilityLoss = true, bool allowGM = false, Spell const* spell = nullptr, bool delayed = false);
|
||||
static void Kill(Unit* killer, Unit* victim, bool durabilityLoss = true, WeaponAttackType attackType = BASE_ATTACK, SpellInfo const* spellProto = nullptr, Spell const* spell = nullptr);
|
||||
void KillSelf(bool durabilityLoss = true, WeaponAttackType attackType = BASE_ATTACK, SpellInfo const* spellProto = nullptr, Spell const* spell = nullptr) { Kill(this, this, durabilityLoss, attackType, spellProto, spell); };
|
||||
static int32 DealHeal(Unit* healer, Unit* victim, uint32 addhealth);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -2243,7 +2243,7 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr<Charact
|
||||
|
||||
// Get level from LFGDungeonEntry because the one from AreaTableEntry is not valid
|
||||
// If area level is too big, do not add new taxi
|
||||
if (lfgDungeon->minlevel > level)
|
||||
if (lfgDungeon->MinLevel > level)
|
||||
{
|
||||
FillTaxiMask(field, 0);
|
||||
continue;
|
||||
|
||||
@@ -505,16 +505,26 @@ void InstanceScript::DoRespawnGameObject(ObjectGuid uiGuid, uint32 uiTimeToDespa
|
||||
{
|
||||
if (GameObject* go = instance->GetGameObject(uiGuid))
|
||||
{
|
||||
//not expect any of these should ever be handled
|
||||
if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE || go->GetGoType() == GAMEOBJECT_TYPE_DOOR ||
|
||||
go->GetGoType() == GAMEOBJECT_TYPE_BUTTON || go->GetGoType() == GAMEOBJECT_TYPE_TRAP)
|
||||
return;
|
||||
switch (go->GetGoType())
|
||||
{
|
||||
case GAMEOBJECT_TYPE_DOOR:
|
||||
case GAMEOBJECT_TYPE_BUTTON:
|
||||
case GAMEOBJECT_TYPE_TRAP:
|
||||
case GAMEOBJECT_TYPE_FISHINGNODE:
|
||||
// not expect any of these should ever be handled
|
||||
LOG_ERROR("scripts", "InstanceScript: DoRespawnGameObject can't respawn gameobject entry {}, because type is {}.", go->GetEntry(), go->GetGoType());
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (go->isSpawned())
|
||||
return;
|
||||
|
||||
go->SetRespawnTime(uiTimeToDespawn);
|
||||
}
|
||||
else
|
||||
LOG_DEBUG("scripts", "InstanceScript: DoRespawnGameObject failed");
|
||||
}
|
||||
|
||||
void InstanceScript::DoRespawnCreature(ObjectGuid guid, bool force)
|
||||
|
||||
@@ -6387,7 +6387,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
|
||||
SpellPeriodicAuraLogInfo pInfo(this, damage, overkill, absorb, resist, 0.0f, crit);
|
||||
target->SendPeriodicAuraLog(&pInfo);
|
||||
|
||||
Unit::DealDamage(caster, target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true);
|
||||
Unit::DealDamage(caster, target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true, false);
|
||||
|
||||
Unit::ProcDamageAndSpell(caster, target, caster ? procAttacker : 0, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo(), nullptr, GetEffIndex(), nullptr, &dmgInfo);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "IWorld.h"
|
||||
|
||||
class Unit;
|
||||
class IWorld;
|
||||
|
||||
void IWorld::AddDelayedDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss)
|
||||
{
|
||||
DelayedDamage delayedDamage;
|
||||
delayedDamage.attacker = attacker;
|
||||
delayedDamage.victim = victim;
|
||||
delayedDamage.damage = damage;
|
||||
delayedDamage.cleanDamage = cleanDamage;
|
||||
delayedDamage.damagetype = damagetype;
|
||||
delayedDamage.damageSchoolMask = damageSchoolMask;
|
||||
delayedDamage.spellProto = spellProto;
|
||||
delayedDamage.durabilityLoss = durabilityLoss;
|
||||
_delayedDamages.push_back(delayedDamage);
|
||||
}
|
||||
@@ -24,15 +24,17 @@
|
||||
#include "ObjectGuid.h"
|
||||
#include "QueryResult.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Unit.h"
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
class IWorld;
|
||||
class Player;
|
||||
class WorldPacket;
|
||||
class WorldSession;
|
||||
class Player;
|
||||
|
||||
/// Storage class for commands issued for delayed execution
|
||||
struct AC_GAME_API CliCommandHolder
|
||||
@@ -138,6 +140,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,
|
||||
@@ -515,10 +518,13 @@ enum Rates
|
||||
class IWorld
|
||||
{
|
||||
public:
|
||||
std::list<DelayedDamage> _delayedDamages;
|
||||
|
||||
virtual ~IWorld() = default;
|
||||
[[nodiscard]] virtual WorldSession* FindSession(uint32 id) const = 0;
|
||||
[[nodiscard]] virtual WorldSession* FindOfflineSession(uint32 id) const = 0;
|
||||
[[nodiscard]] virtual WorldSession* FindOfflineSessionForCharacterGUID(ObjectGuid::LowType guidLow) const = 0;
|
||||
virtual void AddDelayedDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss);
|
||||
virtual void AddSession(WorldSession* s) = 0;
|
||||
virtual bool KickSession(uint32 id) = 0;
|
||||
virtual void UpdateMaxSessionCounters() = 0;
|
||||
|
||||
@@ -1357,6 +1357,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);
|
||||
|
||||
@@ -2062,6 +2065,8 @@ void World::SetInitialWorldSettings()
|
||||
|
||||
_mail_expire_check_timer = GameTime::GetGameTime() + 6h;
|
||||
|
||||
_timers[WUPDATE_DELAYED_DAMAGES].SetInterval(400);
|
||||
|
||||
///- Initialize MapMgr
|
||||
LOG_INFO("server.loading", "Starting Map System");
|
||||
LOG_INFO("server.loading", " ");
|
||||
@@ -2308,6 +2313,12 @@ void World::Update(uint32 diff)
|
||||
{
|
||||
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Check quest reset times"));
|
||||
|
||||
if (_timers[WUPDATE_DELAYED_DAMAGES].Passed())
|
||||
{
|
||||
_timers[WUPDATE_DELAYED_DAMAGES].Reset();
|
||||
ProcessDelayedDamages();
|
||||
}
|
||||
|
||||
/// Handle daily quests reset time
|
||||
if (currentGameTime > _nextDailyQuestReset)
|
||||
{
|
||||
@@ -3340,3 +3351,24 @@ CliCommandHolder::~CliCommandHolder()
|
||||
{
|
||||
free(m_command);
|
||||
}
|
||||
|
||||
void World::AddDelayedDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss)
|
||||
{
|
||||
DelayedDamage delayedDamage;
|
||||
delayedDamage.attacker = attacker;
|
||||
delayedDamage.victim = victim;
|
||||
delayedDamage.damage = damage;
|
||||
delayedDamage.cleanDamage = cleanDamage;
|
||||
delayedDamage.damagetype = damagetype;
|
||||
delayedDamage.damageSchoolMask = damageSchoolMask;
|
||||
delayedDamage.spellProto = spellProto;
|
||||
delayedDamage.durabilityLoss = durabilityLoss;
|
||||
_delayedDamages.push_back(delayedDamage);
|
||||
}
|
||||
|
||||
void World::ProcessDelayedDamages()
|
||||
{
|
||||
for (auto& damage : _delayedDamages)
|
||||
Unit::DealDamage(damage.attacker, damage.victim, damage.damage, damage.cleanDamage, damage.damagetype, damage.damageSchoolMask, damage.spellProto, damage.durabilityLoss);
|
||||
_delayedDamages.clear();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "QueryResult.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Timer.h"
|
||||
#include "Unit.h"
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <map>
|
||||
@@ -70,6 +71,7 @@ enum WorldTimers
|
||||
WUPDATE_PINGDB,
|
||||
WUPDATE_5_SECS,
|
||||
WUPDATE_WHO_LIST,
|
||||
WUPDATE_DELAYED_DAMAGES,
|
||||
WUPDATE_COUNT
|
||||
};
|
||||
|
||||
@@ -153,6 +155,8 @@ public:
|
||||
World();
|
||||
~World() override;
|
||||
|
||||
std::list<DelayedDamage> _delayedDamages;
|
||||
|
||||
static World* instance();
|
||||
|
||||
static uint32 m_worldLoopCounter;
|
||||
@@ -353,6 +357,10 @@ public:
|
||||
|
||||
void RemoveOldCorpses() override;
|
||||
|
||||
void AddDelayedDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss) override;
|
||||
|
||||
void ProcessDelayedDamages();
|
||||
|
||||
protected:
|
||||
void _UpdateGameTime();
|
||||
// callback for UpdateRealmCharacters
|
||||
|
||||
@@ -222,7 +222,7 @@ public:
|
||||
|
||||
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (_dying && who->GetGUID() != me->GetGUID())
|
||||
if (_dying && who && who->GetGUID() != me->GetGUID())
|
||||
damage = 0;
|
||||
|
||||
if (me->HealthBelowPctDamaged(0, damage) && instance->GetData(DATA_BUG_TRIO_DEATH) < 2 && !_dying)
|
||||
|
||||
@@ -61,6 +61,7 @@ enum Texts
|
||||
SAY_TIRION_INTRO_2 = 1,
|
||||
SAY_TIRION_OUTRO_1 = 2,
|
||||
SAY_TIRION_OUTRO_2 = 3,
|
||||
SAY_TIRION_OUTRO_3 = 4,
|
||||
|
||||
// Terenas Menethil (outro)
|
||||
SAY_TERENAS_OUTRO_1 = 0,
|
||||
@@ -343,6 +344,7 @@ enum MiscData
|
||||
{
|
||||
LIGHT_SNOWSTORM = 2490,
|
||||
LIGHT_SOULSTORM = 2508,
|
||||
EQUIP_ASHBRINGER = 13262,
|
||||
MUSIC_FROZEN_THRONE = 17457,
|
||||
MUSIC_SPECIAL = 17458, // Summon Shambling Horror, Remorseless Winter, Quake, Summon Val'kyr Periodic, Harvest Soul, Vile Spirits
|
||||
MUSIC_FURY_OF_FROSTMOURNE = 17459,
|
||||
@@ -833,8 +835,7 @@ public:
|
||||
_bFordringMustFallYell = true;
|
||||
if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HIGHLORD_TIRION_FORDRING)))
|
||||
{
|
||||
tirion->Yell("The Lich King must fall!", LANG_UNIVERSAL);
|
||||
tirion->PlayDirectSound(17389);
|
||||
tirion->AI()->Talk(SAY_TIRION_OUTRO_3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1354,7 +1355,7 @@ public:
|
||||
{
|
||||
// remove glow on ashbringer and tirion
|
||||
me->RemoveAllAuras();
|
||||
SetEquipmentSlots(true);
|
||||
SetEquipmentSlots(false, EQUIP_ASHBRINGER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1595,7 +1596,7 @@ public:
|
||||
lichKing->SetImmuneToNPC(false);
|
||||
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
|
||||
me->RemoveAllAuras();
|
||||
SetEquipmentSlots(true);
|
||||
SetEquipmentSlots(false, EQUIP_ASHBRINGER);
|
||||
me->Attack(lichKing, true);
|
||||
me->GetMotionMaster()->MovePoint(0, 512.16f, -2120.25f, 840.86f);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@ struct boss_murmur : public BossAI
|
||||
|
||||
void CastSupressionOOC()
|
||||
{
|
||||
me->m_Events.CancelEventGroup(GROUP_OOC_CAST);
|
||||
|
||||
me->m_Events.AddEventAtOffset([this] {
|
||||
if (me->FindNearestCreature(NPC_CABAL_SPELLBINDER, 35.0f))
|
||||
{
|
||||
@@ -125,8 +127,13 @@ struct boss_murmur : public BossAI
|
||||
}
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
if (!who->IsInCombatWith(me))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_JustEngagedWith();
|
||||
|
||||
scheduler.Schedule(28s, [this](TaskContext context)
|
||||
|
||||
@@ -102,7 +102,6 @@ public:
|
||||
{
|
||||
if (Creature* broggok = instance->GetCreature(DATA_BROGGOK))
|
||||
{
|
||||
instance->SetData(DATA_BROGGOK, IN_PROGRESS);
|
||||
broggok->AI()->DoAction(ACTION_PREPARE_BROGGOK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +30,15 @@ DoorData const doorData[] =
|
||||
ObjectData const gameobjectData[] =
|
||||
{
|
||||
{ GO_BROGGOK_DOOR_REAR, DATA_BROGGOK_REAR_DOOR },
|
||||
{ GO_BROGGOK_LEVER, DATA_BROGGOK_LEVER }
|
||||
{ GO_BROGGOK_LEVER, DATA_BROGGOK_LEVER },
|
||||
{ 0, 0, }
|
||||
};
|
||||
|
||||
ObjectData const creatureData[] =
|
||||
{
|
||||
{ NPC_BROGGOK, DATA_BROGGOK },
|
||||
{ NPC_KELIDAN, DATA_KELIDAN }
|
||||
{ NPC_KELIDAN, DATA_KELIDAN },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
class instance_blood_furnace : public InstanceMapScript
|
||||
|
||||
@@ -1163,6 +1163,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
|
||||
@@ -1258,25 +1270,28 @@ struct ItemSetEntry
|
||||
|
||||
struct LFGDungeonEntry
|
||||
{
|
||||
uint32 ID; // 0
|
||||
char const* name[16]; // 1-17 Name lang
|
||||
uint32 minlevel; // 18
|
||||
uint32 maxlevel; // 19
|
||||
uint32 reclevel; // 20
|
||||
uint32 recminlevel; // 21
|
||||
uint32 recmaxlevel; // 22
|
||||
int32 map; // 23
|
||||
uint32 difficulty; // 24
|
||||
uint32 flags; // 25
|
||||
uint32 type; // 26
|
||||
//uint32 unk; // 27
|
||||
//char const* iconname; // 28
|
||||
uint32 expansion; // 29
|
||||
//uint32 unk4; // 30
|
||||
uint32 grouptype; // 31
|
||||
//char const* desc[16]; // 32-47 Description
|
||||
uint32 ID; // 0
|
||||
char const* Name[16]; // 1-16
|
||||
//uint32 Name_lang_mask; // 17
|
||||
uint32 MinLevel; // 18
|
||||
uint32 MaxLevel; // 19
|
||||
uint32 TargetLevel; // 20
|
||||
uint32 TargetLevelMin; // 21
|
||||
uint32 TargetLevelMax; // 22
|
||||
uint32 MapID; // 23
|
||||
uint32 Difficulty; // 24
|
||||
uint32 Flags; // 25
|
||||
uint32 TypeID; // 26
|
||||
//int32 Faction; // 27
|
||||
//char const* TextureFilename; // 28
|
||||
uint32 ExpansionLevel; // 29
|
||||
//uint32 OrderIndex; // 30
|
||||
uint32 GroupID; // 31
|
||||
//char const* Description[16]; // 32-47
|
||||
//uint32 Description_lang_mask; // 48
|
||||
|
||||
// Helpers
|
||||
[[nodiscard]] uint32 Entry() const { return ID + (type << 24); }
|
||||
[[nodiscard]] uint32 Entry() const { return ID + (TypeID << 24); }
|
||||
};
|
||||
|
||||
struct LightEntry
|
||||
|
||||
@@ -70,6 +70,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