2026-02-04 22:51:13 +08:00
|
|
|
#include "LXRules.hpp"
|
2026-03-03 17:11:30 +08:00
|
|
|
#include "Shared/Utility.hpp"
|
|
|
|
|
#include "Shared/Name.hpp"
|
2026-02-04 22:51:13 +08:00
|
|
|
#include <set>
|
|
|
|
|
|
|
|
|
|
namespace L = LibCmo;
|
|
|
|
|
namespace C = LibCmo::CK2;
|
|
|
|
|
namespace O = LibCmo::CK2::ObjImpls;
|
|
|
|
|
|
2026-03-03 17:11:30 +08:00
|
|
|
namespace BMapInspector::Ruleset {
|
2026-02-05 14:38:25 +08:00
|
|
|
|
2026-02-04 22:51:13 +08:00
|
|
|
#pragma region LX Rule 1
|
|
|
|
|
|
2026-03-03 17:11:30 +08:00
|
|
|
LXRule1::LXRule1() : Rule::IRule() {}
|
2026-02-04 22:51:13 +08:00
|
|
|
|
|
|
|
|
LXRule1::~LXRule1() {}
|
|
|
|
|
|
|
|
|
|
std::u8string_view LXRule1::GetRuleName() const {
|
2026-03-03 17:11:30 +08:00
|
|
|
return u8"LX1";
|
2026-02-04 22:51:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LXRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
|
|
|
|
auto* ctx = level.GetCKContext();
|
|
|
|
|
|
|
|
|
|
// First we fetch all Ballance element and push them into set.
|
|
|
|
|
std::set<O::CK3dObject*> elements;
|
2026-03-03 17:11:30 +08:00
|
|
|
for (auto* group_name : Shared::Name::Group::ALL_PH) {
|
|
|
|
|
auto* group = Shared::Utility::FetchGroup(ctx, group_name);
|
2026-02-04 22:51:13 +08:00
|
|
|
if (group == nullptr) continue;
|
2026-03-03 17:11:30 +08:00
|
|
|
auto group_objects = Shared::Utility::Iter3dObjects(group);
|
2026-02-04 22:51:13 +08:00
|
|
|
for (auto* group_object : group_objects) {
|
|
|
|
|
elements.emplace(group_object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Then we analyse their mesh, material, texture and fill corresponding set.
|
|
|
|
|
std::set<O::CKMesh*> element_meshes;
|
|
|
|
|
std::set<O::CKMaterial*> element_materials;
|
|
|
|
|
std::set<O::CKTexture*> element_textures;
|
|
|
|
|
for (auto* element_object : elements) {
|
|
|
|
|
auto* mesh = element_object->GetCurrentMesh();
|
|
|
|
|
if (mesh == nullptr) continue;
|
|
|
|
|
|
|
|
|
|
// Add into mesh set
|
|
|
|
|
auto mesh_insert_rv = element_meshes.emplace(mesh);
|
|
|
|
|
|
|
|
|
|
// Only process it if we have inserted it
|
|
|
|
|
// because we do not want to duplicatedly process it.
|
|
|
|
|
if (mesh_insert_rv.second) {
|
|
|
|
|
// Iterate all meshes
|
2026-03-03 17:11:30 +08:00
|
|
|
auto mtls = Shared::Utility::IterMaterial(mesh);
|
2026-02-04 22:51:13 +08:00
|
|
|
for (auto* mtl : mtls) {
|
|
|
|
|
// Add into material set
|
|
|
|
|
auto mtl_insert_rv = element_materials.emplace(mtl);
|
|
|
|
|
|
|
|
|
|
// Also only process it if we have inserted it
|
|
|
|
|
if (mtl_insert_rv.second) {
|
|
|
|
|
// Fetch texture
|
|
|
|
|
auto texture = mtl->GetTexture();
|
|
|
|
|
if (texture == nullptr) continue;
|
|
|
|
|
// And insert it
|
|
|
|
|
element_textures.emplace(texture);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now, check any other object whether use these data.
|
|
|
|
|
for (auto* other_object : level.Get3dObjects()) {
|
|
|
|
|
// If it is element, skip it.
|
|
|
|
|
if (elements.contains(other_object)) continue;
|
|
|
|
|
|
|
|
|
|
// Get mesh
|
|
|
|
|
auto* mesh = other_object->GetCurrentMesh();
|
|
|
|
|
if (mesh == nullptr) continue;
|
|
|
|
|
// And check mesh
|
|
|
|
|
if (element_meshes.contains(mesh)) {
|
2026-03-03 17:11:30 +08:00
|
|
|
reporter.FormatError(u8"Object %s used mesh %s is already used by a Ballance element. "
|
|
|
|
|
u8"This will cause this object can not be rendered correctly in level.",
|
|
|
|
|
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
|
|
|
|
Shared::Utility::QuoteObjectName(mesh).c_str());
|
2026-02-04 22:51:13 +08:00
|
|
|
} else {
|
|
|
|
|
// If not, check material.
|
|
|
|
|
// Iterate all meshes
|
2026-03-03 17:11:30 +08:00
|
|
|
auto mtls = Shared::Utility::IterMaterial(mesh);
|
2026-02-04 22:51:13 +08:00
|
|
|
for (auto* mtl : mtls) {
|
|
|
|
|
if (element_materials.contains(mtl)) {
|
2026-03-03 17:11:30 +08:00
|
|
|
reporter.FormatError(u8"Object %s used material %s (referred by mesh %s) is already used by a Ballance element. "
|
|
|
|
|
u8"This will cause this object can not be rendered correctly in level.",
|
|
|
|
|
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
|
|
|
|
Shared::Utility::QuoteObjectName(mtl).c_str(),
|
|
|
|
|
Shared::Utility::QuoteObjectName(mesh).c_str());
|
2026-02-04 22:51:13 +08:00
|
|
|
} else {
|
|
|
|
|
// Still not, check texture.
|
|
|
|
|
// Fetch texture
|
|
|
|
|
auto texture = mtl->GetTexture();
|
|
|
|
|
if (texture == nullptr) continue;
|
|
|
|
|
// And check it
|
|
|
|
|
if (element_textures.contains(texture)) {
|
|
|
|
|
reporter.FormatError(
|
2026-03-03 17:11:30 +08:00
|
|
|
u8"Object %s used texture %s (referred by mesh %s and material %s) is already used by a Ballance element. "
|
|
|
|
|
u8"This will cause this object can not be rendered correctly in level.",
|
|
|
|
|
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
|
|
|
|
Shared::Utility::QuoteObjectName(texture).c_str(),
|
|
|
|
|
Shared::Utility::QuoteObjectName(mesh).c_str(),
|
|
|
|
|
Shared::Utility::QuoteObjectName(mtl).c_str());
|
2026-02-04 22:51:13 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
2026-03-03 17:11:30 +08:00
|
|
|
} // namespace BMapInspector::Ruleset
|