From f62cf1b0e3e9b12cac4ec3ed4bf14600628afef1 Mon Sep 17 00:00:00 2001 From: Leonid Pospelov Date: Mon, 8 Apr 2024 22:41:36 +0500 Subject: [PATCH] feat(skymp5-server): add playable race filter for espm characters (#1805) --- .../cpp/server_guest_lib/WorldState.cpp | 33 +++++++++++++++++++ .../cpp/server_guest_lib/WorldState.h | 10 ++++++ unit/PartOne_ActivateTest.cpp | 2 ++ 3 files changed, 45 insertions(+) diff --git a/skymp5-server/cpp/server_guest_lib/WorldState.cpp b/skymp5-server/cpp/server_guest_lib/WorldState.cpp index fa25e7975f..88b6d9d1dd 100644 --- a/skymp5-server/cpp/server_guest_lib/WorldState.cpp +++ b/skymp5-server/cpp/server_guest_lib/WorldState.cpp @@ -1,5 +1,7 @@ #include "WorldState.h" +#include "EvaluateTemplate.h" #include "FormCallbacks.h" +#include "LeveledListUtils.h" #include "LocationalDataUtils.h" #include "MpActor.h" #include "MpChangeForms.h" @@ -409,6 +411,37 @@ bool WorldState::AttachEspmRecord(const espm::CombineBrowser& br, return false; } } + + const int kPcLevel = 0; // Shouldn't mean much to races + + // May be not exact the same template chain as it would be in MpActor + // but it's ok for this check + std::vector evaluateChainResult = + LeveledListUtils::EvaluateTemplateChain(br, base, kPcLevel); + std::vector templateChain(evaluateChainResult.size()); + std::transform(evaluateChainResult.begin(), evaluateChainResult.end(), + templateChain.begin(), [&](uint32_t formId) { + return FormDesc::FromFormId(formId, this->espmFiles); + }); + + uint32_t race = EvaluateTemplate( + this, baseId, templateChain, + [&](const auto& npcLookupResult, const auto& npcData) { + return npcLookupResult.ToGlobalId(npcData.race); + }); + + bool isBanned = std::find(bannedEspmCharacterRaceIds.begin(), + bannedEspmCharacterRaceIds.end(), + race) != bannedEspmCharacterRaceIds.end(); + if (isBanned) { + logger->info("Skipping actor {:#x} because it has banned race {:#x}", + record->GetId(), race); + if (optionalOutTrace) { + *optionalOutTrace << fmt::format("Skip NPC due to banned race") + << std::endl; + } + return false; + } } auto locationalData = data.loc; diff --git a/skymp5-server/cpp/server_guest_lib/WorldState.h b/skymp5-server/cpp/server_guest_lib/WorldState.h index 07e4100bc8..fc3903ea2e 100644 --- a/skymp5-server/cpp/server_guest_lib/WorldState.h +++ b/skymp5-server/cpp/server_guest_lib/WorldState.h @@ -235,6 +235,16 @@ class WorldState bool disableVanillaScriptsInExterior = true; + std::vector bannedEspmCharacterRaceIds = { + 0x000e7713, 0x00012e82, 0x001052a3, 0x00088884, 0x0008883a, 0x00088846, + 0x00108272, 0x000a82b9, 0x0008883c, 0x00088794, 0x00088845, 0x0008883d, + 0x00088844, 0x00088840, 0x000a82ba, + + /* Playable races from ArgonianRace to WoodElfRace */ + 0x00013740, 0x00013741, 0x00013742, 0x00013743, 0x00013744, 0x00013745, + 0x00013746, 0x00013747, 0x00013748, 0x00013749 + }; + private: bool AttachEspmRecord(const espm::CombineBrowser& br, const espm::RecordHeader* record, diff --git a/unit/PartOne_ActivateTest.cpp b/unit/PartOne_ActivateTest.cpp index 3829d05f90..ea15b59cef 100644 --- a/unit/PartOne_ActivateTest.cpp +++ b/unit/PartOne_ActivateTest.cpp @@ -26,6 +26,8 @@ PartOne& GetPartOne() std::make_shared(TEST_PEX_DIR)); instance->AttachEspm(&l); + instance->worldState.bannedEspmCharacterRaceIds.clear(); + static std::vector> g_partOneInstances; g_partOneInstances.push_back(instance); return *g_partOneInstances.back();