Skip to content

Commit

Permalink
Merge branch 'main' into chat-http-api
Browse files Browse the repository at this point in the history
  • Loading branch information
aronwk-aaron committed Apr 25, 2024
2 parents 77143fc + 3801a97 commit faee5b7
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 98 deletions.
16 changes: 6 additions & 10 deletions dDatabase/CDClientDatabase/CDClientManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "CDScriptComponentTable.h"
#include "CDSkillBehaviorTable.h"
#include "CDZoneTableTable.h"
#include "CDTamingBuildPuzzleTable.h"
#include "CDVendorComponentTable.h"
#include "CDActivitiesTable.h"
#include "CDPackageComponentTable.h"
Expand All @@ -41,8 +42,6 @@
#include "CDRewardCodesTable.h"
#include "CDPetComponentTable.h"

#include <exception>

#ifndef CDCLIENT_CACHE_ALL
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
// A vanilla CDClient takes about 46MB of memory + the regular world data.
Expand All @@ -55,13 +54,6 @@
#define CDCLIENT_DONT_CACHE_TABLE(x)
#endif

class CDClientConnectionException : public std::exception {
public:
virtual const char* what() const throw() {
return "CDClientDatabase is not connected!";
}
};

// Using a macro to reduce repetitive code and issues from copy and paste.
// As a note, ## in a macro is used to concatenate two tokens together.

Expand Down Expand Up @@ -108,11 +100,14 @@ 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);

void CDClientManager::LoadValuesFromDatabase() {
if (!CDClientDatabase::isConnected) throw CDClientConnectionException();
if (!CDClientDatabase::isConnected) {
throw std::runtime_error{ "CDClientDatabase is not connected!" };
}

CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
CDActivitiesTable::Instance().LoadValuesFromDatabase();
Expand Down Expand Up @@ -152,6 +147,7 @@ void CDClientManager::LoadValuesFromDatabase() {
CDRewardsTable::Instance().LoadValuesFromDatabase();
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
CDZoneTableTable::Instance().LoadValuesFromDatabase();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void CDPetComponentTable::LoadValuesFromDatabase() {
}

void CDPetComponentTable::LoadValuesFromDefaults() {
GetEntriesMutable().insert(std::make_pair(defaultEntry.id, defaultEntry));
GetEntriesMutable().emplace(defaultEntry.id, defaultEntry);
}

CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
};
1 change: 1 addition & 0 deletions dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
"CDRewardsTable.cpp"
"CDScriptComponentTable.cpp"
"CDSkillBehaviorTable.cpp"
"CDTamingBuildPuzzleTable.cpp"
"CDVendorComponentTable.cpp"
"CDZoneTableTable.cpp" PARENT_SCOPE)
107 changes: 28 additions & 79 deletions dGame/dComponents/PetComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "GameMessages.h"
#include "BrickDatabase.h"
#include "CDClientDatabase.h"
#include "CDTamingBuildPuzzleTable.h"
#include "ChatPackets.h"
#include "EntityManager.h"
#include "Character.h"
Expand Down Expand Up @@ -32,15 +33,14 @@
#include "eMissionState.h"
#include "dNavMesh.h"

std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};

/**
* Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID
* while the faction ones could be checked using their respective missions.
*/
std::map<LOT, int32_t> PetComponent::petFlags = {
const std::map<LOT, int32_t> PetComponent::petFlags{
{ 3050, 801 }, // Elephant
{ 3054, 803 }, // Cat
{ 3195, 806 }, // Triceratops
Expand Down Expand Up @@ -87,7 +87,6 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Compone
m_StartPosition = NiPoint3Constant::ZERO;
m_MovementAI = nullptr;
m_TresureTime = 0;
m_Preconditions = nullptr;

std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition"));

Expand Down Expand Up @@ -152,96 +151,53 @@ void PetComponent::OnUse(Entity* originator) {
m_Tamer = LWOOBJID_EMPTY;
}

auto* inventoryComponent = originator->GetComponent<InventoryComponent>();

auto* const inventoryComponent = originator->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) {
return;
}

if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) {
if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) {
return;
}

auto* movementAIComponent = m_Parent->GetComponent<MovementAIComponent>();

auto* const movementAIComponent = m_Parent->GetComponent<MovementAIComponent>();
if (movementAIComponent != nullptr) {
movementAIComponent->Stop();
}

inventoryComponent->DespawnPet();

const auto& cached = buildCache.find(m_Parent->GetLOT());
int32_t imaginationCost = 0;

std::string buildFile;

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()) {
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"));

PetPuzzleData data;
data.buildFile = buildFile;
data.puzzleModelLot = result.getIntField("PuzzleModelLot");
data.timeLimit = result.getFloatField("Timelimit");
data.numValidPieces = result.getIntField("NumValidPieces");
data.imaginationCost = result.getIntField("imagCostPerBuild");
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 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;
}

auto* destroyableComponent = originator->GetComponent<DestroyableComponent>();

const auto* const destroyableComponent = originator->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr) {
return;
}

auto imagination = destroyableComponent->GetImagination();

if (imagination < imaginationCost) {
const auto imagination = destroyableComponent->GetImagination();
if (imagination < entry->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;
}

auto petPosition = m_Parent->GetPosition();
const auto petPosition = m_Parent->GetPosition();

auto originatorPosition = originator->GetPosition();
const auto originatorPosition = originator->GetPosition();

m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition));

float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance");

if (interactionDistance <= 0) {
interactionDistance = 15;
}
Expand Down Expand Up @@ -477,24 +433,23 @@ 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* destroyableComponent = tamer->GetComponent<DestroyableComponent>();

if (destroyableComponent == nullptr) return;

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 {
Expand All @@ -517,17 +472,14 @@ void PetComponent::NotifyTamingBuildSuccess(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();
Expand Down Expand Up @@ -731,13 +683,10 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
}

void PetComponent::StartTimer() {
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;

m_Timer = cached->second.timeLimit;
m_Timer = entry->timeLimit;
}

void PetComponent::ClientFailTamingMinigame() {
Expand Down Expand Up @@ -1086,6 +1035,6 @@ void PetComponent::LoadPetNameFromModeration() {
}
}

void PetComponent::SetPreconditions(std::string& preconditions) {
m_Preconditions = new PreconditionExpression(preconditions);
void PetComponent::SetPreconditions(const std::string& preconditions) {
m_Preconditions = std::make_optional<PreconditionExpression>(preconditions);
}
Loading

0 comments on commit faee5b7

Please sign in to comment.