добавлены новые питомцы

This commit is contained in:
2026-08-04 20:11:52 +04:00
parent 76ca0cc851
commit c905d0558e
69 changed files with 1033 additions and 45 deletions
+107 -17
View File
@@ -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;
}