mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-17 12:04:27 -06:00
Compare commits
29 Commits
script-deb
...
rotation-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e201761cc | ||
|
|
a70c365c23 | ||
|
|
281d9762ef | ||
|
|
002aa896d8 | ||
|
|
f3a5f60d81 | ||
|
|
4c9c773ec5 | ||
|
|
ec6253c80c | ||
| c2dba31f70 | |||
|
|
74630b56c8 | ||
|
|
fd6029ae10 | ||
|
|
ff645a6662 | ||
|
|
e051229fb6 | ||
| ce28834dce | |||
|
|
cbdd5d9bc6 | ||
|
|
62ac65c520 | ||
|
|
5d5bce53d0 | ||
|
|
5791c55a9e | ||
|
|
17d0c45382 | ||
|
|
7dbbef81ac | ||
|
|
06958cb9cd | ||
|
|
69b1a694a6 | ||
|
|
b2609ff6cb | ||
|
|
e8c0b3e6da | ||
|
|
25418fd8b2 | ||
|
|
502c965d97 | ||
|
|
205c190c61 | ||
|
|
d6a21a005f | ||
|
|
fccf2fb0d3 | ||
|
|
4d043398ab |
29
.github/copilot-instructions.md
vendored
Normal file
29
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# GitHub Copilot Instructions
|
||||
|
||||
* c++20 standard, please use the latest features except NO modules.
|
||||
* use `.contains` for searching in associative containers
|
||||
* use const as much as possible. If it can be const, it should be made const
|
||||
* DO NOT USE const_cast EVER.
|
||||
* use `cstdint` bitwidth types ALWAYS for integral types.
|
||||
* NEVER use std::wstring. If wide strings are necessary, use std::u16string with conversion utilties in GeneralUtils.h.
|
||||
* Functions are ALWAYS PascalCase.
|
||||
* local variables are camelCase
|
||||
* NEVER use snake case
|
||||
* indentation is TABS, not SPACES.
|
||||
* TABS are 4 spaces by default
|
||||
* Use trailing braces ALWAYS
|
||||
* global variables are prefixed with `g_`
|
||||
* if global variables or functions are needed, they should be located in an anonymous namespace
|
||||
* Use `GeneralUtils::TryParse` for ANY parsing of strings to integrals.
|
||||
* Use brace initialization when possible.
|
||||
* ALWAYS default initialize variables.
|
||||
* Pointers should be avoided unless necessary. Use references when the pointer has been checked and should not be null
|
||||
* headers should be as compact as possible. Do NOT include extra data that isnt needed.
|
||||
* Remember to include logs (LOG macro uses printf style logging) while putting verbose logs under LOG_DEBUG.
|
||||
* NEVER USE `RakNet::BitStream::ReadBit`
|
||||
* NEVER assume pointers are good, always check if they are null. Once a pointer is checked and is known to be non-null, further accesses no longer need checking
|
||||
* Be wary of TOCTOU. Prevent all possible issues relating to TOCTOU.
|
||||
* new memory allocations should never be used unless absolutely necessary.
|
||||
* new for reconstruction of objects is allowed
|
||||
* Prefer following the format of the file over correct formatting. Consistency over correctness.
|
||||
* When using auto, ALWAYS put a * for pointers.
|
||||
@@ -374,6 +374,21 @@ public:
|
||||
return value->Insert<AmfType>("value", std::make_unique<AmfType>());
|
||||
}
|
||||
|
||||
AMFArrayValue& PushDebug(const NiPoint3& point) {
|
||||
PushDebug<AMFDoubleValue>("X") = point.x;
|
||||
PushDebug<AMFDoubleValue>("Y") = point.y;
|
||||
PushDebug<AMFDoubleValue>("Z") = point.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AMFArrayValue& PushDebug(const NiQuaternion& rot) {
|
||||
PushDebug<AMFDoubleValue>("W") = rot.w;
|
||||
PushDebug<AMFDoubleValue>("X") = rot.x;
|
||||
PushDebug<AMFDoubleValue>("Y") = rot.y;
|
||||
PushDebug<AMFDoubleValue>("Z") = rot.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* The associative portion. These values are key'd with strings to an AMFValue.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// C++
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "dPlatforms.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
|
||||
@@ -305,7 +306,7 @@ namespace GeneralUtils {
|
||||
template<typename Container>
|
||||
inline Container::value_type GetRandomElement(const Container& container) {
|
||||
DluAssert(!container.empty());
|
||||
return container[GenerateRandomNumber<typename Container::value_type>(0, container.size() - 1)];
|
||||
return container[GenerateRandomNumber<typename Container::size_type>(0, container.size() - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,13 +5,43 @@
|
||||
#include "TinyXmlUtils.h"
|
||||
|
||||
#include <ranges>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
// The base LXFML xml file to use when creating new models.
|
||||
std::string g_base = R"(<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<LXFML versionMajor="5" versionMinor="0">
|
||||
<Meta>
|
||||
<Application name="LEGO Universe" versionMajor="0" versionMinor="0"/>
|
||||
<Brand name="LEGOUniverse"/>
|
||||
<BrickSet version="457"/>
|
||||
</Meta>
|
||||
<Bricks>
|
||||
</Bricks>
|
||||
<RigidSystems>
|
||||
</RigidSystems>
|
||||
<GroupSystems>
|
||||
<GroupSystem>
|
||||
</GroupSystem>
|
||||
</GroupSystems>
|
||||
</LXFML>)";
|
||||
}
|
||||
|
||||
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) {
|
||||
Result toReturn;
|
||||
|
||||
// Handle empty or invalid input
|
||||
if (data.empty()) {
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
const auto err = doc.Parse(data.data());
|
||||
// Use length-based parsing to avoid expensive string copy
|
||||
const auto err = doc.Parse(data.data(), data.size());
|
||||
if (err != tinyxml2::XML_SUCCESS) {
|
||||
LOG("Failed to parse xml %s.", StringifiedEnum::ToString(err).data());
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@@ -20,7 +50,6 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
|
||||
|
||||
auto lxfml = reader["LXFML"];
|
||||
if (!lxfml) {
|
||||
LOG("Failed to find LXFML element.");
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@@ -49,16 +78,19 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
|
||||
// Calculate the lowest and highest points on the entire model
|
||||
for (const auto& transformation : transformations | std::views::values) {
|
||||
auto split = GeneralUtils::SplitString(transformation, ',');
|
||||
if (split.size() < 12) {
|
||||
LOG("Not enough in the split?");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto x = GeneralUtils::TryParse<float>(split[9]).value();
|
||||
auto y = GeneralUtils::TryParse<float>(split[10]).value();
|
||||
auto z = GeneralUtils::TryParse<float>(split[11]).value();
|
||||
if (x < lowest.x) lowest.x = x;
|
||||
if (y < lowest.y) lowest.y = y;
|
||||
if (split.size() < 12) continue;
|
||||
|
||||
auto xOpt = GeneralUtils::TryParse<float>(split[9]);
|
||||
auto yOpt = GeneralUtils::TryParse<float>(split[10]);
|
||||
auto zOpt = GeneralUtils::TryParse<float>(split[11]);
|
||||
|
||||
if (!xOpt.has_value() || !yOpt.has_value() || !zOpt.has_value()) continue;
|
||||
|
||||
auto x = xOpt.value();
|
||||
auto y = yOpt.value();
|
||||
auto z = zOpt.value();
|
||||
if (x < lowest.x) lowest.x = x;
|
||||
if (y < lowest.y) lowest.y = y;
|
||||
if (z < lowest.z) lowest.z = z;
|
||||
|
||||
if (highest.x < x) highest.x = x;
|
||||
@@ -87,13 +119,19 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
|
||||
for (auto& transformation : transformations | std::views::values) {
|
||||
auto split = GeneralUtils::SplitString(transformation, ',');
|
||||
if (split.size() < 12) {
|
||||
LOG("Not enough in the split?");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x + curPosition.x;
|
||||
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y + curPosition.y;
|
||||
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z + curPosition.z;
|
||||
auto xOpt = GeneralUtils::TryParse<float>(split[9]);
|
||||
auto yOpt = GeneralUtils::TryParse<float>(split[10]);
|
||||
auto zOpt = GeneralUtils::TryParse<float>(split[11]);
|
||||
|
||||
if (!xOpt.has_value() || !yOpt.has_value() || !zOpt.has_value()) {
|
||||
continue;
|
||||
}
|
||||
auto x = xOpt.value() - newRootPos.x + curPosition.x;
|
||||
auto y = yOpt.value() - newRootPos.y + curPosition.y;
|
||||
auto z = zOpt.value() - newRootPos.z + curPosition.z;
|
||||
std::stringstream stream;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
stream << split[i];
|
||||
@@ -128,3 +166,345 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
|
||||
toReturn.center = newRootPos;
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
// Deep-clone an XMLElement (attributes, text, and child elements) into a target document
|
||||
// with maximum depth protection to prevent infinite loops
|
||||
static tinyxml2::XMLElement* CloneElementDeep(const tinyxml2::XMLElement* src, tinyxml2::XMLDocument& dstDoc, int maxDepth = 100) {
|
||||
if (!src || maxDepth <= 0) return nullptr;
|
||||
auto* dst = dstDoc.NewElement(src->Name());
|
||||
|
||||
// copy attributes
|
||||
for (const tinyxml2::XMLAttribute* attr = src->FirstAttribute(); attr; attr = attr->Next()) {
|
||||
dst->SetAttribute(attr->Name(), attr->Value());
|
||||
}
|
||||
|
||||
// copy children (elements and text)
|
||||
for (const tinyxml2::XMLNode* child = src->FirstChild(); child; child = child->NextSibling()) {
|
||||
if (const tinyxml2::XMLElement* childElem = child->ToElement()) {
|
||||
// Recursively clone child elements with decremented depth
|
||||
auto* clonedChild = CloneElementDeep(childElem, dstDoc, maxDepth - 1);
|
||||
if (clonedChild) dst->InsertEndChild(clonedChild);
|
||||
} else if (const tinyxml2::XMLText* txt = child->ToText()) {
|
||||
auto* n = dstDoc.NewText(txt->Value());
|
||||
dst->InsertEndChild(n);
|
||||
} else if (const tinyxml2::XMLComment* c = child->ToComment()) {
|
||||
auto* n = dstDoc.NewComment(c->Value());
|
||||
dst->InsertEndChild(n);
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
std::vector<Lxfml::Result> Lxfml::Split(const std::string_view data, const NiPoint3& curPosition) {
|
||||
std::vector<Result> results;
|
||||
|
||||
// Handle empty or invalid input
|
||||
if (data.empty()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// Prevent processing extremely large inputs that could cause hangs
|
||||
if (data.size() > 10000000) { // 10MB limit
|
||||
return results;
|
||||
}
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
// Use length-based parsing to avoid expensive string copy
|
||||
const auto err = doc.Parse(data.data(), data.size());
|
||||
if (err != tinyxml2::XML_SUCCESS) {
|
||||
return results;
|
||||
}
|
||||
|
||||
auto* lxfml = doc.FirstChildElement("LXFML");
|
||||
if (!lxfml) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// Build maps: partRef -> Part element, partRef -> Brick element, boneRef -> partRef, brickRef -> Brick element
|
||||
std::unordered_map<std::string, tinyxml2::XMLElement*> partRefToPart;
|
||||
std::unordered_map<std::string, tinyxml2::XMLElement*> partRefToBrick;
|
||||
std::unordered_map<std::string, std::string> boneRefToPartRef;
|
||||
std::unordered_map<std::string, tinyxml2::XMLElement*> brickByRef;
|
||||
|
||||
auto* bricksParent = lxfml->FirstChildElement("Bricks");
|
||||
if (bricksParent) {
|
||||
for (auto* brick = bricksParent->FirstChildElement("Brick"); brick; brick = brick->NextSiblingElement("Brick")) {
|
||||
const char* brickRef = brick->Attribute("refID");
|
||||
if (brickRef) brickByRef.emplace(std::string(brickRef), brick);
|
||||
for (auto* part = brick->FirstChildElement("Part"); part; part = part->NextSiblingElement("Part")) {
|
||||
const char* partRef = part->Attribute("refID");
|
||||
if (partRef) {
|
||||
partRefToPart.emplace(std::string(partRef), part);
|
||||
partRefToBrick.emplace(std::string(partRef), brick);
|
||||
}
|
||||
auto* bone = part->FirstChildElement("Bone");
|
||||
if (bone) {
|
||||
const char* boneRef = bone->Attribute("refID");
|
||||
if (boneRef) boneRefToPartRef.emplace(std::string(boneRef), partRef ? std::string(partRef) : std::string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect RigidSystem elements
|
||||
std::vector<tinyxml2::XMLElement*> rigidSystems;
|
||||
auto* rigidSystemsParent = lxfml->FirstChildElement("RigidSystems");
|
||||
if (rigidSystemsParent) {
|
||||
for (auto* rs = rigidSystemsParent->FirstChildElement("RigidSystem"); rs; rs = rs->NextSiblingElement("RigidSystem")) {
|
||||
rigidSystems.push_back(rs);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect top-level groups (immediate children of GroupSystem)
|
||||
std::vector<tinyxml2::XMLElement*> groupRoots;
|
||||
auto* groupSystemsParent = lxfml->FirstChildElement("GroupSystems");
|
||||
if (groupSystemsParent) {
|
||||
for (auto* gs = groupSystemsParent->FirstChildElement("GroupSystem"); gs; gs = gs->NextSiblingElement("GroupSystem")) {
|
||||
for (auto* group = gs->FirstChildElement("Group"); group; group = group->NextSiblingElement("Group")) {
|
||||
groupRoots.push_back(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Track used bricks and rigidsystems
|
||||
std::unordered_set<std::string> usedBrickRefs;
|
||||
std::unordered_set<tinyxml2::XMLElement*> usedRigidSystems;
|
||||
|
||||
// Track used groups to avoid processing them twice
|
||||
std::unordered_set<tinyxml2::XMLElement*> usedGroups;
|
||||
|
||||
// Helper to create output document from sets of brick refs and rigidsystem pointers
|
||||
auto makeOutput = [&](const std::unordered_set<std::string>& bricksToInclude, const std::vector<tinyxml2::XMLElement*>& rigidSystemsToInclude, const std::vector<tinyxml2::XMLElement*>& groupsToInclude = {}) {
|
||||
tinyxml2::XMLDocument outDoc;
|
||||
outDoc.Parse(g_base.c_str());
|
||||
auto* outRoot = outDoc.FirstChildElement("LXFML");
|
||||
auto* outBricks = outRoot->FirstChildElement("Bricks");
|
||||
auto* outRigidSystems = outRoot->FirstChildElement("RigidSystems");
|
||||
auto* outGroupSystems = outRoot->FirstChildElement("GroupSystems");
|
||||
|
||||
// clone and insert bricks
|
||||
for (const auto& bref : bricksToInclude) {
|
||||
auto it = brickByRef.find(bref);
|
||||
if (it == brickByRef.end()) continue;
|
||||
tinyxml2::XMLElement* cloned = CloneElementDeep(it->second, outDoc);
|
||||
if (cloned) outBricks->InsertEndChild(cloned);
|
||||
}
|
||||
|
||||
// clone and insert rigidsystems
|
||||
for (auto* rsPtr : rigidSystemsToInclude) {
|
||||
tinyxml2::XMLElement* cloned = CloneElementDeep(rsPtr, outDoc);
|
||||
if (cloned) outRigidSystems->InsertEndChild(cloned);
|
||||
}
|
||||
|
||||
// clone and insert group(s) if requested
|
||||
if (outGroupSystems && !groupsToInclude.empty()) {
|
||||
// clear default children
|
||||
while (outGroupSystems->FirstChild()) outGroupSystems->DeleteChild(outGroupSystems->FirstChild());
|
||||
// create a GroupSystem element and append requested groups
|
||||
auto* newGS = outDoc.NewElement("GroupSystem");
|
||||
for (auto* gptr : groupsToInclude) {
|
||||
tinyxml2::XMLElement* clonedG = CloneElementDeep(gptr, outDoc);
|
||||
if (clonedG) newGS->InsertEndChild(clonedG);
|
||||
}
|
||||
outGroupSystems->InsertEndChild(newGS);
|
||||
}
|
||||
|
||||
// Print to string
|
||||
tinyxml2::XMLPrinter printer;
|
||||
outDoc.Print(&printer);
|
||||
// Normalize position and compute center using existing helper
|
||||
std::string xmlString = printer.CStr();
|
||||
if (xmlString.size() > 5000000) { // 5MB limit for normalization
|
||||
Result emptyResult;
|
||||
emptyResult.lxfml = xmlString;
|
||||
return emptyResult;
|
||||
}
|
||||
auto normalized = NormalizePosition(xmlString, curPosition);
|
||||
return normalized;
|
||||
};
|
||||
|
||||
// 1) Process groups (each top-level Group becomes one output; nested groups are included)
|
||||
for (auto* groupRoot : groupRoots) {
|
||||
// Skip if this group was already processed as part of another group
|
||||
if (usedGroups.find(groupRoot) != usedGroups.end()) continue;
|
||||
|
||||
// Helper to collect all partRefs in a group's subtree
|
||||
std::function<void(const tinyxml2::XMLElement*, std::unordered_set<std::string>&)> collectParts = [&](const tinyxml2::XMLElement* g, std::unordered_set<std::string>& partRefs) {
|
||||
if (!g) return;
|
||||
const char* partAttr = g->Attribute("partRefs");
|
||||
if (partAttr) {
|
||||
for (auto& tok : GeneralUtils::SplitString(partAttr, ',')) partRefs.insert(tok);
|
||||
}
|
||||
for (auto* child = g->FirstChildElement("Group"); child; child = child->NextSiblingElement("Group")) collectParts(child, partRefs);
|
||||
};
|
||||
|
||||
// Collect all groups that need to be merged into this output
|
||||
std::vector<tinyxml2::XMLElement*> groupsToInclude{ groupRoot };
|
||||
usedGroups.insert(groupRoot);
|
||||
|
||||
// Build initial sets of bricks and boneRefs from the starting group
|
||||
std::unordered_set<std::string> partRefs;
|
||||
collectParts(groupRoot, partRefs);
|
||||
|
||||
std::unordered_set<std::string> bricksIncluded;
|
||||
std::unordered_set<std::string> boneRefsIncluded;
|
||||
for (const auto& pref : partRefs) {
|
||||
auto pit = partRefToBrick.find(pref);
|
||||
if (pit != partRefToBrick.end()) {
|
||||
const char* bref = pit->second->Attribute("refID");
|
||||
if (bref) bricksIncluded.insert(std::string(bref));
|
||||
}
|
||||
auto partIt = partRefToPart.find(pref);
|
||||
if (partIt != partRefToPart.end()) {
|
||||
auto* bone = partIt->second->FirstChildElement("Bone");
|
||||
if (bone) {
|
||||
const char* bref = bone->Attribute("refID");
|
||||
if (bref) boneRefsIncluded.insert(std::string(bref));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iteratively include any RigidSystems that reference any boneRefsIncluded
|
||||
// and check if those rigid systems' bricks span other groups
|
||||
bool changed = true;
|
||||
std::vector<tinyxml2::XMLElement*> rigidSystemsToInclude;
|
||||
int maxIterations = 1000; // Safety limit to prevent infinite loops
|
||||
int iteration = 0;
|
||||
while (changed && iteration < maxIterations) {
|
||||
changed = false;
|
||||
iteration++;
|
||||
|
||||
// First, expand rigid systems based on current boneRefsIncluded
|
||||
for (auto* rs : rigidSystems) {
|
||||
if (usedRigidSystems.find(rs) != usedRigidSystems.end()) continue;
|
||||
// parse boneRefs of this rigid system (from its <Rigid> children)
|
||||
bool intersects = false;
|
||||
std::vector<std::string> rsBoneRefs;
|
||||
for (auto* rigid = rs->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) {
|
||||
const char* battr = rigid->Attribute("boneRefs");
|
||||
if (!battr) continue;
|
||||
for (auto& tok : GeneralUtils::SplitString(battr, ',')) {
|
||||
rsBoneRefs.push_back(tok);
|
||||
if (boneRefsIncluded.find(tok) != boneRefsIncluded.end()) intersects = true;
|
||||
}
|
||||
}
|
||||
if (!intersects) continue;
|
||||
// include this rigid system and all boneRefs it references
|
||||
usedRigidSystems.insert(rs);
|
||||
rigidSystemsToInclude.push_back(rs);
|
||||
for (const auto& br : rsBoneRefs) {
|
||||
boneRefsIncluded.insert(br);
|
||||
auto bpIt = boneRefToPartRef.find(br);
|
||||
if (bpIt != boneRefToPartRef.end()) {
|
||||
auto partRef = bpIt->second;
|
||||
auto pbIt = partRefToBrick.find(partRef);
|
||||
if (pbIt != partRefToBrick.end()) {
|
||||
const char* bref = pbIt->second->Attribute("refID");
|
||||
if (bref && bricksIncluded.insert(std::string(bref)).second) changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second, check if the newly included bricks span any other groups
|
||||
// If so, merge those groups into the current output
|
||||
for (auto* otherGroup : groupRoots) {
|
||||
if (usedGroups.find(otherGroup) != usedGroups.end()) continue;
|
||||
|
||||
// Collect partRefs from this other group
|
||||
std::unordered_set<std::string> otherPartRefs;
|
||||
collectParts(otherGroup, otherPartRefs);
|
||||
|
||||
// Check if any of these partRefs correspond to bricks we've already included
|
||||
bool spansOtherGroup = false;
|
||||
for (const auto& pref : otherPartRefs) {
|
||||
auto pit = partRefToBrick.find(pref);
|
||||
if (pit != partRefToBrick.end()) {
|
||||
const char* bref = pit->second->Attribute("refID");
|
||||
if (bref && bricksIncluded.find(std::string(bref)) != bricksIncluded.end()) {
|
||||
spansOtherGroup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spansOtherGroup) {
|
||||
// Merge this group into the current output
|
||||
usedGroups.insert(otherGroup);
|
||||
groupsToInclude.push_back(otherGroup);
|
||||
changed = true;
|
||||
|
||||
// Add all partRefs, boneRefs, and bricks from this group
|
||||
for (const auto& pref : otherPartRefs) {
|
||||
auto pit = partRefToBrick.find(pref);
|
||||
if (pit != partRefToBrick.end()) {
|
||||
const char* bref = pit->second->Attribute("refID");
|
||||
if (bref) bricksIncluded.insert(std::string(bref));
|
||||
}
|
||||
auto partIt = partRefToPart.find(pref);
|
||||
if (partIt != partRefToPart.end()) {
|
||||
auto* bone = partIt->second->FirstChildElement("Bone");
|
||||
if (bone) {
|
||||
const char* bref = bone->Attribute("refID");
|
||||
if (bref) boneRefsIncluded.insert(std::string(bref));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iteration >= maxIterations) {
|
||||
// Iteration limit reached, stop processing to prevent infinite loops
|
||||
// The file is likely malformed, so just skip further processing
|
||||
return results;
|
||||
}
|
||||
// include bricks from bricksIncluded into used set
|
||||
for (const auto& b : bricksIncluded) usedBrickRefs.insert(b);
|
||||
|
||||
// make output doc and push result (include all merged groups' XML)
|
||||
auto normalized = makeOutput(bricksIncluded, rigidSystemsToInclude, groupsToInclude);
|
||||
results.push_back(normalized);
|
||||
}
|
||||
|
||||
// 2) Process remaining RigidSystems (each becomes its own file)
|
||||
for (auto* rs : rigidSystems) {
|
||||
if (usedRigidSystems.find(rs) != usedRigidSystems.end()) continue;
|
||||
std::unordered_set<std::string> bricksIncluded;
|
||||
// collect boneRefs referenced by this rigid system
|
||||
for (auto* rigid = rs->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) {
|
||||
const char* battr = rigid->Attribute("boneRefs");
|
||||
if (!battr) continue;
|
||||
for (auto& tok : GeneralUtils::SplitString(battr, ',')) {
|
||||
auto bpIt = boneRefToPartRef.find(tok);
|
||||
if (bpIt != boneRefToPartRef.end()) {
|
||||
auto partRef = bpIt->second;
|
||||
auto pbIt = partRefToBrick.find(partRef);
|
||||
if (pbIt != partRefToBrick.end()) {
|
||||
const char* bref = pbIt->second->Attribute("refID");
|
||||
if (bref) bricksIncluded.insert(std::string(bref));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// mark used
|
||||
for (const auto& b : bricksIncluded) usedBrickRefs.insert(b);
|
||||
usedRigidSystems.insert(rs);
|
||||
|
||||
std::vector<tinyxml2::XMLElement*> rsVec{ rs };
|
||||
auto normalized = makeOutput(bricksIncluded, rsVec);
|
||||
results.push_back(normalized);
|
||||
}
|
||||
|
||||
// 3) Any remaining bricks not included become their own files
|
||||
for (const auto& [bref, brickPtr] : brickByRef) {
|
||||
if (usedBrickRefs.find(bref) != usedBrickRefs.end()) continue;
|
||||
std::unordered_set<std::string> bricksIncluded{ bref };
|
||||
auto normalized = makeOutput(bricksIncluded, {});
|
||||
results.push_back(normalized);
|
||||
usedBrickRefs.insert(bref);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "NiPoint3.h"
|
||||
|
||||
@@ -18,6 +19,7 @@ namespace Lxfml {
|
||||
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
|
||||
// Returns a struct of its new center and the updated LXFML containing these edits.
|
||||
[[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
|
||||
[[nodiscard]] std::vector<Result> Split(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
|
||||
|
||||
// these are only for the migrations due to a bug in one of the implementations.
|
||||
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);
|
||||
|
||||
@@ -11,6 +11,65 @@ Vector3 QuatUtils::Euler(const NiQuaternion& quat) {
|
||||
return glm::eulerAngles(quat);
|
||||
}
|
||||
|
||||
NiQuaternion NiQuaternion::operator*(const float scalar) const noexcept {
|
||||
return NiQuaternion(this->w * scalar, this->x * scalar, this->y * scalar, this->z * scalar);
|
||||
}
|
||||
|
||||
NiQuaternion& NiQuaternion::operator*=(const NiQuaternion& q) {
|
||||
auto& [ow, ox, oy, oz] = q;
|
||||
auto [cw, cx, cy, cz] = *this; // Current rotation copied because otherwise it screws up the math
|
||||
this->w = cw * ow - cx * ox - cy * oy - cz * oz;
|
||||
this->x = cw * ox + cx * ow + cy * oz - cz * oy;
|
||||
this->y = cw * oy + cy * ow + cz * ox - cx * oz;
|
||||
this->z = cw * oz + cz * ow + cx * oy - cy * ox;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NiQuaternion NiQuaternion::operator* (const NiQuaternion& q) const {
|
||||
auto& [ow, ox, oy, oz] = q;
|
||||
return NiQuaternion
|
||||
(
|
||||
/* w */w * ow - x * ox - y * oy - z * oz,
|
||||
/* x */w * ox + x * ow + y * oz - z * oy,
|
||||
/* y */w * oy + y * ow + z * ox - x * oz,
|
||||
/* z */w * oz + z * ow + x * oy - y * ox
|
||||
);
|
||||
}
|
||||
|
||||
NiQuaternion NiQuaternion::operator/(const float& q) const noexcept {
|
||||
return NiQuaternion(this->w / q, this->x / q, this->y / q, this->z / q);
|
||||
}
|
||||
|
||||
void NiQuaternion::Normalize() {
|
||||
float length = Dot(*this);
|
||||
float invLength = 1.0f / std::sqrt(length);
|
||||
*this = *this * invLength;
|
||||
}
|
||||
|
||||
float NiQuaternion::Dot(const NiQuaternion& q) const noexcept {
|
||||
return (this->w * q.w) + (this->x * q.x) + (this->y * q.y) + (this->z * q.z);
|
||||
}
|
||||
|
||||
void NiQuaternion::Inverse() noexcept {
|
||||
NiQuaternion copy = *this;
|
||||
copy.Conjugate();
|
||||
|
||||
const float inv = 1.0f / Dot(*this);
|
||||
*this = copy / inv;
|
||||
}
|
||||
|
||||
void NiQuaternion::Conjugate() noexcept {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
NiQuaternion NiQuaternion::Diff(const NiQuaternion& q) const noexcept {
|
||||
NiQuaternion inv = *this;
|
||||
inv.Inverse();
|
||||
return inv * q;
|
||||
}
|
||||
|
||||
// MARK: Helper Functions
|
||||
|
||||
//! Look from a specific point in space to another point in space (Y-locked)
|
||||
|
||||
@@ -81,6 +81,9 @@ public:
|
||||
[[nodiscard]]
|
||||
AssetStream GetFile(const char* name) const;
|
||||
|
||||
[[nodiscard]]
|
||||
AssetStream GetFile(const std::string& name) const { return GetFile(name.c_str()); };
|
||||
|
||||
private:
|
||||
void LoadPackIndex();
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ constexpr LWOINSTANCEID LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
|
||||
constexpr LWOMAPID LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
|
||||
constexpr uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
|
||||
|
||||
constexpr float PI = 3.14159f;
|
||||
constexpr float PI = 3.14159265358979323846264338327950288f;
|
||||
|
||||
//============ STRUCTS ==============
|
||||
|
||||
|
||||
@@ -50,7 +50,10 @@ enum class eMissionState : int {
|
||||
/**
|
||||
* The mission has been completed before and has now been completed again. Used for daily missions.
|
||||
*/
|
||||
COMPLETE_READY_TO_COMPLETE = 12
|
||||
COMPLETE_READY_TO_COMPLETE = 12,
|
||||
|
||||
// The mission is failed (don't know where this is used)
|
||||
FAILED = 16,
|
||||
};
|
||||
|
||||
#endif //!__MISSIONSTATE__H__
|
||||
|
||||
23
dCommon/dMath.h
Normal file
23
dCommon/dMath.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Darkflame Universe
|
||||
// Copyright 2025
|
||||
|
||||
#ifndef DMATH_H
|
||||
#define DMATH_H
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Math {
|
||||
constexpr float PI = 3.14159265358979323846264338327950288f;
|
||||
constexpr float RATIO_DEG_TO_RAD = PI / 180.0f;
|
||||
constexpr float RATIO_RAD_TO_DEG = 180.0f / PI;
|
||||
|
||||
inline float DegToRad(float degrees) {
|
||||
return degrees * RATIO_DEG_TO_RAD;
|
||||
}
|
||||
|
||||
inline float RadToDeg(float radians) {
|
||||
return radians * RATIO_RAD_TO_DEG;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //!DMATH_H
|
||||
@@ -682,6 +682,9 @@ private:
|
||||
* NOTE: quick as there's no DB lookups
|
||||
*/
|
||||
void DoQuickXMLDataParse();
|
||||
public:
|
||||
const decltype(m_PlayerFlags)& GetPlayerFlags() const { return m_PlayerFlags; }
|
||||
const decltype(m_SessionFlags)& GetSessionFlags() const { return m_SessionFlags; }
|
||||
};
|
||||
|
||||
#endif // CHARACTER_H
|
||||
|
||||
322
dGame/Entity.cpp
322
dGame/Entity.cpp
@@ -84,6 +84,8 @@
|
||||
#include "GhostComponent.h"
|
||||
#include "AchievementVendorComponent.h"
|
||||
#include "VanityUtilities.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "ePlayerFlag.h"
|
||||
|
||||
// Table includes
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
@@ -192,14 +194,17 @@ Entity::~Entity() {
|
||||
}
|
||||
|
||||
void Entity::Initialize() {
|
||||
RegisterMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, this, &Entity::MsgRequestServerObjectInfo);
|
||||
RegisterMsg<GameMessages::RequestServerObjectInfo>(this, &Entity::MsgRequestServerObjectInfo);
|
||||
RegisterMsg<GameMessages::DropClientLoot>(this, &Entity::MsgDropClientLoot);
|
||||
RegisterMsg<GameMessages::GetFactionTokenType>(this, &Entity::MsgGetFactionTokenType);
|
||||
RegisterMsg<GameMessages::PickupItem>(this, &Entity::MsgPickupItem);
|
||||
/**
|
||||
* Setup trigger
|
||||
*/
|
||||
|
||||
const auto triggerInfo = GetVarAsString(u"trigger_id");
|
||||
|
||||
if (!triggerInfo.empty()) AddComponent<TriggerComponent>(triggerInfo);
|
||||
if (!triggerInfo.empty()) AddComponent<TriggerComponent>(-1, triggerInfo);
|
||||
|
||||
/**
|
||||
* Setup groups
|
||||
@@ -234,11 +239,11 @@ void Entity::Initialize() {
|
||||
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
AddComponent<ModelComponent>(-1)->LoadBehaviors();
|
||||
|
||||
AddComponent<RenderComponent>();
|
||||
AddComponent<RenderComponent>(-1);
|
||||
|
||||
auto* destroyableComponent = AddComponent<DestroyableComponent>();
|
||||
auto* destroyableComponent = AddComponent<DestroyableComponent>(-1);
|
||||
destroyableComponent->SetHealth(1);
|
||||
destroyableComponent->SetMaxHealth(1.0f);
|
||||
destroyableComponent->SetFaction(-1, true);
|
||||
@@ -254,37 +259,42 @@ void Entity::Initialize() {
|
||||
*/
|
||||
|
||||
if (m_Character && m_Character->GetParentUser()) {
|
||||
AddComponent<MissionComponent>()->LoadFromXml(m_Character->GetXMLDoc());
|
||||
AddComponent<MissionComponent>(-1)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
}
|
||||
|
||||
const uint32_t petComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PET);
|
||||
if (petComponentId > 0) {
|
||||
AddComponent<PetComponent>(petComponentId);
|
||||
const auto petComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PET);
|
||||
if (petComponentID > 0) {
|
||||
AddComponent<PetComponent>(petComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL) > 0) {
|
||||
AddComponent<MiniGameControlComponent>();
|
||||
const auto minigameControlID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL);
|
||||
if (minigameControlID > 0) {
|
||||
AddComponent<MiniGameControlComponent>(minigameControlID);
|
||||
}
|
||||
|
||||
const uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE);
|
||||
if (possessableComponentId > 0) {
|
||||
AddComponent<PossessableComponent>(possessableComponentId);
|
||||
const auto possessableComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE);
|
||||
if (possessableComponentID > 0) {
|
||||
AddComponent<PossessableComponent>(possessableComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODULE_ASSEMBLY) > 0) {
|
||||
AddComponent<ModuleAssemblyComponent>();
|
||||
const auto moduleAssemblyID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODULE_ASSEMBLY);
|
||||
if (moduleAssemblyID > 0) {
|
||||
AddComponent<ModuleAssemblyComponent>(moduleAssemblyID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_STATS) > 0) {
|
||||
AddComponent<RacingStatsComponent>();
|
||||
const auto racingStatsID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_STATS);
|
||||
if (racingStatsID > 0) {
|
||||
AddComponent<RacingStatsComponent>(racingStatsID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::LUP_EXHIBIT, -1) >= 0) {
|
||||
AddComponent<LUPExhibitComponent>();
|
||||
const auto lupExhibitID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::LUP_EXHIBIT, -1);
|
||||
if (lupExhibitID >= 0) {
|
||||
AddComponent<LUPExhibitComponent>(lupExhibitID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_CONTROL) > 0) {
|
||||
AddComponent<RacingControlComponent>();
|
||||
const auto racingControlID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_CONTROL);
|
||||
if (racingControlID > 0) {
|
||||
AddComponent<RacingControlComponent>(racingControlID);
|
||||
}
|
||||
|
||||
const auto propertyEntranceComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_ENTRANCE);
|
||||
@@ -292,7 +302,7 @@ void Entity::Initialize() {
|
||||
AddComponent<PropertyEntranceComponent>(propertyEntranceComponentID);
|
||||
}
|
||||
|
||||
const int32_t controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
const auto controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
if (controllablePhysicsComponentID > 0) {
|
||||
auto* controllablePhysics = AddComponent<ControllablePhysicsComponent>(controllablePhysicsComponentID);
|
||||
|
||||
@@ -337,46 +347,48 @@ void Entity::Initialize() {
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
}
|
||||
|
||||
const int32_t rigidBodyPhantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS);
|
||||
const auto rigidBodyPhantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS);
|
||||
if (rigidBodyPhantomPhysicsComponentID > 0) {
|
||||
AddComponent<RigidbodyPhantomPhysicsComponent>(rigidBodyPhantomPhysicsComponentID);
|
||||
}
|
||||
|
||||
const int32_t phantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS);
|
||||
const auto phantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS);
|
||||
if (markedAsPhantom || phantomPhysicsComponentID > 0) {
|
||||
AddComponent<PhantomPhysicsComponent>(phantomPhysicsComponentID)->SetPhysicsEffectActive(false);
|
||||
}
|
||||
|
||||
const int32_t havokVehiclePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS);
|
||||
const auto havokVehiclePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS);
|
||||
if (havokVehiclePhysicsComponentID > 0) {
|
||||
auto* havokVehiclePhysicsComponent = AddComponent<HavokVehiclePhysicsComponent>(havokVehiclePhysicsComponentID);
|
||||
havokVehiclePhysicsComponent->SetPosition(m_DefaultPosition);
|
||||
havokVehiclePhysicsComponent->SetRotation(m_DefaultRotation);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) {
|
||||
AddComponent<SoundTriggerComponent>();
|
||||
} else if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_SOUND_TRIGGER, -1) != -1) {
|
||||
AddComponent<RacingSoundTriggerComponent>();
|
||||
const auto soundTriggerID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1);
|
||||
const auto racingSoundTriggerID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_SOUND_TRIGGER, -1);
|
||||
if (soundTriggerID > -1) {
|
||||
AddComponent<SoundTriggerComponent>(soundTriggerID);
|
||||
} else if (racingSoundTriggerID > -1) {
|
||||
AddComponent<RacingSoundTriggerComponent>(racingSoundTriggerID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF) > 0) {
|
||||
AddComponent<BuffComponent>();
|
||||
const auto buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
|
||||
if (buffComponentID > 0) {
|
||||
AddComponent<BuffComponent>(buffComponentID);
|
||||
}
|
||||
|
||||
const int collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE);
|
||||
const auto collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE);
|
||||
|
||||
if (collectibleComponentID > 0) {
|
||||
AddComponent<CollectibleComponent>(GetVarAs<int32_t>(u"collectible_id"));
|
||||
AddComponent<CollectibleComponent>(collectibleComponentID, GetVarAs<int32_t>(u"collectible_id"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple components require the destructible component.
|
||||
*/
|
||||
const int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
|
||||
const int quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
|
||||
const auto quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
|
||||
|
||||
int componentID = -1;
|
||||
int32_t componentID = -1;
|
||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
||||
if (quickBuildComponentID > 0) componentID = quickBuildComponentID;
|
||||
if (buffComponentID > 0) componentID = buffComponentID;
|
||||
@@ -384,7 +396,7 @@ void Entity::Initialize() {
|
||||
|
||||
bool isSmashable = GetVarAs<int32_t>(u"is_smashable") != 0;
|
||||
if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) {
|
||||
DestroyableComponent* comp = AddComponent<DestroyableComponent>();
|
||||
DestroyableComponent* comp = AddComponent<DestroyableComponent>(componentID);
|
||||
auto* const destCompTable = CDClientManager::GetTable<CDDestructibleComponentTable>();
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([componentID](const CDDestructibleComponent& entry) { return (entry.id == componentID); });
|
||||
|
||||
@@ -412,6 +424,7 @@ void Entity::Initialize() {
|
||||
comp->SetIsSmashable(destCompData[0].isSmashable);
|
||||
|
||||
comp->SetLootMatrixID(destCompData[0].LootMatrixIndex);
|
||||
comp->SetCurrencyIndex(destCompData[0].CurrencyIndex);
|
||||
Loot::CacheMatrix(destCompData[0].LootMatrixIndex);
|
||||
|
||||
// Now get currency information
|
||||
@@ -473,27 +486,30 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER) > 0 || m_Character) {
|
||||
const auto characterID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER);
|
||||
if (characterID > 0 || m_Character) {
|
||||
// Character Component always has a possessor, level, and forced movement components
|
||||
AddComponent<PossessorComponent>();
|
||||
AddComponent<PossessorComponent>(characterID);
|
||||
|
||||
// load in the xml for the level
|
||||
AddComponent<LevelProgressionComponent>()->LoadFromXml(m_Character->GetXMLDoc());
|
||||
AddComponent<LevelProgressionComponent>(characterID)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
|
||||
AddComponent<PlayerForcedMovementComponent>();
|
||||
AddComponent<PlayerForcedMovementComponent>(characterID);
|
||||
|
||||
auto& systemAddress = m_Character->GetParentUser() ? m_Character->GetParentUser()->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS;
|
||||
AddComponent<CharacterComponent>(m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
AddComponent<CharacterComponent>(characterID, m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
|
||||
AddComponent<GhostComponent>();
|
||||
AddComponent<GhostComponent>(characterID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) {
|
||||
AddComponent<InventoryComponent>();
|
||||
const auto inventoryID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY);
|
||||
if (inventoryID > 0 || m_Character) {
|
||||
AddComponent<InventoryComponent>(inventoryID);
|
||||
}
|
||||
// if this component exists, then we initialize it. it's value is always 0
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
|
||||
AddComponent<MultiZoneEntranceComponent>();
|
||||
const auto multiZoneEntranceID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1);
|
||||
if (multiZoneEntranceID > -1) {
|
||||
AddComponent<MultiZoneEntranceComponent>(multiZoneEntranceID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -545,7 +561,7 @@ void Entity::Initialize() {
|
||||
}
|
||||
|
||||
if (!scriptName.empty() || client || m_Character || scriptComponentID >= 0) {
|
||||
AddComponent<ScriptComponent>(scriptName, true, client && scriptName.empty());
|
||||
AddComponent<ScriptComponent>(scriptComponentID, scriptName, true, client && scriptName.empty());
|
||||
}
|
||||
|
||||
// ZoneControl script
|
||||
@@ -554,26 +570,27 @@ void Entity::Initialize() {
|
||||
const CDZoneTable* const zoneData = CDZoneTableTable::Query(zoneID.GetMapID());
|
||||
|
||||
if (zoneData != nullptr) {
|
||||
int zoneScriptID = zoneData->scriptID;
|
||||
const int32_t zoneScriptID = zoneData->scriptID;
|
||||
CDScriptComponent zoneScriptData = scriptCompTable->GetByID(zoneScriptID);
|
||||
AddComponent<ScriptComponent>(zoneScriptData.script_name, true);
|
||||
AddComponent<ScriptComponent>(zoneScriptID, zoneScriptData.script_name, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SKILL, -1) != -1 || m_Character) {
|
||||
AddComponent<SkillComponent>();
|
||||
const auto skillID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SKILL, -1);
|
||||
if (skillID > -1 || m_Character) {
|
||||
AddComponent<SkillComponent>(skillID);
|
||||
}
|
||||
|
||||
const auto combatAiId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BASE_COMBAT_AI);
|
||||
if (combatAiId > 0) {
|
||||
AddComponent<BaseCombatAIComponent>(combatAiId);
|
||||
const auto combatAiID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BASE_COMBAT_AI);
|
||||
if (combatAiID > 0) {
|
||||
AddComponent<BaseCombatAIComponent>(combatAiID);
|
||||
}
|
||||
|
||||
if (const int componentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD) > 0) {
|
||||
auto* const quickBuildComponent = AddComponent<QuickBuildComponent>();
|
||||
if (quickBuildComponentID > 0) {
|
||||
auto* const quickBuildComponent = AddComponent<QuickBuildComponent>(quickBuildComponentID);
|
||||
|
||||
CDRebuildComponentTable* const rebCompTable = CDClientManager::GetTable<CDRebuildComponentTable>();
|
||||
const std::vector<CDRebuildComponent> rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == quickBuildComponentID); });
|
||||
const std::vector<CDRebuildComponent> rebCompData = rebCompTable->Query([quickBuildComponentID](CDRebuildComponent entry) { return (entry.id == quickBuildComponentID); });
|
||||
|
||||
if (!rebCompData.empty()) {
|
||||
quickBuildComponent->SetResetTime(rebCompData[0].reset_time);
|
||||
@@ -618,53 +635,63 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SWITCH, -1) != -1) {
|
||||
AddComponent<SwitchComponent>();
|
||||
const auto switchID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SWITCH, -1);
|
||||
if (switchID > -1) {
|
||||
AddComponent<SwitchComponent>(switchID);
|
||||
}
|
||||
|
||||
if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR) > 0)) {
|
||||
AddComponent<VendorComponent>();
|
||||
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1) != -1)) {
|
||||
AddComponent<DonationVendorComponent>();
|
||||
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ACHIEVEMENT_VENDOR, -1) != -1)) {
|
||||
AddComponent<AchievementVendorComponent>();
|
||||
const auto vendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR);
|
||||
const auto donationVendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1);
|
||||
const auto achievementVendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ACHIEVEMENT_VENDOR, -1);
|
||||
if (vendorID > 0) {
|
||||
AddComponent<VendorComponent>(vendorID);
|
||||
} else if (donationVendorID > -1) {
|
||||
AddComponent<DonationVendorComponent>(donationVendorID);
|
||||
} else if (achievementVendorID > -1) {
|
||||
AddComponent<AchievementVendorComponent>(achievementVendorID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) {
|
||||
AddComponent<PropertyVendorComponent>();
|
||||
const auto propertyVendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1);
|
||||
if (propertyVendorID > -1) {
|
||||
AddComponent<PropertyVendorComponent>(propertyVendorID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_MANAGEMENT, -1) != -1) {
|
||||
AddComponent<PropertyManagementComponent>();
|
||||
const auto propertyManagementID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_MANAGEMENT, -1);
|
||||
if (propertyManagementID > -1) {
|
||||
AddComponent<PropertyManagementComponent>(propertyManagementID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BOUNCER, -1) != -1) { // you have to determine it like this because all bouncers have a componentID of 0
|
||||
AddComponent<BouncerComponent>();
|
||||
const auto bouncerID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BOUNCER, -1);
|
||||
if (bouncerID > -1) { // you have to determine it like this because all bouncers have a componentID of 0
|
||||
AddComponent<BouncerComponent>(bouncerID);
|
||||
}
|
||||
|
||||
const int32_t renderComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RENDER);
|
||||
if ((renderComponentId > 0 && m_TemplateID != 2365) || m_Character) {
|
||||
AddComponent<RenderComponent>(renderComponentId);
|
||||
const auto renderComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RENDER);
|
||||
if ((renderComponentID > 0 && m_TemplateID != 2365) || m_Character) {
|
||||
AddComponent<RenderComponent>(renderComponentID);
|
||||
}
|
||||
|
||||
if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MISSION_OFFER) > 0) || m_Character) {
|
||||
AddComponent<MissionOfferComponent>(m_TemplateID);
|
||||
const auto missionOfferComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MISSION_OFFER, -1);
|
||||
if (missionOfferComponentID > -1 || m_Character) {
|
||||
AddComponent<MissionOfferComponent>(missionOfferComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUILD_BORDER, -1) != -1) {
|
||||
AddComponent<BuildBorderComponent>();
|
||||
const auto buildBorderID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUILD_BORDER, -1);
|
||||
if (buildBorderID > -1) {
|
||||
AddComponent<BuildBorderComponent>(buildBorderID);
|
||||
}
|
||||
|
||||
// Scripted activity component
|
||||
const int32_t scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY, -1);
|
||||
if (scriptedActivityID != -1) {
|
||||
const auto scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY, -1);
|
||||
if (scriptedActivityID > -1) {
|
||||
AddComponent<ScriptedActivityComponent>(scriptedActivityID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
const auto modelID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1);
|
||||
if (modelID > -1 && !GetComponent<PetComponent>()) {
|
||||
AddComponent<ModelComponent>(modelID)->LoadBehaviors();
|
||||
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
|
||||
auto* const destroyableComponent = AddComponent<DestroyableComponent>();
|
||||
auto* const destroyableComponent = AddComponent<DestroyableComponent>(-1);
|
||||
destroyableComponent->SetHealth(1);
|
||||
destroyableComponent->SetMaxHealth(1.0f);
|
||||
destroyableComponent->SetFaction(-1, true);
|
||||
@@ -672,9 +699,10 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
PetComponent* petComponent;
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM) > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) {
|
||||
AddComponent<ItemComponent>();
|
||||
PetComponent* petComponent{};
|
||||
const auto itemID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM);
|
||||
if (itemID > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) {
|
||||
AddComponent<ItemComponent>(itemID);
|
||||
}
|
||||
|
||||
// Shooting gallery component
|
||||
@@ -683,16 +711,17 @@ void Entity::Initialize() {
|
||||
AddComponent<ShootingGalleryComponent>(shootingGalleryComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY, -1) != -1) {
|
||||
AddComponent<PropertyComponent>();
|
||||
const auto propertyID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY, -1);
|
||||
if (propertyID > -1) {
|
||||
AddComponent<PropertyComponent>(propertyID);
|
||||
}
|
||||
|
||||
const int rocketId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH);
|
||||
if ((rocketId > 0)) {
|
||||
AddComponent<RocketLaunchpadControlComponent>(rocketId);
|
||||
const auto rocketID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH);
|
||||
if ((rocketID > 0)) {
|
||||
AddComponent<RocketLaunchpadControlComponent>(rocketID);
|
||||
}
|
||||
|
||||
const int32_t railComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RAIL_ACTIVATOR);
|
||||
const auto railComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RAIL_ACTIVATOR);
|
||||
if (railComponentID > 0) {
|
||||
AddComponent<RailActivatorComponent>(railComponentID);
|
||||
}
|
||||
@@ -722,9 +751,9 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
AddComponent<MovementAIComponent>(moveInfo);
|
||||
AddComponent<MovementAIComponent>(movementAIID, moveInfo);
|
||||
}
|
||||
} else if (petComponentId > 0 || combatAiId > 0 && GetComponent<BaseCombatAIComponent>()->GetTetherSpeed() > 0) {
|
||||
} else if (petComponentID > 0 || combatAiID > 0 && GetComponent<BaseCombatAIComponent>()->GetTetherSpeed() > 0) {
|
||||
MovementAIInfo moveInfo{
|
||||
.movementType = "",
|
||||
.wanderRadius = 16,
|
||||
@@ -734,7 +763,7 @@ void Entity::Initialize() {
|
||||
.wanderDelayMax = 5,
|
||||
};
|
||||
|
||||
AddComponent<MovementAIComponent>(moveInfo);
|
||||
AddComponent<MovementAIComponent>(-1, moveInfo);
|
||||
}
|
||||
|
||||
const std::string pathName = GetVarAsString(u"attached_path");
|
||||
@@ -744,10 +773,10 @@ void Entity::Initialize() {
|
||||
if (path) {
|
||||
// if we have a moving platform path, then we need a moving platform component
|
||||
if (path->pathType == PathType::MovingPlatform) {
|
||||
AddComponent<MovingPlatformComponent>(pathName);
|
||||
AddComponent<MovingPlatformComponent>(-1, pathName);
|
||||
} else if (path->pathType == PathType::Movement) {
|
||||
auto* const movementAIcomponent = GetComponent<MovementAIComponent>();
|
||||
if (movementAIcomponent && combatAiId == 0) {
|
||||
if (movementAIcomponent && combatAiID == 0) {
|
||||
movementAIcomponent->SetPath(pathName);
|
||||
} else {
|
||||
MovementAIInfo moveInfo{
|
||||
@@ -759,24 +788,24 @@ void Entity::Initialize() {
|
||||
.wanderDelayMax = 5,
|
||||
};
|
||||
|
||||
AddComponent<MovementAIComponent>(moveInfo);
|
||||
AddComponent<MovementAIComponent>(-1, moveInfo);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// else we still need to setup moving platform if it has a moving platform comp but no path
|
||||
const int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
|
||||
if (movingPlatformComponentId >= 0) {
|
||||
AddComponent<MovingPlatformComponent>(pathName);
|
||||
const auto movingPlatformComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
|
||||
if (movingPlatformComponentID >= 0) {
|
||||
AddComponent<MovingPlatformComponent>(movingPlatformComponentID, pathName);
|
||||
}
|
||||
}
|
||||
|
||||
const int proximityMonitorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROXIMITY_MONITOR);
|
||||
const auto proximityMonitorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROXIMITY_MONITOR);
|
||||
if (proximityMonitorID > 0) {
|
||||
auto* const proxCompTable = CDClientManager::GetTable<CDProximityMonitorComponentTable>();
|
||||
const auto proxCompData = proxCompTable->Query([proximityMonitorID](const CDProximityMonitorComponent& entry) { return (entry.id == proximityMonitorID); });
|
||||
if (proxCompData.size() > 0) {
|
||||
std::vector<std::string> proximityStr = GeneralUtils::SplitString(proxCompData[0].Proximities, ',');
|
||||
AddComponent<ProximityMonitorComponent>(std::stoi(proximityStr[0]), std::stoi(proximityStr[1]));
|
||||
AddComponent<ProximityMonitorComponent>(proximityMonitorID, std::stoi(proximityStr[0]), std::stoi(proximityStr[1]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,12 +911,13 @@ void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationNa
|
||||
|
||||
void Entity::SetProximityRadius(float proxRadius, std::string name) {
|
||||
auto* proxMon = GetComponent<ProximityMonitorComponent>();
|
||||
if (!proxMon) proxMon = AddComponent<ProximityMonitorComponent>();
|
||||
if (!proxMon) proxMon = AddComponent<ProximityMonitorComponent>(-1);
|
||||
proxMon->SetProximityRadius(proxRadius, name);
|
||||
}
|
||||
|
||||
void Entity::SetProximityRadius(dpEntity* entity, std::string name) {
|
||||
ProximityMonitorComponent* proxMon = AddComponent<ProximityMonitorComponent>();
|
||||
auto* proxMon = GetComponent<ProximityMonitorComponent>();
|
||||
if (!proxMon) proxMon = AddComponent<ProximityMonitorComponent>(-1);
|
||||
proxMon->SetProximityRadius(entity, name);
|
||||
}
|
||||
|
||||
@@ -1639,7 +1669,7 @@ void Entity::AddLootItem(const Loot::Info& info) const {
|
||||
|
||||
auto* const characterComponent = GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
LOG("Player %llu has been allowed to pickup %i with id %llu", m_ObjectID, info.lot, info.id);
|
||||
auto& droppedLoot = characterComponent->GetDroppedLoot();
|
||||
droppedLoot[info.id] = info;
|
||||
}
|
||||
@@ -2223,6 +2253,8 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
response.Insert("objectID", std::to_string(m_ObjectID));
|
||||
response.Insert("serverInfo", true);
|
||||
GameMessages::GetObjectReportInfo info{};
|
||||
info.clientID = requestInfo.clientId;
|
||||
info.bVerbose = requestInfo.bVerbose;
|
||||
info.info = response.InsertArray("data");
|
||||
auto& objectInfo = info.info->PushDebug("Object Details");
|
||||
auto* table = CDClientManager::GetTable<CDObjectsTable>();
|
||||
@@ -2236,17 +2268,87 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
|
||||
auto& componentDetails = objectInfo.PushDebug("Component Information");
|
||||
for (const auto [id, component] : m_Components) {
|
||||
componentDetails.PushDebug<AMFStringValue>(StringifiedEnum::ToString(id)) = "";
|
||||
componentDetails.PushDebug(StringifiedEnum::ToString(id));
|
||||
}
|
||||
|
||||
auto& configData = objectInfo.PushDebug("Config Data");
|
||||
for (const auto config : m_Settings) {
|
||||
configData.PushDebug<AMFStringValue>(GeneralUtils::UTF16ToWTF8(config->GetKey())) = config->GetValueAsString();
|
||||
|
||||
}
|
||||
|
||||
HandleMsg(info);
|
||||
|
||||
auto* client = Game::entityManager->GetEntity(requestInfo.clientId);
|
||||
if (client) GameMessages::SendUIMessageServerToSingleClient("ToggleObjectDebugger", response, client->GetSystemAddress());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Entity::MsgDropClientLoot(GameMessages::GameMsg& msg) {
|
||||
auto& dropLootMsg = static_cast<GameMessages::DropClientLoot&>(msg);
|
||||
|
||||
if (dropLootMsg.item != LOT_NULL && dropLootMsg.item != 0) {
|
||||
Loot::Info info{
|
||||
.id = dropLootMsg.lootID,
|
||||
.lot = dropLootMsg.item,
|
||||
.count = dropLootMsg.count,
|
||||
};
|
||||
AddLootItem(info);
|
||||
}
|
||||
|
||||
if (dropLootMsg.item == LOT_NULL && dropLootMsg.currency != 0) {
|
||||
RegisterCoinDrop(dropLootMsg.currency);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Entity::MsgGetFlag(GameMessages::GameMsg& msg) {
|
||||
auto& flagMsg = static_cast<GameMessages::GetFlag&>(msg);
|
||||
if (m_Character) flagMsg.flag = m_Character->GetPlayerFlag(flagMsg.flagID);
|
||||
return true;
|
||||
}
|
||||
bool Entity::MsgGetFactionTokenType(GameMessages::GameMsg& msg) {
|
||||
auto& tokenMsg = static_cast<GameMessages::GetFactionTokenType&>(msg);
|
||||
GameMessages::GetFlag getFlagMsg{};
|
||||
|
||||
getFlagMsg.flagID = ePlayerFlag::ASSEMBLY_FACTION;
|
||||
MsgGetFlag(getFlagMsg);
|
||||
if (getFlagMsg.flag) tokenMsg.tokenType = 8318;
|
||||
|
||||
getFlagMsg.flagID = ePlayerFlag::SENTINEL_FACTION;
|
||||
MsgGetFlag(getFlagMsg);
|
||||
if (getFlagMsg.flag) tokenMsg.tokenType = 8319;
|
||||
|
||||
getFlagMsg.flagID = ePlayerFlag::PARADOX_FACTION;
|
||||
MsgGetFlag(getFlagMsg);
|
||||
if (getFlagMsg.flag) tokenMsg.tokenType = 8320;
|
||||
|
||||
getFlagMsg.flagID = ePlayerFlag::VENTURE_FACTION;
|
||||
MsgGetFlag(getFlagMsg);
|
||||
if (getFlagMsg.flag) tokenMsg.tokenType = 8321;
|
||||
|
||||
LOG("Returning token type %i", tokenMsg.tokenType);
|
||||
return tokenMsg.tokenType != LOT_NULL;
|
||||
}
|
||||
|
||||
bool Entity::MsgPickupItem(GameMessages::GameMsg& msg) {
|
||||
auto& pickupItemMsg = static_cast<GameMessages::PickupItem&>(msg);
|
||||
if (GetObjectID() == pickupItemMsg.lootOwnerID) {
|
||||
PickupItem(pickupItemMsg.lootID);
|
||||
} else {
|
||||
auto* const characterComponent = GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return false;
|
||||
auto& droppedLoot = characterComponent->GetDroppedLoot();
|
||||
const auto it = droppedLoot.find(pickupItemMsg.lootID);
|
||||
if (it != droppedLoot.end()) {
|
||||
CDObjectsTable* objectsTable = CDClientManager::GetTable<CDObjectsTable>();
|
||||
const CDObjects& object = objectsTable->GetByID(it->second.lot);
|
||||
if (object.id != 0 && object.type == "Powerup") {
|
||||
return false; // Let powerups be duplicated
|
||||
}
|
||||
}
|
||||
droppedLoot.erase(pickupItemMsg.lootID);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -176,6 +176,10 @@ public:
|
||||
void AddComponent(eReplicaComponentType componentId, Component* component);
|
||||
|
||||
bool MsgRequestServerObjectInfo(GameMessages::GameMsg& msg);
|
||||
bool MsgDropClientLoot(GameMessages::GameMsg& msg);
|
||||
bool MsgGetFlag(GameMessages::GameMsg& msg);
|
||||
bool MsgGetFactionTokenType(GameMessages::GameMsg& msg);
|
||||
bool MsgPickupItem(GameMessages::GameMsg& msg);
|
||||
|
||||
// This is expceted to never return nullptr, an assert checks this.
|
||||
CppScripts::Script* const GetScript() const;
|
||||
@@ -342,6 +346,12 @@ public:
|
||||
RegisterMsg(msgId, std::bind(handler, self, std::placeholders::_1));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void RegisterMsg(auto* self, const auto handler) {
|
||||
T msg;
|
||||
RegisterMsg(msg.msgId, self, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The observable for player entity position updates.
|
||||
*/
|
||||
@@ -600,5 +610,5 @@ auto Entity::GetComponents() const {
|
||||
|
||||
template<typename... T>
|
||||
auto Entity::GetComponentsMut() const {
|
||||
return std::tuple{GetComponent<T>()...};
|
||||
return std::tuple{ GetComponent<T>()... };
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ void EntityManager::ReloadConfig() {
|
||||
auto hcXpReduction = Game::config->GetValue("hardcore_uscore_reduction");
|
||||
m_HardcoreUscoreReduction = hcXpReduction.empty() ? 1.0f : GeneralUtils::TryParse<float>(hcXpReduction).value_or(1.0f);
|
||||
m_HardcoreMode = GetHardcoreDisabledWorlds().contains(Game::zoneManager->GetZoneID().GetMapID()) ? false : m_HardcoreMode;
|
||||
auto hcCoinKeep = Game::config->GetValue("hardcore_coin_keep");
|
||||
m_HardcoreCoinKeep = hcCoinKeep.empty() ? false : GeneralUtils::TryParse<float>(hcCoinKeep).value_or(0.0f);
|
||||
}
|
||||
|
||||
void EntityManager::Initialize() {
|
||||
|
||||
@@ -81,6 +81,7 @@ public:
|
||||
const std::set<LOT>& GetHardcoreUscoreReducedLots() const { return m_HardcoreUscoreReducedLots; };
|
||||
const std::set<LOT>& GetHardcoreUscoreExcludedEnemies() const { return m_HardcoreUscoreExcludedEnemies; };
|
||||
const std::set<LWOMAPID>& GetHardcoreDisabledWorlds() const { return m_HardcoreDisabledWorlds; };
|
||||
float GetHardcoreCoinKeep() const { return m_HardcoreCoinKeep; }
|
||||
|
||||
// Messaging
|
||||
bool SendMessage(GameMessages::GameMsg& msg) const;
|
||||
@@ -125,6 +126,7 @@ private:
|
||||
std::set<LOT> m_HardcoreUscoreReducedLots{};
|
||||
std::set<LOT> m_HardcoreUscoreExcludedEnemies{};
|
||||
std::set<LWOMAPID> m_HardcoreDisabledWorlds{};
|
||||
float m_HardcoreCoinKeep{};
|
||||
};
|
||||
|
||||
#endif // ENTITYMANAGER_H
|
||||
|
||||
@@ -9,6 +9,16 @@ Team::Team() {
|
||||
lootOption = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
|
||||
}
|
||||
|
||||
LWOOBJID Team::GetNextLootOwner() {
|
||||
lootRound++;
|
||||
|
||||
if (lootRound >= members.size()) {
|
||||
lootRound = 0;
|
||||
}
|
||||
|
||||
return members[lootRound];
|
||||
}
|
||||
|
||||
TeamManager::TeamManager() {
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
struct Team {
|
||||
Team();
|
||||
|
||||
LWOOBJID GetNextLootOwner();
|
||||
LWOOBJID teamID = LWOOBJID_EMPTY;
|
||||
char lootOption = 0;
|
||||
std::vector<LWOOBJID> members{};
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "BitStreamUtils.h"
|
||||
#include "CheatDetection.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "dConfig.h"
|
||||
#include "eCharacterVersion.h"
|
||||
|
||||
UserManager* UserManager::m_Address = nullptr;
|
||||
@@ -92,6 +93,23 @@ void UserManager::Initialize() {
|
||||
StripCR(line);
|
||||
m_PreapprovedNames.push_back(line);
|
||||
}
|
||||
|
||||
// Initialize cached config values and register a handler to update them on config reload
|
||||
// This avoids repeated lookups into dConfig at runtime.
|
||||
if (Game::config) {
|
||||
m_MuteAutoRejectNames = (Game::config->GetValue("mute_auto_reject_names") == "1");
|
||||
m_MuteRestrictTrade = (Game::config->GetValue("mute_restrict_trade") == "1");
|
||||
m_MuteRestrictMail = (Game::config->GetValue("mute_restrict_mail") == "1");
|
||||
|
||||
Game::config->AddConfigHandler([this]() {
|
||||
this->m_MuteAutoRejectNames = (Game::config->GetValue("mute_auto_reject_names") == "1");
|
||||
this->m_MuteRestrictTrade = (Game::config->GetValue("mute_restrict_trade") == "1");
|
||||
this->m_MuteRestrictMail = (Game::config->GetValue("mute_restrict_mail") == "1");
|
||||
});
|
||||
}
|
||||
else {
|
||||
LOG("Warning: dConfig not initialized before UserManager. Cached config values will not be available.");
|
||||
}
|
||||
}
|
||||
|
||||
UserManager::~UserManager() {
|
||||
@@ -301,7 +319,9 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
inStream.Read(eyes);
|
||||
inStream.Read(mouth);
|
||||
|
||||
const auto name = LUWStringName.GetAsString();
|
||||
const bool autoRejectNames = this->GetMuteAutoRejectNames() && u->GetIsMuted();
|
||||
|
||||
const auto name = autoRejectNames ? "" : LUWStringName.GetAsString();
|
||||
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
|
||||
|
||||
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
|
||||
@@ -319,6 +339,10 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
return;
|
||||
}
|
||||
|
||||
if (autoRejectNames) {
|
||||
LOG("AccountID: %i is muted, forcing use of predefined name", u->GetAccountID());
|
||||
}
|
||||
|
||||
if (name.empty()) {
|
||||
LOG("AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
||||
} else {
|
||||
@@ -369,6 +393,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
|
||||
//Check to see if our name was pre-approved:
|
||||
bool nameOk = IsNamePreapproved(name);
|
||||
|
||||
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
||||
|
||||
// If predefined name is invalid, change it to be their object id
|
||||
@@ -448,9 +473,10 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
|
||||
LUWString LUWStringName;
|
||||
inStream.Read(LUWStringName);
|
||||
const auto newName = LUWStringName.GetAsString();
|
||||
auto newName = LUWStringName.GetAsString();
|
||||
|
||||
Character* character = nullptr;
|
||||
const bool autoRejectNames = this->GetMuteAutoRejectNames() && u->GetIsMuted();
|
||||
|
||||
//Check if this user has this character:
|
||||
bool ownsCharacter = CheatDetection::VerifyLwoobjidIsSender(
|
||||
@@ -471,13 +497,30 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
if (!ownsCharacter || !character) {
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
|
||||
} else if (ownsCharacter && character) {
|
||||
if (autoRejectNames) {
|
||||
// Create a random preapproved name (fallback to default if none available)
|
||||
if (!m_FirstNames.empty() && !m_MiddleNames.empty() && !m_LastNames.empty()) {
|
||||
std::string firstName = GeneralUtils::GetRandomElement(m_FirstNames);
|
||||
std::string middleName = GeneralUtils::GetRandomElement(m_MiddleNames);
|
||||
std::string lastName = GeneralUtils::GetRandomElement(m_LastNames);
|
||||
newName = firstName + middleName + lastName;
|
||||
} else {
|
||||
newName = "character" + std::to_string(objectID);
|
||||
}
|
||||
}
|
||||
|
||||
if (newName == character->GetName()) {
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Database::Get()->GetCharacterInfo(newName)) {
|
||||
if (IsNamePreapproved(newName)) {
|
||||
if (autoRejectNames) {
|
||||
Database::Get()->SetCharacterName(objectID, newName);
|
||||
LOG("Character %s auto-renamed to preapproved name %s due to mute", character->GetName().c_str(), newName.c_str());
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
||||
UserManager::RequestCharacterList(sysAddr);
|
||||
} else if (IsNamePreapproved(newName)) {
|
||||
Database::Get()->SetCharacterName(objectID, newName);
|
||||
LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str());
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
||||
|
||||
@@ -41,6 +41,11 @@ public:
|
||||
|
||||
size_t GetUserCount() const { return m_Users.size(); }
|
||||
|
||||
// Access cached config values
|
||||
bool GetMuteAutoRejectNames() const { return m_MuteAutoRejectNames; }
|
||||
bool GetMuteRestrictTrade() const { return m_MuteRestrictTrade; }
|
||||
bool GetMuteRestrictMail() const { return m_MuteRestrictMail; }
|
||||
|
||||
private:
|
||||
static UserManager* m_Address; //Singleton
|
||||
std::map<SystemAddress, User*> m_Users;
|
||||
@@ -50,6 +55,11 @@ private:
|
||||
std::vector<std::string> m_MiddleNames;
|
||||
std::vector<std::string> m_LastNames;
|
||||
std::vector<std::string> m_PreapprovedNames;
|
||||
|
||||
// Cached config values that can change on config reload
|
||||
bool m_MuteAutoRejectNames = false;
|
||||
bool m_MuteRestrictTrade = false;
|
||||
bool m_MuteRestrictMail = false;
|
||||
};
|
||||
|
||||
#endif // USERMANAGER_H
|
||||
|
||||
@@ -114,7 +114,6 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitS
|
||||
context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
||||
|
||||
for (auto validTarget : validTargets) {
|
||||
if (targets.size() >= this->m_maxTargets) break;
|
||||
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue;
|
||||
if (validTarget->GetIsDead()) continue;
|
||||
|
||||
@@ -147,13 +146,28 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitS
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b) {
|
||||
std::sort(targets.begin(), targets.end(), [this, reference, combatAi](Entity* a, Entity* b) {
|
||||
const auto aDistance = Vector3::DistanceSquared(reference, a->GetPosition());
|
||||
const auto bDistance = Vector3::DistanceSquared(reference, b->GetPosition());
|
||||
|
||||
return aDistance > bDistance;
|
||||
return aDistance < bDistance;
|
||||
});
|
||||
|
||||
|
||||
if (m_useAttackPriority) {
|
||||
// this should be using the attack priority column on the destroyable component
|
||||
// We want targets with no threat level to remain the same order as above
|
||||
// std::stable_sort(targets.begin(), targets.end(), [combatAi](Entity* a, Entity* b) {
|
||||
// const auto aThreat = combatAi->GetThreat(a->GetObjectID());
|
||||
// const auto bThreat = combatAi->GetThreat(b->GetObjectID());
|
||||
|
||||
// If enabled for this behavior, prioritize threat over distance
|
||||
// return aThreat > bThreat;
|
||||
// });
|
||||
}
|
||||
|
||||
// After we've sorted and found our closest targets, size the vector down in case there are too many
|
||||
if (m_maxTargets > 0 && targets.size() > m_maxTargets) targets.resize(m_maxTargets);
|
||||
const auto hit = !targets.empty();
|
||||
bitStream.Write(hit);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "UserManager.h"
|
||||
#include "CDMissionsTable.h"
|
||||
|
||||
AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
AchievementVendorComponent::AchievementVendorComponent(Entity* parent, const int32_t componentID) : VendorComponent(parent, componentID) {
|
||||
RefreshInventory(true);
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class Entity;
|
||||
class AchievementVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
|
||||
AchievementVendorComponent(Entity* parent);
|
||||
AchievementVendorComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
void RefreshInventory(bool isCreation = false) override;
|
||||
bool SellsItem(Entity* buyer, const LOT lot);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "CharacterComponent.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Component(parent) {
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
RegisterMsg<GetObjectReportInfo>(this, &ActivityComponent::OnGetObjectReportInfo);
|
||||
/*
|
||||
@@ -39,39 +39,12 @@ ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Compo
|
||||
* if activityID is specified and if that column exists in the activities table, update the activity info with that data.
|
||||
*/
|
||||
|
||||
m_ActivityID = activityID;
|
||||
LoadActivityData(activityID);
|
||||
m_ActivityID = componentID;
|
||||
LoadActivityData(componentID);
|
||||
if (m_Parent->HasVar(u"activityID")) {
|
||||
m_ActivityID = parent->GetVar<int32_t>(u"activityID");
|
||||
LoadActivityData(m_ActivityID);
|
||||
}
|
||||
|
||||
auto* destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent) {
|
||||
// First lookup the loot matrix id for this component id.
|
||||
CDActivityRewardsTable* activityRewardsTable = CDClientManager::GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
|
||||
|
||||
uint32_t startingLMI = 0;
|
||||
|
||||
// If we have one, set the starting loot matrix id to that.
|
||||
if (activityRewards.size() > 0) {
|
||||
startingLMI = activityRewards[0].LootMatrixIndex;
|
||||
}
|
||||
|
||||
if (startingLMI > 0) {
|
||||
// We may have more than 1 loot matrix index to use depending ont the size of the team that is looting the activity.
|
||||
// So this logic will get the rest of the loot matrix indices for this activity.
|
||||
|
||||
std::vector<CDActivityRewards> objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
|
||||
for (const auto& item : objectTemplateActivities) {
|
||||
if (item.activityRating > 0 && item.activityRating < 5) {
|
||||
m_ActivityLootMatrices.insert({ item.activityRating, item.LootMatrixIndex });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void ActivityComponent::LoadActivityData(const int32_t activityId) {
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::GetTable<CDActivitiesTable>();
|
||||
@@ -698,10 +671,6 @@ bool ActivityComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
}
|
||||
}
|
||||
|
||||
auto& lootMatrices = activityInfo.PushDebug("Loot Matrices");
|
||||
for (const auto& [activityRating, lootMatrixID] : m_ActivityLootMatrices) {
|
||||
lootMatrices.PushDebug<AMFIntValue>("Loot Matrix " + std::to_string(activityRating)) = lootMatrixID;
|
||||
}
|
||||
activityInfo.PushDebug<AMFIntValue>("ActivityID") = m_ActivityID;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -341,12 +341,6 @@ public:
|
||||
*/
|
||||
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
|
||||
|
||||
/**
|
||||
* Returns the LMI that this activity points to for a team size
|
||||
* @param teamSize the team size to get the LMI for
|
||||
* @return the LMI that this activity points to for a team size
|
||||
*/
|
||||
uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; }
|
||||
private:
|
||||
|
||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
@@ -370,11 +364,6 @@ private:
|
||||
*/
|
||||
std::vector<ActivityPlayer*> m_ActivityPlayers;
|
||||
|
||||
/**
|
||||
* LMIs for team sizes
|
||||
*/
|
||||
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
|
||||
|
||||
/**
|
||||
* The activity id
|
||||
*/
|
||||
|
||||
@@ -27,8 +27,13 @@
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
#include "dNavMesh.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id) : Component(parent) {
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
{
|
||||
using namespace GameMessages;
|
||||
RegisterMsg<GetObjectReportInfo>(this, &BaseCombatAIComponent::MsgGetObjectReportInfo);
|
||||
}
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
m_DirtyStateOrTarget = true;
|
||||
m_State = AiState::spawn;
|
||||
@@ -43,7 +48,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id)
|
||||
//Grab the aggro information from BaseCombatAI:
|
||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
||||
componentQuery.bind(1, static_cast<int>(id));
|
||||
componentQuery.bind(1, static_cast<int>(componentID));
|
||||
|
||||
auto componentResult = componentQuery.execQuery();
|
||||
|
||||
@@ -111,12 +116,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id)
|
||||
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
|
||||
|
||||
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
const auto controllablePhysicsID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
if (physicsComponentTable != nullptr) {
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
auto* info = physicsComponentTable->GetByID(controllablePhysicsID);
|
||||
if (info != nullptr) {
|
||||
collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||
}
|
||||
@@ -839,3 +844,73 @@ void BaseCombatAIComponent::IgnoreThreat(const LWOOBJID threat, const float valu
|
||||
SetThreat(threat, 0.0f);
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
using enum AiState;
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& cmptType = reportMsg.info->PushDebug("Base Combat AI");
|
||||
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
auto& targetInfo = cmptType.PushDebug("Current Target Info");
|
||||
targetInfo.PushDebug<AMFStringValue>("Current Target ID") = std::to_string(m_Target);
|
||||
// if (m_Target != LWOOBJID_EMPTY) {
|
||||
// LWOGameMessages::ObjGetName nameMsg(m_CurrentTarget);
|
||||
// SEND_GAMEOBJ_MSG(nameMsg);
|
||||
// if (!nameMsg.msg.name.empty()) targetInfo.PushDebug("Name") = nameMsg.msg.name;
|
||||
// }
|
||||
|
||||
auto& roundInfo = cmptType.PushDebug("Round Info");
|
||||
// roundInfo.PushDebug<AMFDoubleValue>("Combat Round Time") = m_CombatRoundLength;
|
||||
// roundInfo.PushDebug<AMFDoubleValue>("Minimum Time") = m_MinRoundLength;
|
||||
// roundInfo.PushDebug<AMFDoubleValue>("Maximum Time") = m_MaxRoundLength;
|
||||
// roundInfo.PushDebug<AMFDoubleValue>("Selected Time") = m_SelectedTime;
|
||||
// roundInfo.PushDebug<AMFDoubleValue>("Combat Start Delay") = m_CombatStartDelay;
|
||||
std::string curState;
|
||||
switch (m_State) {
|
||||
case idle: curState = "Idling"; break;
|
||||
case aggro: curState = "Aggroed"; break;
|
||||
case tether: curState = "Returning to Tether"; break;
|
||||
case spawn: curState = "Spawn"; break;
|
||||
case dead: curState = "Dead"; break;
|
||||
default: curState = "Unknown or Undefined"; break;
|
||||
}
|
||||
cmptType.PushDebug<AMFStringValue>("Current Combat State") = curState;
|
||||
|
||||
//switch (m_CombatBehaviorType) {
|
||||
// case 0: curState = "Passive"; break;
|
||||
// case 1: curState = "Aggressive"; break;
|
||||
// case 2: curState = "Passive (Turret)"; break;
|
||||
// case 3: curState = "Aggressive (Turret)"; break;
|
||||
// default: curState = "Unknown or Undefined"; break;
|
||||
//}
|
||||
//cmptType.PushDebug("Current Combat Behavior State") = curState;
|
||||
|
||||
//switch (m_CombatRole) {
|
||||
// case 0: curState = "Melee"; break;
|
||||
// case 1: curState = "Ranged"; break;
|
||||
// case 2: curState = "Support"; break;
|
||||
// default: curState = "Unknown or Undefined"; break;
|
||||
//}
|
||||
//cmptType.PushDebug("Current Combat Role") = curState;
|
||||
|
||||
auto& tetherPoint = cmptType.PushDebug("Tether Point");
|
||||
tetherPoint.PushDebug<AMFDoubleValue>("X") = m_StartPosition.x;
|
||||
tetherPoint.PushDebug<AMFDoubleValue>("Y") = m_StartPosition.y;
|
||||
tetherPoint.PushDebug<AMFDoubleValue>("Z") = m_StartPosition.z;
|
||||
cmptType.PushDebug<AMFDoubleValue>("Hard Tether Radius") = m_HardTetherRadius;
|
||||
cmptType.PushDebug<AMFDoubleValue>("Soft Tether Radius") = m_SoftTetherRadius;
|
||||
cmptType.PushDebug<AMFDoubleValue>("Aggro Radius") = m_AggroRadius;
|
||||
cmptType.PushDebug<AMFDoubleValue>("Tether Speed") = m_TetherSpeed;
|
||||
cmptType.PushDebug<AMFDoubleValue>("Aggro Speed") = m_TetherSpeed;
|
||||
// cmptType.PushDebug<AMFDoubleValue>("Specified Min Range") = m_SpecificMinRange;
|
||||
// cmptType.PushDebug<AMFDoubleValue>("Specified Max Range") = m_SpecificMaxRange;
|
||||
auto& threats = cmptType.PushDebug("Target Threats");
|
||||
for (const auto& [id, threat] : m_ThreatEntries) {
|
||||
threats.PushDebug<AMFDoubleValue>(std::to_string(id)) = threat;
|
||||
}
|
||||
|
||||
auto& ignoredThreats = cmptType.PushDebug("Temp Ignored Threats");
|
||||
for (const auto& [id, threat] : m_ThreatEntries) {
|
||||
ignoredThreats.PushDebug<AMFDoubleValue>(std::to_string(id) + " - Time") = threat;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class BaseCombatAIComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
|
||||
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
|
||||
BaseCombatAIComponent(Entity* parentEntity, int32_t componentID);
|
||||
~BaseCombatAIComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
@@ -234,6 +234,8 @@ public:
|
||||
// Ignore a threat for a certain amount of time
|
||||
void IgnoreThreat(const LWOOBJID target, const float time);
|
||||
|
||||
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the current target or the target that currently is the largest threat to this entity
|
||||
|
||||
@@ -8,15 +8,33 @@
|
||||
#include "GameMessages.h"
|
||||
#include "BitStream.h"
|
||||
#include "eTriggerEventType.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
|
||||
BouncerComponent::BouncerComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_PetEnabled = false;
|
||||
m_PetBouncerEnabled = false;
|
||||
m_PetSwitchLoaded = false;
|
||||
m_Destination = GeneralUtils::TryParse<NiPoint3>(
|
||||
GeneralUtils::SplitString(m_Parent->GetVarAsString(u"bouncer_destination"), '\x1f'))
|
||||
.value_or(NiPoint3Constant::ZERO);
|
||||
m_Speed = GeneralUtils::TryParse<float>(m_Parent->GetVarAsString(u"bouncer_speed")).value_or(-1.0f);
|
||||
m_UsesHighArc = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"bouncer_uses_high_arc")).value_or(false);
|
||||
m_LockControls = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"lock_controls")).value_or(false);
|
||||
m_IgnoreCollision = !GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"ignore_collision")).value_or(true);
|
||||
m_StickLanding = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"stickLanding")).value_or(false);
|
||||
m_UsesGroupName = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"uses_group_name")).value_or(false);
|
||||
m_GroupName = m_Parent->GetVarAsString(u"grp_name");
|
||||
m_MinNumTargets = GeneralUtils::TryParse<int32_t>(m_Parent->GetVarAsString(u"num_targets_to_activate")).value_or(1);
|
||||
m_CinematicPath = m_Parent->GetVarAsString(u"attached_cinematic_path");
|
||||
|
||||
if (parent->GetLOT() == 7625) {
|
||||
LookupPetSwitch();
|
||||
}
|
||||
|
||||
{
|
||||
using namespace GameMessages;
|
||||
RegisterMsg<GetObjectReportInfo>(this, &BouncerComponent::MsgGetObjectReportInfo);
|
||||
}
|
||||
}
|
||||
|
||||
BouncerComponent::~BouncerComponent() {
|
||||
@@ -94,3 +112,54 @@ void BouncerComponent::LookupPetSwitch() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool BouncerComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& cmptType = reportMsg.info->PushDebug("Bouncer");
|
||||
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
auto& destPos = cmptType.PushDebug("Destination Position");
|
||||
if (m_Destination != NiPoint3Constant::ZERO) {
|
||||
destPos.PushDebug(m_Destination);
|
||||
} else {
|
||||
destPos.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has no target position, is likely missing config data");
|
||||
}
|
||||
|
||||
|
||||
if (m_Speed == -1.0f) {
|
||||
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has no speed value, is likely missing config data");
|
||||
} else {
|
||||
cmptType.PushDebug<AMFDoubleValue>("Bounce Speed") = m_Speed;
|
||||
}
|
||||
cmptType.PushDebug<AMFStringValue>("Bounce trajectory arc") = m_UsesHighArc ? "High Arc" : "Low Arc";
|
||||
cmptType.PushDebug<AMFBoolValue>("Collision Enabled") = m_IgnoreCollision;
|
||||
cmptType.PushDebug<AMFBoolValue>("Stick Landing") = m_StickLanding;
|
||||
cmptType.PushDebug<AMFBoolValue>("Locks character's controls") = m_LockControls;
|
||||
if (!m_CinematicPath.empty()) cmptType.PushDebug<AMFStringValue>("Cinematic Camera Path (plays during bounce)") = m_CinematicPath;
|
||||
|
||||
auto* switchComponent = m_Parent->GetComponent<SwitchComponent>();
|
||||
auto& respondsToFactions = cmptType.PushDebug("Responds to Factions");
|
||||
if (!switchComponent || switchComponent->GetFactionsToRespondTo().empty()) respondsToFactions.PushDebug("Faction 1");
|
||||
else {
|
||||
for (const auto faction : switchComponent->GetFactionsToRespondTo()) {
|
||||
respondsToFactions.PushDebug(("Faction " + std::to_string(faction)));
|
||||
}
|
||||
}
|
||||
|
||||
cmptType.PushDebug<AMFBoolValue>("Uses a group name for interactions") = m_UsesGroupName;
|
||||
if (!m_UsesGroupName) {
|
||||
if (m_MinNumTargets > 1) {
|
||||
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has a required number of objects to activate, but no group for interactions.");
|
||||
}
|
||||
|
||||
if (!m_GroupName.empty()) {
|
||||
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Has a group name for interactions , but is marked to not use that name.");
|
||||
}
|
||||
} else {
|
||||
if (m_GroupName.empty()) {
|
||||
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Set to use a group name for inter actions, but no group name is assigned");
|
||||
}
|
||||
cmptType.PushDebug<AMFIntValue>("Number of interactions to activate bouncer") = m_MinNumTargets;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class BouncerComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
|
||||
BouncerComponent(Entity* parentEntity);
|
||||
BouncerComponent(Entity* parentEntity, const int32_t componentID);
|
||||
~BouncerComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
@@ -51,6 +51,8 @@ public:
|
||||
*/
|
||||
void LookupPetSwitch();
|
||||
|
||||
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Whether this bouncer needs to be activated by a pet
|
||||
@@ -66,6 +68,36 @@ private:
|
||||
* Whether the pet switch for this bouncer has been located
|
||||
*/
|
||||
bool m_PetSwitchLoaded;
|
||||
|
||||
// The bouncer destination
|
||||
NiPoint3 m_Destination;
|
||||
|
||||
// The speed at which the player is bounced
|
||||
float m_Speed{};
|
||||
|
||||
// Whether to use a high arc for the bounce trajectory
|
||||
bool m_UsesHighArc{};
|
||||
|
||||
// Lock controls when bouncing
|
||||
bool m_LockControls{};
|
||||
|
||||
// Ignore collision when bouncing
|
||||
bool m_IgnoreCollision{};
|
||||
|
||||
// Stick the landing afterwards or let the player slide
|
||||
bool m_StickLanding{};
|
||||
|
||||
// Whether or not there is a group name
|
||||
bool m_UsesGroupName{};
|
||||
|
||||
// The group name for targets
|
||||
std::string m_GroupName{};
|
||||
|
||||
// The number of targets to activate the bouncer
|
||||
int32_t m_MinNumTargets{};
|
||||
|
||||
// The cinematic path to play during the bounce
|
||||
std::string m_CinematicPath{};
|
||||
};
|
||||
|
||||
#endif // BOUNCERCOMPONENT_H
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
|
||||
BuffComponent::BuffComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
}
|
||||
|
||||
BuffComponent::~BuffComponent() {
|
||||
|
||||
@@ -51,7 +51,7 @@ class BuffComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
|
||||
explicit BuffComponent(Entity* parent);
|
||||
explicit BuffComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
~BuffComponent();
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "Item.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
|
||||
BuildBorderComponent::BuildBorderComponent(Entity* parent) : Component(parent) {
|
||||
BuildBorderComponent::BuildBorderComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
}
|
||||
|
||||
BuildBorderComponent::~BuildBorderComponent() {
|
||||
|
||||
@@ -18,7 +18,7 @@ class BuildBorderComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
|
||||
BuildBorderComponent(Entity* parent);
|
||||
BuildBorderComponent(Entity* parent, const int32_t componentID);
|
||||
~BuildBorderComponent() override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "MessageType/Game.h"
|
||||
#include <ctime>
|
||||
|
||||
CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) {
|
||||
CharacterComponent::CharacterComponent(Entity* parent, const int32_t componentID, Character* character, const SystemAddress& systemAddress) : Component(parent, componentID) {
|
||||
m_Character = character;
|
||||
|
||||
m_IsRacing = false;
|
||||
@@ -70,7 +70,7 @@ bool CharacterComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
for (const auto zoneID : m_VisitedLevels) {
|
||||
std::stringstream sstream;
|
||||
sstream << "MapID: " << zoneID.GetMapID() << " CloneID: " << zoneID.GetCloneID();
|
||||
vl.PushDebug<AMFStringValue>(sstream.str()) = "";
|
||||
vl.PushDebug(sstream.str());
|
||||
}
|
||||
|
||||
// visited locations
|
||||
@@ -84,6 +84,30 @@ bool CharacterComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
cmptType.PushDebug<AMFIntValue>("Current Activity Type") = GeneralUtils::ToUnderlying(m_CurrentActivity);
|
||||
cmptType.PushDebug<AMFDoubleValue>("Property Clone ID") = m_Character->GetPropertyCloneID();
|
||||
|
||||
auto& flagCmptType = reportInfo.info->PushDebug("Player Flag");
|
||||
auto& allFlags = flagCmptType.PushDebug("All flags");
|
||||
|
||||
for (const auto& [id, flagChunk] : m_Character->GetPlayerFlags()) {
|
||||
const auto base = id * 64;
|
||||
auto flagChunkCopy = flagChunk;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (static_cast<bool>(flagChunkCopy & 1)) {
|
||||
const int32_t flagId = base + i;
|
||||
std::stringstream stream;
|
||||
stream << "Flag: " << flagId;
|
||||
allFlags.PushDebug(stream.str());
|
||||
}
|
||||
flagChunkCopy >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto& sessionFlags = flagCmptType.PushDebug("Session Only Flags");
|
||||
for (const auto flagId : m_Character->GetSessionFlags()) {
|
||||
std::stringstream stream;
|
||||
stream << "Flag: " << flagId;
|
||||
sessionFlags.PushDebug(stream.str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -859,7 +883,7 @@ void CharacterComponent::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) const {
|
||||
character->SetZoneID(zoneID);
|
||||
character->SetZoneInstance(zoneInstance);
|
||||
character->SetZoneClone(zoneClone);
|
||||
|
||||
|
||||
characterComponent->SetLastRocketConfig(u"");
|
||||
characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone));
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class CharacterComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
|
||||
|
||||
CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress);
|
||||
CharacterComponent(Entity* parent, const int32_t componentID, Character* character, const SystemAddress& systemAddress);
|
||||
~CharacterComponent() override;
|
||||
|
||||
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
#include "CollectibleComponent.h"
|
||||
|
||||
#include "MissionComponent.h"
|
||||
#include "dServer.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
CollectibleComponent::CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId) :
|
||||
Component(parentEntity, componentID), m_CollectibleId(collectibleId) {
|
||||
using namespace GameMessages;
|
||||
RegisterMsg<GetObjectReportInfo>(this, &CollectibleComponent::MsgGetObjectReportInfo);
|
||||
}
|
||||
|
||||
void CollectibleComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
|
||||
outBitStream.Write(GetCollectibleId());
|
||||
}
|
||||
|
||||
bool CollectibleComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& cmptType = reportMsg.info->PushDebug("Collectible");
|
||||
auto collectibleID = static_cast<uint32_t>(m_CollectibleId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
|
||||
|
||||
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
|
||||
cmptType.PushDebug<AMFIntValue>("Collectible ID") = GetCollectibleId();
|
||||
cmptType.PushDebug<AMFIntValue>("Mission Tracking ID (for save data)") = collectibleID;
|
||||
|
||||
auto* localCharEntity = Game::entityManager->GetEntity(reportMsg.clientID);
|
||||
bool collected = false;
|
||||
if (localCharEntity) {
|
||||
auto* missionComponent = localCharEntity->GetComponent<MissionComponent>();
|
||||
|
||||
if (m_CollectibleId != 0) {
|
||||
collected = missionComponent->HasCollectible(collectibleID);
|
||||
}
|
||||
}
|
||||
|
||||
cmptType.PushDebug<AMFBoolValue>("Has been collected") = collected;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
class CollectibleComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
|
||||
CollectibleComponent(Entity* parentEntity, int32_t collectibleId) : Component(parentEntity), m_CollectibleId(collectibleId) {}
|
||||
CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId);
|
||||
|
||||
int16_t GetCollectibleId() const { return m_CollectibleId; }
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool isConstruction) override;
|
||||
|
||||
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
private:
|
||||
int16_t m_CollectibleId = 0;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ class Entity;
|
||||
*/
|
||||
class Component {
|
||||
public:
|
||||
Component(Entity* parent) : m_Parent{ parent } {}
|
||||
Component(Entity* parent, const int32_t componentID) : m_Parent{ parent }, m_ComponentID{componentID} {}
|
||||
virtual ~Component() = default;
|
||||
|
||||
/**
|
||||
@@ -28,6 +28,8 @@ public:
|
||||
*/
|
||||
Entity* GetParent() const { return m_Parent; }
|
||||
|
||||
[[nodiscard]] int32_t GetComponentID() const noexcept { return m_ComponentID; }
|
||||
|
||||
/**
|
||||
* Updates the component in the game loop
|
||||
* @param deltaTime time passed since last update
|
||||
@@ -70,4 +72,11 @@ protected:
|
||||
* The entity that owns this component
|
||||
*/
|
||||
Entity* m_Parent;
|
||||
|
||||
// The component ID, this should never be changed after initialization
|
||||
// This is used in various different ways
|
||||
// 1. To identify which entry this component is in its corresponding table
|
||||
// 2. To mark that an Entity should have the component with no database entry (it will be 0 in this case)
|
||||
// 3. The component exists implicitly due to design (CollectibleComponent always has a DestructibleComponent accompanying it). In this case the ID will be -1.
|
||||
const int32_t m_ComponentID;
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "StringifiedEnum.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, int32_t componentId) : PhysicsComponent(entity, componentId) {
|
||||
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, const int32_t componentID) : PhysicsComponent(entity, componentID) {
|
||||
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &ControllablePhysicsComponent::OnGetObjectReportInfo);
|
||||
|
||||
m_Velocity = {};
|
||||
|
||||
@@ -25,7 +25,7 @@ class ControllablePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
|
||||
ControllablePhysicsComponent(Entity* entity, int32_t componentId);
|
||||
ControllablePhysicsComponent(Entity* entity, const int32_t componentID);
|
||||
~ControllablePhysicsComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#include "Logger.h"
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "CDLootMatrixTable.h"
|
||||
#include "CDLootTableTable.h"
|
||||
#include "CDRarityTableTable.h"
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "AmfSerialize.h"
|
||||
@@ -44,7 +47,7 @@
|
||||
Implementation<bool, const Entity*> DestroyableComponent::IsEnemyImplentation;
|
||||
Implementation<bool, const Entity*> DestroyableComponent::IsFriendImplentation;
|
||||
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
m_iArmor = 0;
|
||||
m_fMaxArmor = 0.0f;
|
||||
@@ -694,6 +697,8 @@ void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage)
|
||||
}
|
||||
|
||||
void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType, uint32_t skillID) {
|
||||
if (m_IsDead) return;
|
||||
|
||||
//check if hardcore mode is enabled
|
||||
if (Game::entityManager->GetHardcoreMode()) {
|
||||
DoHardcoreModeDrops(source);
|
||||
@@ -706,6 +711,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
m_IsDead = true;
|
||||
m_KillerID = source;
|
||||
|
||||
auto* owner = Game::entityManager->GetEntity(source);
|
||||
@@ -753,40 +759,11 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
//NANI?!
|
||||
if (!isPlayer) {
|
||||
if (owner != nullptr) {
|
||||
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID());
|
||||
|
||||
if (team != nullptr && m_Parent->GetComponent<BaseCombatAIComponent>() != nullptr) {
|
||||
LWOOBJID specificOwner = LWOOBJID_EMPTY;
|
||||
auto* scriptedActivityComponent = m_Parent->GetComponent<ScriptedActivityComponent>();
|
||||
uint32_t teamSize = team->members.size();
|
||||
uint32_t lootMatrixId = GetLootMatrixID();
|
||||
|
||||
if (scriptedActivityComponent) {
|
||||
lootMatrixId = scriptedActivityComponent->GetLootMatrixForTeamSize(teamSize);
|
||||
}
|
||||
|
||||
if (team->lootOption == 0) { // Round robin
|
||||
specificOwner = TeamManager::Instance()->GetNextLootOwner(team);
|
||||
|
||||
auto* member = Game::entityManager->GetEntity(specificOwner);
|
||||
|
||||
if (member) Loot::DropLoot(member, m_Parent->GetObjectID(), lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
} else {
|
||||
for (const auto memberId : team->members) { // Free for all
|
||||
auto* member = Game::entityManager->GetEntity(memberId);
|
||||
|
||||
if (member == nullptr) continue;
|
||||
|
||||
Loot::DropLoot(member, m_Parent->GetObjectID(), lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else { // drop loot for non team user
|
||||
Loot::DropLoot(owner, m_Parent->GetObjectID(), GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
Loot::DropLoot(owner, m_Parent->GetObjectID(), GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
} else {
|
||||
//Check if this zone allows coin drops
|
||||
if (Game::zoneManager->GetPlayerLoseCoinOnDeath()) {
|
||||
if (Game::zoneManager->GetPlayerLoseCoinOnDeath() && !Game::entityManager->GetHardcoreMode()) {
|
||||
auto* character = m_Parent->GetCharacter();
|
||||
uint64_t coinsTotal = character->GetCoins();
|
||||
const uint64_t minCoinsToLose = Game::zoneManager->GetWorldConfig().coinsLostOnDeathMin;
|
||||
@@ -799,7 +776,15 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
coinsTotal -= coinsToLose;
|
||||
|
||||
Loot::DropLoot(m_Parent, m_Parent->GetObjectID(), -1, coinsToLose, coinsToLose);
|
||||
GameMessages::DropClientLoot lootMsg{};
|
||||
lootMsg.target = m_Parent->GetObjectID();
|
||||
lootMsg.ownerID = m_Parent->GetObjectID();
|
||||
lootMsg.currency = coinsToLose;
|
||||
lootMsg.spawnPos = m_Parent->GetPosition();
|
||||
lootMsg.sourceID = source;
|
||||
lootMsg.item = LOT_NULL;
|
||||
lootMsg.Send();
|
||||
lootMsg.Send(m_Parent->GetSystemAddress());
|
||||
character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
|
||||
}
|
||||
}
|
||||
@@ -1000,7 +985,14 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
for (const auto item : itemMap | std::views::values) {
|
||||
// Don't drop excluded items or null ones
|
||||
if (!item || Game::entityManager->GetHardcoreExcludedItemDrops().contains(item->GetLot())) continue;
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, item->GetLot(), 0, m_Parent->GetPosition(), item->GetCount());
|
||||
GameMessages::DropClientLoot lootMsg{};
|
||||
lootMsg.target = m_Parent->GetObjectID();
|
||||
lootMsg.ownerID = m_Parent->GetObjectID();
|
||||
lootMsg.sourceID = m_Parent->GetObjectID();
|
||||
lootMsg.item = item->GetLot();
|
||||
lootMsg.count = 1;
|
||||
lootMsg.spawnPos = m_Parent->GetPosition();
|
||||
for (int i = 0; i < item->GetCount(); i++) Loot::DropItem(*m_Parent, lootMsg);
|
||||
item->SetCount(0, false, false);
|
||||
}
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
@@ -1012,13 +1004,35 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
//get character:
|
||||
auto* chars = m_Parent->GetCharacter();
|
||||
if (chars) {
|
||||
auto coins = chars->GetCoins();
|
||||
auto oldCoins = chars->GetCoins();
|
||||
// Floor this so there arent coins generated from rounding
|
||||
auto coins = static_cast<uint64_t>(oldCoins * Game::entityManager->GetHardcoreCoinKeep());
|
||||
auto coinsToDrop = oldCoins - coins;
|
||||
LOG("Player had %llu coins, will lose %i coins to have %i", oldCoins, coinsToDrop, coins);
|
||||
|
||||
//lose all coins:
|
||||
chars->SetCoins(0, eLootSourceType::NONE);
|
||||
chars->SetCoins(coins, eLootSourceType::NONE);
|
||||
|
||||
//drop all coins:
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, coins, m_Parent->GetPosition());
|
||||
constexpr auto MAX_TO_DROP_PER_GM = 100'000;
|
||||
GameMessages::DropClientLoot lootMsg{};
|
||||
lootMsg.target = m_Parent->GetObjectID();
|
||||
lootMsg.ownerID = m_Parent->GetObjectID();
|
||||
lootMsg.spawnPos = m_Parent->GetPosition();
|
||||
lootMsg.sourceID = source;
|
||||
lootMsg.item = LOT_NULL;
|
||||
lootMsg.Send();
|
||||
lootMsg.Send(m_Parent->GetSystemAddress());
|
||||
while (coinsToDrop > MAX_TO_DROP_PER_GM) {
|
||||
LOG("Dropping 100,000, %llu left", coinsToDrop);
|
||||
lootMsg.currency = 100'000;
|
||||
lootMsg.Send();
|
||||
lootMsg.Send(m_Parent->GetSystemAddress());
|
||||
coinsToDrop -= 100'000;
|
||||
}
|
||||
lootMsg.currency = coinsToDrop;
|
||||
lootMsg.Send();
|
||||
lootMsg.Send(m_Parent->GetSystemAddress());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1033,8 +1047,8 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
auto maxHealth = GetMaxHealth();
|
||||
const auto uscoreMultiplier = Game::entityManager->GetHardcoreUscoreEnemiesMultiplier();
|
||||
const bool isUscoreReducedLot =
|
||||
Game::entityManager->GetHardcoreUscoreReducedLots().contains(lot) ||
|
||||
Game::entityManager->GetHardcoreUscoreReduced();
|
||||
Game::entityManager->GetHardcoreUscoreReducedLots().contains(lot) ||
|
||||
Game::entityManager->GetHardcoreUscoreReduced();
|
||||
const auto uscoreReduction = isUscoreReducedLot ? Game::entityManager->GetHardcoreUscoreReduction() : 1.0f;
|
||||
|
||||
int uscore = maxHealth * Game::entityManager->GetHardcoreUscoreEnemiesMultiplier() * uscoreReduction;
|
||||
@@ -1049,38 +1063,89 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
|
||||
bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportInfo = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
|
||||
auto& destroyableInfo = reportInfo.info->PushDebug("Destroyable");
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Health") = m_iHealth;
|
||||
destroyableInfo.PushDebug<AMFDoubleValue>("Max Health") = m_fMaxHealth;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Armor") = m_iArmor;
|
||||
destroyableInfo.PushDebug<AMFDoubleValue>("Max Armor") = m_fMaxArmor;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Imagination") = m_iImagination;
|
||||
destroyableInfo.PushDebug<AMFDoubleValue>("Max Imagination") = m_fMaxImagination;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Damage To Absorb") = m_DamageToAbsorb;
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Is GM Immune") = m_IsGMImmune;
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Shielded") = m_IsShielded;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("DestructibleComponent DB Table Template ID") = m_ComponentID;
|
||||
|
||||
if (m_CurrencyIndex == -1) {
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Has Loot Currency") = false;
|
||||
} else {
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Loot Currency ID") = m_CurrencyIndex;
|
||||
auto& detailedCoinInfo = destroyableInfo.PushDebug("Coin Info");
|
||||
detailedCoinInfo.PushDebug<AMFIntValue>("Min Coins") = m_MinCoins;
|
||||
detailedCoinInfo.PushDebug<AMFIntValue>("Max Coins") = m_MaxCoins;
|
||||
}
|
||||
|
||||
if (m_LootMatrixID == -1 || m_LootMatrixID == 0) {
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Has Loot Matrix") = false;
|
||||
} else {
|
||||
auto& lootInfo = destroyableInfo.PushDebug("Loot Info");
|
||||
lootInfo.PushDebug<AMFIntValue>("Loot Matrix ID") = m_LootMatrixID;
|
||||
auto* const componentsRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
auto* const itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
|
||||
auto* const lootMatrixTable = CDClientManager::GetTable<CDLootMatrixTable>();
|
||||
auto* const lootTableTable = CDClientManager::GetTable<CDLootTableTable>();
|
||||
auto* const rarityTableTable = CDClientManager::GetTable<CDRarityTableTable>();
|
||||
|
||||
const auto& matrix = lootMatrixTable->GetMatrix(m_LootMatrixID);
|
||||
|
||||
for (const auto& entry : matrix) {
|
||||
auto& thisEntry = lootInfo.PushDebug("Loot table Index - " + std::to_string(entry.LootTableIndex));
|
||||
thisEntry.PushDebug<AMFDoubleValue>("Percent chance to drop") = entry.percent * 100.0f;
|
||||
thisEntry.PushDebug<AMFDoubleValue>("Minimum amount to drop") = entry.minToDrop;
|
||||
thisEntry.PushDebug<AMFDoubleValue>("Maximum amount to drop") = entry.maxToDrop;
|
||||
const auto& lootTable = lootTableTable->GetTable(entry.LootTableIndex);
|
||||
const auto& rarityTable = rarityTableTable->GetRarityTable(entry.RarityTableIndex);
|
||||
|
||||
auto& thisRarity = thisEntry.PushDebug("Rarity");
|
||||
for (const auto& rarity : rarityTable) {
|
||||
thisRarity.PushDebug<AMFDoubleValue>("Rarity " + std::to_string(rarity.rarity)) = rarity.randmax;
|
||||
}
|
||||
|
||||
auto& thisItems = thisEntry.PushDebug("Drop(s) Info");
|
||||
for (const auto& loot : lootTable) {
|
||||
uint32_t itemComponentId = componentsRegistryTable->GetByIDAndType(loot.itemid, eReplicaComponentType::ITEM);
|
||||
uint32_t rarity = itemComponentTable->GetItemComponentByID(itemComponentId).rarity;
|
||||
auto title = "%[Objects_" + std::to_string(loot.itemid) + "_name] " + std::to_string(loot.itemid);
|
||||
if (loot.MissionDrop) title += " - Mission Drop";
|
||||
thisItems.PushDebug(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto* const entity = Game::entityManager->GetEntity(reportInfo.clientID);
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Is on your team") = entity ? IsFriend(entity) : false;
|
||||
auto& stats = destroyableInfo.PushDebug("Statistics");
|
||||
stats.PushDebug<AMFIntValue>("Health") = m_iHealth;
|
||||
stats.PushDebug<AMFDoubleValue>("Maximum Health") = m_fMaxHealth;
|
||||
stats.PushDebug<AMFIntValue>("Armor") = m_iArmor;
|
||||
stats.PushDebug<AMFDoubleValue>("Maximum Armor") = m_fMaxArmor;
|
||||
stats.PushDebug<AMFIntValue>("Imagination") = m_iImagination;
|
||||
stats.PushDebug<AMFDoubleValue>("Maximum Imagination") = m_fMaxImagination;
|
||||
stats.PushDebug<AMFIntValue>("Damage Absorption Points") = m_DamageToAbsorb;
|
||||
stats.PushDebug<AMFBoolValue>("Is GM Immune") = m_IsGMImmune;
|
||||
stats.PushDebug<AMFBoolValue>("Is Shielded") = m_IsShielded;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Attacks To Block") = m_AttacksToBlock;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Damage Reduction") = m_DamageReduction;
|
||||
auto& factions = destroyableInfo.PushDebug("Factions");
|
||||
size_t i = 0;
|
||||
std::stringstream factionsStream;
|
||||
for (const auto factionID : m_FactionIDs) {
|
||||
factions.PushDebug<AMFStringValue>(std::to_string(i++) + " " + std::to_string(factionID)) = "";
|
||||
factionsStream << factionID << " ";
|
||||
}
|
||||
auto& enemyFactions = destroyableInfo.PushDebug("Enemy Factions");
|
||||
i = 0;
|
||||
|
||||
destroyableInfo.PushDebug<AMFStringValue>("Factions") = factionsStream.str();
|
||||
|
||||
factionsStream.str("");
|
||||
for (const auto enemyFactionID : m_EnemyFactionIDs) {
|
||||
enemyFactions.PushDebug<AMFStringValue>(std::to_string(i++) + " " + std::to_string(enemyFactionID)) = "";
|
||||
factionsStream << enemyFactionID << " ";
|
||||
}
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashable") = m_IsSmashable;
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Dead") = m_IsDead;
|
||||
|
||||
destroyableInfo.PushDebug<AMFStringValue>("Enemy Factions") = factionsStream.str();
|
||||
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Is A Smashable") = m_IsSmashable;
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashed") = m_IsSmashed;
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Module Assembly") = m_IsModuleAssembly;
|
||||
destroyableInfo.PushDebug<AMFDoubleValue>("Explode Factor") = m_ExplodeFactor;
|
||||
destroyableInfo.PushDebug<AMFBoolValue>("Has Threats") = m_HasThreats;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Loot Matrix ID") = m_LootMatrixID;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Min Coins") = m_MinCoins;
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Max Coins") = m_MaxCoins;
|
||||
|
||||
destroyableInfo.PushDebug<AMFStringValue>("Killer ID") = std::to_string(m_KillerID);
|
||||
|
||||
// "Scripts"; idk what to do about scripts yet
|
||||
@@ -1095,7 +1160,25 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
immuneCounts.PushDebug<AMFIntValue>("Quickbuild Interrupt") = m_ImmuneToQuickbuildInterruptCount;
|
||||
immuneCounts.PushDebug<AMFIntValue>("Pull To Point") = m_ImmuneToPullToPointCount;
|
||||
|
||||
destroyableInfo.PushDebug<AMFIntValue>("Death Behavior") = m_DeathBehavior;
|
||||
auto& deathInfo = destroyableInfo.PushDebug("Death Info");
|
||||
deathInfo.PushDebug<AMFBoolValue>("Is Dead") = m_IsDead;
|
||||
switch (m_DeathBehavior) {
|
||||
case 0:
|
||||
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Fade";
|
||||
break;
|
||||
case 1:
|
||||
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Stay";
|
||||
break;
|
||||
case 2:
|
||||
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Immediate";
|
||||
break;
|
||||
case -1:
|
||||
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Invulnerable";
|
||||
break;
|
||||
default:
|
||||
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Other";
|
||||
break;
|
||||
}
|
||||
destroyableInfo.PushDebug<AMFDoubleValue>("Damage Cooldown Timer") = m_DamageCooldownTimer;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -26,7 +26,7 @@ class DestroyableComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
|
||||
|
||||
DestroyableComponent(Entity* parentEntity);
|
||||
DestroyableComponent(Entity* parentEntity, const int32_t componentID);
|
||||
~DestroyableComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
@@ -370,6 +370,8 @@ public:
|
||||
*/
|
||||
uint32_t GetLootMatrixID() const { return m_LootMatrixID; }
|
||||
|
||||
void SetCurrencyIndex(int32_t currencyIndex) { m_CurrencyIndex = currencyIndex; }
|
||||
|
||||
/**
|
||||
* Returns the ID of the entity that killed this entity, if any
|
||||
* @return the ID of the entity that killed this entity, if any
|
||||
@@ -471,6 +473,8 @@ public:
|
||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
bool OnSetFaction(GameMessages::GameMsg& msg);
|
||||
|
||||
void SetIsDead(const bool value) { m_IsDead = value; }
|
||||
|
||||
static Implementation<bool, const Entity*> IsEnemyImplentation;
|
||||
static Implementation<bool, const Entity*> IsFriendImplentation;
|
||||
|
||||
@@ -585,6 +589,9 @@ private:
|
||||
*/
|
||||
uint32_t m_LootMatrixID;
|
||||
|
||||
// The currency index to determine how much loot to drop
|
||||
int32_t m_CurrencyIndex{ -1 };
|
||||
|
||||
/**
|
||||
* The min amount of coins that will drop when this entity is smashed
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "DonationVendorComponent.h"
|
||||
#include "Database.h"
|
||||
|
||||
DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
DonationVendorComponent::DonationVendorComponent(Entity* parent, const int32_t componentID) : VendorComponent(parent, componentID) {
|
||||
//LoadConfigData
|
||||
m_PercentComplete = 0.0;
|
||||
m_TotalDonated = 0;
|
||||
|
||||
@@ -9,7 +9,7 @@ class Entity;
|
||||
class DonationVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
|
||||
DonationVendorComponent(Entity* parent);
|
||||
DonationVendorComponent(Entity* parent, const int32_t componentID);
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
uint32_t GetActivityID() {return m_ActivityId;};
|
||||
void SubmitDonation(uint32_t count);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#include "GhostComponent.h"
|
||||
|
||||
GhostComponent::GhostComponent(Entity* parent) : Component(parent) {
|
||||
#include "Amf3.h"
|
||||
#include "GameMessages.h"
|
||||
|
||||
GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_GhostReferencePoint = NiPoint3Constant::ZERO;
|
||||
m_GhostOverridePoint = NiPoint3Constant::ZERO;
|
||||
m_GhostOverride = false;
|
||||
|
||||
RegisterMsg<GameMessages::GetObjectReportInfo>(this, &GhostComponent::MsgGetObjectReportInfo);
|
||||
}
|
||||
|
||||
GhostComponent::~GhostComponent() {
|
||||
@@ -55,3 +60,12 @@ bool GhostComponent::IsObserved(LWOOBJID id) {
|
||||
void GhostComponent::GhostEntity(LWOOBJID id) {
|
||||
m_ObservedEntities.erase(id);
|
||||
}
|
||||
|
||||
bool GhostComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& cmptType = reportMsg.info->PushDebug("Ghost");
|
||||
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
cmptType.PushDebug<AMFBoolValue>("Is GM Invis") = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ class NiPoint3;
|
||||
class GhostComponent final : public Component {
|
||||
public:
|
||||
static inline const eReplicaComponentType ComponentType = eReplicaComponentType::GHOST;
|
||||
GhostComponent(Entity* parent);
|
||||
GhostComponent(Entity* parent, const int32_t componentID);
|
||||
~GhostComponent() override;
|
||||
|
||||
void SetGhostOverride(bool value) { m_GhostOverride = value; };
|
||||
@@ -39,6 +39,8 @@ public:
|
||||
|
||||
void GhostEntity(const LWOOBJID id);
|
||||
|
||||
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
private:
|
||||
NiPoint3 m_GhostReferencePoint;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "EntityManager.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
|
||||
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent, const int32_t componentID) : PhysicsComponent(parent, componentID) {
|
||||
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &HavokVehiclePhysicsComponent::OnGetObjectReportInfo);
|
||||
|
||||
m_Velocity = NiPoint3Constant::ZERO;
|
||||
|
||||
@@ -13,7 +13,7 @@ class HavokVehiclePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS;
|
||||
|
||||
HavokVehiclePhysicsComponent(Entity* parentEntity, int32_t componentId);
|
||||
HavokVehiclePhysicsComponent(Entity* parentEntity, const int32_t componentID);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
|
||||
@@ -39,10 +39,13 @@
|
||||
#include "CDObjectSkillsTable.h"
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
|
||||
InventoryComponent::InventoryComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
RegisterMsg<GetObjectReportInfo>(this, &InventoryComponent::OnGetObjectReportInfo);
|
||||
this->m_Dirty = true;
|
||||
this->m_Equipped = {};
|
||||
this->m_Pushed = {};
|
||||
@@ -279,7 +282,14 @@ void InventoryComponent::AddItem(
|
||||
|
||||
case 1:
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
GameMessages::SendDropClientLoot(this->m_Parent, this->m_Parent->GetObjectID(), lot, 0, this->m_Parent->GetPosition(), 1);
|
||||
GameMessages::DropClientLoot lootMsg{};
|
||||
lootMsg.target = m_Parent->GetObjectID();
|
||||
lootMsg.ownerID = m_Parent->GetObjectID();
|
||||
lootMsg.sourceID = m_Parent->GetObjectID();
|
||||
lootMsg.item = lot;
|
||||
lootMsg.count = 1;
|
||||
lootMsg.spawnPos = m_Parent->GetPosition();
|
||||
Loot::DropItem(*m_Parent, lootMsg);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -440,7 +450,7 @@ Item* InventoryComponent::FindItemBySubKey(LWOOBJID id, eInventoryType inventory
|
||||
}
|
||||
}
|
||||
|
||||
bool InventoryComponent::HasSpaceForLoot(const std::unordered_map<LOT, int32_t>& loot) {
|
||||
bool InventoryComponent::HasSpaceForLoot(const Loot::Return& loot) {
|
||||
std::unordered_map<eInventoryType, int32_t> spaceOffset{};
|
||||
|
||||
uint32_t slotsNeeded = 0;
|
||||
@@ -626,7 +636,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
|
||||
for (const auto& pair : this->m_Inventories) {
|
||||
auto* inventory = pair.second;
|
||||
|
||||
static const auto EXCLUDED_INVENTORIES = {VENDOR_BUYBACK, MODELS_IN_BBB, ITEM_SETS};
|
||||
static const auto EXCLUDED_INVENTORIES = { VENDOR_BUYBACK, MODELS_IN_BBB, ITEM_SETS };
|
||||
if (std::ranges::find(EXCLUDED_INVENTORIES, inventory->GetType()) != EXCLUDED_INVENTORIES.end()) {
|
||||
continue;
|
||||
}
|
||||
@@ -1793,3 +1803,99 @@ void InventoryComponent::RegenerateItemIDs() {
|
||||
inventory->RegenerateItemIDs();
|
||||
}
|
||||
}
|
||||
|
||||
std::string DebugInvToString(const eInventoryType inv, bool verbose) {
|
||||
switch (inv) {
|
||||
case ITEMS:
|
||||
return "Backpack";
|
||||
case VAULT_ITEMS:
|
||||
return "Bank";
|
||||
case BRICKS:
|
||||
return verbose ? "Bricks" : "Bricks (contents only shown in high-detail report)";
|
||||
case MODELS_IN_BBB:
|
||||
return "Models in BBB";
|
||||
case TEMP_ITEMS:
|
||||
return "Temp Equip";
|
||||
case MODELS:
|
||||
return verbose ? "Model" : "Model (contents only shown in high-detail report)";
|
||||
case TEMP_MODELS:
|
||||
return "Module";
|
||||
case BEHAVIORS:
|
||||
return "B3 Behavior";
|
||||
case PROPERTY_DEEDS:
|
||||
return "Property";
|
||||
case BRICKS_IN_BBB:
|
||||
return "Brick In BBB";
|
||||
case VENDOR:
|
||||
return "Vendor";
|
||||
case VENDOR_BUYBACK:
|
||||
return "BuyBack";
|
||||
case QUEST:
|
||||
return "Quest";
|
||||
case DONATION:
|
||||
return "Donation";
|
||||
case VAULT_MODELS:
|
||||
return "Bank Model";
|
||||
case ITEM_SETS:
|
||||
return "Bank Behavior";
|
||||
case INVALID:
|
||||
return "Invalid";
|
||||
case ALL:
|
||||
return "All";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool InventoryComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& report = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& cmpt = report.info->PushDebug("Inventory");
|
||||
cmpt.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
uint32_t numItems = 0;
|
||||
for (auto* inventory : m_Inventories | std::views::values) numItems += inventory->GetItems().size();
|
||||
cmpt.PushDebug<AMFIntValue>("Inventory Item Count") = numItems;
|
||||
|
||||
auto& itemsInBags = cmpt.PushDebug("Items in bags");
|
||||
for (const auto& [id, inventoryMut] : m_Inventories) {
|
||||
if (!inventoryMut) continue;
|
||||
const auto* const inventory = inventoryMut;
|
||||
auto& curInv = itemsInBags.PushDebug(DebugInvToString(id, report.bVerbose) + " - " + std::to_string(id));
|
||||
for (uint32_t i = 0; i < inventory->GetSize(); i++) {
|
||||
const auto* const item = inventory->FindItemBySlot(i);
|
||||
if (!item) continue;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "%[Objects_" << item->GetLot() << "_name] Slot " << item->GetSlot();
|
||||
auto& slot = curInv.PushDebug(ss.str());
|
||||
slot.PushDebug<AMFStringValue>("Object ID") = std::to_string(item->GetId());
|
||||
slot.PushDebug<AMFIntValue>("LOT") = item->GetLot();
|
||||
if (item->GetSubKey() != LWOOBJID_EMPTY) slot.PushDebug<AMFStringValue>("Subkey") = std::to_string(item->GetSubKey());
|
||||
slot.PushDebug<AMFIntValue>("Count") = item->GetCount();
|
||||
slot.PushDebug<AMFIntValue>("Slot") = item->GetSlot();
|
||||
slot.PushDebug<AMFBoolValue>("Bind on pickup") = item->GetInfo().isBOP;
|
||||
slot.PushDebug<AMFBoolValue>("Bind on equip") = item->GetInfo().isBOE;
|
||||
slot.PushDebug<AMFBoolValue>("Is currently bound") = item->GetBound();
|
||||
auto& extra = slot.PushDebug("Extra Info");
|
||||
for (const auto* const setting : item->GetConfig()) {
|
||||
if (setting) extra.PushDebug<AMFStringValue>(GeneralUtils::UTF16ToWTF8(setting->GetKey())) = setting->GetValueAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& equipped = cmpt.PushDebug("Equipped Items");
|
||||
for (const auto& [location, info] : GetEquippedItems()) {
|
||||
std::stringstream ss;
|
||||
ss << "%[Objects_" << info.lot << "_name]";
|
||||
auto& equipSlot = equipped.PushDebug(ss.str());
|
||||
equipSlot.PushDebug<AMFStringValue>("Location") = location;
|
||||
equipSlot.PushDebug<AMFStringValue>("Object ID") = std::to_string(info.id);
|
||||
equipSlot.PushDebug<AMFIntValue>("Slot") = info.slot;
|
||||
equipSlot.PushDebug<AMFIntValue>("Count") = info.count;
|
||||
auto& extra = equipSlot.PushDebug("Extra Info");
|
||||
for (const auto* const setting : info.config) {
|
||||
if (setting) extra.PushDebug<AMFStringValue>(GeneralUtils::UTF16ToWTF8(setting->GetKey())) = setting->GetValueAsString();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "eInventoryType.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "eLootSourceType.h"
|
||||
#include "Loot.h"
|
||||
|
||||
class Entity;
|
||||
class ItemSet;
|
||||
@@ -67,7 +68,7 @@ public:
|
||||
static constexpr uint32_t MaximumGroupCount = 50;
|
||||
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
||||
InventoryComponent(Entity* parent);
|
||||
InventoryComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
@@ -200,7 +201,7 @@ public:
|
||||
* @param loot a map of items to add and how many to add
|
||||
* @return whether the entity has enough space for all the items
|
||||
*/
|
||||
bool HasSpaceForLoot(const std::unordered_map<LOT, int32_t>& loot);
|
||||
bool HasSpaceForLoot(const Loot::Return& loot);
|
||||
|
||||
/**
|
||||
* Equips an item in the specified slot
|
||||
@@ -410,6 +411,8 @@ public:
|
||||
// Used to migrate a character version, no need to call outside of that context
|
||||
void RegenerateItemIDs();
|
||||
|
||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
~InventoryComponent() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -8,7 +8,7 @@ class ItemComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ITEM;
|
||||
|
||||
ItemComponent(Entity* entity) : Component(entity) {}
|
||||
ItemComponent(Entity* entity, const int32_t componentID) : Component(entity, componentID) {}
|
||||
|
||||
void Serialize(RakNet::BitStream& bitStream, bool isConstruction) override;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ class LUPExhibitComponent final : public Component
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LUP_EXHIBIT;
|
||||
|
||||
LUPExhibitComponent(Entity* parent) : Component(parent) {};
|
||||
LUPExhibitComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {};
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
void NextLUPExhibit();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "CDRewardsTable.h"
|
||||
|
||||
LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component(parent) {
|
||||
LevelProgressionComponent::LevelProgressionComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Parent = parent;
|
||||
m_Level = 1;
|
||||
m_SpeedBase = 500.0f;
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
* Constructor for this component
|
||||
* @param parent parent that contains this component
|
||||
*/
|
||||
LevelProgressionComponent(Entity* parent);
|
||||
LevelProgressionComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ class MiniGameControlComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MINI_GAME_CONTROL;
|
||||
|
||||
MiniGameControlComponent(Entity* parent) : Component(parent) {}
|
||||
MiniGameControlComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {}
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool isConstruction);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#include "MinigameComponent.h"
|
||||
|
||||
void MinigameComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
|
||||
outBitStream.Write<uint32_t>(0x40000000);
|
||||
}
|
||||
@@ -26,8 +26,13 @@
|
||||
std::unordered_map<AchievementCacheKey, std::vector<uint32_t>> MissionComponent::m_AchievementCache = {};
|
||||
|
||||
//! Initializer
|
||||
MissionComponent::MissionComponent(Entity* parent) : Component(parent) {
|
||||
MissionComponent::MissionComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
m_LastUsedMissionOrderUID = Game::zoneManager->GetUniqueMissionIdStartingValue();
|
||||
|
||||
RegisterMsg<GetObjectReportInfo>(this, &MissionComponent::OnGetObjectReportInfo);
|
||||
RegisterMsg<GameMessages::GetMissionState>(this, &MissionComponent::OnGetMissionState);
|
||||
RegisterMsg<GameMessages::MissionNeedsLot>(this, &MissionComponent::OnMissionNeedsLot);
|
||||
}
|
||||
|
||||
//! Destructor
|
||||
@@ -622,3 +627,123 @@ void MissionComponent::ResetMission(const int32_t missionId) {
|
||||
m_Missions.erase(missionId);
|
||||
GameMessages::SendResetMissions(m_Parent, m_Parent->GetSystemAddress(), missionId);
|
||||
}
|
||||
|
||||
void PushMissions(const std::map<uint32_t, Mission*>& missions, AMFArrayValue& V, bool verbose) {
|
||||
for (const auto& [id, mission] : missions) {
|
||||
std::stringstream ss;
|
||||
if (!mission) {
|
||||
ss << "Mission ID: " << id;
|
||||
V.PushDebug(ss.str());
|
||||
} else if (!verbose) {
|
||||
ss << "%[Missions_" << id << "_name]" << ", Mission ID";
|
||||
V.PushDebug<AMFIntValue>(ss.str()) = id;
|
||||
} else {
|
||||
ss << "%[Missions_" << id << "_name]" << ", Mission ID: " << id;
|
||||
auto& missionV = V.PushDebug(ss.str());
|
||||
auto& missionInformation = missionV.PushDebug("Mission Information");
|
||||
|
||||
if (mission->IsComplete()) {
|
||||
missionInformation.PushDebug<AMFStringValue>("Time mission last completed") = std::to_string(mission->GetTimestamp());
|
||||
missionInformation.PushDebug<AMFIntValue>("Number of times completed") = mission->GetCompletions();
|
||||
}
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// missionInformation.PushDebug("Description").PushDebug("None");
|
||||
// missionInformation.PushDebug("Text").PushDebug("None");
|
||||
|
||||
auto& statusInfo = missionInformation.PushDebug("Mission statuses for local player");
|
||||
if (mission->IsAvalible()) statusInfo.PushDebug("Available");
|
||||
if (mission->IsActive()) statusInfo.PushDebug("Active");
|
||||
if (mission->IsReadyToComplete()) statusInfo.PushDebug("Ready To Complete");
|
||||
if (mission->IsComplete()) statusInfo.PushDebug("Completed");
|
||||
if (mission->IsFailed()) statusInfo.PushDebug("Failed");
|
||||
const auto& clientInfo = mission->GetClientInfo();
|
||||
|
||||
statusInfo.PushDebug<AMFBoolValue>("Is an achievement mission") = mission->IsAchievement();
|
||||
statusInfo.PushDebug<AMFBoolValue>("Is an timed mission") = clientInfo.time_limit > 0;
|
||||
auto& taskInfo = statusInfo.PushDebug("Task Info");
|
||||
taskInfo.PushDebug<AMFIntValue>("Number of tasks in this mission") = mission->GetTasks().size();
|
||||
int32_t i = 0;
|
||||
for (const auto* task : mission->GetTasks()) {
|
||||
auto& thisTask = taskInfo.PushDebug("Task " + std::to_string(i));
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// thisTask.PushDebug("Description").PushDebug("%[MissionTasks_" + taskUidStr + "_description]");
|
||||
thisTask.PushDebug<AMFIntValue>("Number done") = std::min(task->GetProgress(), static_cast<uint32_t>(task->GetClientInfo().targetValue));
|
||||
thisTask.PushDebug<AMFIntValue>("Number total needed") = task->GetClientInfo().targetValue;
|
||||
thisTask.PushDebug<AMFIntValue>("Task Type") = task->GetClientInfo().taskType;
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
// auto& chatText = missionInformation.PushDebug("Chat Text for Mission States");
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// chatText.PushDebug("Available Text").PushDebug("%[MissionText_" + idStr + "_chat_state_1]");
|
||||
// chatText.PushDebug("Active Text").PushDebug("%[MissionText_" + idStr + "_chat_state_2]");
|
||||
// chatText.PushDebug("Ready-to-Complete Text").PushDebug("%[MissionText_" + idStr + "_chat_state_3]");
|
||||
// chatText.PushDebug("Complete Text").PushDebug("%[MissionText_" + idStr + "_chat_state_4]");
|
||||
|
||||
if (clientInfo.time_limit > 0) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Time Limit") = clientInfo.time_limit;
|
||||
missionInformation.PushDebug<AMFDoubleValue>("Time Remaining") = 0;
|
||||
}
|
||||
|
||||
if (clientInfo.offer_objectID != -1) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Offer Object LOT") = clientInfo.offer_objectID;
|
||||
}
|
||||
|
||||
if (clientInfo.target_objectID != -1) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Complete Object LOT") = clientInfo.target_objectID;
|
||||
}
|
||||
|
||||
if (!clientInfo.prereqMissionID.empty()) {
|
||||
missionInformation.PushDebug<AMFStringValue>("Requirement Mission IDs") = clientInfo.prereqMissionID;
|
||||
}
|
||||
|
||||
missionInformation.PushDebug<AMFBoolValue>("Is Repeatable") = clientInfo.repeatable;
|
||||
const bool hasNoOfferer = clientInfo.offer_objectID == -1 || clientInfo.offer_objectID == 0;
|
||||
const bool hasNoCompleter = clientInfo.target_objectID == -1 || clientInfo.target_objectID == 0;
|
||||
missionInformation.PushDebug<AMFBoolValue>("Is Achievement") = hasNoOfferer && hasNoCompleter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MissionComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& missionInfo = reportMsg.info->PushDebug("Mission (Laggy)");
|
||||
missionInfo.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
// Sort the missions so they are easier to parse and present to the end user
|
||||
std::map<uint32_t, Mission*> achievements;
|
||||
std::map<uint32_t, Mission*> missions;
|
||||
std::map<uint32_t, Mission*> doneMissions;
|
||||
for (const auto [id, mission] : m_Missions) {
|
||||
if (!mission) continue;
|
||||
else if (mission->IsComplete()) doneMissions[id] = mission;
|
||||
else if (mission->IsAchievement()) achievements[id] = mission;
|
||||
else if (mission->IsMission()) missions[id] = mission;
|
||||
}
|
||||
|
||||
// None of these should be empty, but if they are dont print the field
|
||||
if (!achievements.empty() || !missions.empty()) {
|
||||
auto& incompleteMissions = missionInfo.PushDebug("Incomplete Missions");
|
||||
PushMissions(achievements, incompleteMissions, reportMsg.bVerbose);
|
||||
PushMissions(missions, incompleteMissions, reportMsg.bVerbose);
|
||||
}
|
||||
|
||||
if (!doneMissions.empty()) {
|
||||
auto& completeMissions = missionInfo.PushDebug("Completed Missions");
|
||||
PushMissions(doneMissions, completeMissions, reportMsg.bVerbose);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MissionComponent::OnGetMissionState(GameMessages::GameMsg& msg) {
|
||||
auto misState = static_cast<GameMessages::GetMissionState&>(msg);
|
||||
misState.missionState = GetMissionState(misState.missionID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MissionComponent::OnMissionNeedsLot(GameMessages::GameMsg& msg) {
|
||||
const auto& needMsg = static_cast<GameMessages::MissionNeedsLot&>(msg);
|
||||
return RequiresItem(needMsg.item);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class MissionComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION;
|
||||
|
||||
explicit MissionComponent(Entity* parent);
|
||||
explicit MissionComponent(Entity* parent, const int32_t componentID);
|
||||
~MissionComponent() override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
|
||||
@@ -171,6 +171,9 @@ public:
|
||||
|
||||
void ResetMission(const int32_t missionId);
|
||||
private:
|
||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
bool OnGetMissionState(GameMessages::GameMsg& msg);
|
||||
bool OnMissionNeedsLot(GameMessages::GameMsg& msg);
|
||||
/**
|
||||
* All the missions owned by this entity, mapped by mission ID
|
||||
*/
|
||||
|
||||
@@ -39,19 +39,13 @@ bool OfferedMission::GetAcceptsMission() const {
|
||||
|
||||
//------------------------ MissionOfferComponent below ------------------------
|
||||
|
||||
MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot) : Component(parent) {
|
||||
auto* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
|
||||
auto value = compRegistryTable->GetByIDAndType(parentLot, eReplicaComponentType::MISSION_OFFER, -1);
|
||||
|
||||
if (value != -1) {
|
||||
const uint32_t componentId = value;
|
||||
|
||||
MissionOfferComponent::MissionOfferComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
if (componentID != -1) {
|
||||
// Now lookup the missions in the MissionNPCComponent table
|
||||
auto* missionNpcComponentTable = CDClientManager::GetTable<CDMissionNPCComponentTable>();
|
||||
|
||||
auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
|
||||
return entry.id == static_cast<unsigned>(componentId);
|
||||
auto missions = missionNpcComponentTable->Query([componentID](const CDMissionNPCComponent& entry) {
|
||||
return entry.id == static_cast<unsigned>(componentID);
|
||||
});
|
||||
|
||||
for (auto& mission : missions) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class MissionOfferComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER;
|
||||
|
||||
MissionOfferComponent(Entity* parent, LOT parentLot);
|
||||
MissionOfferComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
/**
|
||||
* Handles the OnUse event triggered by some entity, determines which missions to show based on what they may
|
||||
|
||||
@@ -14,16 +14,18 @@
|
||||
#include "Database.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||
ModelComponent::ModelComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
||||
m_OriginalRotation = m_Parent->GetDefaultRotation();
|
||||
LOG("%f %f %f %f", m_OriginalRotation.x, m_OriginalRotation.y, m_OriginalRotation.z, m_OriginalRotation.w);
|
||||
m_IsPaused = false;
|
||||
m_NumListeningInteract = 0;
|
||||
|
||||
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
||||
RegisterMsg<RequestUse>(this, &ModelComponent::OnRequestUse);
|
||||
RegisterMsg<ResetModelToDefaults>(this, &ModelComponent::OnResetModelToDefaults);
|
||||
RegisterMsg<GetObjectReportInfo>(this, &ModelComponent::OnGetObjectReportInfo);
|
||||
}
|
||||
|
||||
bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
|
||||
@@ -37,6 +39,10 @@ bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
|
||||
m_Parent->SetPosition(m_OriginalPosition);
|
||||
m_Parent->SetRotation(m_OriginalRotation);
|
||||
m_Parent->SetVelocity(NiPoint3Constant::ZERO);
|
||||
GameMessages::SetAngularVelocity setAngVel;
|
||||
setAngVel.target = m_Parent->GetObjectID();
|
||||
setAngVel.angVelocity = NiPoint3Constant::ZERO;
|
||||
setAngVel.Send();
|
||||
|
||||
m_Speed = 3.0f;
|
||||
m_NumListeningInteract = 0;
|
||||
@@ -303,6 +309,38 @@ void ModelComponent::SetVelocity(const NiPoint3& velocity) const {
|
||||
m_Parent->SetVelocity(velocity);
|
||||
}
|
||||
|
||||
bool ModelComponent::TrySetAngularVelocity(const NiPoint3& angularVelocity) const {
|
||||
GameMessages::GetAngularVelocity getAngVel{};
|
||||
getAngVel.target = m_Parent->GetObjectID();
|
||||
if (!getAngVel.Send()) {
|
||||
LOG("Couldn't get angular velocity for %llu", m_Parent->GetObjectID());
|
||||
return false;
|
||||
}
|
||||
|
||||
GameMessages::SetAngularVelocity setAngVel{};
|
||||
setAngVel.target = m_Parent->GetObjectID();
|
||||
if (angularVelocity != NiPoint3Constant::ZERO) {
|
||||
setAngVel.angVelocity = getAngVel.angVelocity;
|
||||
const auto [x, y, z] = angularVelocity * m_Speed;
|
||||
if (x != 0.0f) {
|
||||
if (getAngVel.angVelocity.x != 0.0f) return false;
|
||||
setAngVel.angVelocity.x = x;
|
||||
} else if (y != 0.0f) {
|
||||
if (getAngVel.angVelocity.y != 0.0f) return false;
|
||||
setAngVel.angVelocity.y = y;
|
||||
} else if (z != 0.0f) {
|
||||
if (getAngVel.angVelocity.z != 0.0f) return false;
|
||||
setAngVel.angVelocity.z = z;
|
||||
}
|
||||
} else {
|
||||
setAngVel.angVelocity = angularVelocity;
|
||||
}
|
||||
LOG("Setting angular velocity to %f %f %f", setAngVel.angVelocity.x, setAngVel.angVelocity.y, setAngVel.angVelocity.z);
|
||||
setAngVel.Send();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelComponent::OnChatMessageReceived(const std::string& sMessage) {
|
||||
for (auto& behavior : m_Behaviors) behavior.OnChatMessageReceived(sMessage);
|
||||
}
|
||||
@@ -338,3 +376,19 @@ void ModelComponent::RemoveAttack() {
|
||||
set.Send();
|
||||
}
|
||||
}
|
||||
|
||||
bool ModelComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
if (!reportMsg.info) return false;
|
||||
auto& cmptInfo = reportMsg.info->PushDebug("Model Behaviors (Mutable)");
|
||||
cmptInfo.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
|
||||
cmptInfo.PushDebug<AMFStringValue>("Name") = "Objects_" + std::to_string(m_Parent->GetLOT()) + "_name";
|
||||
cmptInfo.PushDebug<AMFBoolValue>("Has Unique Name") = false;
|
||||
cmptInfo.PushDebug<AMFStringValue>("UGID (from item)") = std::to_string(m_userModelID);
|
||||
cmptInfo.PushDebug<AMFStringValue>("UGID") = std::to_string(m_userModelID);
|
||||
cmptInfo.PushDebug<AMFStringValue>("Description") = "";
|
||||
cmptInfo.PushDebug<AMFIntValue>("Behavior Count") = m_Behaviors.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -27,13 +27,14 @@ class ModelComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODEL;
|
||||
|
||||
ModelComponent(Entity* parent);
|
||||
ModelComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
void LoadBehaviors();
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
bool OnRequestUse(GameMessages::GameMsg& msg);
|
||||
bool OnResetModelToDefaults(GameMessages::GameMsg& msg);
|
||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -144,6 +145,11 @@ public:
|
||||
// Force sets the velocity to a value.
|
||||
void SetVelocity(const NiPoint3& velocity) const;
|
||||
|
||||
// Attempts to set the angular velocity of the model.
|
||||
// If the axis currently has a velocity of zero, returns true.
|
||||
// If the axis is currently controlled by a behavior, returns false.
|
||||
bool TrySetAngularVelocity(const NiPoint3& angularVelocity) const;
|
||||
|
||||
void OnChatMessageReceived(const std::string& sMessage);
|
||||
|
||||
void OnHit();
|
||||
@@ -161,6 +167,8 @@ public:
|
||||
// Decrements the number of strips listening for an attack.
|
||||
// If this is the last strip removing an attack, it will reset the factions to the default of -1.
|
||||
void RemoveAttack();
|
||||
|
||||
float GetSpeed() const noexcept { return m_Speed; }
|
||||
private:
|
||||
|
||||
// Loads a behavior from the database.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ModuleAssemblyComponent.h"
|
||||
|
||||
ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent) : Component(parent) {
|
||||
ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_SubKey = LWOOBJID_EMPTY;
|
||||
m_UseOptionalParts = false;
|
||||
m_AssemblyPartsLOTs = u"";
|
||||
|
||||
@@ -14,7 +14,7 @@ class ModuleAssemblyComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY;
|
||||
|
||||
ModuleAssemblyComponent(Entity* parent);
|
||||
ModuleAssemblyComponent(Entity* parent, const int32_t componentID);
|
||||
~ModuleAssemblyComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace {
|
||||
std::map<LOT, float> m_PhysicsSpeedCache;
|
||||
}
|
||||
|
||||
MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) {
|
||||
MovementAIComponent::MovementAIComponent(Entity* parent, const int32_t componentID, MovementAIInfo info) : Component(parent, componentID) {
|
||||
m_Info = info;
|
||||
m_AtFinalWaypoint = true;
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class MovementAIComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI;
|
||||
|
||||
MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
|
||||
MovementAIComponent(Entity* parentEntity, const int32_t componentID, MovementAIInfo info);
|
||||
|
||||
void SetPath(const std::string pathName);
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ void MoverSubComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsIniti
|
||||
|
||||
//------------- MovingPlatformComponent below --------------
|
||||
|
||||
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) {
|
||||
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const int32_t componentID, const std::string& pathName) : Component(parent, componentID) {
|
||||
m_MoverSubComponentType = eMoverSubComponentType::mover;
|
||||
m_MoverSubComponent = new MoverSubComponent(m_Parent->GetDefaultPosition());
|
||||
m_PathName = GeneralUtils::ASCIIToUTF16(pathName);
|
||||
|
||||
@@ -108,7 +108,7 @@ class MovingPlatformComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM;
|
||||
|
||||
MovingPlatformComponent(Entity* parent, const std::string& pathName);
|
||||
MovingPlatformComponent(Entity* parent, const int32_t componentID, const std::string& pathName);
|
||||
~MovingPlatformComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
|
||||
MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent) : Component(parent) {
|
||||
MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Parent = parent;
|
||||
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs"));
|
||||
std::stringstream ss(zoneString);
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
* Constructor for this component, builds the m_LUPWorlds vector
|
||||
* @param parent parent that contains this component
|
||||
*/
|
||||
MultiZoneEntranceComponent(Entity* parent);
|
||||
MultiZoneEntranceComponent(Entity* parent, const int32_t componentID);
|
||||
~MultiZoneEntranceComponent() override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "Item.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "User.h"
|
||||
#include "SwitchComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "dpWorld.h"
|
||||
#include "UserManager.h"
|
||||
#include "PetDigServer.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "eUnequippableActiveType.h"
|
||||
@@ -21,6 +23,7 @@
|
||||
#include "eUseItemResponse.h"
|
||||
#include "ePlayerFlag.h"
|
||||
|
||||
#include "GeneralUtils.h"
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "dChatFilter.h"
|
||||
@@ -43,9 +46,8 @@ std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
|
||||
* while the faction ones could be checked using their respective missions.
|
||||
*/
|
||||
|
||||
PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } {
|
||||
m_PetInfo = CDClientManager::GetTable<CDPetComponentTable>()->GetByID(componentId); // TODO: Make reference when safe
|
||||
m_ComponentId = componentId;
|
||||
PetComponent::PetComponent(Entity* parentEntity, const int32_t componentID) : Component{ parentEntity, componentID } {
|
||||
m_PetInfo = CDClientManager::GetTable<CDPetComponentTable>()->GetByID(componentID); // TODO: Make reference when safe
|
||||
|
||||
m_Interaction = LWOOBJID_EMPTY;
|
||||
m_Owner = LWOOBJID_EMPTY;
|
||||
@@ -537,7 +539,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
|
||||
|
||||
// Triggers the catch a pet missions
|
||||
constexpr auto PET_FLAG_BASE = 800;
|
||||
tamer->GetCharacter()->SetPlayerFlag(PET_FLAG_BASE + m_ComponentId, true);
|
||||
tamer->GetCharacter()->SetPlayerFlag(PET_FLAG_BASE + m_ComponentID, true);
|
||||
|
||||
auto* missionComponent = tamer->GetComponent<MissionComponent>();
|
||||
|
||||
@@ -554,18 +556,29 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
|
||||
}
|
||||
|
||||
void PetComponent::RequestSetPetName(std::u16string name) {
|
||||
const bool autoRejectNames = UserManager::Instance()->GetMuteAutoRejectNames();
|
||||
|
||||
if (m_Tamer == LWOOBJID_EMPTY) {
|
||||
if (m_Owner != LWOOBJID_EMPTY) {
|
||||
auto* owner = GetOwner();
|
||||
|
||||
m_ModerationStatus = 1; // Pending
|
||||
m_Name = "";
|
||||
// If auto reject names is on, and the user is muted, force use of predefined name
|
||||
if (autoRejectNames && owner && owner->GetCharacter() && owner->GetCharacter()->GetParentUser()->GetIsMuted()) {
|
||||
m_ModerationStatus = 2; // Approved
|
||||
std::string forcedName = "Pet";
|
||||
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ forcedName, static_cast<int32_t>(m_ModerationStatus) });
|
||||
GameMessages::SendSetPetName(m_Owner, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, owner->GetSystemAddress());
|
||||
GameMessages::SendSetPetNameModerated(m_Owner, m_DatabaseId, m_ModerationStatus, owner->GetSystemAddress());
|
||||
} else {
|
||||
m_ModerationStatus = 1; // Pending
|
||||
m_Name = "";
|
||||
|
||||
//Save our pet's new name to the db:
|
||||
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
|
||||
//Save our pet's new name to the db:
|
||||
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
|
||||
|
||||
GameMessages::SendSetPetName(m_Owner, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, owner->GetSystemAddress());
|
||||
GameMessages::SendSetPetNameModerated(m_Owner, m_DatabaseId, m_ModerationStatus, owner->GetSystemAddress());
|
||||
GameMessages::SendSetPetName(m_Owner, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, owner->GetSystemAddress());
|
||||
GameMessages::SendSetPetNameModerated(m_Owner, m_DatabaseId, m_ModerationStatus, owner->GetSystemAddress());
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -587,11 +600,21 @@ void PetComponent::RequestSetPetName(std::u16string name) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_ModerationStatus = 1; // Pending
|
||||
m_Name = "";
|
||||
// If auto reject names is on, and the user is muted, force use of predefined name ELSE proceed with normal name check
|
||||
if (autoRejectNames && tamer->GetCharacter() && tamer->GetCharacter()->GetParentUser()->GetIsMuted()) {
|
||||
m_ModerationStatus = 2; // Approved
|
||||
m_Name = "";
|
||||
std::string forcedName = "Pet";
|
||||
|
||||
//Save our pet's new name to the db:
|
||||
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
|
||||
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ forcedName, static_cast<int32_t>(m_ModerationStatus) });
|
||||
LOG("AccountID: %i is muted, forcing use of predefined pet name", tamer->GetCharacter()->GetParentUser()->GetAccountID());
|
||||
} else {
|
||||
m_ModerationStatus = 1; // Pending
|
||||
m_Name = "";
|
||||
|
||||
//Save our pet's new name to the db:
|
||||
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class PetComponent final : public Component
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET;
|
||||
|
||||
explicit PetComponent(Entity* parentEntity, uint32_t componentId);
|
||||
explicit PetComponent(Entity* parentEntity, const int32_t componentID);
|
||||
~PetComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
@@ -250,11 +250,6 @@ private:
|
||||
*/
|
||||
static std::unordered_map<LWOOBJID, LWOOBJID> currentActivities;
|
||||
|
||||
/**
|
||||
* The ID of the component in the pet component table
|
||||
*/
|
||||
uint32_t m_ComponentId;
|
||||
|
||||
/**
|
||||
* The ID of the model that was built to complete the taming minigame for this pet
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "dpShapeBox.h"
|
||||
#include "dpShapeSphere.h"
|
||||
|
||||
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
|
||||
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, const int32_t componentID) : PhysicsComponent(parent, componentID) {
|
||||
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &PhantomPhysicsComponent::OnGetObjectReportInfo);
|
||||
|
||||
m_Position = m_Parent->GetDefaultPosition();
|
||||
|
||||
@@ -28,7 +28,7 @@ class PhantomPhysicsComponent final : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS;
|
||||
|
||||
PhantomPhysicsComponent(Entity* parent, int32_t componentId);
|
||||
PhantomPhysicsComponent(Entity* parent, const int32_t componentID);
|
||||
~PhantomPhysicsComponent() override;
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "EntityInfo.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
PhysicsComponent::PhysicsComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Position = NiPoint3Constant::ZERO;
|
||||
m_Rotation = QuatUtils::IDENTITY;
|
||||
m_DirtyPosition = false;
|
||||
@@ -23,7 +23,7 @@ PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Compon
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
if (physicsComponentTable) {
|
||||
auto* info = physicsComponentTable->GetByID(componentId);
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
if (info) {
|
||||
m_CollisionGroup = info->collisionGroup;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class dpEntity;
|
||||
|
||||
class PhysicsComponent : public Component {
|
||||
public:
|
||||
PhysicsComponent(Entity* parent, int32_t componentId);
|
||||
PhysicsComponent(Entity* parent, const int32_t componentID);
|
||||
virtual ~PhysicsComponent() = default;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "PlayerForcedMovementComponent.h"
|
||||
|
||||
PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent) : Component(parent) {
|
||||
PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Parent = parent;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
* Constructor for this component
|
||||
* @param parent parent that contains this component
|
||||
*/
|
||||
PlayerForcedMovementComponent(Entity* parent);
|
||||
PlayerForcedMovementComponent(Entity* parent, const int32_t componentID);
|
||||
~PlayerForcedMovementComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Inventory.h"
|
||||
#include "Item.h"
|
||||
|
||||
PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) : Component(parent) {
|
||||
PossessableComponent::PossessableComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Possessor = LWOOBJID_EMPTY;
|
||||
CDItemComponent item = Inventory::FindItemComponent(m_Parent->GetLOT());
|
||||
m_AnimationFlag = static_cast<eAnimationFlags>(item.animationFlag);
|
||||
@@ -12,7 +12,7 @@ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId)
|
||||
// Get the possession Type from the CDClient
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit FROM PossessableComponent WHERE id = ?;");
|
||||
|
||||
query.bind(1, static_cast<int>(componentId));
|
||||
query.bind(1, static_cast<int>(componentID));
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
|
||||
@@ -16,15 +16,10 @@ class PossessableComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE;
|
||||
|
||||
PossessableComponent(Entity* parentEntity, uint32_t componentId);
|
||||
PossessableComponent(Entity* parentEntity, const int32_t componentID);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
/**
|
||||
* @brief mounts the Entity
|
||||
*/
|
||||
void Mount();
|
||||
|
||||
/**
|
||||
* @brief dismounts the Entity
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "eControlScheme.h"
|
||||
#include "eStateChangeType.h"
|
||||
|
||||
PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) {
|
||||
PossessorComponent::PossessorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Possessable = LWOOBJID_EMPTY;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class PossessorComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR;
|
||||
|
||||
PossessorComponent(Entity* parent);
|
||||
PossessorComponent(Entity* parent, const int32_t componentID);
|
||||
~PossessorComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
class PropertyComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY;
|
||||
explicit PropertyComponent(Entity* const parentEntity) noexcept : Component{ parentEntity } {}
|
||||
explicit PropertyComponent(Entity* const parentEntity, const int32_t componentID) noexcept : Component{ parentEntity, componentID } {}
|
||||
};
|
||||
|
||||
#endif // !PROPERTYCOMPONENT_H
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "ePropertySortType.h"
|
||||
#include "User.h"
|
||||
|
||||
PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, uint32_t componentID) : Component(parent) {
|
||||
PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
this->propertyQueries = {};
|
||||
|
||||
auto table = CDClientManager::GetTable<CDPropertyEntranceComponentTable>();
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
class PropertyEntranceComponent final : public Component {
|
||||
public:
|
||||
explicit PropertyEntranceComponent(Entity* parent, uint32_t componentID);
|
||||
explicit PropertyEntranceComponent(Entity* parent, const int32_t componentID);
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_ENTRANCE;
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
|
||||
|
||||
PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Component(parent) {
|
||||
PropertyManagementComponent::PropertyManagementComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
this->owner = LWOOBJID_EMPTY;
|
||||
this->templateId = 0;
|
||||
this->propertyId = LWOOBJID_EMPTY;
|
||||
|
||||
@@ -31,7 +31,7 @@ enum class PropertyPrivacyOption {
|
||||
class PropertyManagementComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_MANAGEMENT;
|
||||
PropertyManagementComponent(Entity* parent);
|
||||
PropertyManagementComponent(Entity* parent, const int32_t componentID);
|
||||
static PropertyManagementComponent* Instance();
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "PropertyManagementComponent.h"
|
||||
#include "UserManager.h"
|
||||
|
||||
PropertyVendorComponent::PropertyVendorComponent(Entity* parent) : Component(parent) {
|
||||
PropertyVendorComponent::PropertyVendorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
}
|
||||
|
||||
void PropertyVendorComponent::OnUse(Entity* originator) {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
class PropertyVendorComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_VENDOR;
|
||||
explicit PropertyVendorComponent(Entity* parent);
|
||||
explicit PropertyVendorComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
/**
|
||||
* Handles a use event from some entity, if the property is cleared this allows the entity to claim it
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
const std::unordered_set<LWOOBJID> ProximityMonitorComponent::m_EmptyObjectSet = {};
|
||||
|
||||
ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, int radiusSmall, int radiusLarge) : Component(parent) {
|
||||
ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, const int32_t componentID, int radiusSmall, int radiusLarge) : Component(parent, componentID) {
|
||||
if (radiusSmall != -1 && radiusLarge != -1) {
|
||||
SetProximityRadius(radiusSmall, "rocketSmall");
|
||||
SetProximityRadius(radiusLarge, "rocketLarge");
|
||||
|
||||
@@ -23,7 +23,7 @@ class ProximityMonitorComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROXIMITY_MONITOR;
|
||||
|
||||
ProximityMonitorComponent(Entity* parentEntity, int smallRadius = -1, int largeRadius = -1);
|
||||
ProximityMonitorComponent(Entity* parentEntity, const int32_t componentID, int smallRadius = -1, int largeRadius = -1);
|
||||
~ProximityMonitorComponent() override;
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "CppScripts.h"
|
||||
|
||||
QuickBuildComponent::QuickBuildComponent(Entity* const entity) : Component{ entity } {
|
||||
QuickBuildComponent::QuickBuildComponent(Entity* const entity, const int32_t componentID) : Component{ entity, componentID } {
|
||||
std::u16string checkPreconditions = entity->GetVar<std::u16string>(u"CheckPrecondition");
|
||||
|
||||
if (!checkPreconditions.empty()) {
|
||||
|
||||
@@ -24,7 +24,7 @@ class QuickBuildComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD;
|
||||
|
||||
QuickBuildComponent(Entity* const entity);
|
||||
QuickBuildComponent(Entity* const entity, const int32_t componentID);
|
||||
~QuickBuildComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
#define M_PI 3.14159265358979323846264338327950288
|
||||
#endif
|
||||
|
||||
RacingControlComponent::RacingControlComponent(Entity* parent)
|
||||
: Component(parent) {
|
||||
RacingControlComponent::RacingControlComponent(Entity* parent, const int32_t componentID)
|
||||
: Component(parent, componentID) {
|
||||
m_PathName = u"MainPath";
|
||||
m_NumberOfLaps = 3;
|
||||
m_RemainingLaps = m_NumberOfLaps;
|
||||
|
||||
@@ -108,7 +108,7 @@ class RacingControlComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL;
|
||||
|
||||
RacingControlComponent(Entity* parentEntity);
|
||||
RacingControlComponent(Entity* parentEntity, const int32_t componentID);
|
||||
~RacingControlComponent();
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -9,7 +9,7 @@ class Entity;
|
||||
class RacingSoundTriggerComponent : public SoundTriggerComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_SOUND_TRIGGER;
|
||||
RacingSoundTriggerComponent(Entity* parent) : SoundTriggerComponent(parent){};
|
||||
RacingSoundTriggerComponent(Entity* parent, const int32_t componentID) : SoundTriggerComponent(parent, componentID){};
|
||||
};
|
||||
|
||||
#endif //!__RACINGSOUNDTRIGGERCOMPONENT__H__
|
||||
|
||||
@@ -8,7 +8,7 @@ class RacingStatsComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_STATS;
|
||||
|
||||
RacingStatsComponent(Entity* parent) : Component(parent) {}
|
||||
RacingStatsComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {}
|
||||
};
|
||||
|
||||
#endif //!__RACINGSTATSCOMPONENT__H__
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
#include "EntityManager.h"
|
||||
#include "eStateChangeType.h"
|
||||
|
||||
RailActivatorComponent::RailActivatorComponent(Entity* parent, int32_t componentID) : Component(parent) {
|
||||
m_ComponentID = componentID;
|
||||
const auto tableData = CDClientManager::GetTable<CDRailActivatorComponentTable>()->GetEntryByID(componentID);;
|
||||
RailActivatorComponent::RailActivatorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
const auto tableData = CDClientManager::GetTable<CDRailActivatorComponentTable>()->GetEntryByID(componentID);
|
||||
|
||||
m_Path = parent->GetVar<std::u16string>(u"rail_path");
|
||||
m_PathDirection = parent->GetVar<bool>(u"rail_path_direction");
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
class RailActivatorComponent final : public Component {
|
||||
public:
|
||||
explicit RailActivatorComponent(Entity* parent, int32_t componentID);
|
||||
explicit RailActivatorComponent(Entity* parent, const int32_t componentID);
|
||||
~RailActivatorComponent() override;
|
||||
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RAIL_ACTIVATOR;
|
||||
@@ -37,12 +37,6 @@ public:
|
||||
*/
|
||||
void OnCancelRailMovement(Entity* originator);
|
||||
private:
|
||||
|
||||
/**
|
||||
* The ID of this component in the components database
|
||||
*/
|
||||
int32_t m_ComponentID;
|
||||
|
||||
/**
|
||||
* The entities that are currently traversing the rail
|
||||
*/
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
std::unordered_map<int32_t, float> RenderComponent::m_DurationCache{};
|
||||
|
||||
RenderComponent::RenderComponent(Entity* const parentEntity, const int32_t componentId) : Component{ parentEntity } {
|
||||
RenderComponent::RenderComponent(Entity* const parentEntity, const int32_t componentID) : Component{ parentEntity, componentID } {
|
||||
m_LastAnimationName = "";
|
||||
if (componentId == -1) return;
|
||||
if (componentID == -1) return;
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM RenderComponent WHERE id = ?;");
|
||||
query.bind(1, componentId);
|
||||
query.bind(1, componentID);
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (!result.eof()) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class RenderComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RENDER;
|
||||
|
||||
RenderComponent(Entity* const parentEntity, const int32_t componentId = -1);
|
||||
RenderComponent(Entity* const parentEntity, const int32_t componentID);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user