добавлены новые питомцы
This commit is contained in:
@@ -22,10 +22,15 @@ namespace moonwell::host
|
||||
constexpr std::string_view kOverridesPath = "WXLSpellOverrides.tsv";
|
||||
constexpr std::string_view kSpellPath = "DBFilesClient\\Spell.dbc";
|
||||
constexpr std::string_view kSkillPath = "DBFilesClient\\SkillLineAbility.dbc";
|
||||
constexpr std::string_view kSpellIconPath = "DBFilesClient\\SpellIcon.dbc";
|
||||
constexpr uint32_t kSpellFields = 234;
|
||||
constexpr uint32_t kSpellRecordSize = kSpellFields * 4;
|
||||
constexpr uint32_t kSkillFields = 14;
|
||||
constexpr uint32_t kSkillRecordSize = kSkillFields * 4;
|
||||
constexpr uint32_t kSpellIconFields = 2;
|
||||
constexpr uint32_t kSpellIconRecordSize = kSpellIconFields * 4;
|
||||
constexpr char8_t kDescriptionPrefixUtf8[] =
|
||||
u8"\u041F\u0440\u0438\u0437\u044B\u0432\u0430\u0435\u0442 \u0438\u043B\u0438 \u043E\u0442\u043F\u0443\u0441\u043A\u0430\u0435\u0442 \u0441\u043F\u0443\u0442\u043D\u0438\u043A\u0430: ";
|
||||
|
||||
struct Override
|
||||
{
|
||||
@@ -33,6 +38,9 @@ namespace moonwell::host
|
||||
uint32_t templateSpellId = 0;
|
||||
uint32_t creatureEntry = 0;
|
||||
uint32_t skillLineAbilityId = 0;
|
||||
uint32_t raceMask = 0;
|
||||
uint32_t spellIconId = 0;
|
||||
std::string iconPath;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
@@ -40,6 +48,7 @@ namespace moonwell::host
|
||||
bool g_ready = false;
|
||||
std::vector<uint8_t> g_spellDbc;
|
||||
std::vector<uint8_t> g_skillDbc;
|
||||
std::vector<uint8_t> g_spellIconDbc;
|
||||
|
||||
uint32_t ReadU32(const uint8_t* data)
|
||||
{
|
||||
@@ -93,26 +102,50 @@ namespace moonwell::host
|
||||
lineStart = lineEnd + 1;
|
||||
if (line.empty() || line.front() == '#') continue;
|
||||
|
||||
std::string_view fields[5];
|
||||
std::vector<std::string_view> fields;
|
||||
size_t fieldStart = 0;
|
||||
bool valid = true;
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
while (true)
|
||||
{
|
||||
const size_t tab = line.find('\t', fieldStart);
|
||||
if (tab == std::string_view::npos) { valid = false; break; }
|
||||
fields[i] = line.substr(fieldStart, tab - fieldStart);
|
||||
if (tab == std::string_view::npos)
|
||||
{
|
||||
fields.push_back(Trim(line.substr(fieldStart)));
|
||||
break;
|
||||
}
|
||||
fields.push_back(Trim(line.substr(fieldStart, tab - fieldStart)));
|
||||
fieldStart = tab + 1;
|
||||
}
|
||||
if (!valid) return false;
|
||||
fields[4] = Trim(line.substr(fieldStart));
|
||||
|
||||
Override item;
|
||||
if (!ParseU32(fields[0], item.spellId) ||
|
||||
!ParseU32(fields[1], item.templateSpellId) ||
|
||||
!ParseU32(fields[2], item.creatureEntry) ||
|
||||
!ParseU32(fields[3], item.skillLineAbilityId) || fields[4].empty())
|
||||
if (fields.size() == 5)
|
||||
{
|
||||
if (!ParseU32(fields[0], item.spellId) ||
|
||||
!ParseU32(fields[1], item.templateSpellId) ||
|
||||
!ParseU32(fields[2], item.creatureEntry) ||
|
||||
!ParseU32(fields[3], item.skillLineAbilityId) || fields[4].empty())
|
||||
return false;
|
||||
item.name.assign(fields[4]);
|
||||
}
|
||||
else if (fields.size() == 8)
|
||||
{
|
||||
if (!ParseU32(fields[0], item.spellId) ||
|
||||
!ParseU32(fields[1], item.templateSpellId) ||
|
||||
!ParseU32(fields[2], item.creatureEntry) ||
|
||||
!ParseU32(fields[3], item.skillLineAbilityId) ||
|
||||
!ParseU32(fields[4], item.raceMask) ||
|
||||
!ParseU32(fields[5], item.spellIconId) || fields[7].empty())
|
||||
return false;
|
||||
if (item.spellIconId != 0)
|
||||
{
|
||||
if (fields[6].empty() || fields[6] == "-") return false;
|
||||
item.iconPath.assign(fields[6]);
|
||||
}
|
||||
item.name.assign(fields[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
item.name.assign(fields[4]);
|
||||
}
|
||||
out.push_back(std::move(item));
|
||||
}
|
||||
return !out.empty();
|
||||
@@ -170,11 +203,13 @@ namespace moonwell::host
|
||||
uint8_t* row = records.data() + recordStart;
|
||||
WriteU32(row, item.spellId);
|
||||
WriteU32(row + 110 * 4, item.creatureEntry);
|
||||
if (item.spellIconId != 0) WriteU32(row + 133 * 4, item.spellIconId);
|
||||
|
||||
const uint32_t nameOffset = stringSize + static_cast<uint32_t>(strings.size());
|
||||
strings.insert(strings.end(), item.name.begin(), item.name.end());
|
||||
strings.push_back(0);
|
||||
const std::string description = "Призывает или отпускает спутника: " + item.name + ".";
|
||||
const std::string description =
|
||||
std::string(reinterpret_cast<const char*>(kDescriptionPrefixUtf8)) + item.name + ".";
|
||||
const uint32_t descriptionOffset = stringSize + static_cast<uint32_t>(strings.size());
|
||||
strings.insert(strings.end(), description.begin(), description.end());
|
||||
strings.push_back(0);
|
||||
@@ -213,6 +248,7 @@ namespace moonwell::host
|
||||
uint8_t* row = records.data() + recordStart;
|
||||
WriteU32(row, item.skillLineAbilityId);
|
||||
WriteU32(row + 8, item.spellId);
|
||||
WriteU32(row + 12, item.raceMask);
|
||||
}
|
||||
|
||||
out.reserve(base.size() + records.size());
|
||||
@@ -224,19 +260,63 @@ namespace moonwell::host
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildSpellIconDbc(const std::vector<uint8_t>& base, const std::vector<Override>& overrides,
|
||||
std::vector<uint8_t>& out)
|
||||
{
|
||||
uint32_t count = 0, stringSize = 0;
|
||||
size_t recordsEnd = 0;
|
||||
if (!ValidateWdbc(base, kSpellIconFields, kSpellIconRecordSize, count, stringSize, recordsEnd))
|
||||
return false;
|
||||
|
||||
std::vector<uint8_t> records;
|
||||
std::vector<uint8_t> strings;
|
||||
for (const Override& item : overrides)
|
||||
{
|
||||
if (item.spellIconId == 0) continue;
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
const uint8_t* row = base.data() + 20 + static_cast<size_t>(i) * kSpellIconRecordSize;
|
||||
if (ReadU32(row) == item.spellIconId) return false;
|
||||
}
|
||||
for (size_t i = 0; i < records.size(); i += kSpellIconRecordSize)
|
||||
if (ReadU32(records.data() + i) == item.spellIconId) return false;
|
||||
|
||||
const uint32_t pathOffset = stringSize + static_cast<uint32_t>(strings.size());
|
||||
const size_t recordStart = records.size();
|
||||
records.resize(recordStart + kSpellIconRecordSize);
|
||||
WriteU32(records.data() + recordStart, item.spellIconId);
|
||||
WriteU32(records.data() + recordStart + 4, pathOffset);
|
||||
strings.insert(strings.end(), item.iconPath.begin(), item.iconPath.end());
|
||||
strings.push_back(0);
|
||||
}
|
||||
|
||||
if (records.empty()) return true;
|
||||
out.reserve(base.size() + records.size() + strings.size());
|
||||
out.insert(out.end(), base.begin(), base.begin() + 20);
|
||||
WriteU32(out.data() + 4, count + static_cast<uint32_t>(records.size() / kSpellIconRecordSize));
|
||||
WriteU32(out.data() + 16, stringSize + static_cast<uint32_t>(strings.size()));
|
||||
out.insert(out.end(), base.begin() + 20, base.begin() + recordsEnd);
|
||||
out.insert(out.end(), records.begin(), records.end());
|
||||
out.insert(out.end(), base.begin() + recordsEnd, base.end());
|
||||
out.insert(out.end(), strings.begin(), strings.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Load()
|
||||
{
|
||||
const std::string root = wxl::host::ClientRoot();
|
||||
wxl::host::mpq::MpqStore store;
|
||||
std::vector<uint8_t> overrideBytes, spellBase, skillBase;
|
||||
std::vector<uint8_t> overrideBytes, spellBase, skillBase, spellIconBase;
|
||||
if (root.empty() || !store.Mount(root) || !store.ReadAll(kOverridesPath, overrideBytes))
|
||||
return;
|
||||
|
||||
std::vector<Override> overrides;
|
||||
if (!ParseOverrides(overrideBytes, overrides) ||
|
||||
!store.ReadAll(kSpellPath, spellBase) || !store.ReadAll(kSkillPath, skillBase) ||
|
||||
!store.ReadAll(kSpellIconPath, spellIconBase) ||
|
||||
!BuildSpellDbc(spellBase, overrides, g_spellDbc) ||
|
||||
!BuildSkillDbc(skillBase, overrides, g_skillDbc))
|
||||
!BuildSkillDbc(skillBase, overrides, g_skillDbc) ||
|
||||
!BuildSpellIconDbc(spellIconBase, overrides, g_spellIconDbc))
|
||||
{
|
||||
WLOG_ERROR("moonwell-spells: failed to build custom companion DBCs");
|
||||
return;
|
||||
@@ -247,10 +327,20 @@ namespace moonwell::host
|
||||
|
||||
bool Provide(std::string_view name, std::vector<uint8_t>& out)
|
||||
{
|
||||
if (!SamePath(name, kSpellPath) && !SamePath(name, kSkillPath)) return false;
|
||||
if (!SamePath(name, kSpellPath) && !SamePath(name, kSkillPath) &&
|
||||
!SamePath(name, kSpellIconPath))
|
||||
return false;
|
||||
std::call_once(g_loadOnce, &Load);
|
||||
if (!g_ready) return false;
|
||||
out = SamePath(name, kSpellPath) ? g_spellDbc : g_skillDbc;
|
||||
if (SamePath(name, kSpellPath))
|
||||
out = g_spellDbc;
|
||||
else if (SamePath(name, kSkillPath))
|
||||
out = g_skillDbc;
|
||||
else
|
||||
{
|
||||
if (g_spellIconDbc.empty()) return false;
|
||||
out = g_spellIconDbc;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user