mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-01-24 13:47:00 +00:00
Merge remote-tracking branch 'upstream/main' into PetFixes
This commit is contained in:
commit
fdd981c582
@ -26,6 +26,7 @@
|
||||
#include "CDScriptComponentTable.h"
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
#include "CDTamingBuildPuzzleTable.h"
|
||||
#include "CDVendorComponentTable.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "CDPackageComponentTable.h"
|
||||
@ -101,6 +102,7 @@ DEFINE_TABLE_STORAGE(CDRewardCodesTable);
|
||||
DEFINE_TABLE_STORAGE(CDRewardsTable);
|
||||
DEFINE_TABLE_STORAGE(CDScriptComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
||||
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
|
||||
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDZoneTableTable);
|
||||
|
||||
@ -148,6 +150,7 @@ void CDClientManager::LoadValuesFromDatabase() {
|
||||
CDRewardsTable::Instance().LoadValuesFromDatabase();
|
||||
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
||||
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
|
||||
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDZoneTableTable::Instance().LoadValuesFromDatabase();
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
#include "CDTamingBuildPuzzleTable.h"
|
||||
|
||||
void CDTamingBuildPuzzleTable::LoadValuesFromDatabase() {
|
||||
// First, get the size of the table
|
||||
uint32_t size = 0;
|
||||
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM TamingBuildPuzzles");
|
||||
while (!tableSize.eof()) {
|
||||
size = tableSize.getIntField(0, 0);
|
||||
tableSize.nextRow();
|
||||
}
|
||||
|
||||
// Reserve the size
|
||||
auto& entries = GetEntriesMutable();
|
||||
entries.reserve(size);
|
||||
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM TamingBuildPuzzles");
|
||||
while (!tableData.eof()) {
|
||||
const auto lot = static_cast<LOT>(tableData.getIntField("NPCLot", LOT_NULL));
|
||||
entries.emplace(lot, CDTamingBuildPuzzle{
|
||||
.puzzleModelLot = lot,
|
||||
.validPieces{ tableData.getStringField("ValidPiecesLXF") },
|
||||
.timeLimit = static_cast<float>(tableData.getFloatField("Timelimit", 30.0f)),
|
||||
.numValidPieces = tableData.getIntField("NumValidPieces", 6),
|
||||
.imaginationCost = tableData.getIntField("imagCostPerBuild", 10)
|
||||
});
|
||||
tableData.nextRow();
|
||||
}
|
||||
}
|
||||
|
||||
const CDTamingBuildPuzzle* CDTamingBuildPuzzleTable::GetByLOT(const LOT lot) const {
|
||||
const auto& entries = GetEntries();
|
||||
const auto itr = entries.find(lot);
|
||||
return itr != entries.cend() ? &itr->second : nullptr;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "CDTable.h"
|
||||
|
||||
/**
|
||||
* Information for the minigame to be completed
|
||||
*/
|
||||
struct CDTamingBuildPuzzle {
|
||||
UNUSED_COLUMN(uint32_t id = 0;)
|
||||
|
||||
// The LOT of the object that is to be created
|
||||
LOT puzzleModelLot = LOT_NULL;
|
||||
|
||||
// The LOT of the NPC
|
||||
UNUSED_COLUMN(LOT npcLot = LOT_NULL;)
|
||||
|
||||
// The .lxfml file that contains the bricks required to build the model
|
||||
std::string validPieces{};
|
||||
|
||||
// The .lxfml file that contains the bricks NOT required to build the model
|
||||
UNUSED_COLUMN(std::string invalidPieces{};)
|
||||
|
||||
// Difficulty value
|
||||
UNUSED_COLUMN(int32_t difficulty = 1;)
|
||||
|
||||
// The time limit to complete the build
|
||||
float timeLimit = 30.0f;
|
||||
|
||||
// The number of pieces required to complete the minigame
|
||||
int32_t numValidPieces = 6;
|
||||
|
||||
// Number of valid pieces
|
||||
UNUSED_COLUMN(int32_t totalNumPieces = 16;)
|
||||
|
||||
// Model name
|
||||
UNUSED_COLUMN(std::string modelName{};)
|
||||
|
||||
// The .lxfml file that contains the full model
|
||||
UNUSED_COLUMN(std::string fullModel{};)
|
||||
|
||||
// The duration of the pet taming minigame
|
||||
UNUSED_COLUMN(float duration = 45.0f;)
|
||||
|
||||
// The imagination cost for the tamer to start the minigame
|
||||
int32_t imaginationCost = 10;
|
||||
};
|
||||
|
||||
class CDTamingBuildPuzzleTable : public CDTable<CDTamingBuildPuzzleTable, std::unordered_map<LOT, CDTamingBuildPuzzle>> {
|
||||
public:
|
||||
/**
|
||||
* Load values from the CD client database
|
||||
*/
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
/**
|
||||
* Gets the pet ability table corresponding to the pet LOT
|
||||
* @returns A pointer to the corresponding table, or nullptr if one cannot be found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const CDTamingBuildPuzzle* GetByLOT(const LOT lot) const;
|
||||
};
|
@ -37,5 +37,6 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
|
||||
"CDRewardsTable.cpp"
|
||||
"CDScriptComponentTable.cpp"
|
||||
"CDSkillBehaviorTable.cpp"
|
||||
"CDTamingBuildPuzzleTable.cpp"
|
||||
"CDVendorComponentTable.cpp"
|
||||
"CDZoneTableTable.cpp" PARENT_SCOPE)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "CDClientDatabase.h"
|
||||
#include "CDPetAbilitiesTable.h"
|
||||
#include "CDPetComponentTable.h"
|
||||
#include "CDTamingBuildPuzzleTable.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Character.h"
|
||||
@ -37,7 +38,6 @@
|
||||
#include "eMissionState.h"
|
||||
#include "dNavMesh.h"
|
||||
|
||||
std::unordered_map<LOT, PetComponent::PuzzleData> PetComponent::buildCache{};
|
||||
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
|
||||
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
|
||||
|
||||
@ -223,74 +223,37 @@ void PetComponent::StartTamingMinigame(Entity* originator) {
|
||||
|
||||
if (!inventoryComponent) return;
|
||||
|
||||
if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) return;
|
||||
if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const movementAIComponent = m_Parent->GetComponent<MovementAIComponent>();
|
||||
|
||||
if (movementAIComponent != nullptr) {
|
||||
if (movementAIComponent) {
|
||||
movementAIComponent->Stop();
|
||||
}
|
||||
|
||||
inventoryComponent->DespawnPet();
|
||||
|
||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
||||
int32_t imaginationCost = 0;
|
||||
|
||||
std::string buildFile;
|
||||
|
||||
// It may make sense to move this minigame-specific logic into another file
|
||||
if (cached == buildCache.end()) {
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;");
|
||||
query.bind(1, static_cast<int>(m_Parent->GetLOT()));
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof()) {
|
||||
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
|
||||
if (!entry) {
|
||||
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to find the puzzle minigame for this pet.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.fieldIsNull("ValidPiecesLXF")) {
|
||||
result.finalize();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
buildFile = std::string(result.getStringField("ValidPiecesLXF"));
|
||||
|
||||
PuzzleData data{
|
||||
.puzzleModelLot = result.getIntField("PuzzleModelLot"),
|
||||
.buildFile = buildFile,
|
||||
.timeLimit = static_cast<float>(result.getFloatField("Timelimit")),
|
||||
.imaginationCost = result.getIntField("imagCostPerBuild"),
|
||||
.numValidPieces = result.getIntField("NumValidPieces")
|
||||
};
|
||||
if (data.timeLimit <= 0) data.timeLimit = 60;
|
||||
imaginationCost = data.imaginationCost;
|
||||
|
||||
buildCache[m_Parent->GetLOT()] = data;
|
||||
|
||||
result.finalize();
|
||||
} else {
|
||||
buildFile = cached->second.buildFile;
|
||||
imaginationCost = cached->second.imaginationCost;
|
||||
}
|
||||
|
||||
const auto* const destroyableComponent = originator->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (!destroyableComponent) return;
|
||||
if (!destroyableComponent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto imagination = destroyableComponent->GetImagination();
|
||||
if (imagination < entry->imaginationCost) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (imagination < imaginationCost) return;
|
||||
|
||||
const auto& bricks = BrickDatabase::GetBricks(buildFile);
|
||||
|
||||
const auto& bricks = BrickDatabase::GetBricks(entry->validPieces);
|
||||
if (bricks.empty()) {
|
||||
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to load the puzzle minigame for this pet.");
|
||||
LOG("Couldn't find %s for minigame!", buildFile.c_str());
|
||||
LOG("Couldn't find %s for minigame!", entry->validPieces.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -302,7 +265,6 @@ void PetComponent::StartTamingMinigame(Entity* originator) {
|
||||
m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition));
|
||||
|
||||
float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance");
|
||||
|
||||
if (interactionDistance <= 0) {
|
||||
interactionDistance = 15;
|
||||
}
|
||||
@ -381,9 +343,8 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
||||
|
||||
if (cached == buildCache.end()) return;
|
||||
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
|
||||
if (!entry) return;
|
||||
|
||||
auto* const destroyableComponent = tamer->GetComponent<DestroyableComponent>();
|
||||
|
||||
@ -391,14 +352,14 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
|
||||
|
||||
auto imagination = destroyableComponent->GetImagination();
|
||||
|
||||
imagination -= cached->second.imaginationCost;
|
||||
imagination -= entry->imaginationCost;
|
||||
|
||||
destroyableComponent->SetImagination(imagination);
|
||||
|
||||
Game::entityManager->SerializeEntity(tamer);
|
||||
|
||||
if (clientFailed) {
|
||||
if (imagination < cached->second.imaginationCost) {
|
||||
if (imagination < entry->imaginationCost) {
|
||||
ClientFailTamingMinigame();
|
||||
}
|
||||
} else {
|
||||
@ -421,17 +382,14 @@ void PetComponent::NotifyTamingBuildSuccess(const NiPoint3 position) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
||||
|
||||
if (cached == buildCache.end()) {
|
||||
return;
|
||||
}
|
||||
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
|
||||
if (!entry) return;
|
||||
|
||||
GameMessages::SendPlayFXEffect(tamer, -1, u"petceleb", "", LWOOBJID_EMPTY, 1, 1, true);
|
||||
RenderComponent::PlayAnimation(tamer, u"rebuild-celebrate");
|
||||
|
||||
EntityInfo info{};
|
||||
info.lot = cached->second.puzzleModelLot;
|
||||
info.lot = entry->puzzleModelLot;
|
||||
info.pos = position;
|
||||
info.rot = NiQuaternionConstant::IDENTITY;
|
||||
info.spawnerID = tamer->GetObjectID();
|
||||
@ -627,13 +585,10 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
|
||||
}
|
||||
|
||||
void PetComponent::StartTimer() {
|
||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
||||
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
|
||||
if (!entry) return;
|
||||
|
||||
if (cached == buildCache.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Timer = cached->second.timeLimit;
|
||||
m_Timer = entry->timeLimit;
|
||||
}
|
||||
|
||||
void PetComponent::ClientFailTamingMinigame() {
|
||||
|
@ -537,7 +537,7 @@ private:
|
||||
/**
|
||||
* Preconditions that need to be met before an entity can tame this pet
|
||||
*/
|
||||
std::optional<PreconditionExpression> m_Preconditions;
|
||||
std::optional<PreconditionExpression> m_Preconditions{};
|
||||
|
||||
/**
|
||||
* Pet information loaded from the CDClientDatabase
|
||||
|
Loading…
Reference in New Issue
Block a user