From b78732f30ca897aea3a5a6e00f1e94f33d133b67 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Mon, 2 Mar 2026 23:51:53 +0800 Subject: [PATCH] feat: add new rule in BMapInspector --- Ballance/BMapInspector/Rule.cpp | 1 + Ballance/BMapInspector/Rule/Shared.hpp | 5 ++ Ballance/BMapInspector/Rule/ZZQRules.cpp | 75 ++++++++++++++++++++++++ Ballance/BMapInspector/Rule/ZZQRules.hpp | 20 +++++++ 4 files changed, 101 insertions(+) diff --git a/Ballance/BMapInspector/Rule.cpp b/Ballance/BMapInspector/Rule.cpp index 5c900a7..f01990e 100644 --- a/Ballance/BMapInspector/Rule.cpp +++ b/Ballance/BMapInspector/Rule.cpp @@ -32,6 +32,7 @@ namespace BMapInspector::Rule { rules.emplace_back(new BBugRule1()); rules.emplace_back(new ZZQRule1()); rules.emplace_back(new ZZQRule2()); + rules.emplace_back(new ZZQRule3()); rules.emplace_back(new SOneRule1()); rules.emplace_back(new SSBRule1()); rules.emplace_back(new LXRule1()); diff --git a/Ballance/BMapInspector/Rule/Shared.hpp b/Ballance/BMapInspector/Rule/Shared.hpp index aef2fd8..2b69f15 100644 --- a/Ballance/BMapInspector/Rule/Shared.hpp +++ b/Ballance/BMapInspector/Rule/Shared.hpp @@ -16,6 +16,11 @@ namespace BMapInspector::Rule::Shared { namespace GroupNames { // clang-format off + constexpr char8_t PS_LEVELSTART[] = u8"PS_Levelstart"; + constexpr char8_t PE_LEVELENDE[] = u8"PE_Levelende"; + constexpr char8_t PC_CHECKPOINTS[] = u8"PC_Checkpoints"; + constexpr char8_t PR_RESETPOINTS[] = u8"PR_Resetpoints"; + constexpr char8_t PHYS_FLOORS[] = u8"Phys_Floors"; constexpr char8_t PHYS_FLOORRAILS[] = u8"Phys_FloorRails"; constexpr char8_t PHYS_FLOORSTOPPER[] = u8"Phys_FloorStopper"; diff --git a/Ballance/BMapInspector/Rule/ZZQRules.cpp b/Ballance/BMapInspector/Rule/ZZQRules.cpp index 947d225..9a59915 100644 --- a/Ballance/BMapInspector/Rule/ZZQRules.cpp +++ b/Ballance/BMapInspector/Rule/ZZQRules.cpp @@ -143,4 +143,79 @@ namespace BMapInspector::Rule { #pragma endregion +#pragma region ZZQ Rule 3 + + constexpr char8_t ZZQ3[] = u8"ZZQ3"; + + ZZQRule3::ZZQRule3() : IRule() {} + + ZZQRule3::~ZZQRule3() {} + + std::u8string_view ZZQRule3::GetRuleName() const { + return ZZQ3; + } + + void ZZQRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const { + auto* ctx = level.GetCKContext(); + Shared::SectorNameBuilder builder; + + auto* level_start = Shared::FetchGroup(ctx, Shared::GroupNames::PS_LEVELSTART); + if (level_start == nullptr) { + reporter.WriteError(ZZQ3, u8R"(Incomplete level: can not find "PS_Levelstart" group.)"); + } else { + switch (level_start->GetObjectCount()) { + case 0: + reporter.WriteError(ZZQ3, u8R"(Incomplete level: there is no object grouped into "PS_Levelstart" group.)"); + break; + case 1: + // OK. Do nothing. + break; + default: + reporter.WriteError(ZZQ3, u8R"(Bad level: there are more than one objects grouped into "PS_Levelstart" group.)"); + break; + } + } + + auto* level_end = Shared::FetchGroup(ctx, Shared::GroupNames::PE_LEVELENDE); + if (level_end == nullptr) { + reporter.WriteError(ZZQ3, u8R"(Incomplete level: can not find "PE_Levelende" group.)"); + } else { + switch (level_end->GetObjectCount()) { + case 0: + reporter.WriteError(ZZQ3, u8R"(Incomplete level: there is no object grouped into "PE_Levelende" group.)"); + break; + case 1: + // OK. Do nothing. + break; + default: + reporter.WriteError(ZZQ3, u8R"(Bad level: there are more than one objects grouped into "PE_Levelende" group.)"); + break; + } + } + + auto* check_points = Shared::FetchGroup(ctx, Shared::GroupNames::PC_CHECKPOINTS); + if (check_points == nullptr) { + reporter + .WriteWarning(ZZQ3, + u8R"(Can not find "PC_Checkpoints" group. This will cause bad render of particle at the level start point.)"); + } + + auto* reset_points = Shared::FetchGroup(ctx, Shared::GroupNames::PR_RESETPOINTS); + if (reset_points == nullptr) { + reporter.WriteError(ZZQ3, u8R"(Incomplete level: can not find "PR_Resetpoints" group.)"); + } else { + if (reset_points->GetObjectCount() == 0) { + reporter.WriteError(ZZQ3, u8R"(Incomplete level: there is no object grouped into "PC_Resetpoints" group.)"); + } + } + + auto sector1_name = builder.get_name(1); + auto* sector1 = Shared::FetchGroup(ctx, sector1_name.c_str()); + if (sector1 == nullptr) { + reporter.WriteError(ZZQ3, u8R"(Incomplete level: can not find "Sector_01" group.)"); + } + } + +#pragma endregion + } // namespace BMapInspector::Rule diff --git a/Ballance/BMapInspector/Rule/ZZQRules.hpp b/Ballance/BMapInspector/Rule/ZZQRules.hpp index 39f1384..2ece704 100644 --- a/Ballance/BMapInspector/Rule/ZZQRules.hpp +++ b/Ballance/BMapInspector/Rule/ZZQRules.hpp @@ -41,4 +41,24 @@ namespace BMapInspector::Rule { void Check(Reporter::Reporter& reporter, Map::Level& level) const override; }; + /** + * @brief ZZQ Rule 3 + * @details + * A minimalist level must contains following items: + * \li One start point. + * \li One end point (spaceship). + * \li One reset point. + * \li "Sector_01" group. + */ + class ZZQRule3 : public IRule { + public: + ZZQRule3(); + virtual ~ZZQRule3(); + YYCC_DELETE_COPY_MOVE(ZZQRule3) + + public: + std::u8string_view GetRuleName() const override; + void Check(Reporter::Reporter& reporter, Map::Level& level) const override; + }; + }