mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-25 06:02:49 +00:00
refactor: modular build items and root part come from ModularBuildComponent
ModularBuildFinish hardcoded the item a finished build becomes (6416 for 3 parts, 8092 for 7) and the car chassis part (8129) that the every-part- swapped check skips. They now come from ModularBuildComponent: createdLOT, <numberOfParts> and the <ExamplePartLOT> of the <rootPart> module (new CDModularBuildComponentTable). Same results with the 1.10.64 cdclient; tests cover the xml parsing and the lookup. Refs #691 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@
|
|||||||
#include "CDRewardsTable.h"
|
#include "CDRewardsTable.h"
|
||||||
#include "CDPropertyEntranceComponentTable.h"
|
#include "CDPropertyEntranceComponentTable.h"
|
||||||
#include "CDPropertyTemplateTable.h"
|
#include "CDPropertyTemplateTable.h"
|
||||||
|
#include "CDModularBuildComponentTable.h"
|
||||||
#include "CDFeatureGatingTable.h"
|
#include "CDFeatureGatingTable.h"
|
||||||
#include "CDRailActivatorComponent.h"
|
#include "CDRailActivatorComponent.h"
|
||||||
#include "CDRewardCodesTable.h"
|
#include "CDRewardCodesTable.h"
|
||||||
@@ -93,6 +94,7 @@ DEFINE_TABLE_STORAGE(CDPetComponentTable);
|
|||||||
DEFINE_TABLE_STORAGE(CDProximityMonitorComponentTable);
|
DEFINE_TABLE_STORAGE(CDProximityMonitorComponentTable);
|
||||||
DEFINE_TABLE_STORAGE(CDPropertyEntranceComponentTable);
|
DEFINE_TABLE_STORAGE(CDPropertyEntranceComponentTable);
|
||||||
DEFINE_TABLE_STORAGE(CDPropertyTemplateTable);
|
DEFINE_TABLE_STORAGE(CDPropertyTemplateTable);
|
||||||
|
DEFINE_TABLE_STORAGE(CDModularBuildComponentTable);
|
||||||
DEFINE_TABLE_STORAGE(CDRailActivatorComponentTable);
|
DEFINE_TABLE_STORAGE(CDRailActivatorComponentTable);
|
||||||
DEFINE_TABLE_STORAGE(CDRarityTableTable);
|
DEFINE_TABLE_STORAGE(CDRarityTableTable);
|
||||||
DEFINE_TABLE_STORAGE(CDRebuildComponentTable);
|
DEFINE_TABLE_STORAGE(CDRebuildComponentTable);
|
||||||
@@ -139,6 +141,7 @@ void CDClientManager::LoadValuesFromDatabase() {
|
|||||||
CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase();
|
CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase();
|
||||||
CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase();
|
CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase();
|
||||||
CDPropertyTemplateTable::Instance().LoadValuesFromDatabase();
|
CDPropertyTemplateTable::Instance().LoadValuesFromDatabase();
|
||||||
|
CDModularBuildComponentTable::Instance().LoadValuesFromDatabase();
|
||||||
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
|
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
|
||||||
CDRarityTableTable::Instance().LoadValuesFromDatabase();
|
CDRarityTableTable::Instance().LoadValuesFromDatabase();
|
||||||
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
|
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "CDModularBuildComponentTable.h"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::optional<int32_t> ValueOf(const std::string& xml, const std::string& element) {
|
||||||
|
const std::regex pattern("<" + element + R"re(\s+value\s*=\s*"(-?\d+)")re");
|
||||||
|
std::smatch match;
|
||||||
|
if (!std::regex_search(xml, match, pattern)) return std::nullopt;
|
||||||
|
return std::stoi(match[1].str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDModularBuildComponentTable::ParseXml(const std::string_view xmlView, CDModularBuildComponent& build) {
|
||||||
|
const std::string xml(xmlView);
|
||||||
|
build.numberOfParts = static_cast<uint32_t>(ValueOf(xml, "numberOfParts").value_or(0));
|
||||||
|
|
||||||
|
const auto rootPart = ValueOf(xml, "rootPart");
|
||||||
|
if (!rootPart) return;
|
||||||
|
|
||||||
|
// Each <Module> has a PartCode and an ExamplePartLOT
|
||||||
|
const std::regex module(R"(<Module\b[\s\S]*?</Module>)");
|
||||||
|
for (auto it = std::sregex_iterator(xml.begin(), xml.end(), module); it != std::sregex_iterator(); ++it) {
|
||||||
|
const auto text = it->str();
|
||||||
|
if (ValueOf(text, "PartCode") == rootPart) {
|
||||||
|
build.rootPartExampleLOT = ValueOf(text, "ExamplePartLOT").value_or(LOT_NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDModularBuildComponentTable::LoadValuesFromDatabase() {
|
||||||
|
auto& entries = GetEntriesMutable();
|
||||||
|
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ModularBuildComponent;");
|
||||||
|
while (!tableData.eof()) {
|
||||||
|
CDModularBuildComponent entry;
|
||||||
|
entry.id = tableData.getIntField("id", 0);
|
||||||
|
entry.buildType = tableData.getIntField("buildType", 0);
|
||||||
|
entry.createdLOT = tableData.getIntField("createdLOT", LOT_NULL);
|
||||||
|
ParseXml(tableData.getStringField("xml", ""), entry);
|
||||||
|
entries.push_back(entry);
|
||||||
|
tableData.nextRow();
|
||||||
|
}
|
||||||
|
tableData.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CDModularBuildComponent> CDModularBuildComponentTable::FindByNumberOfParts(const std::vector<CDModularBuildComponent>& builds, const uint32_t numberOfParts) {
|
||||||
|
for (const auto& build : builds) {
|
||||||
|
if (build.numberOfParts == numberOfParts && build.createdLOT != LOT_NULL) return build;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CDModularBuildComponent> CDModularBuildComponentTable::GetByNumberOfParts(const uint32_t numberOfParts) const {
|
||||||
|
return FindByNumberOfParts(GetEntries(), numberOfParts);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "CDTable.h"
|
||||||
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
// A modular build (rocket, car): ModularBuildComponent with what DLU needs from its xml
|
||||||
|
struct CDModularBuildComponent {
|
||||||
|
uint32_t id{};
|
||||||
|
int32_t buildType{};
|
||||||
|
LOT createdLOT{ LOT_NULL }; // the item the finished build becomes
|
||||||
|
uint32_t numberOfParts{}; // <topology><numberOfParts value>
|
||||||
|
LOT rootPartExampleLOT{ LOT_NULL }; // <Module><ExamplePartLOT> of the module whose PartCode is <rootPart value>
|
||||||
|
};
|
||||||
|
|
||||||
|
class CDModularBuildComponentTable : public CDTable<CDModularBuildComponentTable, std::vector<CDModularBuildComponent>> {
|
||||||
|
public:
|
||||||
|
void LoadValuesFromDatabase();
|
||||||
|
|
||||||
|
// The first build made of this many parts
|
||||||
|
std::optional<CDModularBuildComponent> GetByNumberOfParts(uint32_t numberOfParts) const;
|
||||||
|
|
||||||
|
// Fills numberOfParts and rootPartExampleLOT from a ModularBuildComponent.xml
|
||||||
|
static void ParseXml(std::string_view xml, CDModularBuildComponent& build);
|
||||||
|
static std::optional<CDModularBuildComponent> FindByNumberOfParts(const std::vector<CDModularBuildComponent>& builds, uint32_t numberOfParts);
|
||||||
|
};
|
||||||
@@ -29,6 +29,7 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
|
|||||||
"CDPhysicsComponentTable.cpp"
|
"CDPhysicsComponentTable.cpp"
|
||||||
"CDPropertyEntranceComponentTable.cpp"
|
"CDPropertyEntranceComponentTable.cpp"
|
||||||
"CDPropertyTemplateTable.cpp"
|
"CDPropertyTemplateTable.cpp"
|
||||||
|
"CDModularBuildComponentTable.cpp"
|
||||||
"CDProximityMonitorComponentTable.cpp"
|
"CDProximityMonitorComponentTable.cpp"
|
||||||
"CDRailActivatorComponent.cpp"
|
"CDRailActivatorComponent.cpp"
|
||||||
"CDRarityTableTable.cpp"
|
"CDRarityTableTable.cpp"
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
#include "Sd0.h"
|
#include "Sd0.h"
|
||||||
#include "ScriptComponent.h"
|
#include "ScriptComponent.h"
|
||||||
#include "BrickByBrick.h"
|
#include "BrickByBrick.h"
|
||||||
|
#include "CDClientManager.h"
|
||||||
|
#include "CDModularBuildComponentTable.h"
|
||||||
#include "User.h"
|
#include "User.h"
|
||||||
#include "UserManager.h"
|
#include "UserManager.h"
|
||||||
|
|
||||||
@@ -290,6 +292,8 @@ namespace GameMessages {
|
|||||||
|
|
||||||
auto* temp = inv->GetInventory(TEMP_MODELS);
|
auto* temp = inv->GetInventory(TEMP_MODELS);
|
||||||
std::vector<LOT> modList;
|
std::vector<LOT> modList;
|
||||||
|
// The build this many parts make (3: a rocket, 7: a car), from ModularBuildComponent
|
||||||
|
const auto build = CDClientManager::GetTable<CDModularBuildComponentTable>()->GetByNumberOfParts(static_cast<uint32_t>(count));
|
||||||
auto& oldPartList = character->GetVar<std::string>(u"currentModifiedBuild");
|
auto& oldPartList = character->GetVar<std::string>(u"currentModifiedBuild");
|
||||||
bool everyPieceSwapped = !oldPartList.empty(); // If the player didn't put a build in initially, then they should not get this achievement.
|
bool everyPieceSwapped = !oldPartList.empty(); // If the player didn't put a build in initially, then they should not get this achievement.
|
||||||
if (count >= 3 && count < 8) {
|
if (count >= 3 && count < 8) {
|
||||||
@@ -316,8 +320,8 @@ namespace GameMessages {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Doing this check for 1 singular mission that needs to know when you've swapped every part out during a car modular build.
|
// Doing this check for 1 singular mission that needs to know when you've swapped every part out during a car modular build.
|
||||||
// since all 8129's are the same, skip checking that
|
// Every build has the same root part (the car's chassis), so it is not checked
|
||||||
if (mod != 8129) {
|
if (!build || mod != build->rootPartExampleLOT) {
|
||||||
if (oldPartList.find(GeneralUtils::UTF16ToWTF8(modToStr)) != std::string::npos) everyPieceSwapped = false;
|
if (oldPartList.find(GeneralUtils::UTF16ToWTF8(modToStr)) != std::string::npos) everyPieceSwapped = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -331,10 +335,8 @@ namespace GameMessages {
|
|||||||
|
|
||||||
LWOOBJID newID = ObjectIDManager::GetPersistentID();
|
LWOOBJID newID = ObjectIDManager::GetPersistentID();
|
||||||
|
|
||||||
if (count == 3) {
|
if (build) {
|
||||||
inv->AddItem(6416, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newID);
|
inv->AddItem(build->createdLOT, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newID);
|
||||||
} else if (count == 7) {
|
|
||||||
inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* pCharacter = character->GetCharacter();
|
auto* pCharacter = character->GetCharacter();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ set(DGAMETEST_SOURCES
|
|||||||
"ItemSetPassiveAbilityTests.cpp"
|
"ItemSetPassiveAbilityTests.cpp"
|
||||||
"PowerUpStatisticTests.cpp"
|
"PowerUpStatisticTests.cpp"
|
||||||
"BrickByBrickTests.cpp"
|
"BrickByBrickTests.cpp"
|
||||||
|
"ModularBuildTableTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(dComponentsTests)
|
add_subdirectory(dComponentsTests)
|
||||||
|
|||||||
59
tests/dGameTests/ModularBuildTableTests.cpp
Normal file
59
tests/dGameTests/ModularBuildTableTests.cpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#include "CDModularBuildComponentTable.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
// ModularBuildComponent's xml, laid out as the cdclient has it (a car: root part 0; a rocket: root part 2)
|
||||||
|
namespace {
|
||||||
|
constexpr std::string_view CAR_XML = R"(<ModularBuild name="Car">
|
||||||
|
<Assembly LOT="8092" PhysicsType="30" PhysicsID="13" />
|
||||||
|
<topology>
|
||||||
|
<numberOfParts value="7"/>
|
||||||
|
<rootPart value="0" />
|
||||||
|
<connection name="wheels" myPartid="0" myLocation="CHASSIS" connectingPart="1" optional="1" />
|
||||||
|
</topology>
|
||||||
|
<Module name="chassis" >
|
||||||
|
<PartCode value="0" />
|
||||||
|
<ExamplePartLOT value="8129" />
|
||||||
|
<InitialGhost templateID="7716" textureName="2"/>
|
||||||
|
</Module>
|
||||||
|
<Module name="wheels" >
|
||||||
|
<PartCode value="1" />
|
||||||
|
<ExamplePartLOT value="8130" />
|
||||||
|
</Module>
|
||||||
|
</ModularBuild>)";
|
||||||
|
|
||||||
|
constexpr std::string_view ROCKET_XML = "<ModularBuild>\r\n <Assembly LOT=\"6416\" PhysicsType=\"3\" PhysicsID=\"2692\" />\r\n"
|
||||||
|
" <topology>\r\n\t\t<numberOfParts value=\"3\" />\r\n\t\t<rootPart value=\"2\" />\r\n </topology>\r\n"
|
||||||
|
"\t\t<Module name=\"nose\">\r\n\t\t\t <PartCode value=\"0\" />\r\n\t\t\t <ExamplePartLOT value=\"4713\" />\r\n\t\t</Module>\r\n"
|
||||||
|
"\t\t<Module name=\"engine\">\r\n\t\t\t <PartCode value=\"2\" />\r\n\t\t\t <ExamplePartLOT value=\"4715\" />\r\n\t\t</Module>\r\n</ModularBuild>";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ModularBuildTableTests, ReadsPartsAndRootPartFromTheXml) {
|
||||||
|
CDModularBuildComponent car;
|
||||||
|
CDModularBuildComponentTable::ParseXml(CAR_XML, car);
|
||||||
|
EXPECT_EQ(car.numberOfParts, 7u);
|
||||||
|
EXPECT_EQ(car.rootPartExampleLOT, 8129);
|
||||||
|
|
||||||
|
CDModularBuildComponent rocket;
|
||||||
|
CDModularBuildComponentTable::ParseXml(ROCKET_XML, rocket);
|
||||||
|
EXPECT_EQ(rocket.numberOfParts, 3u);
|
||||||
|
EXPECT_EQ(rocket.rootPartExampleLOT, 4715);
|
||||||
|
|
||||||
|
CDModularBuildComponent empty;
|
||||||
|
CDModularBuildComponentTable::ParseXml("", empty);
|
||||||
|
EXPECT_EQ(empty.numberOfParts, 0u);
|
||||||
|
EXPECT_EQ(empty.rootPartExampleLOT, LOT_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ModularBuildTableTests, FindsTheBuildByItsNumberOfParts) {
|
||||||
|
const std::vector<CDModularBuildComponent> builds = {
|
||||||
|
{ .id = 5, .buildType = 6, .createdLOT = 8092, .numberOfParts = 7, .rootPartExampleLOT = 8129 },
|
||||||
|
{ .id = 6, .buildType = 3, .createdLOT = 6416, .numberOfParts = 3, .rootPartExampleLOT = 4715 },
|
||||||
|
{ .id = 3, .buildType = 3, .createdLOT = 6416, .numberOfParts = 3, .rootPartExampleLOT = 4715 },
|
||||||
|
};
|
||||||
|
EXPECT_EQ(CDModularBuildComponentTable::FindByNumberOfParts(builds, 7)->createdLOT, 8092);
|
||||||
|
EXPECT_EQ(CDModularBuildComponentTable::FindByNumberOfParts(builds, 3)->createdLOT, 6416);
|
||||||
|
for (const uint32_t parts : { 0u, 4u, 5u, 6u, 8u }) {
|
||||||
|
EXPECT_FALSE(CDModularBuildComponentTable::FindByNumberOfParts(builds, parts).has_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user