mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-17 03:37:08 -06:00
Compare commits
5 Commits
components
...
FallSpeedB
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e7be44ab6 | |||
| 37f60d9177 | |||
| 7d3f538456 | |||
| 565b23227e | |||
| 4e5b38d602 |
@@ -10,7 +10,7 @@ trim_trailing_whitespace=true
|
||||
end_of_line=lf
|
||||
insert_final_newline=true
|
||||
|
||||
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli,tcc,tpp}]
|
||||
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli}]
|
||||
|
||||
vc_generate_documentation_comments=doxygen_slash_star
|
||||
|
||||
|
||||
26
.github/workflows/build-and-test.yml
vendored
26
.github/workflows/build-and-test.yml
vendored
@@ -36,16 +36,22 @@ jobs:
|
||||
testPreset: "ci-${{matrix.os}}"
|
||||
- name: artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ github.ref == 'ref/head/main' }}
|
||||
with:
|
||||
name: build-${{matrix.os}}
|
||||
path: |
|
||||
build/*Server*
|
||||
build/*.ini
|
||||
build/*.so
|
||||
build/*.dll
|
||||
build/vanity/
|
||||
build/navmeshes/
|
||||
build/migrations/
|
||||
build/*.dcf
|
||||
!build/*.pdb
|
||||
!build/d*/
|
||||
build
|
||||
!build/tests
|
||||
!build/Testing
|
||||
!build/CMakeFiles
|
||||
!build/DartConfiguration.tcl
|
||||
!build/CTestTestfile.cmake
|
||||
!build/CMakeCache.txt
|
||||
!build/build.ninja
|
||||
!build/_deps
|
||||
!build/cmake_install.cmake
|
||||
!build/*.a
|
||||
!build/*.lib
|
||||
!build/*.dir
|
||||
!build/*.vcxproj
|
||||
!build/*.vcxproj.filters
|
||||
|
||||
@@ -305,7 +305,7 @@ file(
|
||||
file(
|
||||
GLOB HEADERS_DGAME
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dGame/dEntity/Entity.h
|
||||
${PROJECT_SOURCE_DIR}/dGame/Entity.h
|
||||
${PROJECT_SOURCE_DIR}/dGame/dGameMessages/GameMessages.h
|
||||
${PROJECT_SOURCE_DIR}/dGame/EntityManager.h
|
||||
${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h
|
||||
|
||||
@@ -17,7 +17,7 @@ __dynamic=1
|
||||
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
|
||||
# __compile_backtrace__=1
|
||||
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with.
|
||||
__maria_db_connector_compile_jobs__=
|
||||
__maria_db_connector_compile_jobs__=1
|
||||
# When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
|
||||
__enable_testing__=1
|
||||
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
|
||||
|
||||
@@ -338,7 +338,7 @@ This is a Work in Progress, but below are some quick links to documentaion for s
|
||||
## Former Contributors
|
||||
* TheMachine
|
||||
* Matthew
|
||||
* [Raine](https://github.com/uwainium)
|
||||
* [Raine](https://github.com/Rainebannister)
|
||||
* Bricknave
|
||||
|
||||
## Special Thanks
|
||||
|
||||
@@ -1,79 +1,77 @@
|
||||
#include "AMFDeserialize.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "AMFFormat.h"
|
||||
|
||||
/**
|
||||
* AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf
|
||||
* AMF3 Deserializer written by EmosewaMC
|
||||
*/
|
||||
|
||||
AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream* inStream) {
|
||||
AMFValue* AMFDeserialize::Read(RakNet::BitStream* inStream) {
|
||||
if (!inStream) return nullptr;
|
||||
AMFBaseValue* returnValue = nullptr;
|
||||
AMFValue* returnValue = nullptr;
|
||||
// Read in the value type from the bitStream
|
||||
eAmf marker;
|
||||
int8_t marker;
|
||||
inStream->Read(marker);
|
||||
// Based on the typing, create the value associated with that and return the base value class
|
||||
switch (marker) {
|
||||
case eAmf::Undefined: {
|
||||
returnValue = new AMFBaseValue();
|
||||
case AMFValueType::AMFUndefined: {
|
||||
returnValue = new AMFUndefinedValue();
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Null: {
|
||||
case AMFValueType::AMFNull: {
|
||||
returnValue = new AMFNullValue();
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::False: {
|
||||
returnValue = new AMFBoolValue(false);
|
||||
case AMFValueType::AMFFalse: {
|
||||
returnValue = new AMFFalseValue();
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::True: {
|
||||
returnValue = new AMFBoolValue(true);
|
||||
case AMFValueType::AMFTrue: {
|
||||
returnValue = new AMFTrueValue();
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Integer: {
|
||||
case AMFValueType::AMFInteger: {
|
||||
returnValue = ReadAmfInteger(inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Double: {
|
||||
case AMFValueType::AMFDouble: {
|
||||
returnValue = ReadAmfDouble(inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::String: {
|
||||
case AMFValueType::AMFString: {
|
||||
returnValue = ReadAmfString(inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Array: {
|
||||
case AMFValueType::AMFArray: {
|
||||
returnValue = ReadAmfArray(inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
// These values are unimplemented in the live client and will remain unimplemented
|
||||
// unless someone modifies the client to allow serializing of these values.
|
||||
case eAmf::XMLDoc:
|
||||
case eAmf::Date:
|
||||
case eAmf::Object:
|
||||
case eAmf::XML:
|
||||
case eAmf::ByteArray:
|
||||
case eAmf::VectorInt:
|
||||
case eAmf::VectorUInt:
|
||||
case eAmf::VectorDouble:
|
||||
case eAmf::VectorObject:
|
||||
case eAmf::Dictionary: {
|
||||
throw marker;
|
||||
// TODO We do not need these values, but if someone wants to implement them
|
||||
// then please do so and add the corresponding unit tests.
|
||||
case AMFValueType::AMFXMLDoc:
|
||||
case AMFValueType::AMFDate:
|
||||
case AMFValueType::AMFObject:
|
||||
case AMFValueType::AMFXML:
|
||||
case AMFValueType::AMFByteArray:
|
||||
case AMFValueType::AMFVectorInt:
|
||||
case AMFValueType::AMFVectorUInt:
|
||||
case AMFValueType::AMFVectorDouble:
|
||||
case AMFValueType::AMFVectorObject:
|
||||
case AMFValueType::AMFDictionary: {
|
||||
throw static_cast<AMFValueType>(marker);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
|
||||
throw static_cast<AMFValueType>(marker);
|
||||
break;
|
||||
}
|
||||
return returnValue;
|
||||
@@ -101,7 +99,7 @@ uint32_t AMFDeserialize::ReadU29(RakNet::BitStream* inStream) {
|
||||
return actualNumber;
|
||||
}
|
||||
|
||||
const std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
|
||||
std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
|
||||
auto length = ReadU29(inStream);
|
||||
// Check if this is a reference
|
||||
bool isReference = length % 2 == 1;
|
||||
@@ -115,39 +113,48 @@ const std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
|
||||
return value;
|
||||
} else {
|
||||
// Length is a reference to a previous index - use that as the read in value
|
||||
return accessedElements.at(length);
|
||||
return accessedElements[length];
|
||||
}
|
||||
}
|
||||
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream* inStream) {
|
||||
AMFValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream* inStream) {
|
||||
auto doubleValue = new AMFDoubleValue();
|
||||
double value;
|
||||
inStream->Read<double>(value);
|
||||
return new AMFDoubleValue(value);
|
||||
doubleValue->SetDoubleValue(value);
|
||||
return doubleValue;
|
||||
}
|
||||
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream* inStream) {
|
||||
AMFValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream* inStream) {
|
||||
auto arrayValue = new AMFArrayValue();
|
||||
|
||||
// Read size of dense array
|
||||
auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
|
||||
// Then read associative portion
|
||||
|
||||
// Then read Key'd portion
|
||||
while (true) {
|
||||
auto key = ReadString(inStream);
|
||||
// No more associative values when we encounter an empty string key
|
||||
// No more values when we encounter an empty string
|
||||
if (key.size() == 0) break;
|
||||
arrayValue->Insert(key, Read(inStream));
|
||||
arrayValue->InsertValue(key, Read(inStream));
|
||||
}
|
||||
|
||||
// Finally read dense portion
|
||||
for (uint32_t i = 0; i < sizeOfDenseArray; i++) {
|
||||
arrayValue->Insert(i, Read(inStream));
|
||||
arrayValue->PushBackValue(Read(inStream));
|
||||
}
|
||||
|
||||
return arrayValue;
|
||||
}
|
||||
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfString(RakNet::BitStream* inStream) {
|
||||
return new AMFStringValue(ReadString(inStream));
|
||||
AMFValue* AMFDeserialize::ReadAmfString(RakNet::BitStream* inStream) {
|
||||
auto stringValue = new AMFStringValue();
|
||||
stringValue->SetStringValue(ReadString(inStream));
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream* inStream) {
|
||||
return new AMFIntValue(ReadU29(inStream));
|
||||
AMFValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream* inStream) {
|
||||
auto integerValue = new AMFIntegerValue();
|
||||
integerValue->SetIntegerValue(ReadU29(inStream));
|
||||
return integerValue;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class AMFBaseValue;
|
||||
|
||||
class AMFValue;
|
||||
class AMFDeserialize {
|
||||
public:
|
||||
/**
|
||||
@@ -15,7 +14,7 @@ public:
|
||||
* @param inStream inStream to read value from.
|
||||
* @return Returns an AMFValue with all the information from the bitStream in it.
|
||||
*/
|
||||
AMFBaseValue* Read(RakNet::BitStream* inStream);
|
||||
AMFValue* Read(RakNet::BitStream* inStream);
|
||||
private:
|
||||
/**
|
||||
* @brief Private method to read a U29 integer from a bitstream
|
||||
@@ -31,7 +30,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return The read string
|
||||
*/
|
||||
const std::string ReadString(RakNet::BitStream* inStream);
|
||||
std::string ReadString(RakNet::BitStream* inStream);
|
||||
|
||||
/**
|
||||
* @brief Read an AMFDouble value from a bitStream
|
||||
@@ -39,7 +38,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return Double value represented as an AMFValue
|
||||
*/
|
||||
AMFBaseValue* ReadAmfDouble(RakNet::BitStream* inStream);
|
||||
AMFValue* ReadAmfDouble(RakNet::BitStream* inStream);
|
||||
|
||||
/**
|
||||
* @brief Read an AMFArray from a bitStream
|
||||
@@ -47,7 +46,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return Array value represented as an AMFValue
|
||||
*/
|
||||
AMFBaseValue* ReadAmfArray(RakNet::BitStream* inStream);
|
||||
AMFValue* ReadAmfArray(RakNet::BitStream* inStream);
|
||||
|
||||
/**
|
||||
* @brief Read an AMFString from a bitStream
|
||||
@@ -55,7 +54,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return String value represented as an AMFValue
|
||||
*/
|
||||
AMFBaseValue* ReadAmfString(RakNet::BitStream* inStream);
|
||||
AMFValue* ReadAmfString(RakNet::BitStream* inStream);
|
||||
|
||||
/**
|
||||
* @brief Read an AMFInteger from a bitStream
|
||||
@@ -63,7 +62,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return Integer value represented as an AMFValue
|
||||
*/
|
||||
AMFBaseValue* ReadAmfInteger(RakNet::BitStream* inStream);
|
||||
AMFValue* ReadAmfInteger(RakNet::BitStream* inStream);
|
||||
|
||||
/**
|
||||
* List of strings read so far saved to be read by reference.
|
||||
|
||||
156
dCommon/AMFFormat.cpp
Normal file
156
dCommon/AMFFormat.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "AMFFormat.h"
|
||||
|
||||
// AMFInteger
|
||||
void AMFIntegerValue::SetIntegerValue(uint32_t value) {
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
uint32_t AMFIntegerValue::GetIntegerValue() {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
// AMFDouble
|
||||
void AMFDoubleValue::SetDoubleValue(double value) {
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
double AMFDoubleValue::GetDoubleValue() {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
// AMFString
|
||||
void AMFStringValue::SetStringValue(const std::string& value) {
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
std::string AMFStringValue::GetStringValue() {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
// AMFXMLDoc
|
||||
void AMFXMLDocValue::SetXMLDocValue(const std::string& value) {
|
||||
this->xmlData = value;
|
||||
}
|
||||
|
||||
std::string AMFXMLDocValue::GetXMLDocValue() {
|
||||
return this->xmlData;
|
||||
}
|
||||
|
||||
// AMFDate
|
||||
void AMFDateValue::SetDateValue(uint64_t value) {
|
||||
this->millisecondTimestamp = value;
|
||||
}
|
||||
|
||||
uint64_t AMFDateValue::GetDateValue() {
|
||||
return this->millisecondTimestamp;
|
||||
}
|
||||
|
||||
// AMFArray Insert Value
|
||||
void AMFArrayValue::InsertValue(const std::string& key, AMFValue* value) {
|
||||
this->associative.insert(std::make_pair(key, value));
|
||||
}
|
||||
|
||||
// AMFArray Remove Value
|
||||
void AMFArrayValue::RemoveValue(const std::string& key) {
|
||||
_AMFArrayMap_::iterator it = this->associative.find(key);
|
||||
if (it != this->associative.end()) {
|
||||
this->associative.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// AMFArray Get Associative Iterator Begin
|
||||
_AMFArrayMap_::iterator AMFArrayValue::GetAssociativeIteratorValueBegin() {
|
||||
return this->associative.begin();
|
||||
}
|
||||
|
||||
// AMFArray Get Associative Iterator End
|
||||
_AMFArrayMap_::iterator AMFArrayValue::GetAssociativeIteratorValueEnd() {
|
||||
return this->associative.end();
|
||||
}
|
||||
|
||||
// AMFArray Push Back Value
|
||||
void AMFArrayValue::PushBackValue(AMFValue* value) {
|
||||
this->dense.push_back(value);
|
||||
}
|
||||
|
||||
// AMFArray Pop Back Value
|
||||
void AMFArrayValue::PopBackValue() {
|
||||
this->dense.pop_back();
|
||||
}
|
||||
|
||||
// AMFArray Get Dense List Size
|
||||
uint32_t AMFArrayValue::GetDenseValueSize() {
|
||||
return (uint32_t)this->dense.size();
|
||||
}
|
||||
|
||||
// AMFArray Get Dense Iterator Begin
|
||||
_AMFArrayList_::iterator AMFArrayValue::GetDenseIteratorBegin() {
|
||||
return this->dense.begin();
|
||||
}
|
||||
|
||||
// AMFArray Get Dense Iterator End
|
||||
_AMFArrayList_::iterator AMFArrayValue::GetDenseIteratorEnd() {
|
||||
return this->dense.end();
|
||||
}
|
||||
|
||||
AMFArrayValue::~AMFArrayValue() {
|
||||
for (auto valueToDelete : GetDenseArray()) {
|
||||
if (valueToDelete) delete valueToDelete;
|
||||
}
|
||||
for (auto valueToDelete : GetAssociativeMap()) {
|
||||
if (valueToDelete.second) delete valueToDelete.second;
|
||||
}
|
||||
}
|
||||
|
||||
// AMFObject Constructor
|
||||
AMFObjectValue::AMFObjectValue(std::vector<std::pair<std::string, AMFValueType>> traits) {
|
||||
this->traits.reserve(traits.size());
|
||||
std::vector<std::pair<std::string, AMFValueType>>::iterator it = traits.begin();
|
||||
while (it != traits.end()) {
|
||||
this->traits.insert(std::make_pair(it->first, std::make_pair(it->second, new AMFNullValue())));
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// AMFObject Set Value
|
||||
void AMFObjectValue::SetTraitValue(const std::string& trait, AMFValue* value) {
|
||||
if (value) {
|
||||
_AMFObjectTraits_::iterator it = this->traits.find(trait);
|
||||
if (it != this->traits.end()) {
|
||||
if (it->second.first == value->GetValueType()) {
|
||||
it->second.second = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AMFObject Get Value
|
||||
AMFValue* AMFObjectValue::GetTraitValue(const std::string& trait) {
|
||||
_AMFObjectTraits_::iterator it = this->traits.find(trait);
|
||||
if (it != this->traits.end()) {
|
||||
return it->second.second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// AMFObject Get Trait Iterator Begin
|
||||
_AMFObjectTraits_::iterator AMFObjectValue::GetTraitsIteratorBegin() {
|
||||
return this->traits.begin();
|
||||
}
|
||||
|
||||
// AMFObject Get Trait Iterator End
|
||||
_AMFObjectTraits_::iterator AMFObjectValue::GetTraitsIteratorEnd() {
|
||||
return this->traits.end();
|
||||
}
|
||||
|
||||
// AMFObject Get Trait Size
|
||||
uint32_t AMFObjectValue::GetTraitArrayCount() {
|
||||
return (uint32_t)this->traits.size();
|
||||
}
|
||||
|
||||
AMFObjectValue::~AMFObjectValue() {
|
||||
for (auto valueToDelete = GetTraitsIteratorBegin(); valueToDelete != GetTraitsIteratorEnd(); valueToDelete++) {
|
||||
if (valueToDelete->second.second) delete valueToDelete->second.second;
|
||||
}
|
||||
}
|
||||
413
dCommon/AMFFormat.h
Normal file
413
dCommon/AMFFormat.h
Normal file
@@ -0,0 +1,413 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "dCommonVars.h"
|
||||
|
||||
// C++
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
/*!
|
||||
\file AMFFormat.hpp
|
||||
\brief A class for managing AMF values
|
||||
*/
|
||||
|
||||
class AMFValue; // Forward declaration
|
||||
|
||||
// Definitions
|
||||
#define _AMFArrayMap_ std::unordered_map<std::string, AMFValue*>
|
||||
#define _AMFArrayList_ std::vector<AMFValue*>
|
||||
|
||||
#define _AMFObjectTraits_ std::unordered_map<std::string, std::pair<AMFValueType, AMFValue*>>
|
||||
#define _AMFObjectDynamicTraits_ std::unordered_map<std::string, AMFValue*>
|
||||
|
||||
//! An enum for each AMF value type
|
||||
enum AMFValueType : unsigned char {
|
||||
AMFUndefined = 0x00, //!< An undefined AMF Value
|
||||
AMFNull = 0x01, //!< A null AMF value
|
||||
AMFFalse = 0x02, //!< A false AMF value
|
||||
AMFTrue = 0x03, //!< A true AMF value
|
||||
AMFInteger = 0x04, //!< An integer AMF value
|
||||
AMFDouble = 0x05, //!< A double AMF value
|
||||
AMFString = 0x06, //!< A string AMF value
|
||||
AMFXMLDoc = 0x07, //!< An XML Doc AMF value
|
||||
AMFDate = 0x08, //!< A date AMF value
|
||||
AMFArray = 0x09, //!< An array AMF value
|
||||
AMFObject = 0x0A, //!< An object AMF value
|
||||
AMFXML = 0x0B, //!< An XML AMF value
|
||||
AMFByteArray = 0x0C, //!< A byte array AMF value
|
||||
AMFVectorInt = 0x0D, //!< An integer vector AMF value
|
||||
AMFVectorUInt = 0x0E, //!< An unsigned integer AMF value
|
||||
AMFVectorDouble = 0x0F, //!< A double vector AMF value
|
||||
AMFVectorObject = 0x10, //!< An object vector AMF value
|
||||
AMFDictionary = 0x11 //!< A dictionary AMF value
|
||||
};
|
||||
|
||||
//! An enum for the object value types
|
||||
enum AMFObjectValueType : unsigned char {
|
||||
AMFObjectAnonymous = 0x01,
|
||||
AMFObjectTyped = 0x02,
|
||||
AMFObjectDynamic = 0x03,
|
||||
AMFObjectExternalizable = 0x04
|
||||
};
|
||||
|
||||
//! The base AMF value class
|
||||
class AMFValue {
|
||||
public:
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
virtual AMFValueType GetValueType() = 0;
|
||||
virtual ~AMFValue() {};
|
||||
};
|
||||
|
||||
//! A typedef for a pointer to an AMF value
|
||||
typedef AMFValue* NDGFxValue;
|
||||
|
||||
|
||||
// The various AMF value types
|
||||
|
||||
//! The undefined value AMF type
|
||||
class AMFUndefinedValue : public AMFValue {
|
||||
private:
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFUndefined;
|
||||
};
|
||||
|
||||
//! The null value AMF type
|
||||
class AMFNullValue : public AMFValue {
|
||||
private:
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFNull;
|
||||
};
|
||||
|
||||
//! The false value AMF type
|
||||
class AMFFalseValue : public AMFValue {
|
||||
private:
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFFalse;
|
||||
};
|
||||
|
||||
//! The true value AMF type
|
||||
class AMFTrueValue : public AMFValue {
|
||||
private:
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFTrue;
|
||||
};
|
||||
|
||||
//! The integer value AMF type
|
||||
class AMFIntegerValue : public AMFValue {
|
||||
private:
|
||||
uint32_t value; //!< The value of the AMF type
|
||||
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFInteger;
|
||||
//! Sets the integer value
|
||||
/*!
|
||||
\param value The value to set
|
||||
*/
|
||||
void SetIntegerValue(uint32_t value);
|
||||
|
||||
//! Gets the integer value
|
||||
/*!
|
||||
\return The integer value
|
||||
*/
|
||||
uint32_t GetIntegerValue();
|
||||
};
|
||||
|
||||
//! The double value AMF type
|
||||
class AMFDoubleValue : public AMFValue {
|
||||
private:
|
||||
double value; //!< The value of the AMF type
|
||||
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFDouble;
|
||||
//! Sets the double value
|
||||
/*!
|
||||
\param value The value to set to
|
||||
*/
|
||||
void SetDoubleValue(double value);
|
||||
|
||||
//! Gets the double value
|
||||
/*!
|
||||
\return The double value
|
||||
*/
|
||||
double GetDoubleValue();
|
||||
};
|
||||
|
||||
//! The string value AMF type
|
||||
class AMFStringValue : public AMFValue {
|
||||
private:
|
||||
std::string value; //!< The value of the AMF type
|
||||
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFString;
|
||||
//! Sets the string value
|
||||
/*!
|
||||
\param value The string value to set to
|
||||
*/
|
||||
void SetStringValue(const std::string& value);
|
||||
|
||||
//! Gets the string value
|
||||
/*!
|
||||
\return The string value
|
||||
*/
|
||||
std::string GetStringValue();
|
||||
};
|
||||
|
||||
//! The XML doc value AMF type
|
||||
class AMFXMLDocValue : public AMFValue {
|
||||
private:
|
||||
std::string xmlData; //!< The value of the AMF type
|
||||
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFXMLDoc;
|
||||
//! Sets the XML Doc value
|
||||
/*!
|
||||
\param value The value to set to
|
||||
*/
|
||||
void SetXMLDocValue(const std::string& value);
|
||||
|
||||
//! Gets the XML Doc value
|
||||
/*!
|
||||
\return The XML Doc value
|
||||
*/
|
||||
std::string GetXMLDocValue();
|
||||
};
|
||||
|
||||
//! The date value AMF type
|
||||
class AMFDateValue : public AMFValue {
|
||||
private:
|
||||
uint64_t millisecondTimestamp; //!< The time in milliseconds since the ephoch
|
||||
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() { return ValueType; }
|
||||
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFDate;
|
||||
//! Sets the date time
|
||||
/*!
|
||||
\param value The value to set to
|
||||
*/
|
||||
void SetDateValue(uint64_t value);
|
||||
|
||||
//! Gets the date value
|
||||
/*!
|
||||
\return The date value in milliseconds since the epoch
|
||||
*/
|
||||
uint64_t GetDateValue();
|
||||
};
|
||||
|
||||
//! The array value AMF type
|
||||
// This object will manage it's own memory map and list. Do not delete its values.
|
||||
class AMFArrayValue : public AMFValue {
|
||||
private:
|
||||
_AMFArrayMap_ associative; //!< The array map (associative part)
|
||||
_AMFArrayList_ dense; //!< The array list (dense part)
|
||||
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() override { return ValueType; }
|
||||
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFArray;
|
||||
|
||||
~AMFArrayValue() override;
|
||||
//! Inserts an item into the array map for a specific key
|
||||
/*!
|
||||
\param key The key to set
|
||||
\param value The value to add
|
||||
*/
|
||||
void InsertValue(const std::string& key, AMFValue* value);
|
||||
|
||||
//! Removes an item for a specific key
|
||||
/*!
|
||||
\param key The key to remove
|
||||
*/
|
||||
void RemoveValue(const std::string& key);
|
||||
|
||||
//! Finds an AMF value
|
||||
/*!
|
||||
\return The AMF value if found, nullptr otherwise
|
||||
*/
|
||||
template <typename T>
|
||||
T* FindValue(const std::string& key) const {
|
||||
_AMFArrayMap_::const_iterator it = this->associative.find(key);
|
||||
if (it != this->associative.end() && T::ValueType == it->second->GetValueType()) {
|
||||
return dynamic_cast<T*>(it->second);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
//! Returns where the associative iterator begins
|
||||
/*!
|
||||
\return Where the array map iterator begins
|
||||
*/
|
||||
_AMFArrayMap_::iterator GetAssociativeIteratorValueBegin();
|
||||
|
||||
//! Returns where the associative iterator ends
|
||||
/*!
|
||||
\return Where the array map iterator ends
|
||||
*/
|
||||
_AMFArrayMap_::iterator GetAssociativeIteratorValueEnd();
|
||||
|
||||
//! Pushes back a value into the array list
|
||||
/*!
|
||||
\param value The value to push back
|
||||
*/
|
||||
void PushBackValue(AMFValue* value);
|
||||
|
||||
//! Pops back the last value in the array list
|
||||
void PopBackValue();
|
||||
|
||||
//! Gets the count of the dense list
|
||||
/*!
|
||||
\return The dense list size
|
||||
*/
|
||||
uint32_t GetDenseValueSize();
|
||||
|
||||
//! Gets a specific value from the list for the specified index
|
||||
/*!
|
||||
\param index The index to get
|
||||
*/
|
||||
template <typename T>
|
||||
T* GetValueAt(uint32_t index) {
|
||||
if (index >= this->dense.size()) return nullptr;
|
||||
AMFValue* foundValue = this->dense.at(index);
|
||||
return T::ValueType == foundValue->GetValueType() ? dynamic_cast<T*>(foundValue) : nullptr;
|
||||
};
|
||||
|
||||
//! Returns where the dense iterator begins
|
||||
/*!
|
||||
\return Where the iterator begins
|
||||
*/
|
||||
_AMFArrayList_::iterator GetDenseIteratorBegin();
|
||||
|
||||
//! Returns where the dense iterator ends
|
||||
/*!
|
||||
\return Where the iterator ends
|
||||
*/
|
||||
_AMFArrayList_::iterator GetDenseIteratorEnd();
|
||||
|
||||
//! Returns the associative map
|
||||
/*!
|
||||
\return The associative map
|
||||
*/
|
||||
_AMFArrayMap_ GetAssociativeMap() { return this->associative; };
|
||||
|
||||
//! Returns the dense array
|
||||
/*!
|
||||
\return The dense array
|
||||
*/
|
||||
_AMFArrayList_ GetDenseArray() { return this->dense; };
|
||||
};
|
||||
|
||||
//! The anonymous object value AMF type
|
||||
class AMFObjectValue : public AMFValue {
|
||||
private:
|
||||
_AMFObjectTraits_ traits; //!< The object traits
|
||||
|
||||
//! Returns the AMF value type
|
||||
/*!
|
||||
\return The AMF value type
|
||||
*/
|
||||
AMFValueType GetValueType() override { return ValueType; }
|
||||
~AMFObjectValue() override;
|
||||
|
||||
public:
|
||||
static const AMFValueType ValueType = AMFObject;
|
||||
//! Constructor
|
||||
/*!
|
||||
\param traits The traits to set
|
||||
*/
|
||||
AMFObjectValue(std::vector<std::pair<std::string, AMFValueType>> traits);
|
||||
|
||||
//! Gets the object value type
|
||||
/*!
|
||||
\return The object value type
|
||||
*/
|
||||
virtual AMFObjectValueType GetObjectValueType() { return AMFObjectAnonymous; }
|
||||
|
||||
//! Sets the value of a trait
|
||||
/*!
|
||||
\param trait The trait to set the value for
|
||||
\param value The AMF value to set
|
||||
*/
|
||||
void SetTraitValue(const std::string& trait, AMFValue* value);
|
||||
|
||||
//! Gets a trait value
|
||||
/*!
|
||||
\param trait The trait to get the value for
|
||||
\return The trait value
|
||||
*/
|
||||
AMFValue* GetTraitValue(const std::string& trait);
|
||||
|
||||
//! Gets the beginning of the object traits iterator
|
||||
/*!
|
||||
\return The AMF trait array iterator begin
|
||||
*/
|
||||
_AMFObjectTraits_::iterator GetTraitsIteratorBegin();
|
||||
|
||||
//! Gets the end of the object traits iterator
|
||||
/*!
|
||||
\return The AMF trait array iterator begin
|
||||
*/
|
||||
_AMFObjectTraits_::iterator GetTraitsIteratorEnd();
|
||||
|
||||
//! Gets the amount of traits
|
||||
/*!
|
||||
\return The amount of traits
|
||||
*/
|
||||
uint32_t GetTraitArrayCount();
|
||||
};
|
||||
259
dCommon/AMFFormat_BitStream.cpp
Normal file
259
dCommon/AMFFormat_BitStream.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
#include "AMFFormat_BitStream.h"
|
||||
|
||||
// Writes an AMFValue pointer to a RakNet::BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFValue*>(AMFValue* value) {
|
||||
if (value != nullptr) {
|
||||
AMFValueType type = value->GetValueType();
|
||||
|
||||
switch (type) {
|
||||
case AMFUndefined: {
|
||||
AMFUndefinedValue* v = (AMFUndefinedValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFNull: {
|
||||
AMFNullValue* v = (AMFNullValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFFalse: {
|
||||
AMFFalseValue* v = (AMFFalseValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFTrue: {
|
||||
AMFTrueValue* v = (AMFTrueValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFInteger: {
|
||||
AMFIntegerValue* v = (AMFIntegerValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFDouble: {
|
||||
AMFDoubleValue* v = (AMFDoubleValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFString: {
|
||||
AMFStringValue* v = (AMFStringValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFXMLDoc: {
|
||||
AMFXMLDocValue* v = (AMFXMLDocValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFDate: {
|
||||
AMFDateValue* v = (AMFDateValue*)value;
|
||||
this->Write(*v);
|
||||
break;
|
||||
}
|
||||
|
||||
case AMFArray: {
|
||||
this->Write((AMFArrayValue*)value);
|
||||
break;
|
||||
}
|
||||
case AMFObject:
|
||||
case AMFXML:
|
||||
case AMFByteArray:
|
||||
case AMFVectorInt:
|
||||
case AMFVectorUInt:
|
||||
case AMFVectorDouble:
|
||||
case AMFVectorObject:
|
||||
case AMFDictionary:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A private function to write an value to a RakNet::BitStream
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||
unsigned char b4 = (unsigned char)v;
|
||||
if (v < 0x00200000) {
|
||||
b4 = b4 & 0x7F;
|
||||
if (v > 0x7F) {
|
||||
unsigned char b3;
|
||||
v = v >> 7;
|
||||
b3 = ((unsigned char)(v)) | 0x80;
|
||||
if (v > 0x7F) {
|
||||
unsigned char b2;
|
||||
v = v >> 7;
|
||||
b2 = ((unsigned char)(v)) | 0x80;
|
||||
bs->Write(b2);
|
||||
}
|
||||
|
||||
bs->Write(b3);
|
||||
}
|
||||
} else {
|
||||
unsigned char b1;
|
||||
unsigned char b2;
|
||||
unsigned char b3;
|
||||
|
||||
v = v >> 8;
|
||||
b3 = ((unsigned char)(v)) | 0x80;
|
||||
v = v >> 7;
|
||||
b2 = ((unsigned char)(v)) | 0x80;
|
||||
v = v >> 7;
|
||||
b1 = ((unsigned char)(v)) | 0x80;
|
||||
|
||||
bs->Write(b1);
|
||||
bs->Write(b2);
|
||||
bs->Write(b3);
|
||||
}
|
||||
|
||||
bs->Write(b4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a flag number to a RakNet::BitStream
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
|
||||
v = (v << 1) | 0x01;
|
||||
WriteUInt29(bs, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an AMFString to a RakNet::BitStream
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
|
||||
WriteFlagNumber(bs, (uint32_t)str.size());
|
||||
bs->Write(str.c_str(), (uint32_t)str.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an U16 to a bitstream
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU16(RakNet::BitStream* bs, uint16_t value) {
|
||||
bs->Write(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an U32 to a bitstream
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU32(RakNet::BitStream* bs, uint32_t value) {
|
||||
bs->Write(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an U64 to a bitstream
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU64(RakNet::BitStream* bs, uint64_t value) {
|
||||
bs->Write(value);
|
||||
}
|
||||
|
||||
|
||||
// Writes an AMFUndefinedValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFUndefinedValue>(AMFUndefinedValue value) {
|
||||
this->Write(AMFUndefined);
|
||||
}
|
||||
|
||||
// Writes an AMFNullValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFNullValue>(AMFNullValue value) {
|
||||
this->Write(AMFNull);
|
||||
}
|
||||
|
||||
// Writes an AMFFalseValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFFalseValue>(AMFFalseValue value) {
|
||||
this->Write(AMFFalse);
|
||||
}
|
||||
|
||||
// Writes an AMFTrueValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFTrueValue>(AMFTrueValue value) {
|
||||
this->Write(AMFTrue);
|
||||
}
|
||||
|
||||
// Writes an AMFIntegerValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFIntegerValue>(AMFIntegerValue value) {
|
||||
this->Write(AMFInteger);
|
||||
WriteUInt29(this, value.GetIntegerValue());
|
||||
}
|
||||
|
||||
// Writes an AMFDoubleValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFDoubleValue>(AMFDoubleValue value) {
|
||||
this->Write(AMFDouble);
|
||||
double d = value.GetDoubleValue();
|
||||
WriteAMFU64(this, *((unsigned long long*) & d));
|
||||
}
|
||||
|
||||
// Writes an AMFStringValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFStringValue>(AMFStringValue value) {
|
||||
this->Write(AMFString);
|
||||
std::string v = value.GetStringValue();
|
||||
WriteAMFString(this, v);
|
||||
}
|
||||
|
||||
// Writes an AMFXMLDocValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFXMLDocValue>(AMFXMLDocValue value) {
|
||||
this->Write(AMFXMLDoc);
|
||||
std::string v = value.GetXMLDocValue();
|
||||
WriteAMFString(this, v);
|
||||
}
|
||||
|
||||
// Writes an AMFDateValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFDateValue>(AMFDateValue value) {
|
||||
this->Write(AMFDate);
|
||||
uint64_t date = value.GetDateValue();
|
||||
WriteAMFU64(this, date);
|
||||
}
|
||||
|
||||
// Writes an AMFArrayValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFArrayValue*>(AMFArrayValue* value) {
|
||||
this->Write(AMFArray);
|
||||
uint32_t denseSize = value->GetDenseValueSize();
|
||||
WriteFlagNumber(this, denseSize);
|
||||
|
||||
_AMFArrayMap_::iterator it = value->GetAssociativeIteratorValueBegin();
|
||||
_AMFArrayMap_::iterator end = value->GetAssociativeIteratorValueEnd();
|
||||
|
||||
while (it != end) {
|
||||
WriteAMFString(this, it->first);
|
||||
this->Write(it->second);
|
||||
it++;
|
||||
}
|
||||
|
||||
this->Write(AMFNull);
|
||||
|
||||
if (denseSize > 0) {
|
||||
_AMFArrayList_::iterator it2 = value->GetDenseIteratorBegin();
|
||||
_AMFArrayList_::iterator end2 = value->GetDenseIteratorEnd();
|
||||
|
||||
while (it2 != end2) {
|
||||
this->Write(*it2);
|
||||
it2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
dCommon/AMFFormat_BitStream.h
Normal file
92
dCommon/AMFFormat_BitStream.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "AMFFormat.h"
|
||||
|
||||
// RakNet
|
||||
#include <BitStream.h>
|
||||
|
||||
/*!
|
||||
\file AMFFormat_BitStream.h
|
||||
\brief A class that implements native writing of AMF values to RakNet::BitStream
|
||||
*/
|
||||
|
||||
// We are using the RakNet namespace
|
||||
namespace RakNet {
|
||||
//! Writes an AMFValue pointer to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFValue*>(AMFValue* value);
|
||||
|
||||
//! Writes an AMFUndefinedValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFUndefinedValue>(AMFUndefinedValue value);
|
||||
|
||||
//! Writes an AMFNullValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFNullValue>(AMFNullValue value);
|
||||
|
||||
//! Writes an AMFFalseValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFFalseValue>(AMFFalseValue value);
|
||||
|
||||
//! Writes an AMFTrueValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFTrueValue>(AMFTrueValue value);
|
||||
|
||||
//! Writes an AMFIntegerValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFIntegerValue>(AMFIntegerValue value);
|
||||
|
||||
//! Writes an AMFDoubleValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFDoubleValue>(AMFDoubleValue value);
|
||||
|
||||
//! Writes an AMFStringValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFStringValue>(AMFStringValue value);
|
||||
|
||||
//! Writes an AMFXMLDocValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFXMLDocValue>(AMFXMLDocValue value);
|
||||
|
||||
//! Writes an AMFDateValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFDateValue>(AMFDateValue value);
|
||||
|
||||
//! Writes an AMFArrayValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFArrayValue*>(AMFArrayValue* value);
|
||||
} // namespace RakNet
|
||||
367
dCommon/Amf3.h
367
dCommon/Amf3.h
@@ -1,367 +0,0 @@
|
||||
#ifndef __AMF3__H__
|
||||
#define __AMF3__H__
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "dLogger.h"
|
||||
#include "Game.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
enum class eAmf : uint8_t {
|
||||
Undefined = 0x00, // An undefined AMF Value
|
||||
Null = 0x01, // A null AMF value
|
||||
False = 0x02, // A false AMF value
|
||||
True = 0x03, // A true AMF value
|
||||
Integer = 0x04, // An integer AMF value
|
||||
Double = 0x05, // A double AMF value
|
||||
String = 0x06, // A string AMF value
|
||||
XMLDoc = 0x07, // Unused in the live client and cannot be serialized without modification. An XML Doc AMF value
|
||||
Date = 0x08, // Unused in the live client and cannot be serialized without modification. A date AMF value
|
||||
Array = 0x09, // An array AMF value
|
||||
Object = 0x0A, // Unused in the live client and cannot be serialized without modification. An object AMF value
|
||||
XML = 0x0B, // Unused in the live client and cannot be serialized without modification. An XML AMF value
|
||||
ByteArray = 0x0C, // Unused in the live client and cannot be serialized without modification. A byte array AMF value
|
||||
VectorInt = 0x0D, // Unused in the live client and cannot be serialized without modification. An integer vector AMF value
|
||||
VectorUInt = 0x0E, // Unused in the live client and cannot be serialized without modification. An unsigned integer AMF value
|
||||
VectorDouble = 0x0F, // Unused in the live client and cannot be serialized without modification. A double vector AMF value
|
||||
VectorObject = 0x10, // Unused in the live client and cannot be serialized without modification. An object vector AMF value
|
||||
Dictionary = 0x11 // Unused in the live client and cannot be serialized without modification. A dictionary AMF value
|
||||
};
|
||||
|
||||
class AMFBaseValue {
|
||||
public:
|
||||
virtual eAmf GetValueType() { return eAmf::Undefined; };
|
||||
AMFBaseValue() {};
|
||||
virtual ~AMFBaseValue() {};
|
||||
};
|
||||
|
||||
template<typename ValueType>
|
||||
class AMFValue : public AMFBaseValue {
|
||||
public:
|
||||
AMFValue() {};
|
||||
AMFValue(ValueType value) { SetValue(value); };
|
||||
virtual ~AMFValue() override {};
|
||||
|
||||
eAmf GetValueType() override { return eAmf::Undefined; };
|
||||
|
||||
const ValueType& GetValue() { return data; };
|
||||
void SetValue(ValueType value) { data = value; };
|
||||
protected:
|
||||
ValueType data;
|
||||
};
|
||||
|
||||
// As a string this is much easier to write and read from a BitStream.
|
||||
template<>
|
||||
class AMFValue<const char*> : public AMFBaseValue {
|
||||
public:
|
||||
AMFValue() {};
|
||||
AMFValue(const char* value) { SetValue(std::string(value)); };
|
||||
virtual ~AMFValue() override {};
|
||||
|
||||
eAmf GetValueType() override { return eAmf::String; };
|
||||
|
||||
const std::string& GetValue() { return data; };
|
||||
void SetValue(std::string value) { data = value; };
|
||||
protected:
|
||||
std::string data;
|
||||
};
|
||||
|
||||
typedef AMFValue<std::nullptr_t> AMFNullValue;
|
||||
typedef AMFValue<bool> AMFBoolValue;
|
||||
typedef AMFValue<int32_t> AMFIntValue;
|
||||
typedef AMFValue<std::string> AMFStringValue;
|
||||
typedef AMFValue<double> AMFDoubleValue;
|
||||
|
||||
template<> inline eAmf AMFValue<std::nullptr_t>::GetValueType() { return eAmf::Null; };
|
||||
template<> inline eAmf AMFValue<bool>::GetValueType() { return this->data ? eAmf::True : eAmf::False; };
|
||||
template<> inline eAmf AMFValue<int32_t>::GetValueType() { return eAmf::Integer; };
|
||||
template<> inline eAmf AMFValue<uint32_t>::GetValueType() { return eAmf::Integer; };
|
||||
template<> inline eAmf AMFValue<std::string>::GetValueType() { return eAmf::String; };
|
||||
template<> inline eAmf AMFValue<double>::GetValueType() { return eAmf::Double; };
|
||||
|
||||
/**
|
||||
* The AMFArrayValue object holds 2 types of lists:
|
||||
* An associative list where a key maps to a value
|
||||
* A Dense list where elements are stored back to back
|
||||
*
|
||||
* Objects that are Registered are owned by this object
|
||||
* and are not to be deleted by a caller.
|
||||
*/
|
||||
class AMFArrayValue : public AMFBaseValue {
|
||||
|
||||
typedef std::unordered_map<std::string, AMFBaseValue*> AMFAssociative;
|
||||
typedef std::vector<AMFBaseValue*> AMFDense;
|
||||
|
||||
public:
|
||||
eAmf GetValueType() override { return eAmf::Array; };
|
||||
|
||||
~AMFArrayValue() override {
|
||||
for (auto valueToDelete : GetDense()) {
|
||||
if (valueToDelete) {
|
||||
delete valueToDelete;
|
||||
valueToDelete = nullptr;
|
||||
}
|
||||
}
|
||||
for (auto valueToDelete : GetAssociative()) {
|
||||
if (valueToDelete.second) {
|
||||
delete valueToDelete.second;
|
||||
valueToDelete.second = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the Associative portion of the object
|
||||
*/
|
||||
inline AMFAssociative& GetAssociative() { return this->associative; };
|
||||
|
||||
/**
|
||||
* Returns the dense portion of the object
|
||||
*/
|
||||
inline AMFDense& GetDense() { return this->dense; };
|
||||
|
||||
/**
|
||||
* Inserts an AMFValue into the associative portion with the given key.
|
||||
* If a duplicate is attempted to be inserted, it is ignored and the
|
||||
* first value with that key is kept in the map.
|
||||
*
|
||||
* These objects are not to be deleted by the caller as they are owned by
|
||||
* the AMFArray object which manages its own memory.
|
||||
*
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*
|
||||
* @return The inserted element if the type matched,
|
||||
* or nullptr if a key existed and was not the same type
|
||||
*/
|
||||
template<typename ValueType>
|
||||
std::pair<AMFValue<ValueType>*, bool> Insert(const std::string& key, ValueType value) {
|
||||
auto element = associative.find(key);
|
||||
AMFValue<ValueType>* val = nullptr;
|
||||
bool found = true;
|
||||
if (element == associative.end()) {
|
||||
val = new AMFValue<ValueType>(value);
|
||||
associative.insert(std::make_pair(key, val));
|
||||
} else {
|
||||
val = dynamic_cast<AMFValue<ValueType>*>(element->second);
|
||||
found = false;
|
||||
}
|
||||
return std::make_pair(val, found);
|
||||
};
|
||||
|
||||
// Associates an array with a string key
|
||||
std::pair<AMFBaseValue*, bool> Insert(const std::string& key) {
|
||||
auto element = associative.find(key);
|
||||
AMFArrayValue* val = nullptr;
|
||||
bool found = true;
|
||||
if (element == associative.end()) {
|
||||
val = new AMFArrayValue();
|
||||
associative.insert(std::make_pair(key, val));
|
||||
} else {
|
||||
val = dynamic_cast<AMFArrayValue*>(element->second);
|
||||
found = false;
|
||||
}
|
||||
return std::make_pair(val, found);
|
||||
};
|
||||
|
||||
// Associates an array with an integer key
|
||||
std::pair<AMFBaseValue*, bool> Insert(const uint32_t& index) {
|
||||
AMFArrayValue* val = nullptr;
|
||||
bool inserted = false;
|
||||
if (index >= dense.size()) {
|
||||
dense.resize(index + 1);
|
||||
val = new AMFArrayValue();
|
||||
dense.at(index) = val;
|
||||
inserted = true;
|
||||
}
|
||||
return std::make_pair(dynamic_cast<AMFArrayValue*>(dense.at(index)), inserted);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Inserts an AMFValue into the AMFArray key'd by index.
|
||||
* Attempting to insert the same key to the same value twice overwrites
|
||||
* the previous value with the new one.
|
||||
*
|
||||
* @param index The index to associate with the value
|
||||
* @param value The value to insert
|
||||
* @return The inserted element, or nullptr if the type did not match
|
||||
* what was at the index.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
std::pair<AMFValue<ValueType>*, bool> Insert(const uint32_t& index, ValueType value) {
|
||||
AMFValue<ValueType>* val = nullptr;
|
||||
bool inserted = false;
|
||||
if (index >= this->dense.size()) {
|
||||
this->dense.resize(index + 1);
|
||||
val = new AMFValue<ValueType>(value);
|
||||
this->dense.at(index) = val;
|
||||
inserted = true;
|
||||
}
|
||||
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(this->dense.at(index)), inserted);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts an AMFValue into the associative portion with the given key.
|
||||
* If a duplicate is attempted to be inserted, it replaces the original
|
||||
*
|
||||
* The inserted element is now owned by this object and is not to be deleted
|
||||
*
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*/
|
||||
void Insert(const std::string& key, AMFBaseValue* value) {
|
||||
auto element = associative.find(key);
|
||||
if (element != associative.end() && element->second) {
|
||||
delete element->second;
|
||||
element->second = value;
|
||||
} else {
|
||||
associative.insert(std::make_pair(key, value));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts an AMFValue into the associative portion with the given index.
|
||||
* If a duplicate is attempted to be inserted, it replaces the original
|
||||
*
|
||||
* The inserted element is now owned by this object and is not to be deleted
|
||||
*
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*/
|
||||
void Insert(const uint32_t index, AMFBaseValue* value) {
|
||||
if (index < dense.size()) {
|
||||
AMFDense::iterator itr = dense.begin() + index;
|
||||
if (*itr) delete dense.at(index);
|
||||
} else {
|
||||
dense.resize(index + 1);
|
||||
}
|
||||
dense.at(index) = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pushes an AMFValue into the back of the dense portion.
|
||||
*
|
||||
* These objects are not to be deleted by the caller as they are owned by
|
||||
* the AMFArray object which manages its own memory.
|
||||
*
|
||||
* @param value The value to insert
|
||||
*
|
||||
* @return The inserted pointer, or nullptr should the key already be in use.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
inline AMFValue<ValueType>* Push(ValueType value) {
|
||||
return Insert(this->dense.size(), value).first;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the key from the associative portion
|
||||
*
|
||||
* The pointer removed is now no longer managed by this container
|
||||
*
|
||||
* @param key The key to remove from the associative portion
|
||||
*/
|
||||
void Remove(const std::string& key, bool deleteValue = true) {
|
||||
AMFAssociative::iterator it = this->associative.find(key);
|
||||
if (it != this->associative.end()) {
|
||||
if (deleteValue) delete it->second;
|
||||
this->associative.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the last element in the dense portion, deleting it in the process.
|
||||
*/
|
||||
void Remove(const uint32_t index) {
|
||||
if (!this->dense.empty() && index < this->dense.size()) {
|
||||
auto itr = this->dense.begin() + index;
|
||||
if (*itr) delete (*itr);
|
||||
this->dense.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
void Pop() {
|
||||
if (!this->dense.empty()) Remove(this->dense.size() - 1);
|
||||
}
|
||||
|
||||
AMFArrayValue* GetArray(const std::string& key) {
|
||||
AMFAssociative::const_iterator it = this->associative.find(key);
|
||||
if (it != this->associative.end()) {
|
||||
return dynamic_cast<AMFArrayValue*>(it->second);
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
AMFArrayValue* GetArray(const uint32_t index) {
|
||||
return index >= this->dense.size() ? nullptr : dynamic_cast<AMFArrayValue*>(this->dense.at(index));
|
||||
};
|
||||
|
||||
inline AMFArrayValue* InsertArray(const std::string& key) {
|
||||
return static_cast<AMFArrayValue*>(Insert(key).first);
|
||||
};
|
||||
|
||||
inline AMFArrayValue* InsertArray(const uint32_t index) {
|
||||
return static_cast<AMFArrayValue*>(Insert(index).first);
|
||||
};
|
||||
|
||||
inline AMFArrayValue* PushArray() {
|
||||
return static_cast<AMFArrayValue*>(Insert(this->dense.size()).first);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an AMFValue by the key from the associative portion and converts it
|
||||
* to the AmfValue template type. If the key did not exist, it is inserted.
|
||||
*
|
||||
* @tparam The target object type
|
||||
* @param key The key to lookup
|
||||
*
|
||||
* @return The AMFValue
|
||||
*/
|
||||
template <typename AmfType>
|
||||
AMFValue<AmfType>* Get(const std::string& key) const {
|
||||
AMFAssociative::const_iterator it = this->associative.find(key);
|
||||
return it != this->associative.end() ?
|
||||
dynamic_cast<AMFValue<AmfType>*>(it->second) :
|
||||
nullptr;
|
||||
};
|
||||
|
||||
// Get from the array but dont cast it
|
||||
AMFBaseValue* Get(const std::string& key) const {
|
||||
AMFAssociative::const_iterator it = this->associative.find(key);
|
||||
return it != this->associative.end() ? it->second : nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get an AMFValue object at a position in the dense portion.
|
||||
* Gets an AMFValue by the index from the dense portion and converts it
|
||||
* to the AmfValue template type. If the index did not exist, it is inserted.
|
||||
*
|
||||
* @tparam The target object type
|
||||
* @param index The index to get
|
||||
* @return The casted object, or nullptr.
|
||||
*/
|
||||
template <typename AmfType>
|
||||
AMFValue<AmfType>* Get(uint32_t index) const {
|
||||
return index < this->dense.size() ?
|
||||
dynamic_cast<AMFValue<AmfType>*>(this->dense.at(index)) :
|
||||
nullptr;
|
||||
};
|
||||
|
||||
// Get from the dense but dont cast it
|
||||
AMFBaseValue* Get(const uint32_t index) const {
|
||||
return index < this->dense.size() ? this->dense.at(index) : nullptr;
|
||||
};
|
||||
private:
|
||||
/**
|
||||
* The associative portion. These values are key'd with strings to an AMFValue.
|
||||
*/
|
||||
AMFAssociative associative;
|
||||
|
||||
/**
|
||||
* The dense portion. These AMFValue's are stored one after
|
||||
* another with the most recent addition being at the back.
|
||||
*/
|
||||
AMFDense dense;
|
||||
};
|
||||
|
||||
#endif //!__AMF3__H__
|
||||
@@ -1,184 +0,0 @@
|
||||
#include "AmfSerialize.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
// Writes an AMFValue pointer to a RakNet::BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
|
||||
eAmf type = value.GetValueType();
|
||||
this->Write(type);
|
||||
switch (type) {
|
||||
case eAmf::Integer: {
|
||||
this->Write<AMFIntValue&>(*static_cast<AMFIntValue*>(&value));
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Double: {
|
||||
this->Write<AMFDoubleValue&>(*static_cast<AMFDoubleValue*>(&value));
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::String: {
|
||||
this->Write<AMFStringValue&>(*static_cast<AMFStringValue*>(&value));
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Array: {
|
||||
this->Write<AMFArrayValue&>(*static_cast<AMFArrayValue*>(&value));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Game::logger->Log("AmfSerialize", "Encountered unwritable AMFType %i!", type);
|
||||
}
|
||||
case eAmf::Undefined:
|
||||
case eAmf::Null:
|
||||
case eAmf::False:
|
||||
case eAmf::True:
|
||||
case eAmf::Date:
|
||||
case eAmf::Object:
|
||||
case eAmf::XML:
|
||||
case eAmf::XMLDoc:
|
||||
case eAmf::ByteArray:
|
||||
case eAmf::VectorInt:
|
||||
case eAmf::VectorUInt:
|
||||
case eAmf::VectorDouble:
|
||||
case eAmf::VectorObject:
|
||||
case eAmf::Dictionary:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A private function to write an value to a RakNet::BitStream
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||
unsigned char b4 = (unsigned char)v;
|
||||
if (v < 0x00200000) {
|
||||
b4 = b4 & 0x7F;
|
||||
if (v > 0x7F) {
|
||||
unsigned char b3;
|
||||
v = v >> 7;
|
||||
b3 = ((unsigned char)(v)) | 0x80;
|
||||
if (v > 0x7F) {
|
||||
unsigned char b2;
|
||||
v = v >> 7;
|
||||
b2 = ((unsigned char)(v)) | 0x80;
|
||||
bs->Write(b2);
|
||||
}
|
||||
|
||||
bs->Write(b3);
|
||||
}
|
||||
} else {
|
||||
unsigned char b1;
|
||||
unsigned char b2;
|
||||
unsigned char b3;
|
||||
|
||||
v = v >> 8;
|
||||
b3 = ((unsigned char)(v)) | 0x80;
|
||||
v = v >> 7;
|
||||
b2 = ((unsigned char)(v)) | 0x80;
|
||||
v = v >> 7;
|
||||
b1 = ((unsigned char)(v)) | 0x80;
|
||||
|
||||
bs->Write(b1);
|
||||
bs->Write(b2);
|
||||
bs->Write(b3);
|
||||
}
|
||||
|
||||
bs->Write(b4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a flag number to a RakNet::BitStream
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
|
||||
v = (v << 1) | 0x01;
|
||||
WriteUInt29(bs, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an AMFString to a RakNet::BitStream
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
|
||||
WriteFlagNumber(bs, (uint32_t)str.size());
|
||||
bs->Write(str.c_str(), (uint32_t)str.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an U16 to a bitstream
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU16(RakNet::BitStream* bs, uint16_t value) {
|
||||
bs->Write(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an U32 to a bitstream
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU32(RakNet::BitStream* bs, uint32_t value) {
|
||||
bs->Write(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an U64 to a bitstream
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU64(RakNet::BitStream* bs, uint64_t value) {
|
||||
bs->Write(value);
|
||||
}
|
||||
|
||||
// Writes an AMFIntegerValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value) {
|
||||
WriteUInt29(this, value.GetValue());
|
||||
}
|
||||
|
||||
// Writes an AMFDoubleValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value) {
|
||||
double d = value.GetValue();
|
||||
WriteAMFU64(this, *reinterpret_cast<uint64_t*>(&d));
|
||||
}
|
||||
|
||||
// Writes an AMFStringValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value) {
|
||||
WriteAMFString(this, value.GetValue());
|
||||
}
|
||||
|
||||
// Writes an AMFArrayValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value) {
|
||||
uint32_t denseSize = value.GetDense().size();
|
||||
WriteFlagNumber(this, denseSize);
|
||||
|
||||
auto it = value.GetAssociative().begin();
|
||||
auto end = value.GetAssociative().end();
|
||||
|
||||
while (it != end) {
|
||||
WriteAMFString(this, it->first);
|
||||
this->Write<AMFBaseValue&>(*it->second);
|
||||
it++;
|
||||
}
|
||||
|
||||
this->Write(eAmf::Null);
|
||||
|
||||
if (denseSize > 0) {
|
||||
auto it2 = value.GetDense().begin();
|
||||
auto end2 = value.GetDense().end();
|
||||
|
||||
while (it2 != end2) {
|
||||
this->Write<AMFBaseValue&>(**it2);
|
||||
it2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "Amf3.h"
|
||||
|
||||
// RakNet
|
||||
#include <BitStream.h>
|
||||
|
||||
/*!
|
||||
\file AmfSerialize.h
|
||||
\brief A class that implements native writing of AMF values to RakNet::BitStream
|
||||
*/
|
||||
|
||||
// We are using the RakNet namespace
|
||||
namespace RakNet {
|
||||
//! Writes an AMFValue pointer to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value);
|
||||
|
||||
//! Writes an AMFIntegerValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value);
|
||||
|
||||
//! Writes an AMFDoubleValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value);
|
||||
|
||||
//! Writes an AMFStringValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value);
|
||||
|
||||
//! Writes an AMFArrayValue to a RakNet::BitStream
|
||||
/*!
|
||||
\param value The value to write
|
||||
*/
|
||||
template <>
|
||||
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value);
|
||||
} // namespace RakNet
|
||||
@@ -1,6 +1,6 @@
|
||||
set(DCOMMON_SOURCES
|
||||
set(DCOMMON_SOURCES "AMFFormat.cpp"
|
||||
"AMFDeserialize.cpp"
|
||||
"AmfSerialize.cpp"
|
||||
"AMFFormat_BitStream.cpp"
|
||||
"BinaryIO.cpp"
|
||||
"dConfig.cpp"
|
||||
"Diagnostics.cpp"
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef __DLUASSERT__H__
|
||||
#define __DLUASSERT__H__
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
# define DluAssert(expression) assert(expression)
|
||||
#else
|
||||
# define DluAssert(expression)
|
||||
#endif
|
||||
|
||||
#endif //!__DLUASSERT__H__
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
|
||||
virtual void WriteToPacket(RakNet::BitStream* packet) = 0;
|
||||
|
||||
virtual const std::u16string& GetKey() const = 0;
|
||||
virtual const std::u16string& GetKey() = 0;
|
||||
|
||||
virtual eLDFType GetValueType() = 0;
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
/*!
|
||||
\return The key
|
||||
*/
|
||||
const std::u16string& GetKey(void) const override { return this->key; }
|
||||
const std::u16string& GetKey(void) override { return this->key; }
|
||||
|
||||
//! Gets the LDF Type
|
||||
/*!
|
||||
|
||||
@@ -56,13 +56,6 @@ const uint32_t LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID
|
||||
const uint16_t LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
|
||||
const uint16_t LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
|
||||
const uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
|
||||
const LOT LOT_ZONE_CONTROL = 2365;
|
||||
const LOT LOT_3D_AMBIENT_SOUND = 6368;
|
||||
const LOT LOT_MODEL_IN_WORLD = 14;
|
||||
const LOT LOT_THINKING_CAP = 6086;
|
||||
const LOT LOT_ROCKET = 6416;
|
||||
const LOT LOT_LEGACY_RESPAWN_POINT = 4945;
|
||||
|
||||
|
||||
const float PI = 3.14159f;
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef __EPHYSICSBEHAVIORTYPE__H__
|
||||
#define __EPHYSICSBEHAVIORTYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class ePhysicsBehaviorType : int32_t {
|
||||
INVALID = -1,
|
||||
GROUND,
|
||||
FLYING,
|
||||
STANDARD,
|
||||
DYNAMIC
|
||||
};
|
||||
|
||||
#endif //!__EPHYSICSBEHAVIORTYPE__H__
|
||||
@@ -11,14 +11,14 @@ enum class eReplicaComponentType : uint32_t {
|
||||
CHARACTER,
|
||||
SCRIPT,
|
||||
BOUNCER,
|
||||
DESTROYABLE,
|
||||
BUFF, // buff is really 98, this is DESTROYABLE
|
||||
GHOST,
|
||||
SKILL,
|
||||
SPAWN,
|
||||
SPAWNER,
|
||||
ITEM,
|
||||
MODULAR_BUILD,
|
||||
BUILD_CONTROLLER,
|
||||
BUILD_ACTIVATOR,
|
||||
REBUILD,
|
||||
REBUILD_START,
|
||||
REBUILD_ACTIVATOR,
|
||||
ICON_ONLY,
|
||||
VENDOR,
|
||||
INVENTORY,
|
||||
@@ -33,8 +33,8 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PET,
|
||||
PLATFORM_BOUNDARY,
|
||||
MODULE,
|
||||
JETPACKPAD,
|
||||
HAVOK_VEHICLE_PHYSICS,
|
||||
ARCADE,
|
||||
VEHICLE_PHYSICS, // Havok demo based
|
||||
MOVEMENT_AI,
|
||||
EXHIBIT,
|
||||
OVERHEAD_ICON,
|
||||
@@ -46,23 +46,23 @@ enum class eReplicaComponentType : uint32_t {
|
||||
SCRIPTED_ACTIVITY,
|
||||
PHANTOM_PHYSICS,
|
||||
SPRINGPAD,
|
||||
MODEL_BEHAVIOR,
|
||||
MODEL,
|
||||
PROPERTY_ENTRANCE,
|
||||
FX,
|
||||
PROPERTY_MANAGEMENT,
|
||||
VEHICLE_PHYSICS,
|
||||
VEHICLE_PHYSICS_NEW, // internal physics based on havok
|
||||
PHYSICS_SYSTEM,
|
||||
QUICK_BUILD,
|
||||
SWITCH,
|
||||
MINIGAME_CONTROL,
|
||||
CHANGLING_BUILD,
|
||||
ZONE_CONTROL, // Minigame
|
||||
CHANGLING,
|
||||
CHOICE_BUILD,
|
||||
PACKAGE,
|
||||
SOUND_REPEATER,
|
||||
SOUND_AMBIENT_2D,
|
||||
SOUND_AMBIENT_3D,
|
||||
PRECONDITION,
|
||||
FLAG,
|
||||
PLAYER_FLAG,
|
||||
CUSTOM_BUILD_ASSEMBLY,
|
||||
BASE_COMBAT_AI,
|
||||
MODULE_ASSEMBLY,
|
||||
@@ -71,8 +71,8 @@ enum class eReplicaComponentType : uint32_t {
|
||||
GENERIC_ACTIVATOR,
|
||||
PROPERTY_VENDOR,
|
||||
HF_LIGHT_DIRECTION_GADGET,
|
||||
ROCKET_LAUNCHPAD_CONTROL,
|
||||
ROCKET_ANIMATION_CONTROL,
|
||||
ROCKET_LAUNCH,
|
||||
ROCKET_LANDING,
|
||||
TRIGGER,
|
||||
DROPPED_LOOT,
|
||||
RACING_CONTROL,
|
||||
@@ -84,7 +84,7 @@ enum class eReplicaComponentType : uint32_t {
|
||||
SOUND_TRIGGER,
|
||||
PROXIMITY_MONITOR,
|
||||
RACING_SOUND_TRIGGER,
|
||||
CHAT_BUBBLE,
|
||||
CHAT,
|
||||
FRIENDS_LIST,
|
||||
GUILD,
|
||||
LOCAL_SYSTEM,
|
||||
@@ -101,27 +101,27 @@ enum class eReplicaComponentType : uint32_t {
|
||||
TRADE,
|
||||
USER_CONTROL,
|
||||
IGNORE_LIST,
|
||||
MULTI_ZONE_ENTRANCE,
|
||||
BUFF,
|
||||
ROCKET_LAUNCH_LUP,
|
||||
BUFF_REAL, // the real buff component, should just be name BUFF
|
||||
INTERACTION_MANAGER,
|
||||
DONATION_VENDOR,
|
||||
COMBAT_MEDIATOR,
|
||||
ACHIEVEMENT_VENDOR,
|
||||
GATE_RUSH_CONTROL,
|
||||
COMMENDATION_VENDOR,
|
||||
UNKNOWN_103,
|
||||
RAIL_ACTIVATOR,
|
||||
ROLLER,
|
||||
PLAYER_FORCED_MOVEMENT,
|
||||
CRAFTING,
|
||||
POSSESSABLE,
|
||||
LEVEL_PROGRESSION,
|
||||
POSSESSION,
|
||||
POSSESSOR,
|
||||
MOUNT_CONTROL,
|
||||
UNKNOWN_112,
|
||||
PROPERTY_PLAQUE,
|
||||
BUILD_BORDER,
|
||||
UNKNOWN_115,
|
||||
UNKOWN_115,
|
||||
CULLING_PLANE,
|
||||
NUMBER_OF_COMPONENTS
|
||||
DESTROYABLE = 1000 // Actually 7
|
||||
};
|
||||
|
||||
#endif //!__EREPLICACOMPONENTTYPE__H__
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
#ifndef __EUGCMODERATIONSTATUS__H__
|
||||
#define __EUGCMODERATIONSTATUS__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eUgcModerationStatus : uint32_t {
|
||||
NoStatus,
|
||||
Approved,
|
||||
Rejected,
|
||||
};
|
||||
|
||||
#endif //!__EUGCMODERATIONSTATUS__H__
|
||||
@@ -16,6 +16,9 @@
|
||||
// Enable this to cache all entries in each table for fast access, comes with more memory cost
|
||||
//#define CDCLIENT_CACHE_ALL
|
||||
|
||||
// Enable this to skip some unused columns in some tables
|
||||
#define UNUSED(v)
|
||||
|
||||
/*!
|
||||
\file CDClientDatabase.hpp
|
||||
\brief An interface between the CDClient.sqlite file and the server
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
CDClientManager::CDClientManager() {
|
||||
CDActivityRewardsTable::Instance();
|
||||
CDAnimationsTable::Instance();
|
||||
UNUSED(CDAnimationsTable::Instance());
|
||||
CDBehaviorParameterTable::Instance();
|
||||
CDBehaviorTemplateTable::Instance();
|
||||
CDComponentsRegistryTable::Instance();
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "Singleton.h"
|
||||
|
||||
#define UNUSED_TABLE(v)
|
||||
|
||||
/**
|
||||
* Initialize the CDClient tables so they are all loaded into memory.
|
||||
*/
|
||||
|
||||
@@ -1,83 +1,56 @@
|
||||
#include "CDAnimationsTable.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "Game.h"
|
||||
|
||||
bool CDAnimationsTable::CacheData(CppSQLite3Statement& queryToCache) {
|
||||
auto tableData = queryToCache.execQuery();
|
||||
// If we received a bad lookup, cache it anyways so we do not run the query again.
|
||||
if (tableData.eof()) return false;
|
||||
CDAnimationsTable::CDAnimationsTable(void) {
|
||||
|
||||
do {
|
||||
std::string animation_type = tableData.getStringField("animation_type", "");
|
||||
DluAssert(!animation_type.empty());
|
||||
AnimationGroupID animationGroupID = tableData.getIntField("animationGroupID", -1);
|
||||
DluAssert(animationGroupID != -1);
|
||||
// First, get the size of the table
|
||||
unsigned int size = 0;
|
||||
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Animations");
|
||||
while (!tableSize.eof()) {
|
||||
size = tableSize.getIntField(0, 0);
|
||||
|
||||
CDAnimation entry;
|
||||
tableSize.nextRow();
|
||||
}
|
||||
|
||||
tableSize.finalize();
|
||||
|
||||
// Reserve the size
|
||||
this->entries.reserve(size);
|
||||
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Animations");
|
||||
while (!tableData.eof()) {
|
||||
CDAnimations entry;
|
||||
entry.animationGroupID = tableData.getIntField("animationGroupID", -1);
|
||||
entry.animation_type = tableData.getStringField("animation_type", "");
|
||||
entry.animation_name = tableData.getStringField("animation_name", "");
|
||||
entry.chance_to_play = tableData.getFloatField("chance_to_play", 1.0f);
|
||||
UNUSED_COLUMN(entry.min_loops = tableData.getIntField("min_loops", 0);)
|
||||
UNUSED_COLUMN(entry.max_loops = tableData.getIntField("max_loops", 0);)
|
||||
entry.animation_length = tableData.getFloatField("animation_length", 0.0f);
|
||||
UNUSED_COLUMN(entry.hideEquip = tableData.getIntField("hideEquip", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.ignoreUpperBody = tableData.getIntField("ignoreUpperBody", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.restartable = tableData.getIntField("restartable", 0) == 1;)
|
||||
UNUSED_COLUMN(entry.face_animation_name = tableData.getStringField("face_animation_name", "");)
|
||||
UNUSED_COLUMN(entry.priority = tableData.getFloatField("priority", 0.0f);)
|
||||
UNUSED_COLUMN(entry.blendTime = tableData.getFloatField("blendTime", 0.0f);)
|
||||
entry.chance_to_play = tableData.getFloatField("chance_to_play", -1.0f);
|
||||
entry.min_loops = tableData.getIntField("min_loops", -1);
|
||||
entry.max_loops = tableData.getIntField("max_loops", -1);
|
||||
entry.animation_length = tableData.getFloatField("animation_length", -1.0f);
|
||||
entry.hideEquip = tableData.getIntField("hideEquip", -1) == 1 ? true : false;
|
||||
entry.ignoreUpperBody = tableData.getIntField("ignoreUpperBody", -1) == 1 ? true : false;
|
||||
entry.restartable = tableData.getIntField("restartable", -1) == 1 ? true : false;
|
||||
entry.face_animation_name = tableData.getStringField("face_animation_name", "");
|
||||
entry.priority = tableData.getFloatField("priority", -1.0f);
|
||||
entry.blendTime = tableData.getFloatField("blendTime", -1.0f);
|
||||
|
||||
this->animations[CDAnimationKey(animation_type, animationGroupID)].push_back(entry);
|
||||
this->entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
} while (!tableData.eof());
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDAnimationsTable::CacheAnimations(const CDAnimationKey animationKey) {
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Animations WHERE animationGroupID = ? and animation_type = ?");
|
||||
query.bind(1, static_cast<int32_t>(animationKey.second));
|
||||
query.bind(2, animationKey.first.c_str());
|
||||
// If we received a bad lookup, cache it anyways so we do not run the query again.
|
||||
if (!CacheData(query)) {
|
||||
this->animations[animationKey];
|
||||
}
|
||||
std::vector<CDAnimations> CDAnimationsTable::Query(std::function<bool(CDAnimations)> predicate) {
|
||||
|
||||
std::vector<CDAnimations> data = cpplinq::from(this->entries)
|
||||
>> cpplinq::where(predicate)
|
||||
>> cpplinq::to_vector();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void CDAnimationsTable::CacheAnimationGroup(AnimationGroupID animationGroupID) {
|
||||
auto animationEntryCached = this->animations.find(CDAnimationKey("", animationGroupID));
|
||||
if (animationEntryCached != this->animations.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Animations WHERE animationGroupID = ?");
|
||||
query.bind(1, static_cast<int32_t>(animationGroupID));
|
||||
|
||||
// Cache the query so we don't run the query again.
|
||||
CacheData(query);
|
||||
this->animations[CDAnimationKey("", animationGroupID)];
|
||||
std::vector<CDAnimations> CDAnimationsTable::GetEntries(void) const {
|
||||
return this->entries;
|
||||
}
|
||||
|
||||
CDAnimationLookupResult CDAnimationsTable::GetAnimation(const AnimationID& animationType, const std::string& previousAnimationName, const AnimationGroupID animationGroupID) {
|
||||
CDAnimationKey animationKey(animationType, animationGroupID);
|
||||
auto animationEntryCached = this->animations.find(animationKey);
|
||||
if (animationEntryCached == this->animations.end()) {
|
||||
this->CacheAnimations(animationKey);
|
||||
}
|
||||
|
||||
auto animationEntry = this->animations.find(animationKey);
|
||||
// If we have only one animation, return it regardless of the chance to play.
|
||||
if (animationEntry->second.size() == 1) {
|
||||
return CDAnimationLookupResult(animationEntry->second.front());
|
||||
}
|
||||
auto randomAnimation = GeneralUtils::GenerateRandomNumber<float>(0, 1);
|
||||
|
||||
for (auto& animationEntry : animationEntry->second) {
|
||||
randomAnimation -= animationEntry.chance_to_play;
|
||||
// This is how the client gets the random animation.
|
||||
if (animationEntry.animation_name != previousAnimationName && randomAnimation <= 0.0f) return CDAnimationLookupResult(animationEntry);
|
||||
}
|
||||
|
||||
return CDAnimationLookupResult();
|
||||
}
|
||||
|
||||
@@ -1,66 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include <list>
|
||||
|
||||
struct CDAnimation {
|
||||
// unsigned int animationGroupID;
|
||||
// std::string animation_type;
|
||||
// The above two are a pair to represent a primary key in the map.
|
||||
struct CDAnimations {
|
||||
unsigned int animationGroupID; //!< The animation group ID
|
||||
std::string animation_type; //!< The animation type
|
||||
std::string animation_name; //!< The animation name
|
||||
float chance_to_play; //!< The chance to play the animation
|
||||
UNUSED_COLUMN(unsigned int min_loops;) //!< The minimum number of loops
|
||||
UNUSED_COLUMN(unsigned int max_loops;) //!< The maximum number of loops
|
||||
unsigned int min_loops; //!< The minimum number of loops
|
||||
unsigned int max_loops; //!< The maximum number of loops
|
||||
float animation_length; //!< The animation length
|
||||
UNUSED_COLUMN(bool hideEquip;) //!< Whether or not to hide the equip
|
||||
UNUSED_COLUMN(bool ignoreUpperBody;) //!< Whether or not to ignore the upper body
|
||||
UNUSED_COLUMN(bool restartable;) //!< Whether or not the animation is restartable
|
||||
UNUSED_COLUMN(std::string face_animation_name;) //!< The face animation name
|
||||
UNUSED_COLUMN(float priority;) //!< The priority
|
||||
UNUSED_COLUMN(float blendTime;) //!< The blend time
|
||||
bool hideEquip; //!< Whether or not to hide the equip
|
||||
bool ignoreUpperBody; //!< Whether or not to ignore the upper body
|
||||
bool restartable; //!< Whether or not the animation is restartable
|
||||
std::string face_animation_name; //!< The face animation name
|
||||
float priority; //!< The priority
|
||||
float blendTime; //!< The blend time
|
||||
};
|
||||
|
||||
typedef LookupResult<CDAnimation> CDAnimationLookupResult;
|
||||
|
||||
class CDAnimationsTable : public CDTable<CDAnimationsTable> {
|
||||
typedef int32_t AnimationGroupID;
|
||||
typedef std::string AnimationID;
|
||||
typedef std::pair<std::string, AnimationGroupID> CDAnimationKey;
|
||||
public:
|
||||
/**
|
||||
* Given an animationType and the previousAnimationName played, return the next animationType to play.
|
||||
* If there are more than 1 animationTypes that can be played, one is selected at random but also does not allow
|
||||
* the previousAnimationName to be played twice.
|
||||
*
|
||||
* @param animationType The animationID to lookup
|
||||
* @param previousAnimationName The previously played animation
|
||||
* @param animationGroupID The animationGroupID to lookup
|
||||
* @return CDAnimationLookupResult
|
||||
*/
|
||||
[[nodiscard]] CDAnimationLookupResult GetAnimation(const AnimationID& animationType, const std::string& previousAnimationName, const AnimationGroupID animationGroupID);
|
||||
|
||||
/**
|
||||
* Cache a full AnimationGroup by its ID.
|
||||
*/
|
||||
void CacheAnimationGroup(AnimationGroupID animationGroupID);
|
||||
private:
|
||||
std::vector<CDAnimations> entries;
|
||||
|
||||
/**
|
||||
* Cache all animations given a premade key
|
||||
*/
|
||||
void CacheAnimations(const CDAnimationKey animationKey);
|
||||
public:
|
||||
CDAnimationsTable();
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDAnimations> Query(std::function<bool(CDAnimations)> predicate);
|
||||
|
||||
/**
|
||||
* Run the query responsible for caching the data.
|
||||
* @param queryToCache
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool CacheData(CppSQLite3Statement& queryToCache);
|
||||
|
||||
/**
|
||||
* Each animation is key'd by its animationName and its animationGroupID. Each
|
||||
* animation has a possible list of animations. This is because there can be animations have a percent chance to play so one is selected at random.
|
||||
*/
|
||||
std::map<CDAnimationKey, std::list<CDAnimation>> animations;
|
||||
std::vector<CDAnimations> GetEntries(void) const;
|
||||
};
|
||||
|
||||
@@ -90,11 +90,4 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
|
||||
return defaultValue;
|
||||
#endif
|
||||
}
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> CDComponentsRegistryTable::GetTemplateComponents(LOT templateId) {
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> components;
|
||||
for (int8_t i = 0; static_cast<eReplicaComponentType>(i) < eReplicaComponentType::NUMBER_OF_COMPONENTS; i++) {
|
||||
auto compId = GetByIDAndType(templateId, static_cast<eReplicaComponentType>(i), -1);
|
||||
if (compId != -1) components.push_back(std::make_pair(static_cast<eReplicaComponentType>(i), compId));
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
struct CDComponentsRegistry {
|
||||
@@ -19,5 +18,4 @@ private:
|
||||
public:
|
||||
CDComponentsRegistryTable();
|
||||
int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0);
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> GetTemplateComponents(LOT templateId);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "CDItemComponentTable.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#include "eItemType.h"
|
||||
|
||||
CDItemComponent CDItemComponentTable::Default = {};
|
||||
|
||||
//! Constructor
|
||||
@@ -76,8 +74,8 @@ CDItemComponentTable::CDItemComponentTable(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int id) {
|
||||
const auto& it = this->entries.find(id);
|
||||
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) {
|
||||
const auto& it = this->entries.find(skillID);
|
||||
if (it != this->entries.end()) {
|
||||
return it->second;
|
||||
}
|
||||
@@ -85,11 +83,11 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int i
|
||||
#ifndef CDCLIENT_CACHE_ALL
|
||||
std::stringstream query;
|
||||
|
||||
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(id);
|
||||
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(skillID);
|
||||
|
||||
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
|
||||
if (tableData.eof()) {
|
||||
entries.insert(std::make_pair(id, Default));
|
||||
entries.insert(std::make_pair(skillID, Default));
|
||||
return Default;
|
||||
}
|
||||
|
||||
@@ -100,7 +98,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int i
|
||||
entry.baseValue = tableData.getIntField("baseValue", -1);
|
||||
entry.isKitPiece = tableData.getIntField("isKitPiece", -1) == 1 ? true : false;
|
||||
entry.rarity = tableData.getIntField("rarity", 0);
|
||||
entry.itemType = static_cast<eItemType>(tableData.getIntField("itemType", -1));
|
||||
entry.itemType = tableData.getIntField("itemType", -1);
|
||||
entry.itemInfo = tableData.getInt64Field("itemInfo", -1);
|
||||
entry.inLootTable = tableData.getIntField("inLootTable", -1) == 1 ? true : false;
|
||||
entry.inVendor = tableData.getIntField("inVendor", -1) == 1 ? true : false;
|
||||
@@ -142,7 +140,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int i
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
const auto& it2 = this->entries.find(id);
|
||||
const auto& it2 = this->entries.find(skillID);
|
||||
if (it2 != this->entries.end()) {
|
||||
return it2->second;
|
||||
}
|
||||
|
||||
@@ -4,15 +4,13 @@
|
||||
#include "CDTable.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
enum class eItemType : int32_t;
|
||||
|
||||
struct CDItemComponent {
|
||||
unsigned int id; //!< The Component ID
|
||||
std::string equipLocation; //!< The equip location
|
||||
unsigned int baseValue; //!< The monetary base value of the item
|
||||
bool isKitPiece; //!< Whether or not the item belongs to a kit
|
||||
unsigned int rarity; //!< The rarity of the item
|
||||
eItemType itemType; //!< The item type
|
||||
unsigned int itemType; //!< The item type
|
||||
int64_t itemInfo; //!< The item info
|
||||
bool inLootTable; //!< Whether or not the item is in a loot table
|
||||
bool inVendor; //!< Whether or not the item is in a vendor inventory
|
||||
@@ -60,7 +58,7 @@ public:
|
||||
static std::map<LOT, uint32_t> ParseCraftingCurrencies(const CDItemComponent& itemComponent);
|
||||
|
||||
// Gets an entry by ID
|
||||
const CDItemComponent& GetItemComponentByID(unsigned int id);
|
||||
const CDItemComponent& GetItemComponentByID(unsigned int skillID);
|
||||
|
||||
static CDItemComponent Default;
|
||||
};
|
||||
|
||||
@@ -37,7 +37,10 @@ CDPhysicsComponentTable::~CDPhysicsComponentTable() {
|
||||
}
|
||||
|
||||
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) {
|
||||
auto itr = m_entries.find(componentID);
|
||||
return itr != m_entries.end() ? itr->second : nullptr;
|
||||
for (auto e : m_entries) {
|
||||
if (e.first == componentID) return e.second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "CDClientDatabase.h"
|
||||
#include "Singleton.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
@@ -16,12 +15,6 @@
|
||||
#endif
|
||||
#include "cpplinq.hpp"
|
||||
|
||||
// Used for legacy
|
||||
#define UNUSED(x)
|
||||
|
||||
// Enable this to skip some unused columns in some tables
|
||||
#define UNUSED_COLUMN(v)
|
||||
|
||||
#pragma warning (disable : 4244) //Disable double to float conversion warnings
|
||||
#pragma warning (disable : 4715) //Disable "not all control paths return a value"
|
||||
|
||||
@@ -30,15 +23,3 @@ class CDTable : public Singleton<Table> {
|
||||
protected:
|
||||
virtual ~CDTable() = default;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class LookupResult {
|
||||
typedef std::pair<T, bool> DataType;
|
||||
public:
|
||||
LookupResult() { m_data.first = T(); m_data.second = false; };
|
||||
LookupResult(T& data) { m_data.first = data; m_data.second = true; };
|
||||
inline const T& Data() { return m_data.first; };
|
||||
inline const bool& FoundData() { return m_data.second; };
|
||||
private:
|
||||
DataType m_data;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
set(DGAME_SOURCES "Character.cpp"
|
||||
"Entity.cpp"
|
||||
"EntityManager.cpp"
|
||||
"LeaderboardManager.cpp"
|
||||
"Player.cpp"
|
||||
|
||||
@@ -418,7 +418,7 @@ void Character::WriteToDatabase() {
|
||||
delete printer;
|
||||
}
|
||||
|
||||
void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||
void Character::SetPlayerFlag(const int32_t flagId, const bool value) {
|
||||
// If the flag is already set, we don't have to recalculate it
|
||||
if (GetPlayerFlag(flagId) == value) return;
|
||||
|
||||
@@ -465,7 +465,7 @@ void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||
GameMessages::SendNotifyClientFlagChange(m_ObjectID, flagId, value, m_ParentUser->GetSystemAddress());
|
||||
}
|
||||
|
||||
bool Character::GetPlayerFlag(const uint32_t flagId) const {
|
||||
bool Character::GetPlayerFlag(const int32_t flagId) const {
|
||||
// Calculate the index first
|
||||
const auto flagIndex = uint32_t(std::floor(flagId / 64));
|
||||
|
||||
|
||||
@@ -415,14 +415,14 @@ public:
|
||||
* @param flagId the ID of the flag to set
|
||||
* @param value the value to set for the flag
|
||||
*/
|
||||
void SetPlayerFlag(uint32_t flagId, bool value);
|
||||
void SetPlayerFlag(int32_t flagId, bool value);
|
||||
|
||||
/**
|
||||
* Gets the value for a certain character flag
|
||||
* @param flagId the ID of the flag to get a value for
|
||||
* @return the value of the flag given the ID (the default is false, obviously)
|
||||
*/
|
||||
bool GetPlayerFlag(uint32_t flagId) const;
|
||||
bool GetPlayerFlag(int32_t flagId) const;
|
||||
|
||||
/**
|
||||
* Notifies the character that they're now muted
|
||||
|
||||
2044
dGame/Entity.cpp
Normal file
2044
dGame/Entity.cpp
Normal file
File diff suppressed because it is too large
Load Diff
502
dGame/Entity.h
Normal file
502
dGame/Entity.h
Normal file
@@ -0,0 +1,502 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
#include "LDFFormat.h"
|
||||
#include "eKillType.h"
|
||||
|
||||
namespace Loot {
|
||||
class Info;
|
||||
};
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLDocument;
|
||||
};
|
||||
|
||||
class Player;
|
||||
class EntityInfo;
|
||||
class User;
|
||||
class Spawner;
|
||||
class ScriptComponent;
|
||||
class dpEntity;
|
||||
class EntityTimer;
|
||||
class Component;
|
||||
class Item;
|
||||
class Character;
|
||||
class EntityCallbackTimer;
|
||||
enum class eTriggerEventType;
|
||||
enum class eGameMasterLevel : uint8_t;
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
enum class eReplicaPacketType : uint8_t;
|
||||
enum class eCinematicEvent : uint32_t;
|
||||
|
||||
namespace CppScripts {
|
||||
class Script;
|
||||
};
|
||||
|
||||
/**
|
||||
* An entity in the world. Has multiple components.
|
||||
*/
|
||||
class Entity {
|
||||
public:
|
||||
explicit Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity = nullptr);
|
||||
virtual ~Entity();
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
bool operator==(const Entity& other) const;
|
||||
bool operator!=(const Entity& other) const;
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
const LWOOBJID& GetObjectID() const { return m_ObjectID; }
|
||||
|
||||
const LOT GetLOT() const { return m_TemplateID; }
|
||||
|
||||
Character* GetCharacter() const { return m_Character; }
|
||||
|
||||
eGameMasterLevel GetGMLevel() const { return m_GMLevel; }
|
||||
|
||||
uint8_t GetCollectibleID() const { return uint8_t(m_CollectibleID); }
|
||||
|
||||
Entity* GetParentEntity() const { return m_ParentEntity; }
|
||||
|
||||
std::vector<std::string>& GetGroups() { return m_Groups; };
|
||||
|
||||
Spawner* GetSpawner() const { return m_Spawner; }
|
||||
|
||||
LWOOBJID GetSpawnerID() const { return m_SpawnerID; }
|
||||
|
||||
const std::vector<LDFBaseData*>& GetSettings() const { return m_Settings; }
|
||||
|
||||
const std::vector<LDFBaseData*>& GetNetworkSettings() const { return m_NetworkSettings; }
|
||||
|
||||
bool GetIsDead() const;
|
||||
|
||||
bool GetPlayerReadyForUpdates() const { return m_PlayerIsReadyForUpdates; }
|
||||
|
||||
bool GetIsGhostingCandidate() const;
|
||||
|
||||
int8_t GetObservers() const;
|
||||
|
||||
uint16_t GetNetworkId() const;
|
||||
|
||||
Entity* GetOwner() const;
|
||||
|
||||
const NiPoint3& GetDefaultPosition() const;
|
||||
|
||||
const NiQuaternion& GetDefaultRotation() const;
|
||||
|
||||
float GetDefaultScale() const;
|
||||
|
||||
const NiPoint3& GetPosition() const;
|
||||
|
||||
const NiQuaternion& GetRotation() const;
|
||||
|
||||
virtual User* GetParentUser() const;
|
||||
|
||||
virtual SystemAddress GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; };
|
||||
|
||||
/**
|
||||
* Setters
|
||||
*/
|
||||
|
||||
void SetCharacter(Character* value) { m_Character = value; }
|
||||
|
||||
void SetGMLevel(eGameMasterLevel value);
|
||||
|
||||
void SetOwnerOverride(LWOOBJID value);
|
||||
|
||||
void SetPlayerReadyForUpdates() { m_PlayerIsReadyForUpdates = true; }
|
||||
|
||||
void SetObservers(int8_t value);
|
||||
|
||||
void SetNetworkId(uint16_t id);
|
||||
|
||||
void SetPosition(NiPoint3 position);
|
||||
|
||||
void SetRotation(NiQuaternion rotation);
|
||||
|
||||
virtual void SetRespawnPos(NiPoint3 position) {}
|
||||
|
||||
virtual void SetRespawnRot(NiQuaternion rotation) {}
|
||||
|
||||
virtual void SetSystemAddress(const SystemAddress& value) {};
|
||||
|
||||
/**
|
||||
* Component management
|
||||
*/
|
||||
|
||||
Component* GetComponent(eReplicaComponentType componentID) const;
|
||||
|
||||
template<typename T>
|
||||
T* GetComponent() const;
|
||||
|
||||
template<typename T>
|
||||
bool TryGetComponent(eReplicaComponentType componentId, T*& component) const;
|
||||
|
||||
bool HasComponent(eReplicaComponentType componentId) const;
|
||||
|
||||
void AddComponent(eReplicaComponentType componentId, Component* component);
|
||||
|
||||
std::vector<ScriptComponent*> GetScriptComponents();
|
||||
|
||||
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName);
|
||||
void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName);
|
||||
|
||||
void SetProximityRadius(float proxRadius, std::string name);
|
||||
void SetProximityRadius(dpEntity* entity, std::string name);
|
||||
|
||||
void AddChild(Entity* child);
|
||||
void RemoveChild(Entity* child);
|
||||
void RemoveParent();
|
||||
void AddTimer(std::string name, float time);
|
||||
void AddCallbackTimer(float time, std::function<void()> callback);
|
||||
bool HasTimer(const std::string& name);
|
||||
void CancelCallbackTimers();
|
||||
void CancelAllTimers();
|
||||
void CancelTimer(const std::string& name);
|
||||
|
||||
void AddToGroup(const std::string& group);
|
||||
bool IsPlayer() const;
|
||||
|
||||
std::unordered_map<eReplicaComponentType, Component*>& GetComponents() { return m_Components; } // TODO: Remove
|
||||
|
||||
void WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
|
||||
void WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
|
||||
void ResetFlags();
|
||||
void UpdateXMLDoc(tinyxml2::XMLDocument* doc);
|
||||
void Update(float deltaTime);
|
||||
|
||||
// Events
|
||||
void OnCollisionProximity(LWOOBJID otherEntity, const std::string& proxName, const std::string& status);
|
||||
void OnCollisionPhantom(LWOOBJID otherEntity);
|
||||
void OnCollisionLeavePhantom(LWOOBJID otherEntity);
|
||||
|
||||
void OnFireEventServerSide(Entity* sender, std::string args, int32_t param1 = -1, int32_t param2 = -1, int32_t param3 = -1);
|
||||
void OnActivityStateChangeRequest(const LWOOBJID senderID, const int32_t value1, const int32_t value2,
|
||||
const std::u16string& stringValue);
|
||||
void OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName,
|
||||
float_t pathTime, float_t totalTime, int32_t waypoint);
|
||||
|
||||
void NotifyObject(Entity* sender, const std::string& name, int32_t param1 = 0, int32_t param2 = 0);
|
||||
void OnEmoteReceived(int32_t emote, Entity* target);
|
||||
|
||||
void OnUse(Entity* originator);
|
||||
|
||||
void OnHitOrHealResult(Entity* attacker, int32_t damage);
|
||||
void OnHit(Entity* attacker);
|
||||
|
||||
void OnZonePropertyEditBegin();
|
||||
void OnZonePropertyEditEnd();
|
||||
void OnZonePropertyModelEquipped();
|
||||
void OnZonePropertyModelPlaced(Entity* player);
|
||||
void OnZonePropertyModelPickedUp(Entity* player);
|
||||
void OnZonePropertyModelRemoved(Entity* player);
|
||||
void OnZonePropertyModelRemovedWhileEquipped(Entity* player);
|
||||
void OnZonePropertyModelRotated(Entity* player);
|
||||
|
||||
void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData);
|
||||
void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier);
|
||||
|
||||
void Smash(const LWOOBJID source = LWOOBJID_EMPTY, const eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"");
|
||||
void Kill(Entity* murderer = nullptr);
|
||||
void AddRebuildCompleteCallback(const std::function<void(Entity* user)>& callback) const;
|
||||
void AddCollisionPhantomCallback(const std::function<void(Entity* target)>& callback);
|
||||
void AddDieCallback(const std::function<void()>& callback);
|
||||
void Resurrect();
|
||||
|
||||
void AddLootItem(const Loot::Info& info);
|
||||
void PickupItem(const LWOOBJID& objectID);
|
||||
|
||||
bool CanPickupCoins(uint64_t count);
|
||||
void RegisterCoinDrop(uint64_t count);
|
||||
|
||||
void ScheduleKillAfterUpdate(Entity* murderer = nullptr);
|
||||
void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr);
|
||||
void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; }
|
||||
|
||||
virtual NiPoint3 GetRespawnPosition() const { return NiPoint3::ZERO; }
|
||||
virtual NiQuaternion GetRespawnRotation() const { return NiQuaternion::IDENTITY; }
|
||||
|
||||
void Sleep();
|
||||
void Wake();
|
||||
bool IsSleeping() const;
|
||||
|
||||
/*
|
||||
* Utility
|
||||
*/
|
||||
/**
|
||||
* Retroactively corrects the model vault size due to incorrect initialization in a previous patch.
|
||||
*
|
||||
*/
|
||||
void RetroactiveVaultSize();
|
||||
bool GetBoolean(const std::u16string& name) const;
|
||||
int32_t GetI32(const std::u16string& name) const;
|
||||
int64_t GetI64(const std::u16string& name) const;
|
||||
|
||||
void SetBoolean(const std::u16string& name, bool value);
|
||||
void SetI32(const std::u16string& name, int32_t value);
|
||||
void SetI64(const std::u16string& name, int64_t value);
|
||||
|
||||
bool HasVar(const std::u16string& name) const;
|
||||
|
||||
template<typename T>
|
||||
const T& GetVar(const std::u16string& name) const;
|
||||
|
||||
template<typename T>
|
||||
void SetVar(const std::u16string& name, T value);
|
||||
|
||||
void SendNetworkVar(const std::string& data, const SystemAddress& sysAddr);
|
||||
|
||||
template<typename T>
|
||||
void SetNetworkVar(const std::u16string& name, T value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
template<typename T>
|
||||
void SetNetworkVar(const std::u16string& name, std::vector<T> value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
template<typename T>
|
||||
T GetNetworkVar(const std::u16string& name);
|
||||
|
||||
/**
|
||||
* Get the LDF value and cast it as T.
|
||||
*/
|
||||
template<typename T>
|
||||
T GetVarAs(const std::u16string& name) const;
|
||||
|
||||
/**
|
||||
* Get the LDF data.
|
||||
*/
|
||||
LDFBaseData* GetVarData(const std::u16string& name) const;
|
||||
|
||||
/**
|
||||
* Get the LDF value and convert it to a string.
|
||||
*/
|
||||
std::string GetVarAsString(const std::u16string& name) const;
|
||||
|
||||
/*
|
||||
* Collision
|
||||
*/
|
||||
std::vector<LWOOBJID>& GetTargetsInPhantom();
|
||||
|
||||
Entity* GetScheduledKiller() { return m_ScheduleKiller; }
|
||||
|
||||
protected:
|
||||
LWOOBJID m_ObjectID;
|
||||
|
||||
LOT m_TemplateID;
|
||||
|
||||
std::vector<LDFBaseData*> m_Settings;
|
||||
std::vector<LDFBaseData*> m_NetworkSettings;
|
||||
|
||||
NiPoint3 m_DefaultPosition;
|
||||
NiQuaternion m_DefaultRotation;
|
||||
float m_Scale;
|
||||
|
||||
Spawner* m_Spawner;
|
||||
LWOOBJID m_SpawnerID;
|
||||
|
||||
bool m_HasSpawnerNodeID;
|
||||
uint32_t m_SpawnerNodeID;
|
||||
|
||||
Character* m_Character;
|
||||
|
||||
Entity* m_ParentEntity; //For spawners and the like
|
||||
std::vector<Entity*> m_ChildEntities;
|
||||
eGameMasterLevel m_GMLevel;
|
||||
uint16_t m_CollectibleID;
|
||||
std::vector<std::string> m_Groups;
|
||||
uint16_t m_NetworkID;
|
||||
std::vector<std::function<void()>> m_DieCallbacks;
|
||||
std::vector<std::function<void(Entity* target)>> m_PhantomCollisionCallbacks;
|
||||
|
||||
std::unordered_map<eReplicaComponentType, Component*> m_Components;
|
||||
std::vector<EntityTimer*> m_Timers;
|
||||
std::vector<EntityTimer*> m_PendingTimers;
|
||||
std::vector<EntityCallbackTimer*> m_CallbackTimers;
|
||||
|
||||
bool m_ShouldDestroyAfterUpdate = false;
|
||||
|
||||
LWOOBJID m_OwnerOverride;
|
||||
|
||||
Entity* m_ScheduleKiller;
|
||||
|
||||
bool m_PlayerIsReadyForUpdates = false;
|
||||
|
||||
bool m_IsGhostingCandidate = false;
|
||||
|
||||
int8_t m_Observers = 0;
|
||||
|
||||
bool m_IsParentChildDirty = true;
|
||||
|
||||
/*
|
||||
* Collision
|
||||
*/
|
||||
std::vector<LWOOBJID> m_TargetsInPhantom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Template definitions.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
bool Entity::TryGetComponent(const eReplicaComponentType componentId, T*& component) const {
|
||||
const auto& index = m_Components.find(componentId);
|
||||
|
||||
if (index == m_Components.end()) {
|
||||
component = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
component = dynamic_cast<T*>(index->second);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* Entity::GetComponent() const {
|
||||
return dynamic_cast<T*>(GetComponent(T::ComponentType));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
const T& Entity::GetVar(const std::u16string& name) const {
|
||||
auto* data = GetVarData(name);
|
||||
|
||||
if (data == nullptr) {
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
|
||||
auto* typed = dynamic_cast<LDFData<T>*>(data);
|
||||
|
||||
if (typed == nullptr) {
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
|
||||
return typed->GetValue();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Entity::GetVarAs(const std::u16string& name) const {
|
||||
const auto data = GetVarAsString(name);
|
||||
|
||||
T value;
|
||||
|
||||
if (!GeneralUtils::TryParse(data, value)) {
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Entity::SetVar(const std::u16string& name, T value) {
|
||||
auto* data = GetVarData(name);
|
||||
|
||||
if (data == nullptr) {
|
||||
auto* data = new LDFData<T>(name, value);
|
||||
|
||||
m_Settings.push_back(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto* typed = dynamic_cast<LDFData<T>*>(data);
|
||||
|
||||
if (typed == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
typed->SetValue(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Entity::SetNetworkVar(const std::u16string& name, T value, const SystemAddress& sysAddr) {
|
||||
LDFData<T>* newData = nullptr;
|
||||
|
||||
for (auto* data : m_NetworkSettings) {
|
||||
if (data->GetKey() != name)
|
||||
continue;
|
||||
|
||||
newData = dynamic_cast<LDFData<T>*>(data);
|
||||
if (newData != nullptr) {
|
||||
newData->SetValue(value);
|
||||
} else { // If we're changing types
|
||||
m_NetworkSettings.erase(
|
||||
std::remove(m_NetworkSettings.begin(), m_NetworkSettings.end(), data), m_NetworkSettings.end()
|
||||
);
|
||||
delete data;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (newData == nullptr) {
|
||||
newData = new LDFData<T>(name, value);
|
||||
}
|
||||
|
||||
m_NetworkSettings.push_back(newData);
|
||||
SendNetworkVar(newData->GetString(true), sysAddr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Entity::SetNetworkVar(const std::u16string& name, std::vector<T> values, const SystemAddress& sysAddr) {
|
||||
std::stringstream updates;
|
||||
auto index = 1;
|
||||
|
||||
for (const auto& value : values) {
|
||||
LDFData<T>* newData = nullptr;
|
||||
const auto& indexedName = name + u"." + GeneralUtils::to_u16string(index);
|
||||
|
||||
for (auto* data : m_NetworkSettings) {
|
||||
if (data->GetKey() != indexedName)
|
||||
continue;
|
||||
|
||||
newData = dynamic_cast<LDFData<T>*>(data);
|
||||
newData->SetValue(value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (newData == nullptr) {
|
||||
newData = new LDFData<T>(indexedName, value);
|
||||
}
|
||||
|
||||
m_NetworkSettings.push_back(newData);
|
||||
|
||||
if (index == values.size()) {
|
||||
updates << newData->GetString(true);
|
||||
} else {
|
||||
updates << newData->GetString(true) << "\n";
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
SendNetworkVar(updates.str(), sysAddr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Entity::GetNetworkVar(const std::u16string& name) {
|
||||
for (auto* data : m_NetworkSettings) {
|
||||
if (data == nullptr || data->GetKey() != name)
|
||||
continue;
|
||||
|
||||
auto* typed = dynamic_cast<LDFData<T>*>(data);
|
||||
if (typed == nullptr)
|
||||
continue;
|
||||
|
||||
return typed->GetValue();
|
||||
}
|
||||
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "eReplicaPacketType.h"
|
||||
#include "CollectibleComponent.h"
|
||||
|
||||
EntityManager* EntityManager::m_Address = nullptr;
|
||||
|
||||
@@ -519,18 +518,16 @@ void EntityManager::UpdateGhosting(Player* player) {
|
||||
entity->SetObservers(entity->GetObservers() - 1);
|
||||
} else if (!observed && ghostingDistanceMin > distance) {
|
||||
// Check collectables, don't construct if it has been collected
|
||||
auto* collectibleComponent = entity->GetComponent<CollectibleComponent>();
|
||||
if (collectibleComponent) {
|
||||
uint32_t collectionId = collectibleComponent->GetCollectibleId();
|
||||
uint32_t collectionId = entity->GetCollectibleID();
|
||||
|
||||
if (collectionId != 0) {
|
||||
collectionId = static_cast<uint32_t>(collectionId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
|
||||
if (collectionId != 0) {
|
||||
collectionId = static_cast<uint32_t>(collectionId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
|
||||
|
||||
if (missionComponent->HasCollectible(collectionId)) {
|
||||
continue;
|
||||
}
|
||||
if (missionComponent->HasCollectible(collectionId)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
player->ObserveEntity(id);
|
||||
|
||||
ConstructEntity(entity, player->GetSystemAddress());
|
||||
@@ -602,7 +599,7 @@ void EntityManager::ScheduleForKill(Entity* entity) {
|
||||
if (!entity)
|
||||
return;
|
||||
|
||||
auto* switchComp = entity->GetComponent<SwitchComponent>();
|
||||
SwitchComponent* switchComp = entity->GetComponent<SwitchComponent>();
|
||||
if (switchComp) {
|
||||
entity->TriggerEvent(eTriggerEventType::DEACTIVATED, entity);
|
||||
}
|
||||
|
||||
@@ -51,17 +51,21 @@ User* Player::GetParentUser() const {
|
||||
return m_ParentUser;
|
||||
}
|
||||
|
||||
SystemAddress Player::GetSystemAddress() const {
|
||||
return m_SystemAddress;
|
||||
}
|
||||
|
||||
void Player::SetSystemAddress(const SystemAddress& value) {
|
||||
m_SystemAddress = value;
|
||||
}
|
||||
|
||||
void Player::SetRespawnPosition(const NiPoint3& position) {
|
||||
void Player::SetRespawnPos(const NiPoint3 position) {
|
||||
m_respawnPos = position;
|
||||
|
||||
m_Character->SetRespawnPoint(dZoneManager::Instance()->GetZone()->GetWorldID(), position);
|
||||
}
|
||||
|
||||
void Player::SetRespawnRotation(const NiQuaternion& rotation) {
|
||||
void Player::SetRespawnRot(const NiQuaternion rotation) {
|
||||
m_respawnRot = rotation;
|
||||
}
|
||||
|
||||
@@ -247,7 +251,7 @@ const std::vector<Player*>& Player::GetAllPlayers() {
|
||||
return m_Players;
|
||||
}
|
||||
|
||||
uint64_t Player::GetDroppedCoins() const {
|
||||
uint64_t Player::GetDroppedCoins() {
|
||||
return m_DroppedCoins;
|
||||
}
|
||||
|
||||
@@ -282,12 +286,16 @@ Player::~Player() {
|
||||
|
||||
if (IsPlayer()) {
|
||||
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
|
||||
zoneControl->GetScript()->OnPlayerExit(zoneControl, this);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerExit(zoneControl, this);
|
||||
}
|
||||
|
||||
std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
scriptEntity->GetScript()->OnPlayerExit(scriptEntity, this);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
|
||||
script->OnPlayerExit(scriptEntity, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
|
||||
User* GetParentUser() const override;
|
||||
|
||||
const SystemAddress GetSystemAddress() const override { return m_SystemAddress; }
|
||||
SystemAddress GetSystemAddress() const override;
|
||||
|
||||
NiPoint3 GetRespawnPosition() const override;
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
|
||||
std::map<LWOOBJID, Loot::Info>& GetDroppedLoot();
|
||||
|
||||
uint64_t GetDroppedCoins() const;
|
||||
uint64_t GetDroppedCoins();
|
||||
|
||||
/**
|
||||
* Setters
|
||||
@@ -44,9 +44,9 @@ public:
|
||||
|
||||
void SetSystemAddress(const SystemAddress& value) override;
|
||||
|
||||
void SetRespawnPosition(const NiPoint3& position) override;
|
||||
void SetRespawnPos(NiPoint3 position) override;
|
||||
|
||||
void SetRespawnRotation(const NiQuaternion& rotation) override;
|
||||
void SetRespawnRot(NiQuaternion rotation) override;
|
||||
|
||||
void SetGhostReferencePoint(const NiPoint3& value);
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ void Trade::SendUpdateToOther(LWOOBJID participant) {
|
||||
if (inventoryComponent == nullptr) return;
|
||||
|
||||
for (const auto tradeItem : itemIds) {
|
||||
auto item = inventoryComponent->FindItemById(tradeItem.itemId);
|
||||
auto* item = inventoryComponent->FindItemById(tradeItem.itemId);
|
||||
|
||||
if (item == nullptr) return;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BuffComponent.h"
|
||||
|
||||
|
||||
void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target == LWOOBJID_EMPTY ? context->originator : branch.target);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "dLogger.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
@@ -162,7 +162,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
}
|
||||
|
||||
auto* destroyableComponent = targetEntity->GetComponent<DestroyableComponent>();
|
||||
if (!destroyableComponent || !destroyableComponent->GetParentEntity()) {
|
||||
if (!destroyableComponent || !destroyableComponent->GetParent()) {
|
||||
Game::logger->Log("BasicAttackBehavior", "No destroyable component on %llu", branch.target);
|
||||
return;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
|
||||
bitStream->Write(armorDamageDealt);
|
||||
bitStream->Write(healthDamageDealt);
|
||||
bitStream->Write(targetEntity->IsDead());
|
||||
bitStream->Write(targetEntity->GetIsDead());
|
||||
}
|
||||
|
||||
bitStream->Write(successState);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include "EchoSyncSkill.h"
|
||||
#include "PhantomPhysicsComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "eConnectionType.h"
|
||||
|
||||
@@ -331,8 +331,8 @@ std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, in
|
||||
}
|
||||
|
||||
if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) {
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (!destroyableComponent) {
|
||||
DestroyableComponent* destroyableComponent;
|
||||
if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
|
||||
return targets;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_strea
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE));
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
Game::logger->Log("HealBehavior", "Failed to find destroyable component for %(llu)!", branch.target);
|
||||
|
||||
@@ -4,7 +4,7 @@ void LootBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
auto target = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (!target) return;
|
||||
|
||||
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
|
||||
controllablePhysicsComponent->AddPickupRadiusScale(m_Scale);
|
||||
@@ -22,7 +22,7 @@ void LootBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext br
|
||||
auto target = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (!target) return;
|
||||
|
||||
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
|
||||
controllablePhysicsComponent->RemovePickupRadiusScale(m_Scale);
|
||||
|
||||
@@ -16,7 +16,7 @@ void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_str
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE));
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
Game::logger->Log("RepairBehavior", "Failed to find destroyable component for %(llu)!", branch.target);
|
||||
|
||||
@@ -9,7 +9,9 @@ void SkillEventBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit
|
||||
auto* caster = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) {
|
||||
target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(target)) {
|
||||
script->OnSkillEventFired(target, caster, *this->m_effectHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +21,8 @@ SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
auto* caster = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) {
|
||||
target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(target)) {
|
||||
script->OnSkillEventFired(target, caster, *this->m_effectHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityInfo.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
@@ -53,10 +53,10 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
entity->SetOwnerOverride(context->originator);
|
||||
|
||||
// Unset the flag to reposition the player, this makes it harder to glitch out of the map
|
||||
auto* quickBuildComponent = entity->GetComponent<QuickBuildComponent>();
|
||||
auto* rebuildComponent = entity->GetComponent<RebuildComponent>();
|
||||
|
||||
if (quickBuildComponent != nullptr) {
|
||||
quickBuildComponent->SetRepositionPlayer(false);
|
||||
if (rebuildComponent != nullptr) {
|
||||
rebuildComponent->SetRepositionPlayer(false);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->ConstructEntity(entity);
|
||||
@@ -87,9 +87,9 @@ void SpawnBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE));
|
||||
|
||||
if (!destroyable) {
|
||||
if (destroyable == nullptr) {
|
||||
entity->Smash(context->originator);
|
||||
|
||||
return;
|
||||
|
||||
@@ -33,7 +33,7 @@ void StunBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
* If our target is an enemy we can go ahead and stun it.
|
||||
*/
|
||||
|
||||
auto* combatAiComponent = target->GetComponent<BaseCombatAIComponent>();
|
||||
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(target->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
|
||||
|
||||
if (combatAiComponent == nullptr) {
|
||||
return;
|
||||
@@ -56,7 +56,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
|
||||
* See if we can stun ourselves
|
||||
*/
|
||||
|
||||
auto* combatAiComponent = self->GetComponent<BaseCombatAIComponent>();
|
||||
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(self->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
|
||||
|
||||
if (combatAiComponent == nullptr) {
|
||||
return;
|
||||
@@ -91,7 +91,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
|
||||
* If our target is an enemy we can go ahead and stun it.
|
||||
*/
|
||||
|
||||
auto* combatAiComponent = target->GetComponent<BaseCombatAIComponent>();
|
||||
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(target->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
|
||||
|
||||
if (combatAiComponent == nullptr) {
|
||||
return;
|
||||
|
||||
@@ -30,7 +30,7 @@ void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
|
||||
Game::logger->LogDebug("SwitchBehavior", "[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination());
|
||||
|
||||
if (state) {
|
||||
if (state || (entity->GetLOT() == 8092 && destroyableComponent->GetImagination() >= m_imagination)) {
|
||||
this->m_actionTrue->Handle(context, bitStream, branch);
|
||||
} else {
|
||||
this->m_actionFalse->Handle(context, bitStream, branch);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
#include <vector>
|
||||
@@ -148,7 +148,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entity->IsDead()) continue;
|
||||
if (entity->GetIsDead()) continue;
|
||||
|
||||
const auto otherPosition = entity->GetPosition();
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ void VentureVisionBehavior::Handle(BehaviorContext* context, RakNet::BitStream*
|
||||
const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (targetEntity) {
|
||||
auto* characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
auto characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent) {
|
||||
if (m_show_collectibles) characterComponent->AddVentureVisionEffect(m_ShowCollectibles);
|
||||
@@ -24,7 +24,7 @@ void VentureVisionBehavior::UnCast(BehaviorContext* context, BehaviorBranchConte
|
||||
const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (targetEntity) {
|
||||
auto* characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
auto characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent) {
|
||||
if (m_show_collectibles) characterComponent->RemoveVentureVisionEffect(m_ShowCollectibles);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include "eMissionTaskType.h"
|
||||
|
||||
#ifndef __ACHIEVEMENTCACHEKEY__H__
|
||||
#define __ACHIEVEMENTCACHEKEY__H__
|
||||
|
||||
#include "eMissionTaskType.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
class AchievementCacheKey {
|
||||
public:
|
||||
AchievementCacheKey() {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#include "AchievementVendorComponent.h"
|
||||
|
||||
AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef __ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
#define __ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
|
||||
#include "VendorComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
class AchievementVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
|
||||
AchievementVendorComponent(Entity* parent);
|
||||
};
|
||||
|
||||
|
||||
#endif //!__ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
@@ -1,621 +0,0 @@
|
||||
#include "ActivityComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "Character.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include <WorldPackets.h>
|
||||
#include "EntityManager.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "Player.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "dServer.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "dConfig.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Loot.h"
|
||||
#include "eMissionTaskType.h"
|
||||
#include "eMatchUpdate.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
|
||||
#include "CDCurrencyTableTable.h"
|
||||
#include "CDActivityRewardsTable.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "LeaderboardManager.h"
|
||||
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
m_ActivityID = componentId;
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||
|
||||
for (CDActivities activity : activities) {
|
||||
m_ActivityInfo = activity;
|
||||
if (static_cast<LeaderboardType>(activity.leaderboardType) == LeaderboardType::Racing && Game::config->GetValue("solo_racing") == "1") {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
}
|
||||
|
||||
const auto& transferOverride = parent->GetVar<std::u16string>(u"transferZoneID");
|
||||
if (!transferOverride.empty()) {
|
||||
m_ActivityInfo.instanceMapID = std::stoi(GeneralUtils::UTF16ToWTF8(transferOverride));
|
||||
|
||||
// TODO: LU devs made me do it (for some reason cannon cove instancer is marked to go to GF survival)
|
||||
// NOTE: 1301 is GF survival
|
||||
if (m_ActivityInfo.instanceMapID == 1301) {
|
||||
m_ActivityInfo.instanceMapID = 1302;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto* destroyableComponent = m_ParentEntity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent) {
|
||||
// check for LMIs and set the loot LMIs
|
||||
CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
|
||||
|
||||
uint32_t startingLMI = 0;
|
||||
|
||||
if (activityRewards.size() > 0) {
|
||||
startingLMI = activityRewards[0].LootMatrixIndex;
|
||||
}
|
||||
|
||||
if (startingLMI > 0) {
|
||||
// now time for bodge :)
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityComponent::~ActivityComponent()
|
||||
= default;
|
||||
|
||||
void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
|
||||
outBitStream->Write(true);
|
||||
outBitStream->Write<uint32_t>(m_ActivityPlayers.size());
|
||||
|
||||
if (!m_ActivityPlayers.empty()) {
|
||||
for (const auto& activityPlayer : m_ActivityPlayers) {
|
||||
|
||||
outBitStream->Write<LWOOBJID>(activityPlayer->playerID);
|
||||
for (const auto& activityValue : activityPlayer->values) {
|
||||
outBitStream->Write<float_t>(activityValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::ReloadConfig() {
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||
for (auto activity : activities) {
|
||||
auto mapID = m_ActivityInfo.instanceMapID;
|
||||
if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
} else {
|
||||
m_ActivityInfo.minTeamSize = activity.minTeamSize;
|
||||
m_ActivityInfo.minTeams = activity.minTeams;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
|
||||
if (m_ActivityInfo.ActivityID == 103) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id == "LobbyExit") {
|
||||
PlayerLeave(player->GetObjectID());
|
||||
} else if (id == "PlayButton") {
|
||||
PlayerJoin(player);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerJoin(Entity* player) {
|
||||
if (m_ActivityInfo.ActivityID == 103 || PlayerIsInQueue(player) || !IsValidActivity(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
|
||||
if (HasLobby()) {
|
||||
PlayerJoinLobby(player);
|
||||
} else if (!IsPlayedBy(player)) {
|
||||
auto* instance = NewInstance();
|
||||
instance->AddParticipant(player);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerJoinLobby(Entity* player) {
|
||||
if (!m_ParentEntity->HasComponent(eReplicaComponentType::QUICK_BUILD))
|
||||
GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby
|
||||
LobbyPlayer* newLobbyPlayer = new LobbyPlayer();
|
||||
newLobbyPlayer->entityID = player->GetObjectID();
|
||||
Lobby* playerLobby = nullptr;
|
||||
|
||||
auto* character = player->GetCharacter();
|
||||
if (character != nullptr)
|
||||
character->SetLastNonInstanceZoneID(dZoneManager::Instance()->GetZone()->GetWorldID());
|
||||
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
if (lobby->players.size() < m_ActivityInfo.maxTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() < m_ActivityInfo.maxTeams) {
|
||||
// If an empty slot in an existing lobby is found
|
||||
lobby->players.push_back(newLobbyPlayer);
|
||||
playerLobby = lobby;
|
||||
|
||||
// Update the joining player on players already in the lobby, and update players already in the lobby on the joining player
|
||||
std::string matchUpdateJoined = "player=9:" + std::to_string(player->GetObjectID()) + "\nplayerName=0:" + player->GetCharacter()->GetName();
|
||||
for (LobbyPlayer* joinedPlayer : lobby->players) {
|
||||
auto* entity = joinedPlayer->GetEntity();
|
||||
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string matchUpdate = "player=9:" + std::to_string(entity->GetObjectID()) + "\nplayerName=0:" + entity->GetCharacter()->GetName();
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchUpdate, eMatchUpdate::PLAYER_ADDED);
|
||||
PlayerReady(entity, joinedPlayer->ready);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateJoined, eMatchUpdate::PLAYER_ADDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!playerLobby) {
|
||||
// If all lobbies are full
|
||||
playerLobby = new Lobby();
|
||||
playerLobby->players.push_back(newLobbyPlayer);
|
||||
playerLobby->timer = m_ActivityInfo.waitTime / 1000;
|
||||
m_Queue.push_back(playerLobby);
|
||||
}
|
||||
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && playerLobby->players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && playerLobby->players.size() >= m_ActivityInfo.minTeams) {
|
||||
// Update the joining player on the match timer
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(playerLobby->timer);
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerLeave(LWOOBJID playerID) {
|
||||
|
||||
// Removes the player from a lobby and notifies the others, not applicable for non-lobby instances
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (int i = 0; i < lobby->players.size(); ++i) {
|
||||
if (lobby->players[i]->entityID == playerID) {
|
||||
std::string matchUpdateLeft = "player=9:" + std::to_string(playerID);
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
auto* entity = lobbyPlayer->GetEntity();
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft, eMatchUpdate::PLAYER_REMOVED);
|
||||
}
|
||||
|
||||
delete lobby->players[i];
|
||||
lobby->players[i] = nullptr;
|
||||
lobby->players.erase(lobby->players.begin() + i);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::Update(float deltaTime) {
|
||||
std::vector<Lobby*> lobbiesToRemove{};
|
||||
// Ticks all the lobbies, not applicable for non-instance activities
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
auto* entity = player->GetEntity();
|
||||
if (entity == nullptr) {
|
||||
PlayerLeave(player->entityID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lobby->players.empty()) {
|
||||
lobbiesToRemove.push_back(lobby);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the match time for all players
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && lobby->players.size() >= m_ActivityInfo.minTeamSize
|
||||
|| m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() >= m_ActivityInfo.minTeams) {
|
||||
if (lobby->timer == m_ActivityInfo.waitTime / 1000) {
|
||||
for (LobbyPlayer* joinedPlayer : lobby->players) {
|
||||
auto* entity = joinedPlayer->GetEntity();
|
||||
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
lobby->timer -= deltaTime;
|
||||
}
|
||||
|
||||
bool lobbyReady = true;
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
if (player->ready) continue;
|
||||
lobbyReady = false;
|
||||
}
|
||||
|
||||
// If everyone's ready, jump the timer
|
||||
if (lobbyReady && lobby->timer > m_ActivityInfo.startDelay / 1000) {
|
||||
lobby->timer = m_ActivityInfo.startDelay / 1000;
|
||||
|
||||
// Update players in lobby on switch to start delay
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
auto* entity = player->GetEntity();
|
||||
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_START);
|
||||
}
|
||||
}
|
||||
|
||||
// The timer has elapsed, start the instance
|
||||
if (lobby->timer <= 0.0f) {
|
||||
Game::logger->Log("ActivityComponent", "Setting up instance.");
|
||||
ActivityInstance* instance = NewInstance();
|
||||
LoadPlayersIntoInstance(instance, lobby->players);
|
||||
instance->StartZone();
|
||||
lobbiesToRemove.push_back(lobby);
|
||||
}
|
||||
}
|
||||
|
||||
while (!lobbiesToRemove.empty()) {
|
||||
RemoveLobby(lobbiesToRemove.front());
|
||||
lobbiesToRemove.erase(lobbiesToRemove.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::RemoveLobby(Lobby* lobby) {
|
||||
for (int i = 0; i < m_Queue.size(); ++i) {
|
||||
if (m_Queue[i] == lobby) {
|
||||
m_Queue.erase(m_Queue.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ActivityComponent::HasLobby() const {
|
||||
// If the player is not in the world he has to be, create a lobby for the transfer
|
||||
return m_ActivityInfo.instanceMapID != UINT_MAX && m_ActivityInfo.instanceMapID != Game::server->GetZoneID();
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsValidActivity(Entity* player) {
|
||||
// Makes it so that scripted activities with an unimplemented map cannot be joined
|
||||
/*if (player->GetGMLevel() < eGameMasterLevel::DEVELOPER && (m_ActivityInfo.instanceMapID == 1302 || m_ActivityInfo.instanceMapID == 1301)) {
|
||||
if (m_ParentEntity->GetLOT() == 4860) {
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
missionComponent->CompleteMission(229);
|
||||
}
|
||||
|
||||
ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready.");
|
||||
static_cast<Player*>(player)->SendToZone(dZoneManager::Instance()->GetZone()->GetWorldID()); // Gets them out of this stuck state
|
||||
|
||||
return false;
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ActivityComponent::PlayerIsInQueue(Entity* player) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (player->GetObjectID() == lobbyPlayer->entityID) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsPlayedBy(Entity* player) const {
|
||||
for (const auto* instance : this->m_Instances) {
|
||||
for (const auto* instancePlayer : instance->GetParticipants()) {
|
||||
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == player->GetObjectID())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
|
||||
for (const auto* instance : this->m_Instances) {
|
||||
for (const auto* instancePlayer : instance->GetParticipants()) {
|
||||
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == playerID)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::TakeCost(Entity* player) const {
|
||||
if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0)
|
||||
return true;
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
if (inventoryComponent == nullptr)
|
||||
return false;
|
||||
|
||||
if (inventoryComponent->GetLotCount(m_ActivityInfo.optionalCostLOT) < m_ActivityInfo.optionalCostCount)
|
||||
return false;
|
||||
|
||||
inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerReady(Entity* player, bool bReady) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (lobbyPlayer->entityID == player->GetObjectID()) {
|
||||
|
||||
lobbyPlayer->ready = bReady;
|
||||
|
||||
// Update players in lobby on player being ready
|
||||
std::string matchReadyUpdate = "player=9:" + std::to_string(player->GetObjectID());
|
||||
eMatchUpdate readyStatus = eMatchUpdate::PLAYER_READY;
|
||||
if (!bReady) readyStatus = eMatchUpdate::PLAYER_NOT_READY;
|
||||
for (LobbyPlayer* otherPlayer : lobby->players) {
|
||||
auto* entity = otherPlayer->GetEntity();
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate, readyStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityInstance* ActivityComponent::NewInstance() {
|
||||
auto* instance = new ActivityInstance(m_ParentEntity, m_ActivityInfo);
|
||||
m_Instances.push_back(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
|
||||
for (LobbyPlayer* player : lobby) {
|
||||
auto* entity = player->GetEntity();
|
||||
if (entity == nullptr || !TakeCost(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
instance->AddParticipant(entity);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<ActivityInstance*>& ActivityComponent::GetInstances() const {
|
||||
return m_Instances;
|
||||
}
|
||||
|
||||
ActivityInstance* ActivityComponent::GetInstance(const LWOOBJID playerID) {
|
||||
for (const auto* instance : GetInstances()) {
|
||||
for (const auto* participant : instance->GetParticipants()) {
|
||||
if (participant->GetObjectID() == playerID)
|
||||
return const_cast<ActivityInstance*>(instance);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActivityComponent::ClearInstances() {
|
||||
for (ActivityInstance* instance : m_Instances) {
|
||||
delete instance;
|
||||
}
|
||||
m_Instances.clear();
|
||||
}
|
||||
|
||||
ActivityPlayer* ActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
|
||||
for (auto* activityData : m_ActivityPlayers) {
|
||||
if (activityData->playerID == playerID) {
|
||||
return activityData;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
|
||||
for (size_t i = 0; i < m_ActivityPlayers.size(); i++) {
|
||||
if (m_ActivityPlayers[i]->playerID == playerID) {
|
||||
delete m_ActivityPlayers[i];
|
||||
m_ActivityPlayers[i] = nullptr;
|
||||
|
||||
m_ActivityPlayers.erase(m_ActivityPlayers.begin() + i);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityPlayer* ActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr)
|
||||
return data;
|
||||
|
||||
m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} });
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
return GetActivityPlayerData(playerID);
|
||||
}
|
||||
|
||||
float_t ActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) {
|
||||
auto value = -1.0f;
|
||||
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr) {
|
||||
value = data->values[std::min(index, (uint32_t)9)];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) {
|
||||
auto* data = AddActivityPlayerData(playerID);
|
||||
if (data != nullptr) {
|
||||
data->values[std::min(index, (uint32_t)9)] = value;
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerRemove(LWOOBJID playerID) {
|
||||
for (auto* instance : GetInstances()) {
|
||||
auto participants = instance->GetParticipants();
|
||||
for (const auto* participant : participants) {
|
||||
if (participant != nullptr && participant->GetObjectID() == playerID) {
|
||||
instance->RemoveParticipant(participant);
|
||||
RemoveActivityPlayerData(playerID);
|
||||
|
||||
// If the instance is empty after the delete of the participant, delete the instance too
|
||||
if (instance->GetParticipants().empty()) {
|
||||
m_Instances.erase(std::find(m_Instances.begin(), m_Instances.end(), instance));
|
||||
delete instance;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityInstance::StartZone() {
|
||||
if (m_Participants.empty())
|
||||
return;
|
||||
|
||||
const auto& participants = GetParticipants();
|
||||
if (participants.empty())
|
||||
return;
|
||||
|
||||
auto* leader = participants[0];
|
||||
LWOZONEID zoneId = LWOZONEID(m_ActivityInfo.instanceMapID, 0, leader->GetCharacter()->GetPropertyCloneID());
|
||||
|
||||
// only make a team if we have more than one participant
|
||||
if (participants.size() > 1) {
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
|
||||
|
||||
bitStream.Write(leader->GetObjectID());
|
||||
bitStream.Write(m_Participants.size());
|
||||
|
||||
for (const auto& participant : m_Participants) {
|
||||
bitStream.Write(participant);
|
||||
}
|
||||
|
||||
bitStream.Write(zoneId);
|
||||
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
}
|
||||
|
||||
const auto cloneId = GeneralUtils::GenerateRandomNumber<uint32_t>(1, UINT32_MAX);
|
||||
for (Entity* player : participants) {
|
||||
const auto objid = player->GetObjectID();
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, m_ActivityInfo.instanceMapID, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
|
||||
auto* player = EntityManager::Instance()->GetEntity(objid);
|
||||
if (player == nullptr)
|
||||
return;
|
||||
|
||||
Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
if (player->GetCharacter()) {
|
||||
player->GetCharacter()->SetZoneID(zoneID);
|
||||
player->GetCharacter()->SetZoneInstance(zoneInstance);
|
||||
player->GetCharacter()->SetZoneClone(zoneClone);
|
||||
}
|
||||
|
||||
WorldPackets::SendTransferToWorld(player->GetSystemAddress(), serverIP, serverPort, mythranShift);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
m_NextZoneCloneID++;
|
||||
}
|
||||
|
||||
void ActivityInstance::RewardParticipant(Entity* participant) {
|
||||
auto* missionComponent = participant->GetComponent<MissionComponent>();
|
||||
if (missionComponent) {
|
||||
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityInfo.ActivityID);
|
||||
}
|
||||
|
||||
// First, get the activity data
|
||||
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
|
||||
|
||||
if (!activityRewards.empty()) {
|
||||
uint32_t minCoins = 0;
|
||||
uint32_t maxCoins = 0;
|
||||
|
||||
auto* currencyTableTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
|
||||
std::vector<CDCurrencyTable> currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == activityRewards[0].CurrencyIndex && entry.npcminlevel == 1); });
|
||||
|
||||
if (!currencyTable.empty()) {
|
||||
minCoins = currencyTable[0].minvalue;
|
||||
maxCoins = currencyTable[0].maxvalue;
|
||||
}
|
||||
|
||||
LootGenerator::Instance().DropLoot(participant, m_ParentEntity, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Entity*> ActivityInstance::GetParticipants() const {
|
||||
std::vector<Entity*> entities;
|
||||
entities.reserve(m_Participants.size());
|
||||
|
||||
for (const auto& id : m_Participants) {
|
||||
auto* entity = EntityManager::Instance()->GetEntity(id);
|
||||
if (entity != nullptr)
|
||||
entities.push_back(entity);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
void ActivityInstance::AddParticipant(Entity* participant) {
|
||||
const auto id = participant->GetObjectID();
|
||||
if (std::count(m_Participants.begin(), m_Participants.end(), id))
|
||||
return;
|
||||
|
||||
m_Participants.push_back(id);
|
||||
}
|
||||
|
||||
void ActivityInstance::RemoveParticipant(const Entity* participant) {
|
||||
const auto loadedParticipant = std::find(m_Participants.begin(), m_Participants.end(), participant->GetObjectID());
|
||||
if (loadedParticipant != m_Participants.end()) {
|
||||
m_Participants.erase(loadedParticipant);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ActivityInstance::GetScore() const {
|
||||
return score;
|
||||
}
|
||||
|
||||
void ActivityInstance::SetScore(uint32_t score) {
|
||||
this->score = score;
|
||||
}
|
||||
|
||||
Entity* LobbyPlayer::GetEntity() const {
|
||||
return EntityManager::Instance()->GetEntity(entityID);
|
||||
}
|
||||
@@ -1,379 +0,0 @@
|
||||
/*
|
||||
* Darkflame Universe
|
||||
* Copyright 2018
|
||||
*/
|
||||
|
||||
#include "CDClientManager.h"
|
||||
|
||||
#ifndef ACTIVITYCOMPONENT_H
|
||||
#define ACTIVITYCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include "CDActivitiesTable.h"
|
||||
|
||||
/**
|
||||
* Represents an instance of an activity, having participants and score
|
||||
*/
|
||||
class ActivityInstance {
|
||||
public:
|
||||
ActivityInstance(Entity* parent, CDActivities activityInfo) { m_ParentEntity = parent; m_ActivityInfo = activityInfo; };
|
||||
//~ActivityInstance();
|
||||
|
||||
/**
|
||||
* Adds an entity to this activity
|
||||
* @param participant the entity to add
|
||||
*/
|
||||
void AddParticipant(Entity* participant);
|
||||
|
||||
/**
|
||||
* Removes all the participants from this activity
|
||||
*/
|
||||
void ClearParticipants() { m_Participants.clear(); };
|
||||
|
||||
/**
|
||||
* Starts the instance world for this activity and sends all participants there
|
||||
*/
|
||||
void StartZone();
|
||||
|
||||
/**
|
||||
* Gives the rewards for completing this activity to some participant
|
||||
* @param participant the participant to give rewards to
|
||||
*/
|
||||
void RewardParticipant(Entity* participant);
|
||||
|
||||
/**
|
||||
* Removes a participant from this activity
|
||||
* @param participant the participant to remove
|
||||
*/
|
||||
void RemoveParticipant(const Entity* participant);
|
||||
|
||||
/**
|
||||
* Returns all the participants of this activity
|
||||
* @return all the participants of this activity
|
||||
*/
|
||||
std::vector<Entity*> GetParticipants() const;
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
uint32_t GetScore() const;
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
void SetScore(uint32_t score);
|
||||
private:
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
uint32_t score = 0;
|
||||
|
||||
/**
|
||||
* The instance ID of this activity
|
||||
*/
|
||||
uint32_t m_NextZoneCloneID = 0;
|
||||
|
||||
/**
|
||||
* The database information for this activity
|
||||
*/
|
||||
CDActivities m_ActivityInfo;
|
||||
|
||||
/**
|
||||
* The entity that owns this activity (the entity that has the ActivityComponent)
|
||||
*/
|
||||
Entity* m_ParentEntity;
|
||||
|
||||
/**
|
||||
* All the participants of this activity
|
||||
*/
|
||||
std::vector<LWOOBJID> m_Participants;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an entity in a lobby
|
||||
*/
|
||||
struct LobbyPlayer {
|
||||
|
||||
/**
|
||||
* The ID of the entity that is in the lobby
|
||||
*/
|
||||
LWOOBJID entityID;
|
||||
|
||||
/**
|
||||
* Whether or not the entity is ready
|
||||
*/
|
||||
bool ready = false;
|
||||
|
||||
/**
|
||||
* Returns the entity that is in the lobby
|
||||
* @return the entity that is in the lobby
|
||||
*/
|
||||
Entity* GetEntity() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a lobby of players with a timer until it should start the activity
|
||||
*/
|
||||
struct Lobby {
|
||||
|
||||
/**
|
||||
* The lobby of players
|
||||
*/
|
||||
std::vector<LobbyPlayer*> players;
|
||||
|
||||
/**
|
||||
* The timer that determines when the activity should start
|
||||
*/
|
||||
float timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the score for the player in an activity, one index might represent score, another one time, etc.
|
||||
*/
|
||||
struct ActivityPlayer {
|
||||
|
||||
/**
|
||||
* The entity that the score is tracked for
|
||||
*/
|
||||
LWOOBJID playerID;
|
||||
|
||||
/**
|
||||
* The list of score for this entity
|
||||
*/
|
||||
float values[10];
|
||||
};
|
||||
|
||||
/**
|
||||
* Welcome to the absolute behemoth that is the activity component. I have now clue how this was managed in
|
||||
* live but I figure somewhat similarly and it's terrible. In a nutshell, this components handles any activity that
|
||||
* can be done in the game from quick builds to boss fights to races. On top of that, this component handles instancing
|
||||
* and lobbying.
|
||||
*/
|
||||
class ActivityComponent : public Component {
|
||||
public:
|
||||
ActivityComponent(Entity* parent, int32_t componentId);
|
||||
~ActivityComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const;
|
||||
|
||||
/**
|
||||
* Makes some entity join the minigame, if it's a lobbied one, the entity will be placed in the lobby
|
||||
* @param player the entity to join the game
|
||||
*/
|
||||
void PlayerJoin(Entity* player);
|
||||
|
||||
/**
|
||||
* Makes an entity join the lobby for this minigame, if it exists
|
||||
* @param player the entity to join
|
||||
*/
|
||||
void PlayerJoinLobby(Entity* player);
|
||||
|
||||
/**
|
||||
* Makes the player leave the lobby
|
||||
* @param playerID the entity to leave the lobby
|
||||
*/
|
||||
void PlayerLeave(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Removes the entity from the minigame (and its score)
|
||||
* @param playerID the entity to remove from the minigame
|
||||
*/
|
||||
void PlayerRemove(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Adds all the players to an instance of some activity
|
||||
* @param instance the instance to load the players into
|
||||
* @param lobby the players to load into the instance
|
||||
*/
|
||||
void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const;
|
||||
|
||||
/**
|
||||
* Removes a lobby from the activity manager
|
||||
* @param lobby the lobby to remove
|
||||
*/
|
||||
void RemoveLobby(Lobby* lobby);
|
||||
|
||||
/**
|
||||
* Marks a player as (un)ready in a lobby
|
||||
* @param player the entity to mark
|
||||
* @param bReady true if the entity is ready, false otherwise
|
||||
*/
|
||||
void PlayerReady(Entity* player, bool bReady);
|
||||
|
||||
/**
|
||||
* Returns the ID of this activity
|
||||
* @return the ID of this activity
|
||||
*/
|
||||
int GetActivityID() { return m_ActivityInfo.ActivityID; }
|
||||
|
||||
/**
|
||||
* Returns if this activity has a lobby, e.g. if it needs to instance players to some other map
|
||||
* @return true if this activity has a lobby, false otherwise
|
||||
*/
|
||||
bool HasLobby() const;
|
||||
|
||||
/**
|
||||
* Checks if a player is currently waiting in a lobby
|
||||
* @param player the entity to check for
|
||||
* @return true if the entity is waiting in a lobby, false otherwise
|
||||
*/
|
||||
bool PlayerIsInQueue(Entity* player);
|
||||
|
||||
/**
|
||||
* Checks if an entity is currently playing this activity
|
||||
* @param player the entity to check
|
||||
* @return true if the entity is playing this lobby, false otherwise
|
||||
*/
|
||||
bool IsPlayedBy(Entity* player) const;
|
||||
|
||||
/**
|
||||
* Checks if an entity is currently playing this activity
|
||||
* @param playerID the entity to check
|
||||
* @return true if the entity is playing this lobby, false otherwise
|
||||
*/
|
||||
bool IsPlayedBy(LWOOBJID playerID) const;
|
||||
|
||||
/**
|
||||
* Legacy: used to check for unimplemented maps, gladly, this now just returns true :)
|
||||
*/
|
||||
bool IsValidActivity(Entity* player);
|
||||
|
||||
/**
|
||||
* Removes the cost of the activity (e.g. green imaginate) for the entity that plays this activity
|
||||
* @param player the entity to take cost for
|
||||
* @return true if the cost was successfully deducted, false otherwise
|
||||
*/
|
||||
bool TakeCost(Entity* player) const;
|
||||
|
||||
/**
|
||||
* Handles any response from a player clicking on a lobby / instance menu
|
||||
* @param player the entity that clicked
|
||||
* @param id the message that was passed
|
||||
*/
|
||||
void HandleMessageBoxResponse(Entity* player, const std::string& id);
|
||||
|
||||
/**
|
||||
* Creates a new instance for this activity
|
||||
* @return a new instance for this activity
|
||||
*/
|
||||
ActivityInstance* NewInstance();
|
||||
|
||||
/**
|
||||
* Returns all the currently active instances of this activity
|
||||
* @return all the currently active instances of this activity
|
||||
*/
|
||||
const std::vector<ActivityInstance*>& GetInstances() const;
|
||||
|
||||
/**
|
||||
* Returns the instance that some entity is currently playing in
|
||||
* @param playerID the entity to check for
|
||||
* @return if any, the instance that the entity is currently in
|
||||
*/
|
||||
ActivityInstance* GetInstance(const LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* @brief Reloads the config settings for this component
|
||||
*
|
||||
*/
|
||||
void ReloadConfig();
|
||||
|
||||
/**
|
||||
* Removes all the instances
|
||||
*/
|
||||
void ClearInstances();
|
||||
|
||||
/**
|
||||
* Returns all the score for the players that are currently playing this activity
|
||||
* @return
|
||||
*/
|
||||
std::vector<ActivityPlayer*> GetActivityPlayers() { return m_ActivityPlayers; };
|
||||
|
||||
/**
|
||||
* Returns activity data for a specific entity (e.g. score and such).
|
||||
* @param playerID the entity to get data for
|
||||
* @return the activity data (score) for the passed player in this activity, if it exists
|
||||
*/
|
||||
ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Sets some score value for an entity
|
||||
* @param playerID the entity to set score for
|
||||
* @param index the score index to set
|
||||
* @param value the value to set in for that index
|
||||
*/
|
||||
void SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value);
|
||||
|
||||
/**
|
||||
* Returns activity score for the passed parameters
|
||||
* @param playerID the entity to get score for
|
||||
* @param index the index to get score for
|
||||
* @return activity score for the passed parameters
|
||||
*/
|
||||
float_t GetActivityValue(LWOOBJID playerID, uint32_t index);
|
||||
|
||||
/**
|
||||
* Removes activity score tracking for some entity
|
||||
* @param playerID the entity to remove score for
|
||||
*/
|
||||
void RemoveActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Adds activity score tracking for some entity
|
||||
* @param playerID the entity to add the activity score for
|
||||
* @return the created entry
|
||||
*/
|
||||
ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Sets the mapID that this activity points to
|
||||
* @param mapID the map ID to set
|
||||
*/
|
||||
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:
|
||||
|
||||
/**
|
||||
* The database information for this activity
|
||||
*/
|
||||
CDActivities m_ActivityInfo;
|
||||
|
||||
/**
|
||||
* All the active instances of this activity
|
||||
*/
|
||||
std::vector<ActivityInstance*> m_Instances;
|
||||
|
||||
/**
|
||||
* The current lobbies for this activity
|
||||
*/
|
||||
std::vector<Lobby*> m_Queue;
|
||||
|
||||
/**
|
||||
* All the activity score for the players in this activity
|
||||
*/
|
||||
std::vector<ActivityPlayer*> m_ActivityPlayers;
|
||||
|
||||
/**
|
||||
* LMIs for team sizes
|
||||
*/
|
||||
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
|
||||
|
||||
/**
|
||||
* The activity id
|
||||
*
|
||||
*/
|
||||
int32_t m_ActivityID;
|
||||
};
|
||||
|
||||
#endif // ACTIVITYCOMPONENT_H
|
||||
@@ -1,10 +1,6 @@
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include <BitStream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityManager.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
@@ -19,32 +15,32 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "SkillComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t componentId) : Component(parent) {
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
m_ComponentId = componentId;
|
||||
SetAiState(AiState::Spawn);
|
||||
SetAiState(AiState::spawn);
|
||||
m_Timer = 1.0f;
|
||||
m_StartPosition = parent->GetPosition();
|
||||
m_MovementAI = nullptr;
|
||||
m_Disabled = false;
|
||||
m_SkillEntries = {};
|
||||
m_MovementAI = nullptr;
|
||||
m_SoftTimer = 5.0f;
|
||||
m_dpEntity = nullptr;
|
||||
m_dpEntityEnemy = nullptr;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LoadTemplateData() {
|
||||
//Grab the aggro information from BaseCombatAI:
|
||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
||||
componentQuery.bind(1, m_ComponentId);
|
||||
componentQuery.bind(1, (int)id);
|
||||
|
||||
auto componentResult = componentQuery.execQuery();
|
||||
|
||||
@@ -67,12 +63,21 @@ void BaseCombatAIComponent::LoadTemplateData() {
|
||||
|
||||
componentResult.finalize();
|
||||
|
||||
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
||||
// radii if it is greater than the one in the database.
|
||||
if (m_Parent) {
|
||||
auto aggroRadius = m_Parent->GetVar<float>(u"aggroRadius");
|
||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
||||
auto tetherRadius = m_Parent->GetVar<float>(u"tetherRadius");
|
||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find skills
|
||||
*/
|
||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
||||
skillQuery.bind(1, m_ParentEntity->GetLOT());
|
||||
skillQuery.bind(1, (int)parent->GetLOT());
|
||||
|
||||
auto result = skillQuery.execQuery();
|
||||
|
||||
@@ -85,51 +90,44 @@ void BaseCombatAIComponent::LoadTemplateData() {
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
||||
m_SkillEntries.push_back(AiSkillEntry(skillId, 0, abilityCooldown, behavior));
|
||||
std::stringstream behaviorQuery;
|
||||
|
||||
AiSkillEntry entry = { skillId, 0, abilityCooldown, behavior };
|
||||
|
||||
m_SkillEntries.push_back(entry);
|
||||
|
||||
result.nextRow();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LoadConfigData() {
|
||||
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
||||
// radii if it is greater than the one in the database.
|
||||
if (m_ParentEntity) {
|
||||
auto aggroRadius = m_ParentEntity->GetVar<float>(u"aggroRadius");
|
||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
||||
auto tetherRadius = m_ParentEntity->GetVar<float>(u"tetherRadius");
|
||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Startup() {
|
||||
Stun(1.0f);
|
||||
|
||||
// Add physics
|
||||
/*
|
||||
* Add physics
|
||||
*/
|
||||
|
||||
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
|
||||
|
||||
auto* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
if (!componentRegistryTable) return;
|
||||
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
auto* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
||||
if (physicsComponentTable != nullptr) {
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
if (info != nullptr) {
|
||||
collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!physicsComponentTable) return;
|
||||
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
if (info) collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||
|
||||
// Why are these new'd here and then deleted by the dpworld??
|
||||
//Create a phantom physics volume so we can detect when we're aggro'd.
|
||||
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius);
|
||||
m_dpEntityEnemy = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius, false);
|
||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), m_AggroRadius);
|
||||
m_dpEntityEnemy = new dpEntity(m_Parent->GetObjectID(), m_AggroRadius, false);
|
||||
|
||||
m_dpEntity->SetCollisionGroup(collisionGroup);
|
||||
m_dpEntityEnemy->SetCollisionGroup(collisionGroup);
|
||||
|
||||
m_dpEntity->SetPosition(m_ParentEntity->GetPosition());
|
||||
m_dpEntityEnemy->SetPosition(m_ParentEntity->GetPosition());
|
||||
m_dpEntity->SetPosition(m_Parent->GetPosition());
|
||||
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
|
||||
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
dpWorld::Instance().AddEntity(m_dpEntityEnemy);
|
||||
@@ -137,29 +135,28 @@ void BaseCombatAIComponent::Startup() {
|
||||
}
|
||||
|
||||
BaseCombatAIComponent::~BaseCombatAIComponent() {
|
||||
if (m_dpEntity) dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
if (m_dpEntity)
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
|
||||
if (m_dpEntityEnemy) dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
||||
m_MovementAI = nullptr;
|
||||
m_dpEntity = nullptr;
|
||||
m_dpEntityEnemy = nullptr;
|
||||
if (m_dpEntityEnemy)
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
//First, we need to process physics:
|
||||
if (!m_dpEntity) return;
|
||||
|
||||
m_dpEntity->SetPosition(m_ParentEntity->GetPosition()); //make sure our position is synced with our dpEntity
|
||||
m_dpEntityEnemy->SetPosition(m_ParentEntity->GetPosition());
|
||||
m_dpEntity->SetPosition(m_Parent->GetPosition()); //make sure our position is synced with our dpEntity
|
||||
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
|
||||
|
||||
//Process enter events
|
||||
for (auto en : m_dpEntity->GetNewObjects()) {
|
||||
m_ParentEntity->OnCollisionPhantom(en->GetObjectID());
|
||||
m_Parent->OnCollisionPhantom(en->GetObjectID());
|
||||
}
|
||||
|
||||
//Process exit events
|
||||
for (auto en : m_dpEntity->GetRemovedObjects()) {
|
||||
m_ParentEntity->OnCollisionLeavePhantom(en->GetObjectID());
|
||||
m_Parent->OnCollisionLeavePhantom(en->GetObjectID());
|
||||
}
|
||||
|
||||
// Check if we should stop the tether effect
|
||||
@@ -168,35 +165,39 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
const auto& info = m_MovementAI->GetInfo();
|
||||
if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared(
|
||||
m_StartPosition,
|
||||
m_ParentEntity->GetPosition()) < 20 * 20 && m_TetherTime <= 0)
|
||||
m_Parent->GetPosition()) < 20 * 20 && m_TetherTime <= 0)
|
||||
) {
|
||||
GameMessages::SendStopFXEffect(m_ParentEntity, true, "tether");
|
||||
GameMessages::SendStopFXEffect(m_Parent, true, "tether");
|
||||
m_TetherEffectActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_SoftTimer <= 0.0f) {
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
|
||||
m_SoftTimer = 5.0f;
|
||||
} else {
|
||||
m_SoftTimer -= deltaTime;
|
||||
}
|
||||
|
||||
if (m_Disabled || m_ParentEntity->IsDead()) return;
|
||||
if (m_Disabled || m_Parent->GetIsDead())
|
||||
return;
|
||||
bool stunnedThisFrame = m_Stunned;
|
||||
CalculateCombat(deltaTime); // Putting this here for now
|
||||
|
||||
if (m_StartPosition == NiPoint3::ZERO) {
|
||||
m_StartPosition = m_ParentEntity->GetPosition();
|
||||
m_StartPosition = m_Parent->GetPosition();
|
||||
}
|
||||
|
||||
if (!m_MovementAI) {
|
||||
m_MovementAI = m_ParentEntity->GetComponent<MovementAIComponent>();
|
||||
if (!m_MovementAI) return;
|
||||
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>();
|
||||
|
||||
if (m_MovementAI == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stunnedThisFrame) {
|
||||
m_MovementAI->Stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,25 +207,24 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
}
|
||||
|
||||
switch (m_State) {
|
||||
case AiState::Spawn:
|
||||
case AiState::spawn:
|
||||
Stun(2.0f);
|
||||
SetAiState(AiState::Idle);
|
||||
SetAiState(AiState::idle);
|
||||
break;
|
||||
|
||||
case AiState::Idle:
|
||||
case AiState::idle:
|
||||
Wander();
|
||||
break;
|
||||
|
||||
case AiState::Aggro:
|
||||
case AiState::aggro:
|
||||
OnAggro();
|
||||
break;
|
||||
|
||||
case AiState::Tether:
|
||||
case AiState::tether:
|
||||
OnTether();
|
||||
break;
|
||||
|
||||
default:
|
||||
Game::logger->Log("BaseCombatAIComponent", "Entity %i is in an invalid state %i", m_ParentEntity->GetLOT(), m_State);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
bool hadRemainingDowntime = m_SkillTime > 0.0f;
|
||||
if (m_SkillTime > 0.0f) m_SkillTime -= deltaTime;
|
||||
|
||||
auto* rebuild = m_ParentEntity->GetComponent<QuickBuildComponent>();
|
||||
auto* rebuild = m_Parent->GetComponent<RebuildComponent>();
|
||||
|
||||
if (rebuild != nullptr) {
|
||||
const auto state = rebuild->GetState();
|
||||
@@ -253,9 +253,11 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
}
|
||||
}
|
||||
|
||||
auto* skillComponent = m_ParentEntity->GetComponent<SkillComponent>();
|
||||
auto* skillComponent = m_Parent->GetComponent<SkillComponent>();
|
||||
|
||||
if (!skillComponent) return;
|
||||
if (skillComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
skillComponent->CalculateUpdate(deltaTime);
|
||||
|
||||
@@ -285,7 +287,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
}
|
||||
|
||||
if (!m_TetherEffectActive && m_OutOfCombat && (m_OutOfCombatTime -= deltaTime) <= 0) {
|
||||
auto* destroyableComponent = m_ParentEntity->GetComponent<DestroyableComponent>();
|
||||
auto* destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent != nullptr && destroyableComponent->HasFaction(4)) {
|
||||
auto serilizationRequired = false;
|
||||
@@ -303,10 +305,10 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
}
|
||||
|
||||
if (serilizationRequired) {
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
GameMessages::SendPlayFXEffect(m_ParentEntity->GetObjectID(), 6270, u"tether", "tether");
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 6270, u"tether", "tether");
|
||||
|
||||
m_TetherEffectActive = true;
|
||||
|
||||
@@ -327,19 +329,19 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
SetTarget(newTarget);
|
||||
|
||||
if (m_Target != LWOOBJID_EMPTY) {
|
||||
if (m_State == AiState::Idle) {
|
||||
if (m_State == AiState::idle) {
|
||||
m_Timer = 0;
|
||||
}
|
||||
|
||||
SetAiState(AiState::Aggro);
|
||||
SetAiState(AiState::aggro);
|
||||
} else {
|
||||
SetAiState(AiState::Idle);
|
||||
SetAiState(AiState::idle);
|
||||
}
|
||||
|
||||
if (!hasSkillToCast) return;
|
||||
|
||||
if (m_Target == LWOOBJID_EMPTY) {
|
||||
SetAiState(AiState::Idle);
|
||||
SetAiState(AiState::idle);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -364,7 +366,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
m_MovementAI->Stop();
|
||||
}
|
||||
|
||||
SetAiState(AiState::Aggro);
|
||||
SetAiState(AiState::aggro);
|
||||
|
||||
m_Timer = 0;
|
||||
|
||||
@@ -380,6 +382,8 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
}
|
||||
|
||||
LWOOBJID BaseCombatAIComponent::FindTarget() {
|
||||
//const auto reference = m_MovementAI == nullptr ? m_StartPosition : m_MovementAI->ApproximateLocation();
|
||||
|
||||
NiPoint3 reference = m_StartPosition;
|
||||
|
||||
if (m_MovementAI) reference = m_MovementAI->ApproximateLocation();
|
||||
@@ -492,10 +496,10 @@ LWOOBJID BaseCombatAIComponent::FindTarget() {
|
||||
std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
|
||||
std::vector<LWOOBJID> targets;
|
||||
|
||||
for (auto id : m_ParentEntity->GetTargetsInPhantom()) {
|
||||
for (auto id : m_Parent->GetTargetsInPhantom()) {
|
||||
auto* other = EntityManager::Instance()->GetEntity(id);
|
||||
|
||||
const auto distance = Vector3::DistanceSquared(m_ParentEntity->GetPosition(), other->GetPosition());
|
||||
const auto distance = Vector3::DistanceSquared(m_Parent->GetPosition(), other->GetPosition());
|
||||
|
||||
if (distance > m_AggroRadius * m_AggroRadius) continue;
|
||||
|
||||
@@ -505,12 +509,25 @@ std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
|
||||
return targets;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::IsMech() {
|
||||
switch (m_Parent->GetLOT()) {
|
||||
case 6253:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(m_DirtyStateOrTarget);
|
||||
if (bIsInitialUpdate || m_DirtyStateOrTarget) {
|
||||
outBitStream->Write(m_State);
|
||||
outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate);
|
||||
if (m_DirtyStateOrTarget || bIsInitialUpdate) {
|
||||
outBitStream->Write(uint32_t(m_State));
|
||||
outBitStream->Write(m_Target);
|
||||
if (!bIsInitialUpdate) m_DirtyStateOrTarget = false;
|
||||
m_DirtyStateOrTarget = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,13 +535,13 @@ void BaseCombatAIComponent::SetAiState(AiState newState) {
|
||||
if (newState == this->m_State) return;
|
||||
this->m_State = newState;
|
||||
m_DirtyStateOrTarget = true;
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||
auto* entity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
if (!entity) {
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BaseCombatAIComponent", "Invalid entity for checking validity (%llu)!", target);
|
||||
|
||||
return false;
|
||||
@@ -532,19 +549,21 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (!destroyable) return false;
|
||||
if (destroyable == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* referenceDestroyable = m_ParentEntity->GetComponent<DestroyableComponent>();
|
||||
auto* referenceDestroyable = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (!referenceDestroyable) {
|
||||
Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_ParentEntity->GetObjectID());
|
||||
if (referenceDestroyable == nullptr) {
|
||||
Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_Parent->GetObjectID());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* quickbuild = entity->GetComponent<QuickBuildComponent>();
|
||||
auto* quickbuild = entity->GetComponent<RebuildComponent>();
|
||||
|
||||
if (quickbuild) {
|
||||
if (quickbuild != nullptr) {
|
||||
const auto state = quickbuild->GetState();
|
||||
|
||||
if (state != eRebuildState::COMPLETED) {
|
||||
@@ -556,7 +575,7 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||
|
||||
auto candidateList = destroyable->GetFactionIDs();
|
||||
|
||||
for (const auto value : candidateList) {
|
||||
for (auto value : candidateList) {
|
||||
if (std::find(enemyList.begin(), enemyList.end(), value) != enemyList.end()) {
|
||||
return true;
|
||||
}
|
||||
@@ -569,7 +588,7 @@ void BaseCombatAIComponent::SetTarget(const LWOOBJID target) {
|
||||
if (this->m_Target == target) return;
|
||||
m_Target = target;
|
||||
m_DirtyStateOrTarget = true;
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
Entity* BaseCombatAIComponent::GetTargetEntity() const {
|
||||
@@ -578,7 +597,8 @@ Entity* BaseCombatAIComponent::GetTargetEntity() const {
|
||||
|
||||
void BaseCombatAIComponent::Taunt(LWOOBJID offender, float threat) {
|
||||
// Can't taunt self
|
||||
if (offender == m_ParentEntity->GetObjectID()) return;
|
||||
if (offender == m_Parent->GetObjectID())
|
||||
return;
|
||||
|
||||
m_ThreatEntries[offender] += threat;
|
||||
m_DirtyThreat = true;
|
||||
@@ -613,7 +633,9 @@ void BaseCombatAIComponent::ClearThreat() {
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Wander() {
|
||||
if (!m_MovementAI->AtFinalWaypoint()) return;
|
||||
if (!m_MovementAI->AtFinalWaypoint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_MovementAI->SetHaltDistance(0);
|
||||
|
||||
@@ -656,7 +678,9 @@ void BaseCombatAIComponent::OnAggro() {
|
||||
|
||||
auto* target = GetTargetEntity();
|
||||
|
||||
if (!target) return;
|
||||
if (target == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_MovementAI->SetHaltDistance(m_AttackRadius);
|
||||
|
||||
@@ -679,7 +703,7 @@ void BaseCombatAIComponent::OnAggro() {
|
||||
|
||||
m_MovementAI->SetDestination(targetPos);
|
||||
|
||||
SetAiState(AiState::Tether);
|
||||
SetAiState(AiState::tether);
|
||||
}
|
||||
|
||||
m_Timer += 0.5f;
|
||||
@@ -705,7 +729,7 @@ void BaseCombatAIComponent::OnTether() {
|
||||
|
||||
m_MovementAI->SetDestination(m_StartPosition);
|
||||
|
||||
SetAiState(AiState::Aggro);
|
||||
SetAiState(AiState::aggro);
|
||||
} else {
|
||||
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
|
||||
|
||||
@@ -717,18 +741,62 @@ void BaseCombatAIComponent::OnTether() {
|
||||
m_Timer += 0.5f;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::GetStunned() const {
|
||||
return m_Stunned;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetStunned(const bool value) {
|
||||
m_Stunned = value;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::GetStunImmune() const {
|
||||
return m_StunImmune;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetStunImmune(bool value) {
|
||||
m_StunImmune = value;
|
||||
}
|
||||
|
||||
float BaseCombatAIComponent::GetTetherSpeed() const {
|
||||
return m_TetherSpeed;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetTetherSpeed(float value) {
|
||||
m_TetherSpeed = value;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Stun(const float time) {
|
||||
if (m_StunImmune || m_StunTime > time) return;
|
||||
if (m_StunImmune || m_StunTime > time) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_StunTime = time;
|
||||
|
||||
m_Stunned = true;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LookAt(const NiPoint3& point) {
|
||||
if (m_Stunned) return;
|
||||
float BaseCombatAIComponent::GetAggroRadius() const {
|
||||
return m_AggroRadius;
|
||||
}
|
||||
|
||||
m_ParentEntity->SetRotation(NiQuaternion::LookAt(m_ParentEntity->GetPosition(), point));
|
||||
void BaseCombatAIComponent::SetAggroRadius(const float value) {
|
||||
m_AggroRadius = value;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LookAt(const NiPoint3& point) {
|
||||
if (m_Stunned) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Parent->SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), point));
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetDisabled(bool value) {
|
||||
m_Disabled = value;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::GetDistabled() const {
|
||||
return m_Disabled;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Sleep() {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
@@ -20,25 +19,20 @@ class Entity;
|
||||
/**
|
||||
* The current state of the AI
|
||||
*/
|
||||
enum class AiState : int32_t {
|
||||
Idle = 0, // Doing nothing
|
||||
Aggro, // Waiting for an enemy to cross / running back to spawn
|
||||
Tether, // Chasing an enemy
|
||||
Spawn, // Spawning into the world
|
||||
Dead // Killed
|
||||
enum class AiState : int {
|
||||
idle = 0, // Doing nothing
|
||||
aggro, // Waiting for an enemy to cross / running back to spawn
|
||||
tether, // Chasing an enemy
|
||||
spawn, // Spawning into the world
|
||||
dead // Killed
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a skill that can be cast by this enemy, including its cooldowns, which determines how often the skill
|
||||
* may be cast.
|
||||
*/
|
||||
struct AiSkillEntry {
|
||||
AiSkillEntry(uint32_t skillId, float cooldown, float abilityCooldown, Behavior* behavior) {
|
||||
this->skillId = skillId;
|
||||
this->cooldown = cooldown;
|
||||
this->abilityCooldown = abilityCooldown;
|
||||
this->behavior = behavior;
|
||||
}
|
||||
struct AiSkillEntry
|
||||
{
|
||||
uint32_t skillId;
|
||||
|
||||
float cooldown;
|
||||
@@ -51,16 +45,13 @@ struct AiSkillEntry {
|
||||
/**
|
||||
* Handles the AI of entities, making them wander, tether and attack their enemies
|
||||
*/
|
||||
class BaseCombatAIComponent final : public Component {
|
||||
class BaseCombatAIComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
|
||||
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
|
||||
~BaseCombatAIComponent() override;
|
||||
|
||||
void LoadTemplateData() override;
|
||||
void LoadConfigData() override;
|
||||
void Startup() override;
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
@@ -155,37 +146,37 @@ public:
|
||||
* Gets whether or not the entity is currently stunned
|
||||
* @return whether the entity is currently stunned
|
||||
*/
|
||||
bool GetStunned() const { return m_Stunned; }
|
||||
bool GetStunned() const;
|
||||
|
||||
/**
|
||||
* (un)stuns the entity, determining whether it'll be able to attack other entities
|
||||
* @param value whether the enemy is stunned
|
||||
*/
|
||||
void SetStunned(bool value) { m_Stunned = value; }
|
||||
void SetStunned(bool value);
|
||||
|
||||
/**
|
||||
* Gets if this entity may be stunned
|
||||
* @return if this entity may be stunned
|
||||
*/
|
||||
bool GetStunImmune() const { return m_StunImmune; }
|
||||
bool GetStunImmune() const;
|
||||
|
||||
/**
|
||||
* Set the stun immune value, determining if the entity may be stunned
|
||||
* @param value
|
||||
*/
|
||||
void SetStunImmune(bool value) { m_StunImmune = value; }
|
||||
void SetStunImmune(bool value);
|
||||
|
||||
/**
|
||||
* Gets the current speed at which an entity runs when tethering
|
||||
* @return the current speed at which an entity runs when tethering
|
||||
*/
|
||||
float GetTetherSpeed() const { return m_TetherSpeed; }
|
||||
float GetTetherSpeed() const;
|
||||
|
||||
/**
|
||||
* Sets the speed at which an entity will tether
|
||||
* @param value the new tether speed
|
||||
*/
|
||||
void SetTetherSpeed(float value) { m_TetherSpeed = value; }
|
||||
void SetTetherSpeed(float value);
|
||||
|
||||
/**
|
||||
* Stuns the entity for a certain amount of time, will not work if the entity is stun immune
|
||||
@@ -197,13 +188,13 @@ public:
|
||||
* Gets the radius that will cause this entity to get aggro'd, causing a target chase
|
||||
* @return the aggro radius of the entity
|
||||
*/
|
||||
float GetAggroRadius() const { return m_AggroRadius; }
|
||||
float GetAggroRadius() const;
|
||||
|
||||
/**
|
||||
* Sets the aggro radius, causing the entity to start chasing enemies in this range
|
||||
* @param value the aggro radius to set
|
||||
*/
|
||||
void SetAggroRadius(float value) { m_AggroRadius = value; }
|
||||
void SetAggroRadius(float value);
|
||||
|
||||
/**
|
||||
* Makes the entity look at a certain point in space
|
||||
@@ -215,13 +206,13 @@ public:
|
||||
* (dis)ables the AI, causing it to stop/start attacking enemies
|
||||
* @param value
|
||||
*/
|
||||
void SetDisabled(bool value) { m_Disabled = value; }
|
||||
void SetDisabled(bool value);
|
||||
|
||||
/**
|
||||
* Gets the current state of the AI, whether or not it's looking for enemies to attack
|
||||
* @return
|
||||
*/
|
||||
bool GetDistabled() const { return m_Disabled; }
|
||||
bool GetDistabled() const;
|
||||
|
||||
/**
|
||||
* Turns the entity asleep, stopping updates to its physics volumes
|
||||
@@ -395,9 +386,7 @@ private:
|
||||
* Whether the current entity is a mech enemy, needed as mechs tether radius works differently
|
||||
* @return whether this entity is a mech
|
||||
*/
|
||||
bool IsMech() const { return m_ParentEntity->GetLOT() == 6253; };
|
||||
|
||||
int32_t m_ComponentId;
|
||||
bool IsMech();
|
||||
};
|
||||
|
||||
#endif // BASECOMBATAICOMPONENT_H
|
||||
|
||||
@@ -6,50 +6,66 @@
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "GameMessages.h"
|
||||
#include "BitStream.h"
|
||||
#include <BitStream.h>
|
||||
#include "eTriggerEventType.h"
|
||||
|
||||
BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
|
||||
m_BounceOnCollision = false;
|
||||
m_DirtyBounceInfo = false;
|
||||
m_PetEnabled = false;
|
||||
m_PetBouncerEnabled = false;
|
||||
m_PetSwitchLoaded = false;
|
||||
|
||||
if (parent->GetLOT() == 7625) {
|
||||
LookupPetSwitch();
|
||||
}
|
||||
}
|
||||
|
||||
void BouncerComponent::Startup() {
|
||||
if (m_ParentEntity->GetLOT() == 7625) LookupPetSwitch();
|
||||
BouncerComponent::~BouncerComponent() {
|
||||
}
|
||||
|
||||
void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyBounceInfo);
|
||||
if (bIsInitialUpdate || m_DirtyBounceInfo) {
|
||||
outBitStream->Write(m_BounceOnCollision);
|
||||
if (!bIsInitialUpdate) m_DirtyBounceInfo = false;
|
||||
outBitStream->Write(m_PetEnabled);
|
||||
if (m_PetEnabled) {
|
||||
outBitStream->Write(m_PetBouncerEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void BouncerComponent::SetBounceOnCollision(bool value) {
|
||||
if (m_BounceOnCollision == value) return;
|
||||
m_BounceOnCollision = value;
|
||||
m_DirtyBounceInfo = true;
|
||||
Entity* BouncerComponent::GetParentEntity() const {
|
||||
return m_Parent;
|
||||
}
|
||||
|
||||
void BouncerComponent::SetBouncerEnabled(bool value) {
|
||||
m_BounceOnCollision = value;
|
||||
void BouncerComponent::SetPetEnabled(bool value) {
|
||||
m_PetEnabled = value;
|
||||
|
||||
GameMessages::SendBouncerActiveStatus(m_ParentEntity->GetObjectID(), value, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
void BouncerComponent::SetPetBouncerEnabled(bool value) {
|
||||
m_PetBouncerEnabled = value;
|
||||
|
||||
GameMessages::SendBouncerActiveStatus(m_Parent->GetObjectID(), value, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
|
||||
if (value) {
|
||||
m_ParentEntity->TriggerEvent(eTriggerEventType::PET_ON_SWITCH, m_ParentEntity);
|
||||
GameMessages::SendPlayFXEffect(m_ParentEntity->GetObjectID(), 1513, u"create", "PetOnSwitch");
|
||||
m_Parent->TriggerEvent(eTriggerEventType::PET_ON_SWITCH, m_Parent);
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 1513, u"create", "PetOnSwitch", LWOOBJID_EMPTY, 1, 1, true);
|
||||
} else {
|
||||
m_ParentEntity->TriggerEvent(eTriggerEventType::PET_OFF_SWITCH, m_ParentEntity);
|
||||
GameMessages::SendStopFXEffect(m_ParentEntity, true, "PetOnSwitch");
|
||||
m_Parent->TriggerEvent(eTriggerEventType::PET_OFF_SWITCH, m_Parent);
|
||||
GameMessages::SendStopFXEffect(m_Parent, true, "PetOnSwitch");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool BouncerComponent::GetPetEnabled() const {
|
||||
return m_PetEnabled;
|
||||
}
|
||||
|
||||
bool BouncerComponent::GetPetBouncerEnabled() const {
|
||||
return m_PetBouncerEnabled;
|
||||
}
|
||||
|
||||
void BouncerComponent::LookupPetSwitch() {
|
||||
const auto& groups = m_ParentEntity->GetGroups();
|
||||
const auto& groups = m_Parent->GetGroups();
|
||||
|
||||
for (const auto& group : groups) {
|
||||
const auto& entities = EntityManager::Instance()->GetEntitiesInGroup(group);
|
||||
@@ -57,22 +73,24 @@ void BouncerComponent::LookupPetSwitch() {
|
||||
for (auto* entity : entities) {
|
||||
auto* switchComponent = entity->GetComponent<SwitchComponent>();
|
||||
|
||||
if (!switchComponent) continue;
|
||||
switchComponent->SetPetBouncer(this);
|
||||
if (switchComponent != nullptr) {
|
||||
switchComponent->SetPetBouncer(this);
|
||||
|
||||
m_DirtyBounceInfo = true;
|
||||
m_PetSwitchLoaded = true;
|
||||
m_PetEnabled = true;
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
|
||||
Game::logger->Log("BouncerComponent", "Loaded bouncer %i:%llu", m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID());
|
||||
return;
|
||||
Game::logger->Log("BouncerComponent", "Loaded pet bouncer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float retryTime = 0.5f;
|
||||
Game::logger->Log("BouncerComponent", "Failed to load pet bouncer for %i:%llu, trying again in %f seconds", m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID(), retryTime);
|
||||
if (!m_PetSwitchLoaded) {
|
||||
Game::logger->Log("BouncerComponent", "Failed to load pet bouncer");
|
||||
|
||||
m_ParentEntity->AddCallbackTimer(retryTime, [this]() {
|
||||
LookupPetSwitch();
|
||||
});
|
||||
m_Parent->AddCallbackTimer(0.5f, [this]() {
|
||||
LookupPetSwitch();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,27 +10,41 @@
|
||||
/**
|
||||
* Attached to bouncer entities, allowing other entities to bounce off of it
|
||||
*/
|
||||
class BouncerComponent final : public Component {
|
||||
class BouncerComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
|
||||
BouncerComponent(Entity* parentEntity);
|
||||
~BouncerComponent() override;
|
||||
|
||||
void Startup() override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
Entity* GetParentEntity() const;
|
||||
|
||||
/**
|
||||
* Sets whether or not this bouncer needs to be activated by a pet
|
||||
* @param value whether or not this bouncer needs to be activated by a pet
|
||||
*/
|
||||
void SetBounceOnCollision(bool value);
|
||||
void SetPetEnabled(bool value);
|
||||
|
||||
/**
|
||||
* Sets whether or not this bouncer is currently being activated by a pet, allowing entities to bounce off of it,
|
||||
* also displays FX accordingly.
|
||||
* @param value whether or not this bouncer is activated by a pet
|
||||
*/
|
||||
void SetBouncerEnabled(bool value);
|
||||
void SetPetBouncerEnabled(bool value);
|
||||
|
||||
/**
|
||||
* Gets whether this bouncer should be enabled using pets
|
||||
* @return whether this bouncer should be enabled using pets
|
||||
*/
|
||||
bool GetPetEnabled() const;
|
||||
|
||||
/**
|
||||
* Gets whether this bouncer is currently activated by a pet
|
||||
* @return whether this bouncer is currently activated by a pet
|
||||
*/
|
||||
bool GetPetBouncerEnabled() const;
|
||||
|
||||
/**
|
||||
* Finds the switch used to activate this bouncer if its pet-enabled and stores this components' state there
|
||||
@@ -38,12 +52,20 @@ public:
|
||||
void LookupPetSwitch();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Whether this bouncer needs to be activated by a pet
|
||||
*/
|
||||
bool m_PetEnabled;
|
||||
|
||||
/**
|
||||
* Whether this bouncer is currently being activated by a pet
|
||||
*/
|
||||
bool m_BounceOnCollision;
|
||||
bool m_PetBouncerEnabled;
|
||||
|
||||
bool m_DirtyBounceInfo;
|
||||
/**
|
||||
* Whether the pet switch for this bouncer has been located
|
||||
*/
|
||||
bool m_PetSwitchLoaded;
|
||||
};
|
||||
|
||||
#endif // BOUNCERCOMPONENT_H
|
||||
|
||||
@@ -14,12 +14,20 @@
|
||||
|
||||
std::unordered_map<int32_t, std::vector<BuffParameter>> BuffComponent::m_Cache{};
|
||||
|
||||
BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
|
||||
}
|
||||
|
||||
BuffComponent::~BuffComponent() {
|
||||
}
|
||||
|
||||
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
if (!bIsInitialUpdate) return;
|
||||
|
||||
outBitStream->Write(!m_Buffs.empty());
|
||||
if (!m_Buffs.empty()) {
|
||||
if (m_Buffs.empty()) {
|
||||
outBitStream->Write0();
|
||||
} else {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write<uint32_t>(m_Buffs.size());
|
||||
|
||||
for (const auto& buff : m_Buffs) {
|
||||
outBitStream->Write<uint32_t>(buff.first);
|
||||
outBitStream->Write0();
|
||||
@@ -39,7 +47,7 @@ void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUp
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write0(); // immunities
|
||||
outBitStream->Write0();
|
||||
}
|
||||
|
||||
void BuffComponent::Update(float deltaTime) {
|
||||
@@ -47,28 +55,30 @@ void BuffComponent::Update(float deltaTime) {
|
||||
* Loop through all buffs and apply deltaTime to ther time.
|
||||
* If they have expired, remove the buff and break.
|
||||
*/
|
||||
for (auto& [buffId, buffInfo] : m_Buffs) {
|
||||
for (auto& buff : m_Buffs) {
|
||||
// For damage buffs
|
||||
if (buffInfo.tick != 0.0f && buffInfo.stacks > 0) {
|
||||
buffInfo.tickTime -= deltaTime;
|
||||
if (buff.second.tick != 0.0f && buff.second.stacks > 0) {
|
||||
buff.second.tickTime -= deltaTime;
|
||||
|
||||
if (buffInfo.tickTime <= 0.0f) {
|
||||
buffInfo.tickTime = buffInfo.tick;
|
||||
buffInfo.stacks--;
|
||||
if (buff.second.tickTime <= 0.0f) {
|
||||
buff.second.tickTime = buff.second.tick;
|
||||
buff.second.stacks--;
|
||||
|
||||
SkillComponent::HandleUnmanaged(buffInfo.behaviorID, m_ParentEntity->GetObjectID(), buffInfo.source);
|
||||
SkillComponent::HandleUnmanaged(buff.second.behaviorID, m_Parent->GetObjectID(), buff.second.source);
|
||||
}
|
||||
}
|
||||
|
||||
// These are indefinate buffs, don't update them.
|
||||
if (buffInfo.time == 0.0f) continue;
|
||||
if (buff.second.time == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buffInfo.time -= deltaTime;
|
||||
buff.second.time -= deltaTime;
|
||||
|
||||
if (buffInfo.time <= 0.0f) {
|
||||
RemoveBuff(buffId);
|
||||
if (buff.second.time <= 0.0f) {
|
||||
RemoveBuff(buff.first);
|
||||
|
||||
break; // Break because we modified or may modify the map.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,9 +87,11 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff,
|
||||
bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) {
|
||||
// Prevent buffs from stacking.
|
||||
if (HasBuff(id)) return;
|
||||
if (HasBuff(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameMessages::SendAddBuff(m_ParentEntity->GetObjectID(), source, (uint32_t)id,
|
||||
GameMessages::SendAddBuff(const_cast<LWOOBJID&>(m_Parent->GetObjectID()), source, (uint32_t)id,
|
||||
(uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
|
||||
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone);
|
||||
|
||||
@@ -88,14 +100,14 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
int32_t behaviorID = 0;
|
||||
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
auto* skillBehaviorTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.name == "overtime") {
|
||||
auto* behaviorTemplateTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
|
||||
behaviorID = skillBehaviorTable->GetSkillByID(parameter.values.skillId).behaviorID;
|
||||
stacks = static_cast<int32_t>(parameter.values.stacks);
|
||||
tick = parameter.values.tick;
|
||||
const auto unknown2 = parameter.values.unknown2; // Always 0
|
||||
behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID;
|
||||
stacks = static_cast<int32_t>(parameter.values[1]);
|
||||
tick = parameter.values[2];
|
||||
const auto unknown2 = parameter.values[3]; // Always 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,28 +122,34 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
buff.source = source;
|
||||
buff.behaviorID = behaviorID;
|
||||
|
||||
m_Buffs.insert_or_assign(id, buff);
|
||||
m_Buffs.emplace(id, buff);
|
||||
}
|
||||
|
||||
void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity) {
|
||||
const auto& iter = m_Buffs.find(id);
|
||||
|
||||
if (iter == m_Buffs.end()) return;
|
||||
if (iter == m_Buffs.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameMessages::SendRemoveBuff(m_ParentEntity, fromUnEquip, removeImmunity, id);
|
||||
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
|
||||
|
||||
m_Buffs.erase(iter);
|
||||
|
||||
RemoveBuffEffect(id);
|
||||
}
|
||||
|
||||
bool BuffComponent::HasBuff(int32_t id) {
|
||||
return m_Buffs.find(id) != m_Buffs.end();
|
||||
}
|
||||
|
||||
void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.name == "max_health") {
|
||||
const auto maxHealth = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
@@ -139,7 +157,7 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
} else if (parameter.name == "max_armor") {
|
||||
const auto maxArmor = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
@@ -147,13 +165,13 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
} else if (parameter.name == "max_imagination") {
|
||||
const auto maxImagination = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
destroyable->SetMaxImagination(destroyable->GetMaxImagination() + maxImagination);
|
||||
} else if (parameter.name == "speed") {
|
||||
auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = this->GetParent()->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
const auto speed = parameter.value;
|
||||
controllablePhysicsComponent->AddSpeedboost(speed);
|
||||
@@ -167,29 +185,29 @@ void BuffComponent::RemoveBuffEffect(int32_t id) {
|
||||
if (parameter.name == "max_health") {
|
||||
const auto maxHealth = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (!destroyable) return;
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
destroyable->SetMaxHealth(destroyable->GetMaxHealth() - maxHealth);
|
||||
} else if (parameter.name == "max_armor") {
|
||||
const auto maxArmor = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (!destroyable) return;
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
destroyable->SetMaxArmor(destroyable->GetMaxArmor() - maxArmor);
|
||||
} else if (parameter.name == "max_imagination") {
|
||||
const auto maxImagination = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (!destroyable) return;
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
destroyable->SetMaxImagination(destroyable->GetMaxImagination() - maxImagination);
|
||||
} else if (parameter.name == "speed") {
|
||||
auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = this->GetParent()->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
const auto speed = parameter.value;
|
||||
controllablePhysicsComponent->RemoveSpeedboost(speed);
|
||||
@@ -198,19 +216,27 @@ void BuffComponent::RemoveBuffEffect(int32_t id) {
|
||||
}
|
||||
|
||||
void BuffComponent::RemoveAllBuffs() {
|
||||
for (const auto& [buffId, buffInfo] : m_Buffs) {
|
||||
RemoveBuffEffect(buffId);
|
||||
for (const auto& buff : m_Buffs) {
|
||||
RemoveBuffEffect(buff.first);
|
||||
}
|
||||
|
||||
m_Buffs.clear();
|
||||
}
|
||||
|
||||
void BuffComponent::Reset() {
|
||||
RemoveAllBuffs();
|
||||
}
|
||||
|
||||
void BuffComponent::ReApplyBuffs() {
|
||||
for (const auto& [buffId, buffInfo] : m_Buffs) {
|
||||
ApplyBuffEffect(buffId);
|
||||
for (const auto& buff : m_Buffs) {
|
||||
ApplyBuffEffect(buff.first);
|
||||
}
|
||||
}
|
||||
|
||||
Entity* BuffComponent::GetParent() const {
|
||||
return m_Parent;
|
||||
}
|
||||
|
||||
void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
// Load buffs
|
||||
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
||||
@@ -219,7 +245,9 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
auto* buffElement = dest->FirstChildElement("buff");
|
||||
|
||||
// Old character, no buffs to load
|
||||
if (buffElement) return;
|
||||
if (buffElement == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buffEntry = buffElement->FirstChildElement("b");
|
||||
|
||||
@@ -277,9 +305,12 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffId) {
|
||||
const auto& pair = m_Cache.find(buffId);
|
||||
|
||||
if (pair != m_Cache.end()) return pair->second;
|
||||
if (pair != m_Cache.end()) {
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
query.bind(1, (int)buffId);
|
||||
|
||||
auto result = query.execQuery();
|
||||
@@ -294,15 +325,17 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
||||
param.value = result.getFloatField(2);
|
||||
|
||||
if (!result.fieldIsNull(3)) {
|
||||
const auto parameterInfo = result.getStringField(3);
|
||||
const auto values = GeneralUtils::SplitString(parameterInfo, ',');
|
||||
if (values.size() >= 4) {
|
||||
GeneralUtils::TryParse(values.at(0), param.values.skillId);
|
||||
GeneralUtils::TryParse(values.at(1), param.values.stacks);
|
||||
GeneralUtils::TryParse(values.at(2), param.values.tick);
|
||||
GeneralUtils::TryParse(values.at(3), param.values.unknown2);
|
||||
} else {
|
||||
Game::logger->Log("BuffComponent", "Failed to parse %s into parameter struct. Too few parameters to split on.", parameterInfo);
|
||||
std::istringstream stream(result.getStringField(3));
|
||||
std::string token;
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
try {
|
||||
const auto value = std::stof(token);
|
||||
|
||||
param.values.push_back(value);
|
||||
} catch (std::invalid_argument& exception) {
|
||||
Game::logger->Log("BuffComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,24 +14,20 @@ class Entity;
|
||||
/**
|
||||
* Extra information on effects to apply after applying a buff, for example whether to buff armor, imag or health and by how much
|
||||
*/
|
||||
struct BuffParameter {
|
||||
struct ParameterValues {
|
||||
int32_t skillId = 0;
|
||||
int32_t stacks = 0;
|
||||
float tick = 0.0f;
|
||||
int32_t unknown2 = 0;
|
||||
};
|
||||
int32_t buffId = 0;
|
||||
struct BuffParameter
|
||||
{
|
||||
int32_t buffId;
|
||||
std::string name;
|
||||
float value = 0.0f;
|
||||
ParameterValues values;
|
||||
int32_t effectId = 0;
|
||||
float value;
|
||||
std::vector<float> values;
|
||||
int32_t effectId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Meta information about a buff that can be applied, e.g. how long it's applied, who applied it, etc.
|
||||
*/
|
||||
struct Buff {
|
||||
struct Buff
|
||||
{
|
||||
int32_t id = 0;
|
||||
float time = 0;
|
||||
float tick = 0;
|
||||
@@ -44,11 +40,15 @@ struct Buff {
|
||||
/**
|
||||
* Allows for the application of buffs to the parent entity, altering health, armor and imagination.
|
||||
*/
|
||||
class BuffComponent final : public Component {
|
||||
class BuffComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
|
||||
explicit BuffComponent(Entity* parent) : Component(parent) {};
|
||||
explicit BuffComponent(Entity* parent);
|
||||
|
||||
~BuffComponent();
|
||||
|
||||
Entity* GetParent() const;
|
||||
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
* @param id the id of the buff to find
|
||||
* @return whether or not the entity has a buff with the specified id active
|
||||
*/
|
||||
bool HasBuff(int32_t id) { return m_Buffs.find(id) != m_Buffs.end(); };
|
||||
bool HasBuff(int32_t id);
|
||||
|
||||
/**
|
||||
* Applies the effects of the buffs on the entity, e.g.: changing armor, health, imag, etc.
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
/**
|
||||
* Removes all buffs for the entity and reverses all of their effects
|
||||
*/
|
||||
void Reset() { RemoveAllBuffs(); };
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Applies all effects for all buffs, active or not, again
|
||||
|
||||
@@ -9,40 +9,62 @@
|
||||
#include "Item.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
|
||||
BuildBorderComponent::BuildBorderComponent(Entity* parent) : Component(parent) {
|
||||
}
|
||||
|
||||
BuildBorderComponent::~BuildBorderComponent() {
|
||||
}
|
||||
|
||||
void BuildBorderComponent::OnUse(Entity* originator) {
|
||||
if (!originator->GetCharacter()) return;
|
||||
if (originator->GetCharacter()) {
|
||||
const auto& entities = EntityManager::Instance()->GetEntitiesInGroup("PropertyPlaque");
|
||||
|
||||
const auto& entities = EntityManager::Instance()->GetEntitiesInGroup("PropertyPlaque");
|
||||
auto buildArea = m_Parent->GetObjectID();
|
||||
|
||||
auto buildArea = entities.empty() ? m_ParentEntity->GetObjectID() : entities.front()->GetObjectID();
|
||||
if (!entities.empty()) {
|
||||
buildArea = entities[0]->GetObjectID();
|
||||
|
||||
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
|
||||
Game::logger->Log("BuildBorderComponent", "Using PropertyPlaque");
|
||||
}
|
||||
|
||||
if (!inventoryComponent) return;
|
||||
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
|
||||
|
||||
auto* thinkingHat = inventoryComponent->FindItemByLot(LOT_THINKING_CAP);
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!thinkingHat) return;
|
||||
auto* thinkingHat = inventoryComponent->FindItemByLot(6086);
|
||||
|
||||
Game::logger->Log("BuildBorderComponent", "Using BuildArea %llu for player %llu", buildArea, originator->GetObjectID());
|
||||
if (thinkingHat == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
inventoryComponent->PushEquippedItems();
|
||||
inventoryComponent->PushEquippedItems();
|
||||
|
||||
if (PropertyManagementComponent::Instance()) {
|
||||
GameMessages::SendStartArrangingWithItem(
|
||||
originator,
|
||||
originator->GetSystemAddress(),
|
||||
true,
|
||||
buildArea,
|
||||
originator->GetPosition(),
|
||||
0,
|
||||
thinkingHat->GetId(),
|
||||
thinkingHat->GetLot(),
|
||||
4,
|
||||
0,
|
||||
-1
|
||||
);
|
||||
} else {
|
||||
GameMessages::SendStartArrangingWithItem(originator, originator->GetSystemAddress(), true, buildArea, originator->GetPosition());
|
||||
Game::logger->Log("BuildBorderComponent", "Starting with %llu", buildArea);
|
||||
|
||||
if (PropertyManagementComponent::Instance() != nullptr) {
|
||||
GameMessages::SendStartArrangingWithItem(
|
||||
originator,
|
||||
originator->GetSystemAddress(),
|
||||
true,
|
||||
buildArea,
|
||||
originator->GetPosition(),
|
||||
0,
|
||||
thinkingHat->GetId(),
|
||||
thinkingHat->GetLot(),
|
||||
4,
|
||||
0,
|
||||
-1,
|
||||
NiPoint3::ZERO,
|
||||
0
|
||||
);
|
||||
} else {
|
||||
GameMessages::SendStartArrangingWithItem(originator, originator->GetSystemAddress(), true, buildArea, originator->GetPosition());
|
||||
}
|
||||
|
||||
InventoryComponent* inv = m_Parent->GetComponent<InventoryComponent>();
|
||||
if (!inv) return;
|
||||
inv->PushEquippedItems(); // technically this is supposed to happen automatically... but it doesnt? so just keep this here
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,23 +6,27 @@
|
||||
#ifndef BUILDBORDERCOMPONENT_H
|
||||
#define BUILDBORDERCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
/**
|
||||
* Component for the build border, allowing the user to start building when interacting with it
|
||||
*/
|
||||
class BuildBorderComponent final : public Component {
|
||||
class BuildBorderComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
|
||||
BuildBorderComponent(Entity* parent) : Component(parent) { };
|
||||
BuildBorderComponent(Entity* parent);
|
||||
~BuildBorderComponent() override;
|
||||
|
||||
/**
|
||||
* Causes the originator to start build with this entity as a reference point
|
||||
* @param originator the entity (probably a player) that triggered the event
|
||||
*/
|
||||
void OnUse(Entity* originator) override;
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // BUILDBORDERCOMPONENT_H
|
||||
|
||||
@@ -1,49 +1,37 @@
|
||||
set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
|
||||
"ActivityComponent.cpp"
|
||||
"BaseCombatAIComponent.cpp"
|
||||
set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
|
||||
"BouncerComponent.cpp"
|
||||
"BuffComponent.cpp"
|
||||
"BuildBorderComponent.cpp"
|
||||
"CharacterComponent.cpp"
|
||||
"CollectibleComponent.cpp"
|
||||
"Component.cpp"
|
||||
"ControllablePhysicsComponent.cpp"
|
||||
"DestroyableComponent.cpp"
|
||||
"DonationVendorComponent.cpp"
|
||||
"GateRushComponent.cpp"
|
||||
"InventoryComponent.cpp"
|
||||
"ItemComponent.cpp"
|
||||
"LevelProgressionComponent.cpp"
|
||||
"LUPExhibitComponent.cpp"
|
||||
"MinigameControlComponent.cpp"
|
||||
"MissionComponent.cpp"
|
||||
"MissionOfferComponent.cpp"
|
||||
"ModelBehaviorComponent.cpp"
|
||||
"ModelComponent.cpp"
|
||||
"ModuleAssemblyComponent.cpp"
|
||||
"MovementAIComponent.cpp"
|
||||
"MovingPlatformComponent.cpp"
|
||||
"MutableModelBehaviorComponent.cpp"
|
||||
"PetComponent.cpp"
|
||||
"PhantomPhysicsComponent.cpp"
|
||||
"PlayerForcedMovementComponent.cpp"
|
||||
"PossessableComponent.cpp"
|
||||
"PossessionComponent.cpp"
|
||||
"PossessorComponent.cpp"
|
||||
"PropertyComponent.cpp"
|
||||
"PropertyEntranceComponent.cpp"
|
||||
"PropertyManagementComponent.cpp"
|
||||
"PropertyVendorComponent.cpp"
|
||||
"ProximityMonitorComponent.cpp"
|
||||
"RacingComponent.cpp"
|
||||
"RacingControlComponent.cpp"
|
||||
"RacingSoundTriggerComponent.cpp"
|
||||
"RacingStatsComponent.cpp"
|
||||
"RailActivatorComponent.cpp"
|
||||
"QuickBuildComponent.cpp"
|
||||
"RebuildComponent.cpp"
|
||||
"RenderComponent.cpp"
|
||||
"RigidbodyPhantomPhysicsComponent.cpp"
|
||||
"MultiZoneEntranceComponent.cpp"
|
||||
"RocketLaunchLupComponent.cpp"
|
||||
"RocketLaunchpadControlComponent.cpp"
|
||||
"ScriptComponent.cpp"
|
||||
"ScriptedActivityComponent.cpp"
|
||||
"ShootingGalleryComponent.cpp"
|
||||
"SimplePhysicsComponent.cpp"
|
||||
@@ -51,5 +39,5 @@ set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
|
||||
"SoundTriggerComponent.cpp"
|
||||
"SwitchComponent.cpp"
|
||||
"TriggerComponent.cpp"
|
||||
"HavokVehiclePhysicsComponent.cpp"
|
||||
"VehiclePhysicsComponent.cpp"
|
||||
"VendorComponent.cpp" PARENT_SCOPE)
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "VehiclePhysicsComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Item.h"
|
||||
#include "Amf3.h"
|
||||
#include "AMFFormat.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "eGameActivity.h"
|
||||
|
||||
@@ -66,12 +67,16 @@ bool CharacterComponent::LandingAnimDisabled(int zoneID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CharacterComponent::~CharacterComponent() {
|
||||
}
|
||||
|
||||
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
|
||||
outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
|
||||
outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
|
||||
outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
|
||||
outBitStream->Write(m_Character->GetHairColor());
|
||||
outBitStream->Write(m_Character->GetHairStyle());
|
||||
@@ -118,8 +123,6 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
|
||||
outBitStream->Write(m_RacesFinished);
|
||||
outBitStream->Write(m_FirstPlaceRaceFinishes);
|
||||
|
||||
// This is a really weird bit of serialization. This is serialized as a two bit integer, but the first bit written is always zero.
|
||||
// If the 2 bit integer is exactly 1, we write the rocket configuration.
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write(m_IsLanding);
|
||||
if (m_IsLanding) {
|
||||
@@ -130,24 +133,20 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyGMInfo);
|
||||
if (bIsInitialUpdate || m_DirtyGMInfo) {
|
||||
outBitStream->Write(m_DirtyGMInfo);
|
||||
if (m_DirtyGMInfo) {
|
||||
outBitStream->Write(m_PvpEnabled);
|
||||
outBitStream->Write(m_IsGM);
|
||||
outBitStream->Write(m_GMLevel);
|
||||
outBitStream->Write(m_EditorEnabled);
|
||||
outBitStream->Write(m_EditorLevel);
|
||||
if (!bIsInitialUpdate) m_DirtyGMInfo = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyCurrentActivity);
|
||||
if (bIsInitialUpdate || m_DirtyCurrentActivity) {
|
||||
outBitStream->Write(m_CurrentActivity);
|
||||
if (!bIsInitialUpdate) m_DirtyCurrentActivity = false;
|
||||
}
|
||||
outBitStream->Write(m_DirtyCurrentActivity);
|
||||
if (m_DirtyCurrentActivity) outBitStream->Write(m_CurrentActivity);
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtySocialInfo);
|
||||
if (bIsInitialUpdate || m_DirtySocialInfo) {
|
||||
outBitStream->Write(m_DirtySocialInfo);
|
||||
if (m_DirtySocialInfo) {
|
||||
outBitStream->Write(m_GuildID);
|
||||
outBitStream->Write<unsigned char>(static_cast<unsigned char>(m_GuildName.size()));
|
||||
if (!m_GuildName.empty())
|
||||
@@ -155,7 +154,6 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
|
||||
|
||||
outBitStream->Write(m_IsLEGOClubMember);
|
||||
outBitStream->Write(m_CountryCode);
|
||||
if (!bIsInitialUpdate) m_DirtySocialInfo = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,21 +162,21 @@ bool CharacterComponent::GetPvpEnabled() const {
|
||||
}
|
||||
|
||||
void CharacterComponent::SetPvpEnabled(const bool value) {
|
||||
if (m_PvpEnabled == value) return;
|
||||
m_DirtyGMInfo = true;
|
||||
|
||||
m_PvpEnabled = value;
|
||||
}
|
||||
|
||||
void CharacterComponent::SetGMLevel(eGameMasterLevel gmlevel) {
|
||||
if (m_GMLevel == gmlevel) return;
|
||||
m_DirtyGMInfo = true;
|
||||
m_IsGM = gmlevel > eGameMasterLevel::CIVILIAN;
|
||||
if (gmlevel > eGameMasterLevel::CIVILIAN) m_IsGM = true;
|
||||
else m_IsGM = false;
|
||||
m_GMLevel = gmlevel;
|
||||
}
|
||||
|
||||
void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
auto* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!");
|
||||
return;
|
||||
@@ -198,7 +196,7 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
|
||||
// Load the zone statistics
|
||||
m_ZoneStatistics.clear();
|
||||
m_ZoneStatistics = {};
|
||||
auto zoneStatistics = character->FirstChildElement("zs");
|
||||
|
||||
if (zoneStatistics) {
|
||||
@@ -221,16 +219,20 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
}
|
||||
|
||||
const auto* rocketConfig = character->FindAttribute("lcbp");
|
||||
const tinyxml2::XMLAttribute* rocketConfig = character->FindAttribute("lcbp");
|
||||
|
||||
m_LastRocketConfig = rocketConfig ? GeneralUtils::ASCIIToUTF16(rocketConfig->Value()) : u"";
|
||||
if (rocketConfig) {
|
||||
m_LastRocketConfig = GeneralUtils::ASCIIToUTF16(rocketConfig->Value());
|
||||
} else {
|
||||
m_LastRocketConfig = u"";
|
||||
}
|
||||
|
||||
//
|
||||
// Begin custom attributes
|
||||
//
|
||||
|
||||
// Load the last rocket item ID
|
||||
const auto* lastRocketItemID = character->FindAttribute("lrid");
|
||||
const tinyxml2::XMLAttribute* lastRocketItemID = character->FindAttribute("lrid");
|
||||
if (lastRocketItemID) {
|
||||
m_LastRocketItemID = lastRocketItemID->Int64Value();
|
||||
}
|
||||
@@ -243,11 +245,11 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
m_IsGM = true;
|
||||
m_DirtyGMInfo = true;
|
||||
m_EditorLevel = m_GMLevel;
|
||||
m_EditorEnabled = false; // We're not currently in HF if we're loading in
|
||||
m_EditorEnabled = false; //We're not currently in HF if we're loading in
|
||||
}
|
||||
|
||||
//Annoying guild bs:
|
||||
const auto* guildName = character->FindAttribute("gn");
|
||||
const tinyxml2::XMLAttribute* guildName = character->FindAttribute("gn");
|
||||
if (guildName) {
|
||||
const char* gn = guildName->Value();
|
||||
int64_t gid = 0;
|
||||
@@ -268,8 +270,10 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
if (!m_Character) return;
|
||||
|
||||
// If the loaded zoneID is different from the current zoneID, we are landing
|
||||
m_IsLanding = m_Character->GetZoneID() != Game::server->GetZoneID();
|
||||
//Check to see if we're landing:
|
||||
if (m_Character->GetZoneID() != Game::server->GetZoneID()) {
|
||||
m_IsLanding = true;
|
||||
}
|
||||
|
||||
if (LandingAnimDisabled(m_Character->GetZoneID()) || LandingAnimDisabled(Game::server->GetZoneID()) || m_LastRocketConfig.empty()) {
|
||||
m_IsLanding = false; //Don't make us land on VE/minigames lol
|
||||
@@ -297,7 +301,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
// done with minifig
|
||||
|
||||
auto* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("CharacterComponent", "Failed to find char tag while updating XML!");
|
||||
return;
|
||||
@@ -313,15 +317,15 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
if (!zoneStatistics) zoneStatistics = doc->NewElement("zs");
|
||||
zoneStatistics->DeleteChildren();
|
||||
|
||||
for (const auto&[mapId, zoneStatisticToSave] : m_ZoneStatistics) {
|
||||
for (auto pair : m_ZoneStatistics) {
|
||||
auto zoneStatistic = doc->NewElement("s");
|
||||
|
||||
zoneStatistic->SetAttribute("map", mapId);
|
||||
zoneStatistic->SetAttribute("ac", zoneStatisticToSave.m_AchievementsCollected);
|
||||
zoneStatistic->SetAttribute("bc", zoneStatisticToSave.m_BricksCollected);
|
||||
zoneStatistic->SetAttribute("cc", zoneStatisticToSave.m_CoinsCollected);
|
||||
zoneStatistic->SetAttribute("es", zoneStatisticToSave.m_EnemiesSmashed);
|
||||
zoneStatistic->SetAttribute("qbc", zoneStatisticToSave.m_QuickBuildsCompleted);
|
||||
zoneStatistic->SetAttribute("map", pair.first);
|
||||
zoneStatistic->SetAttribute("ac", pair.second.m_AchievementsCollected);
|
||||
zoneStatistic->SetAttribute("bc", pair.second.m_BricksCollected);
|
||||
zoneStatistic->SetAttribute("cc", pair.second.m_CoinsCollected);
|
||||
zoneStatistic->SetAttribute("es", pair.second.m_EnemiesSmashed);
|
||||
zoneStatistic->SetAttribute("qbc", pair.second.m_QuickBuildsCompleted);
|
||||
|
||||
zoneStatistics->LinkEndChild(zoneStatistic);
|
||||
}
|
||||
@@ -347,7 +351,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
//
|
||||
|
||||
auto newUpdateTimestamp = std::time(nullptr);
|
||||
Game::logger->Log("TotalTimePlayed", "Time since %i last saved: %d", m_Character->GetID(), newUpdateTimestamp - m_LastUpdateTimestamp);
|
||||
Game::logger->Log("TotalTimePlayed", "Time since last save: %d", newUpdateTimestamp - m_LastUpdateTimestamp);
|
||||
|
||||
m_TotalTimePlayed += newUpdateTimestamp - m_LastUpdateTimestamp;
|
||||
character->SetAttribute("time", m_TotalTimePlayed);
|
||||
@@ -388,7 +392,7 @@ Item* CharacterComponent::RocketEquip(Entity* player) {
|
||||
if (!rocket) return rocket;
|
||||
|
||||
// build and define the rocket config
|
||||
for (auto* data : rocket->GetConfig()) {
|
||||
for (LDFBaseData* data : rocket->GetConfig()) {
|
||||
if (data->GetKey() == u"assemblyPartLOTs") {
|
||||
std::string newRocketStr = data->GetValueAsString() + ";";
|
||||
GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
|
||||
@@ -468,7 +472,9 @@ void CharacterComponent::TrackImaginationDelta(int32_t imagination) {
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackArmorDelta(int32_t armor) {
|
||||
if (armor > 0) UpdatePlayerStatistic(TotalArmorRepaired, armor);
|
||||
if (armor > 0) {
|
||||
UpdatePlayerStatistic(TotalArmorRepaired, armor);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackRebuildComplete() {
|
||||
@@ -480,16 +486,17 @@ void CharacterComponent::TrackRebuildComplete() {
|
||||
|
||||
void CharacterComponent::TrackRaceCompleted(bool won) {
|
||||
m_RacesFinished++;
|
||||
if (won) m_FirstPlaceRaceFinishes++;
|
||||
if (won)
|
||||
m_FirstPlaceRaceFinishes++;
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackPositionUpdate(const NiPoint3& newPosition) {
|
||||
const auto distance = NiPoint3::Distance(newPosition, m_ParentEntity->GetPosition());
|
||||
const auto distance = NiPoint3::Distance(newPosition, m_Parent->GetPosition());
|
||||
|
||||
if (m_IsRacing) {
|
||||
UpdatePlayerStatistic(DistanceDriven, static_cast<uint64_t>(distance));
|
||||
UpdatePlayerStatistic(DistanceDriven, (uint64_t)distance);
|
||||
} else {
|
||||
UpdatePlayerStatistic(MetersTraveled, static_cast<uint64_t>(distance));
|
||||
UpdatePlayerStatistic(MetersTraveled, (uint64_t)distance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,45 +667,45 @@ void CharacterComponent::InitializeEmptyStatistics() {
|
||||
|
||||
std::string CharacterComponent::StatisticsToString() const {
|
||||
std::stringstream result;
|
||||
result
|
||||
<< m_CurrencyCollected << ';'
|
||||
<< m_BricksCollected << ';'
|
||||
<< m_SmashablesSmashed << ';'
|
||||
<< m_QuickBuildsCompleted << ';'
|
||||
<< m_EnemiesSmashed << ';'
|
||||
<< m_RocketsUsed << ';'
|
||||
<< m_MissionsCompleted << ';'
|
||||
<< m_PetsTamed << ';'
|
||||
<< m_ImaginationPowerUpsCollected << ';'
|
||||
<< m_LifePowerUpsCollected << ';'
|
||||
<< m_ArmorPowerUpsCollected << ';'
|
||||
<< m_MetersTraveled << ';'
|
||||
<< m_TimesSmashed << ';'
|
||||
<< m_TotalDamageTaken << ';'
|
||||
<< m_TotalDamageHealed << ';'
|
||||
<< m_TotalArmorRepaired << ';'
|
||||
<< m_TotalImaginationRestored << ';'
|
||||
<< m_TotalImaginationUsed << ';'
|
||||
<< m_DistanceDriven << ';'
|
||||
<< m_TimeAirborneInCar << ';'
|
||||
<< m_RacingImaginationPowerUpsCollected << ';'
|
||||
<< m_RacingImaginationCratesSmashed << ';'
|
||||
<< m_RacingCarBoostsActivated << ';'
|
||||
<< m_RacingTimesWrecked << ';'
|
||||
<< m_RacingSmashablesSmashed << ';'
|
||||
<< m_RacesFinished << ';'
|
||||
<< m_FirstPlaceRaceFinishes << ';';
|
||||
result << std::to_string(m_CurrencyCollected) << ';'
|
||||
<< std::to_string(m_BricksCollected) << ';'
|
||||
<< std::to_string(m_SmashablesSmashed) << ';'
|
||||
<< std::to_string(m_QuickBuildsCompleted) << ';'
|
||||
<< std::to_string(m_EnemiesSmashed) << ';'
|
||||
<< std::to_string(m_RocketsUsed) << ';'
|
||||
<< std::to_string(m_MissionsCompleted) << ';'
|
||||
<< std::to_string(m_PetsTamed) << ';'
|
||||
<< std::to_string(m_ImaginationPowerUpsCollected) << ';'
|
||||
<< std::to_string(m_LifePowerUpsCollected) << ';'
|
||||
<< std::to_string(m_ArmorPowerUpsCollected) << ';'
|
||||
<< std::to_string(m_MetersTraveled) << ';'
|
||||
<< std::to_string(m_TimesSmashed) << ';'
|
||||
<< std::to_string(m_TotalDamageTaken) << ';'
|
||||
<< std::to_string(m_TotalDamageHealed) << ';'
|
||||
<< std::to_string(m_TotalArmorRepaired) << ';'
|
||||
<< std::to_string(m_TotalImaginationRestored) << ';'
|
||||
<< std::to_string(m_TotalImaginationUsed) << ';'
|
||||
<< std::to_string(m_DistanceDriven) << ';'
|
||||
<< std::to_string(m_TimeAirborneInCar) << ';'
|
||||
<< std::to_string(m_RacingImaginationPowerUpsCollected) << ';'
|
||||
<< std::to_string(m_RacingImaginationCratesSmashed) << ';'
|
||||
<< std::to_string(m_RacingCarBoostsActivated) << ';'
|
||||
<< std::to_string(m_RacingTimesWrecked) << ';'
|
||||
<< std::to_string(m_RacingSmashablesSmashed) << ';'
|
||||
<< std::to_string(m_RacesFinished) << ';'
|
||||
<< std::to_string(m_FirstPlaceRaceFinishes) << ';';
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
uint64_t CharacterComponent::GetStatisticFromSplit(std::vector<std::string> split, uint32_t index) {
|
||||
return index < split.size() ? std::stoul(split.at(index)) : 0;
|
||||
return split.size() > index ? std::stoul(split.at(index)) : 0;
|
||||
}
|
||||
|
||||
ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) {
|
||||
auto stats = m_ZoneStatistics.find(mapID);
|
||||
if (stats == m_ZoneStatistics.end()) m_ZoneStatistics.insert({ mapID, {0, 0, 0, 0, 0 } });
|
||||
if (stats == m_ZoneStatistics.end())
|
||||
m_ZoneStatistics.insert({ mapID, {0, 0, 0, 0, 0 } });
|
||||
return m_ZoneStatistics.at(mapID);
|
||||
}
|
||||
|
||||
@@ -725,8 +732,8 @@ void CharacterComponent::RemoveVentureVisionEffect(std::string ventureVisionType
|
||||
}
|
||||
|
||||
void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const {
|
||||
if (!m_ParentEntity) return;
|
||||
if (!m_Parent) return;
|
||||
AMFArrayValue arrayToSend;
|
||||
arrayToSend.Insert(ventureVisionType, showFaction);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity ? m_ParentEntity->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", arrayToSend);
|
||||
arrayToSend.InsertValue(ventureVisionType, showFaction ? static_cast<AMFValue*>(new AMFTrueValue()) : static_cast<AMFValue*>(new AMFFalseValue()));
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", &arrayToSend);
|
||||
}
|
||||
|
||||
@@ -60,11 +60,12 @@ enum StatisticID {
|
||||
/**
|
||||
* Represents a character, including their rockets and stats
|
||||
*/
|
||||
class CharacterComponent final : public Component {
|
||||
class CharacterComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
|
||||
|
||||
CharacterComponent(Entity* parent, Character* character);
|
||||
~CharacterComponent() override;
|
||||
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#include "CollectibleComponent.h"
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
void CollectibleComponent::Startup() {
|
||||
m_CollectibleId = GetParentEntity()->GetVarAs<int32_t>(u"collectible_id");
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef __COLLECTIBLECOMPONENT__H__
|
||||
#define __COLLECTIBLECOMPONENT__H__
|
||||
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class CollectibleComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
|
||||
CollectibleComponent(Entity* parent) : Component(parent) { };
|
||||
|
||||
void Startup() override;
|
||||
|
||||
uint32_t GetCollectibleId() const { return m_CollectibleId; }
|
||||
private:
|
||||
uint32_t m_CollectibleId;
|
||||
};
|
||||
|
||||
|
||||
#endif //!__COLLECTIBLECOMPONENT__H__
|
||||
@@ -1,7 +1,30 @@
|
||||
#include "Component.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
Component::Component(Entity* owningEntity) {
|
||||
DluAssert(owningEntity != nullptr);
|
||||
m_ParentEntity = owningEntity;
|
||||
|
||||
Component::Component(Entity* parent) {
|
||||
m_Parent = parent;
|
||||
}
|
||||
|
||||
Component::~Component() {
|
||||
|
||||
}
|
||||
|
||||
Entity* Component::GetParent() const {
|
||||
return m_Parent;
|
||||
}
|
||||
|
||||
void Component::Update(float deltaTime) {
|
||||
|
||||
}
|
||||
|
||||
void Component::OnUse(Entity* originator) {
|
||||
|
||||
}
|
||||
|
||||
void Component::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
}
|
||||
|
||||
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,74 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "tinyxml2.h"
|
||||
#include "../thirdparty/tinyxml2/tinyxml2.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
namespace RakNet {
|
||||
class BitStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component base class, provides methods for game loop updates, usage events and loading and saving to XML.
|
||||
*/
|
||||
class Component {
|
||||
class Component
|
||||
{
|
||||
public:
|
||||
Component(Entity* owningEntity);
|
||||
virtual ~Component() {};
|
||||
Component(Entity* parent);
|
||||
virtual ~Component();
|
||||
|
||||
/**
|
||||
* Gets the owner of this component
|
||||
* @return the owner of this component
|
||||
*/
|
||||
Entity* GetParentEntity() const { return m_ParentEntity; };
|
||||
|
||||
/**
|
||||
* Event called when this component is being used, e.g. when some entity interacted with it
|
||||
* @param originator
|
||||
*/
|
||||
virtual void OnUse(Entity* originator) {};
|
||||
|
||||
/**
|
||||
* Save data from this componennt to character XML
|
||||
* @param doc the document to write data to
|
||||
*/
|
||||
virtual void UpdateXml(tinyxml2::XMLDocument* doc) {};
|
||||
|
||||
/**
|
||||
* Load base data for this component from character XML
|
||||
* @param doc the document to read data from
|
||||
*/
|
||||
virtual void LoadFromXml(tinyxml2::XMLDocument* doc) {};
|
||||
|
||||
/**
|
||||
* Call after you have newed the component to initialize it
|
||||
*/
|
||||
virtual void Startup() {};
|
||||
Entity* GetParent() const;
|
||||
|
||||
/**
|
||||
* Updates the component in the game loop
|
||||
* @param deltaTime time passed since last update
|
||||
*/
|
||||
virtual void Update(float deltaTime) {};
|
||||
virtual void Update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Loads the data of this component from the luz/lvl configuration
|
||||
* Event called when this component is being used, e.g. when some entity interacted with it
|
||||
* @param originator
|
||||
*/
|
||||
virtual void LoadConfigData() {};
|
||||
virtual void OnUse(Entity* originator);
|
||||
|
||||
/**
|
||||
* Loads the data of this component from the cdclient database
|
||||
* Save data from this componennt to character XML
|
||||
* @param doc the document to write data to
|
||||
*/
|
||||
virtual void LoadTemplateData() {};
|
||||
virtual void UpdateXml(tinyxml2::XMLDocument* doc);
|
||||
|
||||
/**
|
||||
* Serializes the component for delivery to the client(s)
|
||||
* Load base data for this component from character XML
|
||||
* @param doc the document to read data from
|
||||
*/
|
||||
virtual void Serialize(RakNet::BitStream* bitStream, bool isConstruction = false) {};
|
||||
virtual void LoadFromXml(tinyxml2::XMLDocument* doc);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The entity that owns this component
|
||||
*/
|
||||
Entity* m_ParentEntity;
|
||||
Entity* m_Parent;
|
||||
};
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
Legend
|
||||
```
|
||||
├── Explicit inheritance
|
||||
├~~ Loaded with Parent
|
||||
├-> SubComponent
|
||||
├-? idk lol, but related
|
||||
|
||||
originalName -> newName
|
||||
|
||||
```
|
||||
|
||||
|
||||
```tree
|
||||
|
||||
LWOActivityComponent
|
||||
├── LWOQuickBuildComponent
|
||||
├── LWOMiniGameControlComponent
|
||||
├── LWOShootingGalleryComponent
|
||||
├── LWOScriptedActivityComponent
|
||||
| └── LWOBaseRacingControlComponent -> RacingControlComponent
|
||||
| ├── LWORacingControlComponent -> RacingComponent
|
||||
| └── LWOGateRushControlComponent -> GateRushComponent
|
||||
LWOBaseCombatAIComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOProximityMonitorComponent
|
||||
LWOProjectilePhysComponent
|
||||
LWOBasePhysComponent
|
||||
├── LWORigidBodyPhantomComponent
|
||||
├── LWOPhantomPhysComponent
|
||||
├── LWOVehiclePhysicsComponent
|
||||
├── LWOSimplePhysComponent
|
||||
| ├~~ LWOPhantomPhysicsComponent
|
||||
├── LWOPhysicsSystemComponent
|
||||
├── LWOHavokVehiclePhysicsComponent
|
||||
├── LWOControllablePhysComponent
|
||||
LWOBaseRenderComponent
|
||||
├── LWOSkinnedRenderComponent
|
||||
├~~ LWOSkinnedRenderComponent
|
||||
├~~ LWOFXComponent
|
||||
LWOBaseVendorComponent
|
||||
├── LWOVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
├── LWODonationVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
├── LWOAchievementVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
LWOBBBComponent_Common
|
||||
├── LWOBBBComponent_Client
|
||||
LWOBlueprintComponent
|
||||
LWOBouncerComponent
|
||||
LWOBuildControllerComponentCommon
|
||||
├── LWOBuildControllerComponent
|
||||
LWOChangelingBuildComponent
|
||||
LWOCharacterComponent
|
||||
├── LWOMinifigComponent
|
||||
├~~ LWOPossessionControlComponent
|
||||
├~~ LWOMountControlComponent
|
||||
├~~ LWOPetCreatorComponent
|
||||
├~~ LWOLevelProgressionComponent
|
||||
├~~ LWOPlayerForcedMovementComponent
|
||||
| ├~? LWOPathfindingControlComponent
|
||||
LWOChestComponent
|
||||
LWOChoiceBuildComponent
|
||||
LWOCollectibleComponent
|
||||
LWOCustomBuildAssemblyComponent
|
||||
LWODestroyableComponent
|
||||
├~~ LWOBuffComponent
|
||||
├~~ LWOStatusEffectComponent
|
||||
LWODropEffectComponent
|
||||
LWODroppedLootComponent
|
||||
LWOExhibitComponent
|
||||
LWOGenericActivatorComponent
|
||||
LWOGhostComponent
|
||||
LWOHFLightDirectionGadgetComponent
|
||||
LWOInventoryComponent_Common
|
||||
├── LWOInventoryComponent_Client
|
||||
| └── LWOInventoryComponent_EquippedItem
|
||||
LWOItemComponent
|
||||
LWOLUPExhibitComponent
|
||||
LWOMissionOfferComponent
|
||||
LWOModelBehaviorComponent
|
||||
├~~ LWOSimplePhysComponent
|
||||
├~~ LWOControllablePhysComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOMutableModelBehaviorComponent
|
||||
LWOModelBuilderComponent
|
||||
LWOModularBuildComponentCommon
|
||||
├── LWOModularBuildComponent
|
||||
LWOModuleAssemblyComponent
|
||||
├── LWOModuleAssemblyComponentCommon
|
||||
LWOModuleComponentCommon
|
||||
├── LWOModuleComponent
|
||||
LWOMovementAIComponent
|
||||
LWOMultiZoneEntranceComponent
|
||||
LWOOverheadIconComponent
|
||||
LWOPetComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~? LWOItemComponent
|
||||
├~? LWOModelBehaviorComponent
|
||||
| ├~~ ...
|
||||
LWOPlatformBoundaryComponent
|
||||
LWOPlatformComponent
|
||||
├-> LWOMoverPlatformSubComponent
|
||||
├-> LWOSimpleMoverPlatformSubComponent
|
||||
├-> LWORotaterPlatformSubComponent
|
||||
LWOPropertyComponent
|
||||
LWOPropertyEntranceComponent
|
||||
LWOPropertyManagementComponent
|
||||
LWOPropertyVendorComponent
|
||||
LWOProximityMonitorComponent
|
||||
LWOSoundTriggerComponent
|
||||
├── LWORacingSoundTriggerComponent
|
||||
LWORacingStatsComponentCommon
|
||||
├── LWORacingStatsComponent
|
||||
LWORocketAnimationControlComponentCommon
|
||||
├── LWORocketAnimationControlComponent
|
||||
LWORocketLaunchpadControlComponentCommon
|
||||
├── LWORocketLaunchpadControlComponent
|
||||
LWOScriptComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOProximityMonitorComponent
|
||||
LWOShowcaseModelHandlerComponent
|
||||
LWOSkillComponent
|
||||
LWOSoundAmbient2DComponent
|
||||
LWOSoundAmbient3DComponent
|
||||
LWOSoundRepeaterComponent
|
||||
LWOSpawnComponent
|
||||
LWOSpringpadComponent
|
||||
LWOSwitchComponent
|
||||
LWOTriggerComponent
|
||||
LocalPlayer - This is a function call in the client which, if the generated Entity is a player, the below components are added. This **must** be done for all players.
|
||||
├~~ LWOInteractionManagerComponent
|
||||
├~~ LWOUserControlComponent
|
||||
├~~ LWOFriendsListComponent
|
||||
├~~ LWOIgnoreListComponent
|
||||
├~~ LWOTextEffectComponent
|
||||
├~~ LWOChatBubbleComponent
|
||||
├~~ LWOGuildComponent
|
||||
├~~ LWOPlayerPetTamingComponent
|
||||
├~~ LWOLocalSystemsComponent
|
||||
├~~ LWOSlashCommandComponent
|
||||
├~~ LWOMissionComponent
|
||||
├~~ LWOPropertyEditorComponent
|
||||
├~~ LWOComponent115
|
||||
├~~ LWOTeamsComponent
|
||||
├~~ LWOChatComponent
|
||||
├~~ LWOPetControlComponent
|
||||
├~~ LWOTradeComponent
|
||||
├~~ LWOPreconditionComponent
|
||||
├~~ LWOFlagComponent
|
||||
├~~ LWOFactionTriggerComponent
|
||||
|
||||
```
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "BitStream.h"
|
||||
#include "dLogger.h"
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
|
||||
#include "dpWorld.h"
|
||||
#include "dpEntity.h"
|
||||
@@ -52,30 +51,34 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
|
||||
m_ImmuneToStunTurnCount = 0;
|
||||
m_ImmuneToStunUseItemCount = 0;
|
||||
|
||||
// Other physics entities we care about will be added by BaseCombatAI
|
||||
if (entity->GetLOT() != 1) return;
|
||||
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
|
||||
return;
|
||||
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
|
||||
if (entity->GetLOT() == 1) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
|
||||
|
||||
float radius = 1.5f;
|
||||
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), radius, false);
|
||||
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
float radius = 1.5f;
|
||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), radius, false);
|
||||
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
}
|
||||
}
|
||||
|
||||
ControllablePhysicsComponent::~ControllablePhysicsComponent() {
|
||||
if (!m_dpEntity) return;
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
if (m_dpEntity) {
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
}
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::Startup() {
|
||||
NiPoint3 pos = m_ParentEntity->GetDefaultPosition();
|
||||
NiQuaternion rot = m_ParentEntity->GetDefaultRotation();
|
||||
SetPosition(pos);
|
||||
SetRotation(rot);
|
||||
void ControllablePhysicsComponent::Update(float deltaTime) {
|
||||
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
//If this is a creation, then we assume the position is dirty, even when it isn't.
|
||||
//This is because new clients will still need to receive the position.
|
||||
//if (bIsInitialUpdate) m_DirtyPosition = true;
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write(m_InJetpackMode);
|
||||
if (m_InJetpackMode) {
|
||||
@@ -96,32 +99,33 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
|
||||
if (m_IgnoreMultipliers) m_DirtyCheats = false;
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyCheats);
|
||||
if (bIsInitialUpdate || m_DirtyCheats) {
|
||||
outBitStream->Write(m_DirtyCheats);
|
||||
if (m_DirtyCheats) {
|
||||
outBitStream->Write(m_GravityScale);
|
||||
outBitStream->Write(m_SpeedMultiplier);
|
||||
if (!bIsInitialUpdate) m_DirtyCheats = false;
|
||||
|
||||
m_DirtyCheats = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyEquippedItemInfo);
|
||||
if (bIsInitialUpdate || m_DirtyEquippedItemInfo) {
|
||||
outBitStream->Write(m_DirtyEquippedItemInfo);
|
||||
if (m_DirtyEquippedItemInfo) {
|
||||
outBitStream->Write(m_PickupRadius);
|
||||
outBitStream->Write(m_InJetpackMode);
|
||||
if (!bIsInitialUpdate) m_DirtyEquippedItemInfo = false;
|
||||
m_DirtyEquippedItemInfo = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyBubble);
|
||||
if (bIsInitialUpdate || m_DirtyBubble) {
|
||||
outBitStream->Write(m_DirtyBubble);
|
||||
if (m_DirtyBubble) {
|
||||
outBitStream->Write(m_IsInBubble);
|
||||
if (m_IsInBubble) {
|
||||
outBitStream->Write(m_BubbleType);
|
||||
outBitStream->Write(m_SpecialAnims);
|
||||
}
|
||||
if (!bIsInitialUpdate) m_DirtyBubble = false;
|
||||
m_DirtyBubble = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
|
||||
if (bIsInitialUpdate || m_DirtyPosition) {
|
||||
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate);
|
||||
if (m_DirtyPosition || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_Position.x);
|
||||
outBitStream->Write(m_Position.y);
|
||||
outBitStream->Write(m_Position.z);
|
||||
@@ -134,22 +138,20 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
outBitStream->Write(m_IsOnGround);
|
||||
outBitStream->Write(m_IsOnRail);
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity);
|
||||
if (bIsInitialUpdate || m_DirtyVelocity) {
|
||||
outBitStream->Write(m_DirtyVelocity);
|
||||
if (m_DirtyVelocity) {
|
||||
outBitStream->Write(m_Velocity.x);
|
||||
outBitStream->Write(m_Velocity.y);
|
||||
outBitStream->Write(m_Velocity.z);
|
||||
if (!bIsInitialUpdate) m_DirtyVelocity = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity);
|
||||
if (bIsInitialUpdate || m_DirtyAngularVelocity) {
|
||||
outBitStream->Write(m_DirtyAngularVelocity);
|
||||
if (m_DirtyAngularVelocity) {
|
||||
outBitStream->Write(m_AngularVelocity.x);
|
||||
outBitStream->Write(m_AngularVelocity.y);
|
||||
outBitStream->Write(m_AngularVelocity.z);
|
||||
if (!bIsInitialUpdate) m_DirtyAngularVelocity = false;
|
||||
}
|
||||
if (!bIsInitialUpdate) m_DirtyPosition = false;
|
||||
|
||||
outBitStream->Write0();
|
||||
}
|
||||
|
||||
@@ -160,45 +162,22 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* characterElem = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!characterElem) {
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_ParentEntity->GetCharacter()->LoadXmlRespawnCheckpoints();
|
||||
m_Parent->GetCharacter()->LoadXmlRespawnCheckpoints();
|
||||
|
||||
characterElem->QueryAttribute("lzx", &m_Position.x);
|
||||
characterElem->QueryAttribute("lzy", &m_Position.y);
|
||||
characterElem->QueryAttribute("lzz", &m_Position.z);
|
||||
characterElem->QueryAttribute("lzrx", &m_Rotation.x);
|
||||
characterElem->QueryAttribute("lzry", &m_Rotation.y);
|
||||
characterElem->QueryAttribute("lzrz", &m_Rotation.z);
|
||||
characterElem->QueryAttribute("lzrw", &m_Rotation.w);
|
||||
character->QueryAttribute("lzx", &m_Position.x);
|
||||
character->QueryAttribute("lzy", &m_Position.y);
|
||||
character->QueryAttribute("lzz", &m_Position.z);
|
||||
character->QueryAttribute("lzrx", &m_Rotation.x);
|
||||
character->QueryAttribute("lzry", &m_Rotation.y);
|
||||
character->QueryAttribute("lzrz", &m_Rotation.z);
|
||||
character->QueryAttribute("lzrw", &m_Rotation.w);
|
||||
|
||||
auto* character = GetParentEntity()->GetCharacter();
|
||||
const auto mapID = Game::server->GetZoneID();
|
||||
|
||||
//If we came from another zone, put us in the starting loc
|
||||
if (character->GetZoneID() != Game::server->GetZoneID() || mapID == 1603) { // Exception for Moon Base as you tend to spawn on the roof.
|
||||
const auto& targetSceneName = character->GetTargetScene();
|
||||
auto* targetScene = EntityManager::Instance()->GetSpawnPointEntity(targetSceneName);
|
||||
|
||||
NiPoint3 pos;
|
||||
NiQuaternion rot;
|
||||
if (character->HasBeenToWorld(mapID) && targetSceneName.empty()) {
|
||||
pos = character->GetRespawnPoint(mapID);
|
||||
rot = dZoneManager::Instance()->GetZone()->GetSpawnRot();
|
||||
} else if (targetScene) {
|
||||
pos = targetScene->GetPosition();
|
||||
rot = targetScene->GetRotation();
|
||||
} else {
|
||||
pos = dZoneManager::Instance()->GetZone()->GetSpawnPos();
|
||||
rot = dZoneManager::Instance()->GetZone()->GetSpawnRot();
|
||||
}
|
||||
SetPosition(pos);
|
||||
SetRotation(rot);
|
||||
}
|
||||
m_DirtyPosition = true;
|
||||
}
|
||||
|
||||
@@ -229,7 +208,9 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
|
||||
if (m_Static || pos == m_Position) return;
|
||||
if (m_Static) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Position.x = pos.x;
|
||||
m_Position.y = pos.y;
|
||||
@@ -240,7 +221,9 @@ void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
|
||||
if (m_Static || rot == m_Rotation) return;
|
||||
if (m_Static) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Rotation = rot;
|
||||
m_DirtyPosition = true;
|
||||
@@ -249,7 +232,9 @@ void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
|
||||
if (m_Static || m_Velocity == vel) return;
|
||||
if (m_Static) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Velocity = vel;
|
||||
m_DirtyPosition = true;
|
||||
@@ -259,7 +244,9 @@ void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
|
||||
if (m_Static || vel == m_AngularVelocity) return;
|
||||
if (m_Static) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_AngularVelocity = vel;
|
||||
m_DirtyPosition = true;
|
||||
@@ -267,15 +254,25 @@ void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetIsOnGround(bool val) {
|
||||
if (m_IsOnGround == val) return;
|
||||
m_IsOnGround = val;
|
||||
m_DirtyPosition = true;
|
||||
m_IsOnGround = val;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetIsOnRail(bool val) {
|
||||
if (m_IsOnRail == val) return;
|
||||
m_IsOnRail = val;
|
||||
m_DirtyPosition = true;
|
||||
m_IsOnRail = val;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetDirtyPosition(bool val) {
|
||||
m_DirtyPosition = val;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
|
||||
m_DirtyVelocity = val;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
|
||||
m_DirtyAngularVelocity = val;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
|
||||
@@ -300,10 +297,10 @@ void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
|
||||
m_PickupRadius = 0.0f;
|
||||
m_DirtyEquippedItemInfo = true;
|
||||
for (uint32_t i = 0; i < m_ActivePickupRadiusScales.size(); i++) {
|
||||
auto candidateRadius = m_ActivePickupRadiusScales.at(i);
|
||||
auto candidateRadius = m_ActivePickupRadiusScales[i];
|
||||
if (m_PickupRadius < candidateRadius) m_PickupRadius = candidateRadius;
|
||||
}
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::AddSpeedboost(float value) {
|
||||
@@ -322,33 +319,33 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
|
||||
}
|
||||
|
||||
// Recalculate speedboost since we removed one
|
||||
m_SpeedBoost = 500.0f;
|
||||
m_SpeedBoost = 0.0f;
|
||||
if (m_ActiveSpeedBoosts.empty()) { // no active speed boosts left, so return to base speed
|
||||
auto* levelProgressionComponent = m_ParentEntity->GetComponent<LevelProgressionComponent>();
|
||||
auto* levelProgressionComponent = m_Parent->GetComponent<LevelProgressionComponent>();
|
||||
if (levelProgressionComponent) m_SpeedBoost = levelProgressionComponent->GetSpeedBase();
|
||||
} else { // Used the last applied speedboost
|
||||
m_SpeedBoost = m_ActiveSpeedBoosts.back();
|
||||
}
|
||||
SetSpeedMultiplier(m_SpeedBoost / 500.0f); // 500 being the base speed
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims) {
|
||||
void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims){
|
||||
if (m_IsInBubble) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "%llu is already in bubble", m_ParentEntity->GetObjectID());
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Already in bubble");
|
||||
return;
|
||||
}
|
||||
m_BubbleType = bubbleType;
|
||||
m_IsInBubble = true;
|
||||
m_DirtyBubble = true;
|
||||
m_SpecialAnims = specialAnims;
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::DeactivateBubbleBuff() {
|
||||
void ControllablePhysicsComponent::DeactivateBubbleBuff(){
|
||||
m_DirtyBubble = true;
|
||||
m_IsInBubble = false;
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
};
|
||||
|
||||
void ControllablePhysicsComponent::SetStunImmunity(
|
||||
@@ -360,9 +357,9 @@ void ControllablePhysicsComponent::SetStunImmunity(
|
||||
const bool bImmuneToStunJump,
|
||||
const bool bImmuneToStunMove,
|
||||
const bool bImmuneToStunTurn,
|
||||
const bool bImmuneToStunUseItem) {
|
||||
const bool bImmuneToStunUseItem){
|
||||
|
||||
if (state == eStateChangeType::POP) {
|
||||
if (state == eStateChangeType::POP){
|
||||
if (bImmuneToStunAttack && m_ImmuneToStunAttackCount > 0) m_ImmuneToStunAttackCount -= 1;
|
||||
if (bImmuneToStunEquip && m_ImmuneToStunEquipCount > 0) m_ImmuneToStunEquipCount -= 1;
|
||||
if (bImmuneToStunInteract && m_ImmuneToStunInteractCount > 0) m_ImmuneToStunInteractCount -= 1;
|
||||
@@ -381,7 +378,7 @@ void ControllablePhysicsComponent::SetStunImmunity(
|
||||
}
|
||||
|
||||
GameMessages::SendSetStunImmunity(
|
||||
m_ParentEntity->GetObjectID(), state, m_ParentEntity->GetSystemAddress(), originator,
|
||||
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(), originator,
|
||||
bImmuneToStunAttack,
|
||||
bImmuneToStunEquip,
|
||||
bImmuneToStunInteract,
|
||||
|
||||
@@ -19,18 +19,18 @@ enum class eStateChangeType : uint32_t;
|
||||
/**
|
||||
* Handles the movement of controllable Entities, e.g. enemies and players
|
||||
*/
|
||||
class ControllablePhysicsComponent final : public Component {
|
||||
class ControllablePhysicsComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
|
||||
ControllablePhysicsComponent(Entity* entity);
|
||||
~ControllablePhysicsComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void ResetFlags();
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
void Startup() override;
|
||||
|
||||
/**
|
||||
* Sets the position of this entity, also ensures this update is serialized next tick.
|
||||
@@ -115,19 +115,19 @@ public:
|
||||
* Mark the position as dirty, forcing a serialization update next tick
|
||||
* @param val whether or not the position is dirty
|
||||
*/
|
||||
void SetDirtyPosition(bool val) { m_DirtyPosition = val; }
|
||||
void SetDirtyPosition(bool val);
|
||||
|
||||
/**
|
||||
* Mark the velocity as dirty, forcing a serializtion update next tick
|
||||
* @param val whether or not the velocity is dirty
|
||||
*/
|
||||
void SetDirtyVelocity(bool val) { m_DirtyVelocity = val; }
|
||||
void SetDirtyVelocity(bool val);
|
||||
|
||||
/**
|
||||
* Mark the angular velocity as dirty, forcing a serialization update next tick
|
||||
* @param val whether or not the angular velocity is dirty
|
||||
*/
|
||||
void SetDirtyAngularVelocity(bool val) { m_DirtyAngularVelocity = val; }
|
||||
void SetDirtyAngularVelocity(bool val);
|
||||
|
||||
/**
|
||||
* Sets whether or not the entity is currently wearing a jetpack
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "AmfSerialize.h"
|
||||
#include "AMFFormat.h"
|
||||
#include "AMFFormat_BitStream.h"
|
||||
#include "GameMessages.h"
|
||||
#include "User.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "CDDestructibleComponentTable.h"
|
||||
#include "EntityManager.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "CppScripts.h"
|
||||
#include "Loot.h"
|
||||
#include "Character.h"
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "MissionComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "PossessionComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "WorldConfig.h"
|
||||
@@ -37,9 +37,8 @@
|
||||
#include "eGameActivity.h"
|
||||
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDCurrencyTableTable.h"
|
||||
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_iArmor = 0;
|
||||
m_fMaxArmor = 0.0f;
|
||||
m_iImagination = 0;
|
||||
@@ -52,7 +51,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent, int32_t componentId)
|
||||
m_IsGMImmune = false;
|
||||
m_IsShielded = false;
|
||||
m_DamageToAbsorb = 0;
|
||||
m_IsModuleAssembly = m_ParentEntity->HasComponent(eReplicaComponentType::MODULE_ASSEMBLY);
|
||||
m_HasBricks = false;
|
||||
m_DirtyThreatList = false;
|
||||
m_HasThreats = false;
|
||||
m_ExplodeFactor = 1.0f;
|
||||
@@ -63,7 +62,6 @@ DestroyableComponent::DestroyableComponent(Entity* parent, int32_t componentId)
|
||||
m_MinCoins = 0;
|
||||
m_MaxCoins = 0;
|
||||
m_DamageReduction = 0;
|
||||
m_ComponentId = componentId;
|
||||
|
||||
m_ImmuneToBasicAttackCount = 0;
|
||||
m_ImmuneToDamageOverTimeCount = 0;
|
||||
@@ -75,71 +73,49 @@ DestroyableComponent::DestroyableComponent(Entity* parent, int32_t componentId)
|
||||
m_ImmuneToQuickbuildInterruptCount = 0;
|
||||
m_ImmuneToPullToPointCount = 0;
|
||||
}
|
||||
void DestroyableComponent::Startup() {
|
||||
|
||||
DestroyableComponent::~DestroyableComponent() {
|
||||
}
|
||||
void DestroyableComponent::LoadConfigData() {
|
||||
SetIsSmashable(GetIsSmashable() | (m_ParentEntity->GetVarAs<int32_t>(u"is_smashable") != 0));
|
||||
}
|
||||
void DestroyableComponent::LoadTemplateData() {
|
||||
if (m_ParentEntity->IsPlayer()) return;
|
||||
auto* destroyableComponentTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
|
||||
auto destroyableDataLookup = destroyableComponentTable->Query([this](CDDestructibleComponent entry) { return (entry.id == this->m_ComponentId); });
|
||||
if (m_ComponentId == -1 || destroyableDataLookup.empty()) {
|
||||
|
||||
void DestroyableComponent::Reinitialize(LOT templateID) {
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
|
||||
int32_t buffComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::BUFF);
|
||||
int32_t collectibleComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::COLLECTIBLE);
|
||||
int32_t rebuildComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::QUICK_BUILD);
|
||||
|
||||
int32_t componentID = 0;
|
||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
||||
if (rebuildComponentID > 0) componentID = rebuildComponentID;
|
||||
if (buffComponentID > 0) componentID = buffComponentID;
|
||||
|
||||
CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
if (componentID > 0) {
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
if (destCompData.size() > 0) {
|
||||
SetHealth(destCompData[0].life);
|
||||
SetImagination(destCompData[0].imagination);
|
||||
SetArmor(destCompData[0].armor);
|
||||
|
||||
SetMaxHealth(destCompData[0].life);
|
||||
SetMaxImagination(destCompData[0].imagination);
|
||||
SetMaxArmor(destCompData[0].armor);
|
||||
|
||||
SetIsSmashable(destCompData[0].isSmashable);
|
||||
}
|
||||
} else {
|
||||
SetHealth(1);
|
||||
SetImagination(0);
|
||||
SetArmor(0);
|
||||
|
||||
SetMaxHealth(1);
|
||||
SetMaxImagination(0);
|
||||
SetMaxArmor(0);
|
||||
|
||||
SetIsSmashable(true);
|
||||
AddFaction(-1);
|
||||
AddFaction(6); //Smashables
|
||||
|
||||
// A race car has 60 imagination, other entities defaults to 0.
|
||||
SetImagination(m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS) ? 60 : 0);
|
||||
SetMaxImagination(m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS) ? 60 : 0);
|
||||
return;
|
||||
}
|
||||
|
||||
auto destroyableData = destroyableDataLookup.at(0);
|
||||
if (m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS)) {
|
||||
destroyableData.imagination = 60;
|
||||
}
|
||||
|
||||
SetHealth(destroyableData.life);
|
||||
SetImagination(destroyableData.imagination);
|
||||
SetArmor(destroyableData.armor);
|
||||
|
||||
SetMaxHealth(destroyableData.life);
|
||||
SetMaxImagination(destroyableData.imagination);
|
||||
SetMaxArmor(destroyableData.armor);
|
||||
|
||||
SetIsSmashable(destroyableData.isSmashable);
|
||||
|
||||
SetLootMatrixID(destroyableData.LootMatrixIndex);
|
||||
|
||||
// Now get currency information
|
||||
uint32_t npcMinLevel = destroyableData.level;
|
||||
uint32_t currencyIndex = destroyableData.CurrencyIndex;
|
||||
|
||||
auto* currencyTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
|
||||
auto currencyValues = currencyTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == currencyIndex && entry.npcminlevel == npcMinLevel); });
|
||||
|
||||
if (currencyValues.size() > 0) {
|
||||
// Set the coins
|
||||
SetMinCoins(currencyValues.at(0).minvalue);
|
||||
SetMaxCoins(currencyValues.at(0).maxvalue);
|
||||
}
|
||||
AddFaction(destroyableData.faction);
|
||||
std::stringstream ss(destroyableData.factionList);
|
||||
std::string tokenStr;
|
||||
|
||||
while (std::getline(ss, tokenStr, ',')) {
|
||||
int32_t factionToAdd = -2;
|
||||
if (!GeneralUtils::TryParse(tokenStr, factionToAdd) || factionToAdd == -2 || factionToAdd == destroyableData.faction) continue;
|
||||
|
||||
AddFaction(factionToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +163,7 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
||||
outBitStream->Write(m_IsSmashed);
|
||||
|
||||
if (m_IsSmashable) {
|
||||
outBitStream->Write(m_IsModuleAssembly);
|
||||
outBitStream->Write(m_HasBricks);
|
||||
outBitStream->Write(m_ExplodeFactor != 1.0f);
|
||||
if (m_ExplodeFactor != 1.0f) outBitStream->Write(m_ExplodeFactor);
|
||||
}
|
||||
@@ -209,7 +185,7 @@ void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buffComponent = m_ParentEntity->GetComponent<BuffComponent>();
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->LoadFromXml(doc);
|
||||
@@ -231,7 +207,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buffComponent = m_ParentEntity->GetComponent<BuffComponent>();
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->UpdateXml(doc);
|
||||
@@ -248,7 +224,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
void DestroyableComponent::SetHealth(int32_t value) {
|
||||
m_DirtyHealth = true;
|
||||
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackHealthDelta(value - m_iHealth);
|
||||
}
|
||||
@@ -268,16 +244,20 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_ParentEntity->GetParentUser()) return;
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
AMFStringValue* amount = new AMFStringValue();
|
||||
amount->SetStringValue(std::to_string(difference));
|
||||
AMFStringValue* type = new AMFStringValue();
|
||||
type->SetStringValue("health");
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(difference));
|
||||
args.Insert("type", "health");
|
||||
args.InsertValue("amount", amount);
|
||||
args.InsertValue("type", type);
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetArmor(int32_t value) {
|
||||
@@ -286,14 +266,14 @@ void DestroyableComponent::SetArmor(int32_t value) {
|
||||
// If Destroyable Component already has zero armor do not trigger the passive ability again.
|
||||
bool hadArmor = m_iArmor > 0;
|
||||
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackArmorDelta(value - m_iArmor);
|
||||
}
|
||||
|
||||
m_iArmor = value;
|
||||
|
||||
auto* inventroyComponent = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
if (m_iArmor == 0 && inventroyComponent != nullptr && hadArmor) {
|
||||
inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::SentinelArmor);
|
||||
}
|
||||
@@ -309,29 +289,33 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_ParentEntity->GetParentUser()) return;
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
AMFStringValue* amount = new AMFStringValue();
|
||||
amount->SetStringValue(std::to_string(value));
|
||||
AMFStringValue* type = new AMFStringValue();
|
||||
type->SetStringValue("armor");
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(value));
|
||||
args.Insert("type", "armor");
|
||||
args.InsertValue("amount", amount);
|
||||
args.InsertValue("type", type);
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetImagination(int32_t value) {
|
||||
m_DirtyHealth = true;
|
||||
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackImaginationDelta(value - m_iImagination);
|
||||
}
|
||||
|
||||
m_iImagination = value;
|
||||
|
||||
auto* inventroyComponent = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
if (m_iImagination == 0 && inventroyComponent != nullptr) {
|
||||
inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::AssemblyImagination);
|
||||
}
|
||||
@@ -349,15 +333,19 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_ParentEntity->GetParentUser()) return;
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
AMFStringValue* amount = new AMFStringValue();
|
||||
amount->SetStringValue(std::to_string(difference));
|
||||
AMFStringValue* type = new AMFStringValue();
|
||||
type->SetStringValue("imagination");
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(difference));
|
||||
args.Insert("type", "imagination");
|
||||
args.InsertValue("amount", amount);
|
||||
args.InsertValue("type", type);
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
|
||||
}
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetDamageToAbsorb(int32_t value) {
|
||||
@@ -479,8 +467,8 @@ bool DestroyableComponent::IsImmune() const {
|
||||
}
|
||||
|
||||
bool DestroyableComponent::IsKnockbackImmune() const {
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
auto* inventoryComponent = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
|
||||
if (characterComponent != nullptr && inventoryComponent != nullptr && characterComponent->GetCurrentActivity() == eGameActivity::QUICKBUILDING) {
|
||||
const auto hasPassive = inventoryComponent->HasAnyPassive({
|
||||
@@ -523,7 +511,7 @@ bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignor
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* targetQuickbuild = targetEntity->GetComponent<QuickBuildComponent>();
|
||||
auto* targetQuickbuild = targetEntity->GetComponent<RebuildComponent>();
|
||||
|
||||
if (targetQuickbuild != nullptr) {
|
||||
const auto state = targetQuickbuild->GetState();
|
||||
@@ -556,7 +544,7 @@ void DestroyableComponent::Heal(const uint32_t health) {
|
||||
|
||||
SetHealth(current);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
|
||||
@@ -574,7 +562,7 @@ void DestroyableComponent::Imagine(const int32_t deltaImagination) {
|
||||
|
||||
SetImagination(current);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
|
||||
@@ -588,7 +576,7 @@ void DestroyableComponent::Repair(const uint32_t armor) {
|
||||
|
||||
SetArmor(current);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
|
||||
@@ -640,13 +628,13 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
SetIsShielded(absorb > 0);
|
||||
|
||||
// Dismount on the possessable hit
|
||||
auto* possessable = m_ParentEntity->GetComponent<PossessableComponent>();
|
||||
auto possessable = m_Parent->GetComponent<PossessableComponent>();
|
||||
if (possessable && possessable->GetDepossessOnHit()) {
|
||||
possessable->Dismount();
|
||||
}
|
||||
|
||||
// Dismount on the possessor hit
|
||||
auto* possessor = m_ParentEntity->GetComponent<PossessionComponent>();
|
||||
auto possessor = m_Parent->GetComponent<PossessorComponent>();
|
||||
if (possessor) {
|
||||
auto possessableId = possessor->GetPossessable();
|
||||
if (possessableId != LWOOBJID_EMPTY) {
|
||||
@@ -657,17 +645,17 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ParentEntity->GetLOT() != 1) {
|
||||
if (m_Parent->GetLOT() != 1) {
|
||||
echo = true;
|
||||
}
|
||||
|
||||
if (echo) {
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
auto* attacker = EntityManager::Instance()->GetEntity(source);
|
||||
m_ParentEntity->OnHit(attacker);
|
||||
m_ParentEntity->OnHitOrHealResult(attacker, sourceDamage);
|
||||
m_Parent->OnHit(attacker);
|
||||
m_Parent->OnHitOrHealResult(attacker, sourceDamage);
|
||||
NotifySubscribers(attacker, sourceDamage);
|
||||
|
||||
for (const auto& cb : m_OnHitCallbacks) {
|
||||
@@ -675,7 +663,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
}
|
||||
|
||||
if (health != 0) {
|
||||
auto* combatComponent = m_ParentEntity->GetComponent<BaseCombatAIComponent>();
|
||||
auto* combatComponent = m_Parent->GetComponent<BaseCombatAIComponent>();
|
||||
|
||||
if (combatComponent != nullptr) {
|
||||
combatComponent->Taunt(source, sourceDamage * 10); // * 10 is arbatrary
|
||||
@@ -685,39 +673,33 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
}
|
||||
|
||||
//check if hardcore mode is enabled
|
||||
if (EntityManager::Instance()->GetHardcoreMode()) {
|
||||
if (EntityManager::Instance()->GetHardcoreMode()) {
|
||||
DoHardcoreModeDrops(source);
|
||||
}
|
||||
}
|
||||
|
||||
Smash(source, eKillType::VIOLENT, u"", skillID);
|
||||
}
|
||||
|
||||
void DestroyableComponent::Subscribe(CppScripts::Script* scriptToAdd) {
|
||||
auto foundScript = std::find(m_SubscribedScripts.begin(), m_SubscribedScripts.end(), scriptToAdd);
|
||||
if (foundScript != m_SubscribedScripts.end()) {
|
||||
Game::logger->LogDebug("DestroyableComponent", "WARNING: Tried to add a script for Entity %llu but the script was already subscribed", m_ParentEntity->GetObjectID());
|
||||
return;
|
||||
}
|
||||
m_SubscribedScripts.push_back(scriptToAdd);
|
||||
Game::logger->LogDebug("DestroyableComponent", "A script has subscribed to entity %llu", m_ParentEntity->GetObjectID());
|
||||
void DestroyableComponent::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd) {
|
||||
m_SubscribedScripts.insert(std::make_pair(scriptObjId, scriptToAdd));
|
||||
Game::logger->LogDebug("DestroyableComponent", "Added script %llu to entity %llu", scriptObjId, m_Parent->GetObjectID());
|
||||
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
}
|
||||
|
||||
void DestroyableComponent::Unsubscribe(CppScripts::Script* scriptToRemove) {
|
||||
auto foundScript = std::find(m_SubscribedScripts.begin(), m_SubscribedScripts.end(), scriptToRemove);
|
||||
void DestroyableComponent::Unsubscribe(LWOOBJID scriptObjId) {
|
||||
auto foundScript = m_SubscribedScripts.find(scriptObjId);
|
||||
if (foundScript != m_SubscribedScripts.end()) {
|
||||
m_SubscribedScripts.erase(foundScript);
|
||||
Game::logger->LogDebug("DestroyableComponent", "Unsubscribed a script from entity %llu", m_ParentEntity->GetObjectID());
|
||||
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
return;
|
||||
Game::logger->LogDebug("DestroyableComponent", "Removed script %llu from entity %llu", scriptObjId, m_Parent->GetObjectID());
|
||||
} else {
|
||||
Game::logger->LogDebug("DestroyableComponent", "Tried to remove a script for Entity %llu but script %llu didnt exist", m_Parent->GetObjectID(), scriptObjId);
|
||||
}
|
||||
Game::logger->LogDebug("DestroyableComponent", "WARNING: Tried to remove a script for Entity %llu but the script was not subscribed", m_ParentEntity->GetObjectID());
|
||||
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
}
|
||||
|
||||
void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage) {
|
||||
for (auto* script : m_SubscribedScripts) {
|
||||
DluAssert(script != nullptr);
|
||||
script->NotifyHitOrHealResult(m_ParentEntity, attacker, damage);
|
||||
for (auto script : m_SubscribedScripts) {
|
||||
script.second->NotifyHitOrHealResult(m_Parent, attacker, damage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,7 +708,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
SetArmor(0);
|
||||
SetHealth(0);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
m_KillerID = source;
|
||||
@@ -738,12 +720,12 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID());
|
||||
|
||||
const auto isEnemy = m_ParentEntity->GetComponent<BaseCombatAIComponent>() != nullptr;
|
||||
const auto isEnemy = m_Parent->GetComponent<BaseCombatAIComponent>() != nullptr;
|
||||
|
||||
auto* inventoryComponent = owner->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent != nullptr && isEnemy) {
|
||||
inventoryComponent->TriggerPassiveAbility(PassiveAbilityTrigger::EnemySmashed, m_ParentEntity);
|
||||
inventoryComponent->TriggerPassiveAbility(PassiveAbilityTrigger::EnemySmashed, m_Parent);
|
||||
}
|
||||
|
||||
auto* missions = owner->GetComponent<MissionComponent>();
|
||||
@@ -759,28 +741,28 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
if (memberMissions == nullptr) continue;
|
||||
|
||||
memberMissions->Progress(eMissionTaskType::SMASH, m_ParentEntity->GetLOT());
|
||||
memberMissions->Progress(eMissionTaskType::USE_SKILL, m_ParentEntity->GetLOT(), skillID);
|
||||
memberMissions->Progress(eMissionTaskType::SMASH, m_Parent->GetLOT());
|
||||
memberMissions->Progress(eMissionTaskType::USE_SKILL, m_Parent->GetLOT(), skillID);
|
||||
}
|
||||
} else {
|
||||
missions->Progress(eMissionTaskType::SMASH, m_ParentEntity->GetLOT());
|
||||
missions->Progress(eMissionTaskType::USE_SKILL, m_ParentEntity->GetLOT(), skillID);
|
||||
missions->Progress(eMissionTaskType::SMASH, m_Parent->GetLOT());
|
||||
missions->Progress(eMissionTaskType::USE_SKILL, m_Parent->GetLOT(), skillID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto isPlayer = m_ParentEntity->IsPlayer();
|
||||
const auto isPlayer = m_Parent->IsPlayer();
|
||||
|
||||
GameMessages::SendDie(m_ParentEntity, source, source, true, killType, deathType, 0, 0, 0, isPlayer, false, 1);
|
||||
GameMessages::SendDie(m_Parent, source, source, true, killType, deathType, 0, 0, 0, isPlayer, false, 1);
|
||||
|
||||
//NANI?!
|
||||
if (!isPlayer) {
|
||||
if (owner != nullptr) {
|
||||
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID());
|
||||
|
||||
if (team != nullptr && m_ParentEntity->GetComponent<BaseCombatAIComponent>() != nullptr) {
|
||||
if (team != nullptr && m_Parent->GetComponent<BaseCombatAIComponent>() != nullptr) {
|
||||
LWOOBJID specificOwner = LWOOBJID_EMPTY;
|
||||
auto* scriptedActivityComponent = m_ParentEntity->GetComponent<ScriptedActivityComponent>();
|
||||
auto* scriptedActivityComponent = m_Parent->GetComponent<ScriptedActivityComponent>();
|
||||
uint32_t teamSize = team->members.size();
|
||||
uint32_t lootMatrixId = GetLootMatrixID();
|
||||
|
||||
@@ -793,24 +775,24 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
auto* member = EntityManager::Instance()->GetEntity(specificOwner);
|
||||
|
||||
if (member) LootGenerator::Instance().DropLoot(member, m_ParentEntity, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
if (member) LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
} else {
|
||||
for (const auto memberId : team->members) { // Free for all
|
||||
auto* member = EntityManager::Instance()->GetEntity(memberId);
|
||||
|
||||
if (member == nullptr) continue;
|
||||
|
||||
LootGenerator::Instance().DropLoot(member, m_ParentEntity, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else { // drop loot for non team user
|
||||
LootGenerator::Instance().DropLoot(owner, m_ParentEntity, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
LootGenerator::Instance().DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Check if this zone allows coin drops
|
||||
if (dZoneManager::Instance()->GetPlayerLoseCoinOnDeath()) {
|
||||
auto* character = m_ParentEntity->GetCharacter();
|
||||
auto* character = m_Parent->GetCharacter();
|
||||
uint64_t coinsTotal = character->GetCoins();
|
||||
const uint64_t minCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMin;
|
||||
if (coinsTotal >= minCoinsToLose) {
|
||||
@@ -822,23 +804,27 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
coinsTotal -= coinsToLose;
|
||||
|
||||
LootGenerator::Instance().DropLoot(m_ParentEntity, m_ParentEntity, -1, coinsToLose, coinsToLose);
|
||||
LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
|
||||
character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
|
||||
}
|
||||
}
|
||||
|
||||
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
|
||||
zoneControl->GetScript()->OnPlayerDied(zoneControl, m_ParentEntity);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerDied(zoneControl, m_Parent);
|
||||
}
|
||||
|
||||
std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
scriptEntity->GetScript()->OnPlayerDied(scriptEntity, m_ParentEntity);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
|
||||
script->OnPlayerDied(scriptEntity, m_Parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_ParentEntity->Kill(owner);
|
||||
m_Parent->Kill(owner);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
|
||||
@@ -849,16 +835,16 @@ void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetStatusImmunity(
|
||||
const eStateChangeType state,
|
||||
const bool bImmuneToBasicAttack,
|
||||
const bool bImmuneToDamageOverTime,
|
||||
const bool bImmuneToKnockback,
|
||||
const bool bImmuneToInterrupt,
|
||||
const bool bImmuneToSpeed,
|
||||
const bool bImmuneToImaginationGain,
|
||||
const bool bImmuneToImaginationLoss,
|
||||
const bool bImmuneToQuickbuildInterrupt,
|
||||
const bool bImmuneToPullToPoint) {
|
||||
const eStateChangeType state,
|
||||
const bool bImmuneToBasicAttack,
|
||||
const bool bImmuneToDamageOverTime,
|
||||
const bool bImmuneToKnockback,
|
||||
const bool bImmuneToInterrupt,
|
||||
const bool bImmuneToSpeed,
|
||||
const bool bImmuneToImaginationGain,
|
||||
const bool bImmuneToImaginationLoss,
|
||||
const bool bImmuneToQuickbuildInterrupt,
|
||||
const bool bImmuneToPullToPoint) {
|
||||
|
||||
if (state == eStateChangeType::POP) {
|
||||
if (bImmuneToBasicAttack && m_ImmuneToBasicAttackCount > 0) m_ImmuneToBasicAttackCount -= 1;
|
||||
@@ -871,7 +857,7 @@ void DestroyableComponent::SetStatusImmunity(
|
||||
if (bImmuneToQuickbuildInterrupt && m_ImmuneToQuickbuildInterruptCount > 0) m_ImmuneToQuickbuildInterruptCount -= 1;
|
||||
if (bImmuneToPullToPoint && m_ImmuneToPullToPointCount > 0) m_ImmuneToPullToPointCount -= 1;
|
||||
|
||||
} else if (state == eStateChangeType::PUSH) {
|
||||
} else if (state == eStateChangeType::PUSH){
|
||||
if (bImmuneToBasicAttack) m_ImmuneToBasicAttackCount += 1;
|
||||
if (bImmuneToDamageOverTime) m_ImmuneToDamageOverTimeCount += 1;
|
||||
if (bImmuneToKnockback) m_ImmuneToKnockbackCount += 1;
|
||||
@@ -884,7 +870,7 @@ void DestroyableComponent::SetStatusImmunity(
|
||||
}
|
||||
|
||||
GameMessages::SendSetStatusImmunity(
|
||||
m_ParentEntity->GetObjectID(), state, m_ParentEntity->GetSystemAddress(),
|
||||
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(),
|
||||
bImmuneToBasicAttack,
|
||||
bImmuneToDamageOverTime,
|
||||
bImmuneToKnockback,
|
||||
@@ -898,7 +884,7 @@ void DestroyableComponent::SetStatusImmunity(
|
||||
}
|
||||
|
||||
void DestroyableComponent::FixStats() {
|
||||
auto* entity = GetParentEntity();
|
||||
auto* entity = GetParent();
|
||||
|
||||
if (entity == nullptr) return;
|
||||
|
||||
@@ -998,43 +984,43 @@ void DestroyableComponent::AddOnHitCallback(const std::function<void(Entity*)>&
|
||||
m_OnHitCallbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
|
||||
//check if this is a player:
|
||||
if (m_ParentEntity->IsPlayer()) {
|
||||
if (m_Parent->IsPlayer()) {
|
||||
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
||||
auto* character = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
auto* character = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto uscore = character->GetUScore();
|
||||
|
||||
auto uscoreToLose = uscore * (EntityManager::Instance()->GetHardcoreLoseUscoreOnDeathPercent() / 100);
|
||||
character->SetUScore(uscore - uscoreToLose);
|
||||
|
||||
GameMessages::SendModifyLEGOScore(m_ParentEntity, m_ParentEntity->GetSystemAddress(), -uscoreToLose, eLootSourceType::MISSION);
|
||||
GameMessages::SendModifyLEGOScore(m_Parent, m_Parent->GetSystemAddress(), -uscoreToLose, eLootSourceType::MISSION);
|
||||
|
||||
if (EntityManager::Instance()->GetHardcoreDropinventoryOnDeath()) {
|
||||
//drop all items from inventory:
|
||||
auto* inventory = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
auto* inventory = m_Parent->GetComponent<InventoryComponent>();
|
||||
if (inventory) {
|
||||
//get the items inventory:
|
||||
auto items = inventory->GetInventory(eInventoryType::ITEMS);
|
||||
if (items) {
|
||||
if (items){
|
||||
auto itemMap = items->GetItems();
|
||||
if (!itemMap.empty()) {
|
||||
if (!itemMap.empty()){
|
||||
for (const auto& item : itemMap) {
|
||||
//drop the item:
|
||||
if (!item.second) continue;
|
||||
// don't drop the thinkng cap
|
||||
if (item.second->GetLot() == 6086) continue;
|
||||
GameMessages::SendDropClientLoot(m_ParentEntity, source, item.second->GetLot(), 0, m_ParentEntity->GetPosition(), item.second->GetCount());
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, item.second->GetLot(), 0, m_Parent->GetPosition(), item.second->GetCount());
|
||||
item.second->SetCount(0, false, false);
|
||||
}
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//get character:
|
||||
auto* chars = m_ParentEntity->GetCharacter();
|
||||
auto* chars = m_Parent->GetCharacter();
|
||||
if (chars) {
|
||||
auto coins = chars->GetCoins();
|
||||
|
||||
@@ -1042,13 +1028,13 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
chars->SetCoins(0, eLootSourceType::NONE);
|
||||
|
||||
//drop all coins:
|
||||
GameMessages::SendDropClientLoot(m_ParentEntity, source, LOT_NULL, coins, m_ParentEntity->GetPosition());
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, coins, m_Parent->GetPosition());
|
||||
}
|
||||
|
||||
// Reload the player since we can't normally reduce uscore from the server and we want the UI to update
|
||||
// do this last so we don't get killed.... again
|
||||
EntityManager::Instance()->DestructEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->ConstructEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->DestructEntity(m_Parent);
|
||||
EntityManager::Instance()->ConstructEntity(m_Parent);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1065,7 +1051,7 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
playerStats->SetUScore(playerStats->GetUScore() + uscore);
|
||||
GameMessages::SendModifyLEGOScore(player, player->GetSystemAddress(), uscore, eLootSourceType::MISSION);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,19 +17,23 @@ enum class eStateChangeType : uint32_t;
|
||||
* Represents the stats of an entity, for example its health, imagination and armor. Also handles factions, which
|
||||
* indicate which enemies this entity has.
|
||||
*/
|
||||
class DestroyableComponent final : public Component {
|
||||
class DestroyableComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
|
||||
|
||||
DestroyableComponent(Entity* parentEntity, int32_t componentId = -1);
|
||||
DestroyableComponent(Entity* parentEntity);
|
||||
~DestroyableComponent() override;
|
||||
|
||||
void Startup() override;
|
||||
void LoadConfigData() override;
|
||||
void LoadTemplateData() override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags);
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
|
||||
/**
|
||||
* Initializes the component using a different LOT
|
||||
* @param templateID the ID to use for initialization
|
||||
*/
|
||||
void Reinitialize(LOT templateID);
|
||||
|
||||
/**
|
||||
* Sets the health of this entity. Makes sure this is serialized on the next tick and if this is a character its
|
||||
* stats will also update.
|
||||
@@ -235,7 +239,7 @@ public:
|
||||
* Returns whether or not this entity has bricks flying out when smashed
|
||||
* @return whether or not this entity has bricks flying out when smashed
|
||||
*/
|
||||
bool GetHasBricks() const { return m_IsModuleAssembly; }
|
||||
bool GetHasBricks() const { return m_HasBricks; }
|
||||
|
||||
/**
|
||||
* Sets the multiplier for the explosion that's visible when the bricks fly out when this entity is smashed
|
||||
@@ -447,15 +451,13 @@ public:
|
||||
*/
|
||||
void NotifySubscribers(Entity* attacker, uint32_t damage);
|
||||
|
||||
void Subscribe(CppScripts::Script* scriptToAdd);
|
||||
void Unsubscribe(CppScripts::Script* scriptToRemove);
|
||||
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd);
|
||||
void Unsubscribe(LWOOBJID scriptObjId);
|
||||
|
||||
// handle hardcode mode drops
|
||||
void DoHardcoreModeDrops(const LWOOBJID source);
|
||||
|
||||
private:
|
||||
// The ID of this component
|
||||
int32_t m_ComponentId;
|
||||
/**
|
||||
* Whether or not the health should be serialized
|
||||
*/
|
||||
@@ -544,7 +546,7 @@ private:
|
||||
/**
|
||||
* Whether this entity has bricks flying out when smashed (causes the client to look up the files)
|
||||
*/
|
||||
bool m_IsModuleAssembly;
|
||||
bool m_HasBricks;
|
||||
|
||||
/**
|
||||
* The rate at which bricks fly out when smashed
|
||||
@@ -587,9 +589,9 @@ private:
|
||||
std::vector<std::function<void(Entity*)>> m_OnHitCallbacks;
|
||||
|
||||
/**
|
||||
* Scripts that are subscribed to this component
|
||||
* The list of scripts subscribed to this components actions
|
||||
*/
|
||||
std::vector<CppScripts::Script*> m_SubscribedScripts;
|
||||
std::map<LWOOBJID, CppScripts::Script*> m_SubscribedScripts;
|
||||
|
||||
/**
|
||||
* status immunity counters
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#include "DonationVendorComponent.h"
|
||||
|
||||
DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
m_PercentComplete = 0.0;
|
||||
m_TotalDonated = 0;
|
||||
m_TotalRemaining = 0;
|
||||
}
|
||||
|
||||
void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
VendorComponent::Serialize(outBitStream, bIsInitialUpdate, flags);
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyDonationVendor);
|
||||
if (bIsInitialUpdate || m_DirtyDonationVendor) {
|
||||
outBitStream->Write(m_PercentComplete);
|
||||
outBitStream->Write(m_TotalDonated);
|
||||
outBitStream->Write(m_TotalRemaining);
|
||||
if (!bIsInitialUpdate) m_DirtyDonationVendor = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DonationVendorComponent::LoadConfigData() {
|
||||
m_ActivityId = m_ParentEntity->GetVar<uint32_t>(u"activityID");
|
||||
VendorComponent::LoadConfigData();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef __DONATIONVENDORCOMPONENT__H__
|
||||
#define __DONATIONVENDORCOMPONENT__H__
|
||||
|
||||
#include "VendorComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
class DonationVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
|
||||
DonationVendorComponent(Entity* parent);
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadConfigData() override;
|
||||
|
||||
void SetPercentComplete(float percentComplete){
|
||||
if (m_PercentComplete == percentComplete) return;
|
||||
m_PercentComplete = percentComplete;
|
||||
m_DirtyDonationVendor = true;
|
||||
}
|
||||
|
||||
void SetTotalDonated(float totalDonated){
|
||||
if (m_TotalDonated == totalDonated) return;
|
||||
m_TotalDonated = totalDonated;
|
||||
m_DirtyDonationVendor = true;
|
||||
}
|
||||
|
||||
void SetTotalRemaining(float totalRemaining){
|
||||
if (m_TotalRemaining == totalRemaining) return;
|
||||
m_TotalRemaining = totalRemaining;
|
||||
m_DirtyDonationVendor = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_DirtyDonationVendor = false;
|
||||
float m_PercentComplete = 0.0;
|
||||
int32_t m_TotalDonated = 0;
|
||||
int32_t m_TotalRemaining = 0;
|
||||
uint32_t m_ActivityId = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif //!__DONATIONVENDORCOMPONENT__H__
|
||||
@@ -1,5 +0,0 @@
|
||||
#include "GateRushComponent.h"
|
||||
|
||||
GateRushComponent::GateRushComponent(Entity* parent, int32_t componentId) : RacingControlComponent(parent, componentId) {
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef __GATERUSHCONTROLCOMPONENT__H__
|
||||
#define __GATERUSHCONTROLCOMPONENT__H__
|
||||
|
||||
#include "RacingControlComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
class GateRushComponent final : public RacingControlComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::GATE_RUSH_CONTROL;
|
||||
GateRushComponent(Entity* parent, int32_t componentId);
|
||||
};
|
||||
|
||||
#endif //!__GATERUSHCONTROLCOMPONENT__H__
|
||||
@@ -1,118 +0,0 @@
|
||||
#include "HavokVehiclePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
|
||||
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : Component(parent) {
|
||||
m_Position = NiPoint3::ZERO;
|
||||
m_Rotation = NiQuaternion::IDENTITY;
|
||||
m_Velocity = NiPoint3::ZERO;
|
||||
m_AngularVelocity = NiPoint3::ZERO;
|
||||
m_IsOnGround = true;
|
||||
m_IsOnRail = false;
|
||||
m_DirtyPosition = true;
|
||||
m_DirtyVelocity = true;
|
||||
m_DirtyAngularVelocity = true;
|
||||
m_EndBehavior = GeneralUtils::GenerateRandomNumber<uint32_t>(0, 7);
|
||||
}
|
||||
|
||||
void HavokVehiclePhysicsComponent::SetPosition(const NiPoint3& pos) {
|
||||
if (pos == m_Position) return;
|
||||
m_DirtyPosition = true;
|
||||
m_Position = pos;
|
||||
}
|
||||
|
||||
void HavokVehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) {
|
||||
if (rot == m_Rotation) return;
|
||||
m_DirtyPosition = true;
|
||||
m_Rotation = rot;
|
||||
}
|
||||
|
||||
void HavokVehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) {
|
||||
if (vel == m_Velocity) return;
|
||||
m_DirtyPosition = true;
|
||||
m_Velocity = vel;
|
||||
}
|
||||
|
||||
void HavokVehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
|
||||
if (vel == m_AngularVelocity) return;
|
||||
m_DirtyPosition = true;
|
||||
m_AngularVelocity = vel;
|
||||
}
|
||||
|
||||
void HavokVehiclePhysicsComponent::SetIsOnGround(bool val) {
|
||||
if (val == m_IsOnGround) return;
|
||||
m_DirtyPosition = true;
|
||||
m_IsOnGround = val;
|
||||
}
|
||||
|
||||
void HavokVehiclePhysicsComponent::SetIsOnRail(bool val) {
|
||||
if (val == m_IsOnRail) return;
|
||||
m_DirtyPosition = true;
|
||||
m_IsOnRail = val;
|
||||
}
|
||||
|
||||
void HavokVehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
|
||||
if (m_RemoteInputInfo == remoteInputInfo) return;
|
||||
this->m_RemoteInputInfo = remoteInputInfo;
|
||||
m_DirtyRemoteInput = true;
|
||||
}
|
||||
|
||||
void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
|
||||
|
||||
if (bIsInitialUpdate || m_DirtyPosition) {
|
||||
if (!bIsInitialUpdate) m_DirtyPosition = false;
|
||||
outBitStream->Write(m_Position.x);
|
||||
outBitStream->Write(m_Position.y);
|
||||
outBitStream->Write(m_Position.z);
|
||||
|
||||
outBitStream->Write(m_Rotation.x);
|
||||
outBitStream->Write(m_Rotation.y);
|
||||
outBitStream->Write(m_Rotation.z);
|
||||
outBitStream->Write(m_Rotation.w);
|
||||
|
||||
outBitStream->Write(m_IsOnGround);
|
||||
outBitStream->Write(m_IsOnRail);
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity);
|
||||
|
||||
if (bIsInitialUpdate || m_DirtyVelocity) {
|
||||
outBitStream->Write(m_Velocity.x);
|
||||
outBitStream->Write(m_Velocity.y);
|
||||
outBitStream->Write(m_Velocity.z);
|
||||
if (!bIsInitialUpdate) m_DirtyVelocity = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity);
|
||||
|
||||
if (bIsInitialUpdate || m_DirtyAngularVelocity) {
|
||||
outBitStream->Write(m_AngularVelocity.x);
|
||||
outBitStream->Write(m_AngularVelocity.y);
|
||||
outBitStream->Write(m_AngularVelocity.z);
|
||||
if (!bIsInitialUpdate) m_DirtyAngularVelocity = false;
|
||||
}
|
||||
|
||||
outBitStream->Write0(); // local_space_info. TODO: Implement this
|
||||
|
||||
outBitStream->Write(m_DirtyRemoteInput || bIsInitialUpdate); // remote_input_info
|
||||
if (m_DirtyRemoteInput || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_RemoteInputInfo.m_RemoteInputX);
|
||||
outBitStream->Write(m_RemoteInputInfo.m_RemoteInputY);
|
||||
outBitStream->Write(m_RemoteInputInfo.m_IsPowersliding);
|
||||
outBitStream->Write(m_RemoteInputInfo.m_IsModified);
|
||||
if (!bIsInitialUpdate) m_DirtyRemoteInput = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(125.0f); // remote_input_ping TODO: Figure out how this should be calculated as it seems to be constant through the whole race.
|
||||
|
||||
if (!bIsInitialUpdate) {
|
||||
outBitStream->Write0();
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write<uint8_t>(m_EndBehavior);
|
||||
outBitStream->Write1(); // is input locked?
|
||||
}
|
||||
|
||||
outBitStream->Write0();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@
|
||||
#include "Component.h"
|
||||
#include "ItemSetPassiveAbility.h"
|
||||
#include "eItemSetPassiveAbilityID.h"
|
||||
#include "PossessionComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "eInventoryType.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "eLootSourceType.h"
|
||||
@@ -35,17 +35,17 @@ enum class eItemType : int32_t;
|
||||
* of different types, each type representing a different group of items, see `eInventoryType` for a list of
|
||||
* inventories.
|
||||
*/
|
||||
class InventoryComponent final : public Component
|
||||
class InventoryComponent : public Component
|
||||
{
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
||||
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr);
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadXml(tinyxml2::XMLDocument* document);
|
||||
void UpdateXml(tinyxml2::XMLDocument* document) override;
|
||||
void ResetFlags() { m_Dirty = false; };
|
||||
void ResetFlags();
|
||||
|
||||
/**
|
||||
* Returns an inventory of the specified type, if it exists
|
||||
@@ -58,12 +58,10 @@ public:
|
||||
* Returns all the inventories this entity has, indexed by type
|
||||
* @return all the inventories this entity has, indexed by type
|
||||
*/
|
||||
const std::map<eInventoryType, Inventory*>& GetInventories() const { return m_Inventories; }
|
||||
const std::map<eInventoryType, Inventory*>& GetInventories() const;
|
||||
|
||||
/**
|
||||
* Returns the amount of items this entity possesses of a certain LOT
|
||||
* This method counts the lot count for all inventories, including inventories the player may not be able to see.
|
||||
* If you need the count in a specific inventory, call the inventory equivalent.
|
||||
* @param lot the lot to search for
|
||||
* @return the amount of items this entity possesses the specified LOT
|
||||
*/
|
||||
@@ -81,7 +79,7 @@ public:
|
||||
* Returns the items that are currently equipped by this entity
|
||||
* @return the items that are currently equipped by this entity
|
||||
*/
|
||||
const EquipmentMap& GetEquippedItems() const { return m_Equipped; }
|
||||
const EquipmentMap& GetEquippedItems() const;
|
||||
|
||||
/**
|
||||
* Adds an item to the inventory of the entity
|
||||
@@ -208,7 +206,7 @@ public:
|
||||
* @param item the Item to unequip
|
||||
* @return if we were successful
|
||||
*/
|
||||
void HandlePossession(Item* item) const;
|
||||
void HandlePossession(Item* item);
|
||||
|
||||
/**
|
||||
* Adds a buff related to equipping a lot to the entity
|
||||
@@ -249,13 +247,13 @@ public:
|
||||
* Sets the current consumable lot
|
||||
* @param lot the lot to set as consumable
|
||||
*/
|
||||
void SetConsumable(LOT lot) { m_Consumable = lot; };
|
||||
void SetConsumable(LOT lot);
|
||||
|
||||
/**
|
||||
* Returns the current consumable lot
|
||||
* @return the current consumable lot
|
||||
*/
|
||||
LOT GetConsumable() const { return m_Consumable; }
|
||||
LOT GetConsumable() const;
|
||||
|
||||
/**
|
||||
* Finds all the buffs related to a lot
|
||||
@@ -287,7 +285,7 @@ public:
|
||||
* Triggers one of the passive abilities from the equipped item set
|
||||
* @param trigger the trigger to fire
|
||||
*/
|
||||
void TriggerPassiveAbility(PassiveAbilityTrigger trigger, Entity* target = nullptr) const;
|
||||
void TriggerPassiveAbility(PassiveAbilityTrigger trigger, Entity* target = nullptr);
|
||||
|
||||
/**
|
||||
* Returns if the entity has any of the passed passive abilities equipped
|
||||
@@ -327,13 +325,13 @@ public:
|
||||
* @param id the id of the object to check for
|
||||
* @return if the provided object ID is in this inventory and is a pet
|
||||
*/
|
||||
bool IsPet(const LWOOBJID& id) const { return m_Pets.find(id) != m_Pets.end(); }
|
||||
bool IsPet(LWOOBJID id) const;
|
||||
|
||||
/**
|
||||
* Removes pet database information from the item with the specified object id
|
||||
* @param id the object id to remove pet info for
|
||||
*/
|
||||
void RemoveDatabasePet(const LWOOBJID& id) { m_Pets.erase(id); }
|
||||
void RemoveDatabasePet(LWOOBJID id);
|
||||
|
||||
/**
|
||||
* Returns the current behavior slot active for the passed item type
|
||||
@@ -361,14 +359,14 @@ public:
|
||||
*
|
||||
* @param equippedItem The item script to lookup and call equip on
|
||||
*/
|
||||
void EquipScripts(Item* equippedItem) const;
|
||||
void EquipScripts(Item* equippedItem);
|
||||
|
||||
/**
|
||||
* Call this when you unequip an item. This calls OnFactionTriggerItemUnequipped for any scripts found on the items.
|
||||
*
|
||||
* @param unequippedItem The item script to lookup and call unequip on
|
||||
*/
|
||||
void UnequipScripts(Item* unequippedItem) const;
|
||||
void UnequipScripts(Item* unequippedItem);
|
||||
|
||||
~InventoryComponent() override;
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#include "ItemComponent.h"
|
||||
#include "Entity.h"
|
||||
#include "eUgcModerationStatus.h"
|
||||
|
||||
ItemComponent::ItemComponent(Entity* parent) : Component(parent) {
|
||||
m_ParentEntity = parent;
|
||||
|
||||
m_DirtyItemInfo = false;
|
||||
|
||||
m_UgId = m_ParentEntity->GetVarAs<LWOOBJID>(u"userModelID");
|
||||
if (m_UgId == LWOOBJID_EMPTY) m_UgId = m_ParentEntity->GetObjectID();
|
||||
|
||||
m_UgModerationStatus = eUgcModerationStatus::NoStatus;
|
||||
|
||||
m_UgDescription = u"";
|
||||
}
|
||||
|
||||
void ItemComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
|
||||
outBitStream->Write(m_DirtyItemInfo || bIsInitialUpdate);
|
||||
if (m_DirtyItemInfo || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_UgId);
|
||||
outBitStream->Write(m_UgModerationStatus);
|
||||
outBitStream->Write(!m_UgDescription.empty());
|
||||
if (!m_UgDescription.empty()){
|
||||
outBitStream->Write<uint32_t>(m_UgDescription.length());
|
||||
outBitStream->Write(reinterpret_cast<const char*>(m_UgDescription.c_str()), m_UgDescription.length() * sizeof(uint16_t));
|
||||
}
|
||||
m_DirtyItemInfo = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#ifndef __ITEMCOMPONENT__H__
|
||||
#define __ITEMCOMPONENT__H__
|
||||
#pragma once
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "RakNetTypes.h"
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
enum class eUgcModerationStatus : uint32_t;
|
||||
|
||||
class ItemComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::ITEM;
|
||||
|
||||
ItemComponent(Entity* parent);
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
void SetUgId(LWOOBJID id) { m_UgId = id; m_DirtyItemInfo = true; };
|
||||
LWOOBJID GetUgId() { return m_UgId; };
|
||||
|
||||
void SetUgModerationStatus(eUgcModerationStatus status) { m_UgModerationStatus = status; m_DirtyItemInfo = true; };
|
||||
eUgcModerationStatus GetUgModerationStatus() { return m_UgModerationStatus; };
|
||||
|
||||
void SetUgDescription(std::u16string description) { m_UgDescription = description; m_DirtyItemInfo = true; };
|
||||
std::u16string GetUgDescription() { return m_UgDescription;};
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* If we have change the item info
|
||||
*/
|
||||
bool m_DirtyItemInfo;
|
||||
|
||||
/**
|
||||
* The ID of the user that made the model
|
||||
*/
|
||||
LWOOBJID m_UgId;
|
||||
|
||||
/**
|
||||
* Whether or not the description of this item is approved.
|
||||
*/
|
||||
eUgcModerationStatus m_UgModerationStatus;
|
||||
|
||||
/**
|
||||
* The user generated description
|
||||
*/
|
||||
std::u16string m_UgDescription;
|
||||
};
|
||||
|
||||
#endif //!__ITEMCOMPONENT__H__
|
||||
@@ -3,32 +3,42 @@
|
||||
#include "EntityManager.h"
|
||||
|
||||
LUPExhibitComponent::LUPExhibitComponent(Entity* parent) : Component(parent) {
|
||||
m_Exhibits = { 11121, 11295, 11423, 11979 };
|
||||
|
||||
m_ExhibitIndex = 0;
|
||||
m_UpdateTimer = 0.0f;
|
||||
m_Exhibit = m_Exhibits.front();
|
||||
m_DirtyExhibitInfo = true;
|
||||
|
||||
m_Exhibit = m_Exhibits[m_ExhibitIndex];
|
||||
|
||||
|
||||
}
|
||||
|
||||
LUPExhibitComponent::~LUPExhibitComponent() {
|
||||
|
||||
}
|
||||
|
||||
void LUPExhibitComponent::Update(float deltaTime) {
|
||||
m_UpdateTimer += deltaTime;
|
||||
if (m_UpdateTimer < 20.0f) return;
|
||||
|
||||
NextExhibit();
|
||||
m_UpdateTimer = 0.0f;
|
||||
if (m_UpdateTimer > 20.0f) {
|
||||
NextExhibit();
|
||||
|
||||
m_UpdateTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void LUPExhibitComponent::NextExhibit() {
|
||||
m_ExhibitIndex++;
|
||||
|
||||
// After 1361 years, this will skip exhibit 4 one time. I think modulo is ok here.
|
||||
m_Exhibit = m_Exhibits.at(m_ExhibitIndex % m_Exhibits.size());
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
if (m_ExhibitIndex >= m_Exhibits.size()) {
|
||||
m_ExhibitIndex = 0;
|
||||
}
|
||||
|
||||
m_Exhibit = m_Exhibits[m_ExhibitIndex];
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyExhibitInfo);
|
||||
if (bIsInitialUpdate || m_DirtyExhibitInfo) {
|
||||
outBitStream->Write(m_Exhibit);
|
||||
if (!bIsInitialUpdate) m_DirtyExhibitInfo = false;
|
||||
}
|
||||
outBitStream->Write1(); // Dirty flag?
|
||||
outBitStream->Write(m_Exhibit);
|
||||
}
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
* Component that handles the LOT that is shown in the LUP exhibit in the LUP world. Works by setting a timer and
|
||||
* switching the LOTs around that we'd like to display.
|
||||
*/
|
||||
class LUPExhibitComponent final : public Component
|
||||
class LUPExhibitComponent : public Component
|
||||
{
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT;
|
||||
|
||||
LUPExhibitComponent(Entity* parent);
|
||||
~LUPExhibitComponent();
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags);
|
||||
|
||||
@@ -35,13 +36,10 @@ private:
|
||||
/**
|
||||
* The list of possible exhibits to show
|
||||
*/
|
||||
const std::vector<LOT> m_Exhibits = { 11121, 11295, 11423, 11979 };
|
||||
std::vector<LOT> m_Exhibits;
|
||||
|
||||
/**
|
||||
* The current index in the exhibit list
|
||||
*/
|
||||
size_t m_ExhibitIndex;
|
||||
|
||||
// Whether or not to notify clients of a change in the visible exhibit
|
||||
bool m_DirtyExhibitInfo;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "CDRewardsTable.h"
|
||||
|
||||
LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component(parent) {
|
||||
m_ParentEntity = parent;
|
||||
m_Parent = parent;
|
||||
m_Level = 1;
|
||||
m_SpeedBase = 500.0f;
|
||||
m_CharacterVersion = eCharacterVersion::LIVE;
|
||||
@@ -35,60 +35,55 @@ void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
uint32_t characterVersion;
|
||||
level->QueryAttribute("cv", &characterVersion);
|
||||
m_CharacterVersion = static_cast<eCharacterVersion>(characterVersion);
|
||||
auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
|
||||
|
||||
if (!controllablePhysicsComponent) return;
|
||||
controllablePhysicsComponent->SetSpeedMultiplier(GetSpeedBase() / 500.0f);
|
||||
}
|
||||
|
||||
void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo);
|
||||
if (bIsInitialUpdate || m_DirtyLevelInfo) {
|
||||
outBitStream->Write(m_Level);
|
||||
if (!bIsInitialUpdate) m_DirtyLevelInfo = false;
|
||||
}
|
||||
if (bIsInitialUpdate || m_DirtyLevelInfo) outBitStream->Write(m_Level);
|
||||
m_DirtyLevelInfo = false;
|
||||
}
|
||||
|
||||
void LevelProgressionComponent::HandleLevelUp() {
|
||||
auto* rewardsTable = CDClientManager::Instance().GetTable<CDRewardsTable>();
|
||||
|
||||
const auto& rewards = rewardsTable->GetByLevelID(m_Level);
|
||||
if (rewards.empty()) return;
|
||||
bool rewardingItem = rewards.size() > 0;
|
||||
|
||||
auto* inventoryComponent = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
|
||||
|
||||
if (!inventoryComponent || !controllablePhysicsComponent) return;
|
||||
// Tell the client we beginning to send level rewards.
|
||||
GameMessages::NotifyLevelRewards(m_ParentEntity->GetObjectID(), m_ParentEntity->GetSystemAddress(), m_Level, true);
|
||||
if (rewardingItem) GameMessages::NotifyLevelRewards(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), m_Level, rewardingItem);
|
||||
|
||||
for (auto* reward : rewards) {
|
||||
switch (reward->rewardType) {
|
||||
case 0:
|
||||
inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LEVEL_REWARD);
|
||||
break;
|
||||
case 4: {
|
||||
case 4:
|
||||
{
|
||||
auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS);
|
||||
if (!items) continue;
|
||||
items->SetSize(items->GetSize() + reward->value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
SetSpeedBase(static_cast<float>(reward->value));
|
||||
SetSpeedBase(static_cast<float>(reward->value) );
|
||||
controllablePhysicsComponent->SetSpeedMultiplier(GetSpeedBase() / 500.0f);
|
||||
break;
|
||||
case 11:
|
||||
case 12:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Tell the client we have finished sending level rewards.
|
||||
GameMessages::NotifyLevelRewards(m_ParentEntity->GetObjectID(), m_ParentEntity->GetSystemAddress(), m_Level, false);
|
||||
if (rewardingItem) GameMessages::NotifyLevelRewards(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), m_Level, !rewardingItem);
|
||||
}
|
||||
|
||||
void LevelProgressionComponent::SetRetroactiveBaseSpeed() {
|
||||
void LevelProgressionComponent::SetRetroactiveBaseSpeed(){
|
||||
if (m_Level >= 20) m_SpeedBase = 525.0f;
|
||||
auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
|
||||
if (controllablePhysicsComponent) controllablePhysicsComponent->SetSpeedMultiplier(m_SpeedBase / 500.0f);
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
class LevelProgressionComponent final : public Component {
|
||||
class LevelProgressionComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION;
|
||||
|
||||
/**
|
||||
* Constructor for this component
|
||||
@@ -45,11 +45,7 @@ public:
|
||||
* Sets the level of the entity
|
||||
* @param level the level to set
|
||||
*/
|
||||
void SetLevel(uint32_t level) {
|
||||
if (m_Level == level) return;
|
||||
m_Level = level;
|
||||
m_DirtyLevelInfo = true;
|
||||
}
|
||||
void SetLevel(uint32_t level) { m_Level = level; m_DirtyLevelInfo = true; }
|
||||
|
||||
/**
|
||||
* Gets the current Speed Base of the entity
|
||||
@@ -102,7 +98,7 @@ private:
|
||||
float m_SpeedBase;
|
||||
|
||||
/**
|
||||
* The Character format version. Certain bug fixes increment this version number.
|
||||
* The Character format version
|
||||
*/
|
||||
eCharacterVersion m_CharacterVersion;
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
#include "Entity.h"
|
||||
#include "MinigameControlComponent.h"
|
||||
|
||||
MinigameControlComponent::MinigameControlComponent(Entity* parent, int32_t componentId) : ActivityComponent(parent, componentId) {
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef __MINIGAMECONTROLCOMPONENT__H__
|
||||
#define __MINIGAMECONTROLCOMPONENT__H__
|
||||
|
||||
#include "ActivityComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
class MinigameControlComponent : public ActivityComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MINIGAME_CONTROL;
|
||||
MinigameControlComponent(Entity* parent, int32_t componentId);
|
||||
};
|
||||
|
||||
#endif //!__MINIGAMECONTROLCOMPONENT__H__
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Darkflame Universe
|
||||
* Copyright 2023
|
||||
* Copyright 2019
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "MissionComponent.h"
|
||||
@@ -12,46 +13,68 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Game.h"
|
||||
#include "Amf3.h"
|
||||
#include "AMFFormat.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "Mail.h"
|
||||
#include "MissionPrerequisites.h"
|
||||
#include "AchievementCacheKey.h"
|
||||
#include "eMissionState.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
// MARK: Mission Component
|
||||
|
||||
std::unordered_map<AchievementCacheKey, std::vector<uint32_t>> MissionComponent::m_AchievementCache = {};
|
||||
|
||||
//! Initializer
|
||||
MissionComponent::MissionComponent(Entity* parent) : Component(parent) {
|
||||
m_LastUsedMissionOrderUID = dZoneManager::Instance()->GetUniqueMissionIdStartingValue();
|
||||
}
|
||||
|
||||
//! Destructor
|
||||
MissionComponent::~MissionComponent() {
|
||||
for (const auto& [missionId, mission] : m_Missions) {
|
||||
delete mission;
|
||||
for (const auto& mission : m_Missions) {
|
||||
delete mission.second;
|
||||
}
|
||||
|
||||
this->m_Missions.clear();
|
||||
}
|
||||
|
||||
|
||||
Mission* MissionComponent::GetMission(const uint32_t missionId) const {
|
||||
if (m_Missions.count(missionId) == 0) return nullptr;
|
||||
if (m_Missions.count(missionId) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto& index = m_Missions.find(missionId);
|
||||
|
||||
return index == m_Missions.end() ? nullptr : index->second;
|
||||
if (index == m_Missions.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return index->second;
|
||||
}
|
||||
|
||||
|
||||
eMissionState MissionComponent::GetMissionState(const uint32_t missionId) const {
|
||||
auto* mission = GetMission(missionId);
|
||||
|
||||
if (!mission) return CanAccept(missionId) ? eMissionState::AVAILABLE : eMissionState::UNKNOWN;
|
||||
if (mission == nullptr) {
|
||||
return CanAccept(missionId) ? eMissionState::AVAILABLE : eMissionState::UNKNOWN;
|
||||
}
|
||||
|
||||
return mission->GetMissionState();
|
||||
}
|
||||
|
||||
|
||||
const std::unordered_map<uint32_t, Mission*>& MissionComponent::GetMissions() const {
|
||||
return m_Missions;
|
||||
}
|
||||
|
||||
|
||||
bool MissionComponent::CanAccept(const uint32_t missionId) const {
|
||||
return MissionPrerequisites::CanAccept(missionId, m_Missions);
|
||||
}
|
||||
|
||||
|
||||
void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipChecks) {
|
||||
if (!skipChecks && !CanAccept(missionId)) {
|
||||
return;
|
||||
@@ -60,7 +83,7 @@ void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipCh
|
||||
// If this is a daily mission, it may already be "accepted"
|
||||
auto* mission = this->GetMission(missionId);
|
||||
|
||||
if (mission) {
|
||||
if (mission != nullptr) {
|
||||
if (mission->GetClientInfo().repeatable) {
|
||||
mission->Accept();
|
||||
if (mission->IsMission()) mission->SetUniqueMissionOrderID(++m_LastUsedMissionOrderUID);
|
||||
@@ -77,41 +100,54 @@ void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipCh
|
||||
|
||||
this->m_Missions.insert_or_assign(missionId, mission);
|
||||
|
||||
//Needs to send a mail
|
||||
if (missionId == 1728) Mail::HandleNotificationRequest(m_ParentEntity->GetSystemAddress(), m_ParentEntity->GetObjectID());
|
||||
if (missionId == 1728) {
|
||||
//Needs to send a mail
|
||||
|
||||
auto address = m_Parent->GetSystemAddress();
|
||||
|
||||
Mail::HandleNotificationRequest(address, m_Parent->GetObjectID());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MissionComponent::CompleteMission(const uint32_t missionId, const bool skipChecks, const bool yieldRewards) {
|
||||
// Get the mission first
|
||||
auto* mission = this->GetMission(missionId);
|
||||
|
||||
if (!mission) {
|
||||
if (mission == nullptr) {
|
||||
AcceptMission(missionId, skipChecks);
|
||||
|
||||
mission = this->GetMission(missionId);
|
||||
|
||||
if (!mission) return;
|
||||
if (mission == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If this mission is not repeatable, and already completed, we stop here.
|
||||
if (mission->IsComplete() && !mission->IsRepeatable()) return;
|
||||
if (mission->IsComplete() && !mission->IsRepeatable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mission->Complete(yieldRewards);
|
||||
}
|
||||
|
||||
void MissionComponent::RemoveMission(const uint32_t missionId) {
|
||||
auto missionItr = m_Missions.find(missionId);
|
||||
void MissionComponent::RemoveMission(uint32_t missionId) {
|
||||
auto* mission = this->GetMission(missionId);
|
||||
|
||||
if (missionItr == m_Missions.end()) return;
|
||||
if (mission == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete missionItr->second;
|
||||
delete mission;
|
||||
|
||||
m_Missions.erase(missionItr);
|
||||
m_Missions.erase(missionId);
|
||||
}
|
||||
|
||||
void MissionComponent::Progress(const eMissionTaskType type, const int32_t value, const LWOOBJID& associate, const std::string& targets, const int32_t count, const bool ignoreAchievements) {
|
||||
for (const auto& [missionId, mission] : m_Missions) {
|
||||
if (!mission) continue;
|
||||
void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID associate, const std::string& targets, int32_t count, bool ignoreAchievements) {
|
||||
for (const auto& pair : m_Missions) {
|
||||
auto* mission = pair.second;
|
||||
|
||||
if (mission->IsAchievement() && ignoreAchievements) continue;
|
||||
|
||||
if (mission->IsComplete()) continue;
|
||||
@@ -127,57 +163,73 @@ void MissionComponent::Progress(const eMissionTaskType type, const int32_t value
|
||||
void MissionComponent::ForceProgress(const uint32_t missionId, const uint32_t taskId, const int32_t value, const bool acceptMission) {
|
||||
auto* mission = GetMission(missionId);
|
||||
|
||||
if (!mission) {
|
||||
if (!acceptMission) return;
|
||||
if (mission == nullptr) {
|
||||
if (!acceptMission) {
|
||||
return;
|
||||
}
|
||||
|
||||
AcceptMission(missionId);
|
||||
|
||||
mission = GetMission(missionId);
|
||||
|
||||
if (!mission) return;
|
||||
if (mission == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::for_each(mission->GetTasks().begin(), mission->GetTasks().end(), [value, taskId](MissionTask* element) {
|
||||
if (element->GetClientInfo().uid != taskId) return;
|
||||
for (auto* element : mission->GetTasks()) {
|
||||
if (element->GetClientInfo().uid != taskId) continue;
|
||||
|
||||
element->AddProgress(value);
|
||||
});
|
||||
}
|
||||
|
||||
if (!mission->IsComplete()) mission->CheckCompletion();
|
||||
if (!mission->IsComplete()) {
|
||||
mission->CheckCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
void MissionComponent::ForceProgressTaskType(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission) {
|
||||
auto* mission = GetMission(missionId);
|
||||
|
||||
if (!mission) {
|
||||
if (!acceptMission) return;
|
||||
if (mission == nullptr) {
|
||||
if (!acceptMission) {
|
||||
return;
|
||||
}
|
||||
|
||||
CDMissions missionInfo;
|
||||
|
||||
if (!GetMissionInfo(missionId, missionInfo)) return;
|
||||
if (!GetMissionInfo(missionId, missionInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (missionInfo.isMission) return;
|
||||
if (missionInfo.isMission) {
|
||||
return;
|
||||
}
|
||||
|
||||
AcceptMission(missionId);
|
||||
|
||||
mission = GetMission(missionId);
|
||||
|
||||
if (!mission) return;
|
||||
if (mission == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::for_each(mission->GetTasks().begin(), mission->GetTasks().end(), [value, taskType](MissionTask* element) {
|
||||
if (element->GetType() != static_cast<eMissionTaskType>(taskType)) return;
|
||||
for (auto* element : mission->GetTasks()) {
|
||||
if (element->GetType() != static_cast<eMissionTaskType>(taskType)) continue;
|
||||
|
||||
element->AddProgress(value);
|
||||
});
|
||||
}
|
||||
|
||||
if (!mission->IsComplete()) mission->CheckCompletion();
|
||||
if (!mission->IsComplete()) {
|
||||
mission->CheckCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
void MissionComponent::ForceProgressValue(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission) {
|
||||
void MissionComponent::ForceProgressValue(uint32_t missionId, uint32_t taskType, int32_t value, bool acceptMission) {
|
||||
auto* mission = GetMission(missionId);
|
||||
|
||||
if (!mission) {
|
||||
if (mission == nullptr) {
|
||||
if (!acceptMission) {
|
||||
return;
|
||||
}
|
||||
@@ -212,11 +264,11 @@ void MissionComponent::ForceProgressValue(const uint32_t missionId, const uint32
|
||||
}
|
||||
}
|
||||
|
||||
bool MissionComponent::GetMissionInfo(const uint32_t missionId, CDMissions& result) const {
|
||||
bool MissionComponent::GetMissionInfo(uint32_t missionId, CDMissions& result) {
|
||||
auto* missionsTable = CDClientManager::Instance().GetTable<CDMissionsTable>();
|
||||
|
||||
const auto missions = missionsTable->Query([=](const CDMissions& entry) {
|
||||
return entry.id == static_cast<uint32_t>(missionId);
|
||||
return entry.id == static_cast<int>(missionId);
|
||||
});
|
||||
|
||||
if (missions.empty()) {
|
||||
@@ -228,7 +280,10 @@ bool MissionComponent::GetMissionInfo(const uint32_t missionId, CDMissions& resu
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MissionComponent::LookForAchievements(const eMissionTaskType type, const int32_t value, const bool progress, const LWOOBJID& associate, const std::string& targets, const int32_t count) {
|
||||
#define MISSION_NEW_METHOD
|
||||
|
||||
bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, bool progress, LWOOBJID associate, const std::string& targets, int32_t count) {
|
||||
#ifdef MISSION_NEW_METHOD
|
||||
// Query for achievments, using the cache
|
||||
const auto& result = QueryAchievements(type, value, targets);
|
||||
|
||||
@@ -236,7 +291,9 @@ bool MissionComponent::LookForAchievements(const eMissionTaskType type, const in
|
||||
|
||||
for (const uint32_t missionID : result) {
|
||||
// Check if we already have this achievement
|
||||
if (GetMission(missionID)) continue;
|
||||
if (GetMission(missionID) != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we can accept this achievement
|
||||
if (!MissionPrerequisites::CanAccept(missionID, m_Missions)) {
|
||||
@@ -254,15 +311,87 @@ bool MissionComponent::LookForAchievements(const eMissionTaskType type, const in
|
||||
|
||||
any = true;
|
||||
|
||||
if (!progress) continue;
|
||||
// Progress mission to bring it up to speed
|
||||
instance->Progress(type, value, associate, targets, count);
|
||||
if (progress) {
|
||||
// Progress mission to bring it up to speed
|
||||
instance->Progress(type, value, associate, targets, count);
|
||||
}
|
||||
}
|
||||
|
||||
return any;
|
||||
#else
|
||||
auto* missionTasksTable = CDClientManager::Instance().GetTable<CDMissionTasksTable>();
|
||||
auto* missionsTable = CDClientManager::Instance().GetTable<CDMissionsTable>();
|
||||
|
||||
auto tasks = missionTasksTable->Query([=](const CDMissionTasks& entry) {
|
||||
return entry.taskType == static_cast<unsigned>(type);
|
||||
});
|
||||
|
||||
auto any = false;
|
||||
|
||||
for (const auto& task : tasks) {
|
||||
if (GetMission(task.id) != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto missionEntries = missionsTable->Query([=](const CDMissions& entry) {
|
||||
return entry.id == static_cast<int>(task.id) && !entry.isMission;
|
||||
});
|
||||
|
||||
if (missionEntries.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto mission = missionEntries[0];
|
||||
|
||||
if (mission.isMission || !MissionPrerequisites::CanAccept(mission.id, m_Missions)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (task.target != value && task.targetGroup != targets) {
|
||||
auto stream = std::istringstream(task.targetGroup);
|
||||
std::string token;
|
||||
|
||||
auto found = false;
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
try {
|
||||
const auto target = std::stoul(token);
|
||||
|
||||
found = target == value;
|
||||
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
} catch (std::invalid_argument& exception) {
|
||||
Game::logger->Log("MissionComponent", "Failed to parse target (%s): (%s)!", token.c_str(), exception.what());
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto* instance = new Mission(this, mission.id);
|
||||
|
||||
m_Missions.insert_or_assign(mission.id, instance);
|
||||
|
||||
if (instance->IsMission()) instance->SetUniqueMissionOrderID(++m_LastUsedMissionOrderUID);
|
||||
|
||||
instance->Accept();
|
||||
|
||||
any = true;
|
||||
|
||||
if (progress) {
|
||||
instance->Progress(type, value, associate, targets, count);
|
||||
}
|
||||
}
|
||||
|
||||
return any;
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& MissionComponent::QueryAchievements(const eMissionTaskType type, const int32_t value, const std::string& targets) {
|
||||
const std::vector<uint32_t>& MissionComponent::QueryAchievements(eMissionTaskType type, int32_t value, const std::string targets) {
|
||||
// Create a hash which represent this query for achievements
|
||||
AchievementCacheKey toFind;
|
||||
toFind.SetType(type);
|
||||
@@ -291,68 +420,95 @@ const std::vector<uint32_t>& MissionComponent::QueryAchievements(const eMissionT
|
||||
// Seek the assosicated mission
|
||||
auto foundMission = false;
|
||||
|
||||
const auto& cdMission = missionsTable->GetByMissionID(task.id, foundMission);
|
||||
const auto& mission = missionsTable->GetByMissionID(task.id, foundMission);
|
||||
|
||||
if (!foundMission || cdMission.isMission) continue;
|
||||
if (!foundMission || mission.isMission) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare the easy values
|
||||
if (task.target == value || task.targetGroup == targets) {
|
||||
result.push_back(cdMission.id);
|
||||
result.push_back(mission.id);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare the target group, array separated by ','
|
||||
for (const auto& possibleMissionStr : GeneralUtils::SplitString(task.targetGroup, ',')) {
|
||||
uint32_t possibleMission;
|
||||
if (GeneralUtils::TryParse(possibleMissionStr, possibleMission) && possibleMission == value) {
|
||||
result.push_back(cdMission.id);
|
||||
auto stream = std::istringstream(task.targetGroup);
|
||||
std::string token;
|
||||
|
||||
break;
|
||||
while (std::getline(stream, token, ',')) {
|
||||
try {
|
||||
if (std::stoi(token) == value) {
|
||||
result.push_back(mission.id);
|
||||
|
||||
continue;
|
||||
}
|
||||
} catch (std::invalid_argument& exception) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
// Insert into cache and return the inserted value.
|
||||
return m_AchievementCache.insert_or_assign(toFind, result).first->second;
|
||||
// Insert into cache
|
||||
m_AchievementCache.insert_or_assign(toFind, result);
|
||||
return m_AchievementCache.find(toFind)->second;
|
||||
}
|
||||
|
||||
bool MissionComponent::RequiresItem(const LOT lot) {
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT type FROM Objects WHERE id = ?;");
|
||||
query.bind(1, static_cast<int>(lot));
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT type FROM Objects WHERE id = ?;");
|
||||
query.bind(1, (int)lot);
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof()) return false;
|
||||
if (result.eof()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!result.fieldIsNull(0)) {
|
||||
const auto type = std::string(result.getStringField(0));
|
||||
|
||||
if (type == "Powerup") return true;
|
||||
result.finalize();
|
||||
|
||||
if (type == "Powerup") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [missionId, mission] : m_Missions) {
|
||||
if (mission->IsComplete()) continue;
|
||||
result.finalize();
|
||||
|
||||
for (const auto& pair : m_Missions) {
|
||||
auto* mission = pair.second;
|
||||
|
||||
if (mission->IsComplete()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto* task : mission->GetTasks()) {
|
||||
if (task->IsComplete() || task->GetType() != eMissionTaskType::GATHER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!task->InAllTargets(lot)) continue;
|
||||
if (!task->InAllTargets(lot)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return LookForAchievements(eMissionTaskType::GATHER, lot, false);
|
||||
|
||||
const auto required = LookForAchievements(eMissionTaskType::GATHER, lot, false);
|
||||
|
||||
return required;
|
||||
}
|
||||
|
||||
|
||||
void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
if (!doc) return;
|
||||
if (doc == nullptr) return;
|
||||
|
||||
auto* mis = doc->FirstChildElement("obj")->FirstChildElement("mis");
|
||||
|
||||
if (!mis) return;
|
||||
if (mis == nullptr) return;
|
||||
|
||||
auto* cur = mis->FirstChildElement("cur");
|
||||
auto* done = mis->FirstChildElement("done");
|
||||
@@ -360,7 +516,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
auto* doneM = done->FirstChildElement();
|
||||
|
||||
while (doneM) {
|
||||
uint32_t missionId;
|
||||
int missionId;
|
||||
|
||||
doneM->QueryAttribute("id", &missionId);
|
||||
|
||||
@@ -377,7 +533,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
uint32_t missionOrder{};
|
||||
while (currentM) {
|
||||
uint32_t missionId;
|
||||
int missionId;
|
||||
|
||||
currentM->QueryAttribute("id", &missionId);
|
||||
|
||||
@@ -387,7 +543,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
|
||||
mission->SetUniqueMissionOrderID(missionOrder);
|
||||
m_LastUsedMissionOrderUID = std::max(missionOrder, m_LastUsedMissionOrderUID);
|
||||
if (missionOrder > m_LastUsedMissionOrderUID) m_LastUsedMissionOrderUID = missionOrder;
|
||||
}
|
||||
|
||||
currentM = currentM->NextSiblingElement();
|
||||
@@ -398,7 +554,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
|
||||
void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
if (!doc) return;
|
||||
if (doc == nullptr) return;
|
||||
|
||||
auto shouldInsertMis = false;
|
||||
|
||||
@@ -406,7 +562,7 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
auto* mis = obj->FirstChildElement("mis");
|
||||
|
||||
if (!mis) {
|
||||
if (mis == nullptr) {
|
||||
mis = doc->NewElement("mis");
|
||||
|
||||
shouldInsertMis = true;
|
||||
@@ -417,33 +573,50 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
auto* done = doc->NewElement("done");
|
||||
auto* cur = doc->NewElement("cur");
|
||||
|
||||
for (const auto& [missionId, mission] : m_Missions) {
|
||||
if (!mission) continue;
|
||||
const auto complete = mission->IsComplete();
|
||||
for (const auto& pair : m_Missions) {
|
||||
auto* mission = pair.second;
|
||||
|
||||
auto* missionElement = doc->NewElement("m");
|
||||
if (mission) {
|
||||
const auto complete = mission->IsComplete();
|
||||
|
||||
if (!complete && mission->IsMission()) missionElement->SetAttribute("o", mission->GetUniqueMissionOrderID());
|
||||
auto* m = doc->NewElement("m");
|
||||
|
||||
mission->UpdateXml(missionElement);
|
||||
if (complete) {
|
||||
mission->UpdateXml(m);
|
||||
|
||||
cur->LinkEndChild(missionElement);
|
||||
done->LinkEndChild(m);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
|
||||
|
||||
mission->UpdateXml(m);
|
||||
|
||||
cur->LinkEndChild(m);
|
||||
}
|
||||
}
|
||||
|
||||
mis->InsertFirstChild(done);
|
||||
mis->InsertEndChild(cur);
|
||||
|
||||
if (shouldInsertMis) obj->LinkEndChild(mis);
|
||||
if (shouldInsertMis) {
|
||||
obj->LinkEndChild(mis);
|
||||
}
|
||||
}
|
||||
|
||||
void MissionComponent::AddCollectible(const int32_t collectibleID) {
|
||||
if (!HasCollectible(collectibleID)) m_Collectibles.push_back(collectibleID);
|
||||
void MissionComponent::AddCollectible(int32_t collectibleID) {
|
||||
// Check if this collectible is already in the list
|
||||
if (HasCollectible(collectibleID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Collectibles.push_back(collectibleID);
|
||||
}
|
||||
|
||||
bool MissionComponent::HasCollectible(const int32_t collectibleID) const {
|
||||
bool MissionComponent::HasCollectible(int32_t collectibleID) {
|
||||
return std::find(m_Collectibles.begin(), m_Collectibles.end(), collectibleID) != m_Collectibles.end();
|
||||
}
|
||||
|
||||
bool MissionComponent::HasMission(const uint32_t missionId) const {
|
||||
bool MissionComponent::HasMission(uint32_t missionId) {
|
||||
return GetMission(missionId) != nullptr;
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ class AchievementCacheKey;
|
||||
* The mission inventory of an entity. Tracks mission state for each mission that can be accepted and allows for
|
||||
* progression of each of the mission task types (see eMissionTaskType).
|
||||
*/
|
||||
class MissionComponent final : public Component
|
||||
class MissionComponent : public Component
|
||||
{
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION;
|
||||
|
||||
explicit MissionComponent(Entity* parent);
|
||||
~MissionComponent() override;
|
||||
@@ -39,35 +39,35 @@ public:
|
||||
* Returns all the missions for this entity, mapped by mission ID
|
||||
* @return the missions for this entity, mapped by mission ID
|
||||
*/
|
||||
const std::unordered_map<uint32_t, Mission*>& GetMissions() const { return m_Missions; };
|
||||
const std::unordered_map<uint32_t, Mission*>& GetMissions() const;
|
||||
|
||||
/**
|
||||
* Returns the mission for the given mission ID, if it exists
|
||||
* @param missionId the id of the mission to get
|
||||
* @return the mission for the given mission ID
|
||||
*/
|
||||
Mission* GetMission(const uint32_t missionId) const;
|
||||
Mission* GetMission(uint32_t missionId) const;
|
||||
|
||||
/**
|
||||
* Returns the current state of the entities progression for the mission of the specified ID
|
||||
* @param missionId the ID of the mission to get the mission state for
|
||||
* @return the mission state of the mission specified by the ID
|
||||
*/
|
||||
eMissionState GetMissionState(const uint32_t missionId) const;
|
||||
eMissionState GetMissionState(uint32_t missionId) const;
|
||||
|
||||
/**
|
||||
* Checks if the entity has all the requirements for accepting the mission specified by the ID.
|
||||
* @param missionId the mission ID to check for if the character may accept it
|
||||
* @return whether this entity can accept the mission represented by the given mission ID
|
||||
*/
|
||||
bool CanAccept(const uint32_t missionId) const;
|
||||
bool CanAccept(uint32_t missionId) const;
|
||||
|
||||
/**
|
||||
* Accepts the mission specified by the ID, if the entity may accept it. Also stores it in the mission inventory.
|
||||
* @param missionId the ID of the mission to accept
|
||||
* @param skipChecks skips the checks for the mission prerequisites
|
||||
*/
|
||||
void AcceptMission(const uint32_t missionId, const bool skipChecks = false);
|
||||
void AcceptMission(uint32_t missionId, bool skipChecks = false);
|
||||
|
||||
/**
|
||||
* Completes the mission specified by the given ID, if the entity has fulfilled all progress requirements.
|
||||
@@ -75,13 +75,13 @@ public:
|
||||
* @param skipChecks skips the checks for having completed all of the mission tasks
|
||||
* @param yieldRewards whether to yield mission rewards, currently unused
|
||||
*/
|
||||
void CompleteMission(const uint32_t missionId, const bool skipChecks = false, const bool yieldRewards = true);
|
||||
void CompleteMission(uint32_t missionId, bool skipChecks = false, bool yieldRewards = true);
|
||||
|
||||
/**
|
||||
* Removes the mission from the entities' mission chain. Not used for normal gameplay but useful for debugging.
|
||||
* @param missionId the ID of the mission to remove
|
||||
*/
|
||||
void RemoveMission(const uint32_t missionId);
|
||||
void RemoveMission(uint32_t missionId);
|
||||
|
||||
/**
|
||||
* Attempts to progress mission tasks for a given type using parameters to progress. Note that this function is
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
* @param count the number to progress by, for example the number of items
|
||||
* @param ignoreAchievements do not progress achievements
|
||||
*/
|
||||
void Progress(const eMissionTaskType type, const int32_t value, const LWOOBJID& associate = 0, const std::string& targets = "", const int32_t count = 1, const bool ignoreAchievements = false);
|
||||
void Progress(eMissionTaskType type, int32_t value, LWOOBJID associate = 0, const std::string& targets = "", int32_t count = 1, bool ignoreAchievements = false);
|
||||
|
||||
/**
|
||||
* Forces progression for a mission and task, ignoring checks
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
* @param value the value to progress with
|
||||
* @param acceptMission accept the mission if it was not already accepted
|
||||
*/
|
||||
void ForceProgress(const uint32_t missionId, const uint32_t taskId, const int32_t value, const bool acceptMission = true);
|
||||
void ForceProgress(uint32_t missionId, uint32_t taskId, int32_t value, bool acceptMission = true);
|
||||
|
||||
/**
|
||||
* Forces progress for all tasks of a certain type that belong to the same mission
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
* @param value the value to progress with
|
||||
* @param acceptMission accept the mission if it wasn't already
|
||||
*/
|
||||
void ForceProgressTaskType(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission = true);
|
||||
void ForceProgressTaskType(uint32_t missionId, uint32_t taskType, int32_t value, bool acceptMission = true);
|
||||
|
||||
/**
|
||||
* Force progresses by checking the value and progressing by 1
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
* @param value the value to check the mission values before progressing
|
||||
* @param acceptMission accept the mission if it wasn't already
|
||||
*/
|
||||
void ForceProgressValue(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission = true);
|
||||
void ForceProgressValue(uint32_t missionId, uint32_t taskType, int32_t value, bool acceptMission = true);
|
||||
|
||||
/**
|
||||
* Returns client database mission information for a mission
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
* @param result the result to store the information in
|
||||
* @return true if the information was succesfully retrieved, false otherwise
|
||||
*/
|
||||
bool GetMissionInfo(const uint32_t missionId, CDMissions& result) const;
|
||||
bool GetMissionInfo(uint32_t missionId, CDMissions& result);
|
||||
|
||||
/**
|
||||
* Checks if there's any achievements we might be able to accept for the given parameters
|
||||
@@ -141,34 +141,34 @@ public:
|
||||
* @param count the number of values to progress by (differs by task type)
|
||||
* @return true if a achievement was accepted, false otherwise
|
||||
*/
|
||||
bool LookForAchievements(const eMissionTaskType type, const int32_t value, const bool progress = true, const LWOOBJID& associate = LWOOBJID_EMPTY, const std::string& targets = "", const int32_t count = 1);
|
||||
bool LookForAchievements(eMissionTaskType type, int32_t value, bool progress = true, LWOOBJID associate = LWOOBJID_EMPTY, const std::string& targets = "", int32_t count = 1);
|
||||
|
||||
/**
|
||||
* Checks if there's a mission active that requires the collection of the specified LOT
|
||||
* @param lot the LOT to check for
|
||||
* @return if there's a mission active that requires the collection of the specified LOT
|
||||
*/
|
||||
bool RequiresItem(const LOT lot);
|
||||
bool RequiresItem(LOT lot);
|
||||
|
||||
/**
|
||||
* Collects a collectable for the entity, unrendering it for the entity
|
||||
* @param collectibleID the ID of the collectable to add
|
||||
*/
|
||||
void AddCollectible(const int32_t collectibleID);
|
||||
void AddCollectible(int32_t collectibleID);
|
||||
|
||||
/**
|
||||
* Checks if the entity already has a collectible of the specified ID
|
||||
* @param collectibleID the ID of the collectible to check
|
||||
* @return if the entity already has a collectible of the specified ID
|
||||
*/
|
||||
bool HasCollectible(const int32_t collectibleID) const;
|
||||
bool HasCollectible(int32_t collectibleID);
|
||||
|
||||
/**
|
||||
* Checks if the entity has a certain mission in its inventory
|
||||
* @param missionId the ID of the mission to check
|
||||
* @return if the entity has a certain mission in its inventory
|
||||
*/
|
||||
bool HasMission(const uint32_t missionId) const;
|
||||
bool HasMission(uint32_t missionId);
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -189,7 +189,7 @@ private:
|
||||
* @param targets optional targets to progress with
|
||||
* @return list of mission IDs (achievements) that can be progressed for the given parameters
|
||||
*/
|
||||
static const std::vector<uint32_t>& QueryAchievements(const eMissionTaskType type, const int32_t value, const std::string& targets);
|
||||
static const std::vector<uint32_t>& QueryAchievements(eMissionTaskType type, int32_t value, const std::string targets);
|
||||
|
||||
/**
|
||||
* As achievements can be hard to query, we here store a list of all the mission IDs that can be unlocked for a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Darkflame Universe
|
||||
* Copyright 2023
|
||||
* Copyright 2019
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
@@ -18,33 +18,68 @@
|
||||
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
|
||||
MissionOfferComponent::MissionOfferComponent(Entity* parent, const int32_t componentId) : Component(parent) {
|
||||
m_ComponentId = componentId;
|
||||
OfferedMission::OfferedMission(const uint32_t missionId, const bool offersMission, const bool acceptsMission) {
|
||||
this->missionId = missionId;
|
||||
this->offersMission = offersMission;
|
||||
this->acceptsMission = acceptsMission;
|
||||
}
|
||||
|
||||
void MissionOfferComponent::LoadTemplateData() {
|
||||
if (m_ComponentId == -1) return;
|
||||
// Now lookup the missions in the MissionNPCComponent table
|
||||
auto* missionNpcComponentTable = CDClientManager::Instance().GetTable<CDMissionNPCComponentTable>();
|
||||
|
||||
auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
|
||||
return entry.id == static_cast<int32_t>(m_ComponentId);
|
||||
});
|
||||
uint32_t OfferedMission::GetMissionId() const {
|
||||
return this->missionId;
|
||||
}
|
||||
|
||||
for (const auto& mission : missions) {
|
||||
this->offeredMissions.emplace_back(
|
||||
std::make_unique<OfferedMission>(mission.missionID, mission.offersMission, mission.acceptsMission)
|
||||
);
|
||||
bool OfferedMission::GetOfferMission() const {
|
||||
return this->offersMission;
|
||||
}
|
||||
|
||||
bool OfferedMission::GetAcceptMission() const {
|
||||
return this->acceptsMission;
|
||||
}
|
||||
|
||||
//------------------------ MissionOfferComponent below ------------------------
|
||||
|
||||
MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot) : Component(parent) {
|
||||
auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
|
||||
auto value = compRegistryTable->GetByIDAndType(parentLot, eReplicaComponentType::MISSION_OFFER, -1);
|
||||
|
||||
if (value != -1) {
|
||||
const uint32_t componentId = value;
|
||||
|
||||
// Now lookup the missions in the MissionNPCComponent table
|
||||
auto* missionNpcComponentTable = CDClientManager::Instance().GetTable<CDMissionNPCComponentTable>();
|
||||
|
||||
auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
|
||||
return entry.id == static_cast<unsigned>(componentId);
|
||||
});
|
||||
|
||||
for (auto& mission : missions) {
|
||||
auto* offeredMission = new OfferedMission(mission.missionID, mission.offersMission, mission.acceptsMission);
|
||||
this->offeredMissions.push_back(offeredMission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MissionOfferComponent::~MissionOfferComponent() {
|
||||
for (auto* mission : this->offeredMissions) {
|
||||
if (mission) {
|
||||
delete mission;
|
||||
mission = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
offeredMissions.clear();
|
||||
}
|
||||
|
||||
void MissionOfferComponent::OnUse(Entity* originator) {
|
||||
OfferMissions(originator);
|
||||
}
|
||||
|
||||
void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifiedMissionId) {
|
||||
// First, get the entity's MissionComponent. If there is not one, then we cannot offer missions to this entity.
|
||||
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
||||
auto* missionComponent = static_cast<MissionComponent*>(entity->GetComponent(eReplicaComponentType::MISSION));
|
||||
|
||||
if (!missionComponent) {
|
||||
Game::logger->Log("MissionOfferComponent", "Unable to get mission component for Entity %llu", entity->GetObjectID());
|
||||
@@ -53,15 +88,17 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
|
||||
|
||||
std::vector<uint32_t> offered{};
|
||||
|
||||
CDMissions missionInfo{};
|
||||
CDMissions info{};
|
||||
|
||||
if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, missionInfo)) {
|
||||
if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& offeredMission : offeredMissions) {
|
||||
if (specifiedMissionId > 0 && (offeredMission->GetMissionId() != specifiedMissionId && !missionInfo.isRandom)) {
|
||||
continue;
|
||||
for (auto* offeredMission : this->offeredMissions) {
|
||||
if (specifiedMissionId > 0) {
|
||||
if (offeredMission->GetMissionId() != specifiedMissionId && !info.isRandom) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// First, check if we already have the mission
|
||||
@@ -69,20 +106,17 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
|
||||
|
||||
auto* mission = missionComponent->GetMission(missionId);
|
||||
|
||||
if (mission) {
|
||||
if (mission != nullptr) {
|
||||
if (specifiedMissionId <= 0) {
|
||||
// Handles the odd case where the offer object should not display the mission again
|
||||
if (!mission->IsComplete() &&
|
||||
mission->GetClientInfo().offer_objectID == m_ParentEntity->GetLOT() &&
|
||||
mission->GetClientInfo().target_objectID != m_ParentEntity->GetLOT() &&
|
||||
mission->IsFetchMission()) {
|
||||
if (!mission->IsComplete() && mission->GetClientInfo().offer_objectID == m_Parent->GetLOT() && mission->GetClientInfo().target_objectID != m_Parent->GetLOT() && mission->IsFetchMission()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// We have the mission, if it is not complete, offer it
|
||||
if (mission->IsActive() || mission->IsReadyToComplete()) {
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_ParentEntity->GetObjectID());
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID());
|
||||
|
||||
offered.push_back(missionId);
|
||||
|
||||
@@ -93,30 +127,50 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
|
||||
const auto canAccept = MissionPrerequisites::CanAccept(missionId, missionComponent->GetMissions());
|
||||
|
||||
// Mission has not yet been accepted - check the prereqs
|
||||
if (!canAccept || !Mission::IsValidMission(missionId, missionInfo)) continue;
|
||||
if (!canAccept)
|
||||
continue;
|
||||
|
||||
// This means the mission is part of a random pool of missions.
|
||||
if (missionInfo.isRandom && missionInfo.randomPool.empty()) continue;
|
||||
if (!Mission::IsValidMission(missionId, info)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (missionInfo.isRandom && !missionInfo.randomPool.empty()) {
|
||||
auto randomMissionPoolStr = GeneralUtils::SplitString(missionInfo.randomPool, ',');
|
||||
const auto& randomPool = info.randomPool;
|
||||
const auto isRandom = info.isRandom;
|
||||
|
||||
std::vector<uint32_t> randomMissions;
|
||||
for (const auto& randomMissionStr : randomMissionPoolStr) {
|
||||
uint32_t randomMission;
|
||||
if (GeneralUtils::TryParse(randomMissionStr, randomMission)) randomMissions.push_back(randomMission);
|
||||
if (isRandom && randomPool.empty()) // This means the mission is part of a random pool of missions.
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isRandom && !randomPool.empty()) {
|
||||
std::istringstream stream(randomPool);
|
||||
std::string token;
|
||||
|
||||
std::vector<uint32_t> randomMissionPool;
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
try {
|
||||
const auto value = std::stoul(token);
|
||||
|
||||
randomMissionPool.push_back(value);
|
||||
} catch (std::invalid_argument& exception) {
|
||||
Game::logger->Log("MissionOfferComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
||||
}
|
||||
}
|
||||
|
||||
if (specifiedMissionId > 0) {
|
||||
if (std::find(randomMissions.begin(), randomMissions.end(), specifiedMissionId) != randomMissions.end() &&
|
||||
MissionPrerequisites::CanAccept(specifiedMissionId, missionComponent->GetMissions())) {
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), specifiedMissionId, m_ParentEntity->GetObjectID());
|
||||
const auto& iter = std::find(randomMissionPool.begin(), randomMissionPool.end(), specifiedMissionId);
|
||||
|
||||
if (iter != randomMissionPool.end() && MissionPrerequisites::CanAccept(specifiedMissionId, missionComponent->GetMissions())) {
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), specifiedMissionId, m_Parent->GetObjectID());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> canAcceptPool;
|
||||
for (const auto& sample : randomMissions) {
|
||||
|
||||
for (const auto sample : randomMissionPool) {
|
||||
const auto state = missionComponent->GetMissionState(sample);
|
||||
|
||||
if (state == eMissionState::ACTIVE ||
|
||||
@@ -126,29 +180,31 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
|
||||
sample == specifiedMissionId) {
|
||||
mission = missionComponent->GetMission(sample);
|
||||
|
||||
if (!mission || mission->IsAchievement()) continue;
|
||||
if (mission == nullptr || mission->IsAchievement()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_ParentEntity->GetObjectID());
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_Parent->GetObjectID());
|
||||
|
||||
canAcceptPool.clear();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (std::find(offered.begin(), offered.end(), sample) == offered.end() &&
|
||||
MissionPrerequisites::CanAccept(sample, missionComponent->GetMissions())) {
|
||||
if (std::find(offered.begin(), offered.end(), sample) == offered.end() && MissionPrerequisites::CanAccept(sample, missionComponent->GetMissions())) {
|
||||
canAcceptPool.push_back(sample);
|
||||
}
|
||||
}
|
||||
|
||||
// If the mission is already active or we already completed one of them today
|
||||
if (canAcceptPool.empty()) continue;
|
||||
if (canAcceptPool.empty())
|
||||
continue;
|
||||
|
||||
const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber<int32_t>(0, canAcceptPool.size() - 1)];
|
||||
const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber<int>(0, canAcceptPool.size() - 1)];
|
||||
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), selected, m_ParentEntity->GetObjectID());
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), selected, m_Parent->GetObjectID());
|
||||
} else if (std::find(offered.begin(), offered.end(), missionId) == offered.end() && offeredMission->GetOfferMission()) {
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_ParentEntity->GetObjectID());
|
||||
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
/*
|
||||
* Darkflame Universe
|
||||
* Copyright 2023
|
||||
* Copyright 2019
|
||||
*/
|
||||
|
||||
#ifndef __MISSIONOFFERCOMPONENT_H__
|
||||
#define __MISSIONOFFERCOMPONENT_H__
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#ifndef MISSIONOFFERCOMPONENT_H
|
||||
#define MISSIONOFFERCOMPONENT_H
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "Component.h"
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
@@ -21,29 +18,25 @@ class Entity;
|
||||
* Light wrapper around missions that may be offered by an entity
|
||||
*/
|
||||
struct OfferedMission {
|
||||
OfferedMission(const uint32_t missionId, const bool offersMission, const bool acceptsMission) {
|
||||
this->missionId = missionId;
|
||||
this->offersMission = offersMission;
|
||||
this->acceptsMission = acceptsMission;
|
||||
};
|
||||
OfferedMission(uint32_t missionId, bool offersMission, bool acceptsMission);
|
||||
|
||||
/**
|
||||
* Returns the ID of the mission
|
||||
* @return the ID of the mission
|
||||
*/
|
||||
uint32_t GetMissionId() const { return missionId; };
|
||||
uint32_t GetMissionId() const;
|
||||
|
||||
/**
|
||||
* Returns if this mission is offered by the entity
|
||||
* @return true if this mission is offered by the entity, false otherwise
|
||||
*/
|
||||
bool GetOfferMission() const { return offersMission; };
|
||||
bool GetOfferMission() const;
|
||||
|
||||
/**
|
||||
* Returns if this mission may be accepted by the entity (currently unused)
|
||||
* @return true if this mission may be accepted by the entity, false otherwise
|
||||
*/
|
||||
bool GetAcceptMission() const { return acceptsMission; };
|
||||
bool GetAcceptMission() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -66,13 +59,12 @@ private:
|
||||
/**
|
||||
* Allows entities to offer missions to other entities, depending on their mission inventory progression.
|
||||
*/
|
||||
class MissionOfferComponent final : public Component {
|
||||
class MissionOfferComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER;
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER;
|
||||
|
||||
MissionOfferComponent(Entity* parent, const int32_t componentId = -1);
|
||||
|
||||
void LoadTemplateData() override;
|
||||
MissionOfferComponent(Entity* parent, LOT parentLot);
|
||||
~MissionOfferComponent() override;
|
||||
|
||||
/**
|
||||
* Handles the OnUse event triggered by some entity, determines which missions to show based on what they may
|
||||
@@ -93,9 +85,7 @@ private:
|
||||
/**
|
||||
* The missions this entity has to offer
|
||||
*/
|
||||
std::vector<std::unique_ptr<OfferedMission>> offeredMissions;
|
||||
|
||||
int32_t m_ComponentId;
|
||||
std::vector<OfferedMission*> offeredMissions;
|
||||
};
|
||||
|
||||
#endif // __MISSIONOFFERCOMPONENT_H__
|
||||
#endif // MISSIONOFFERCOMPONENT_H
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#include "ModelBehaviorComponent.h"
|
||||
#include "Entity.h"
|
||||
#include "ePhysicsBehaviorType.h"
|
||||
|
||||
ModelBehaviorComponent::ModelBehaviorComponent(Entity* parent) : Component(parent) {
|
||||
m_DirtyModelInfo = true;
|
||||
m_IsPickable = false;
|
||||
m_PhysicsType = ePhysicsBehaviorType::STANDARD;
|
||||
m_OriginalPosition = m_ParentEntity->GetDefaultPosition();
|
||||
m_OriginalRotation = m_ParentEntity->GetDefaultRotation();
|
||||
}
|
||||
|
||||
void ModelBehaviorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(m_DirtyModelInfo || bIsInitialUpdate);
|
||||
if (m_DirtyModelInfo || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_IsPickable);
|
||||
outBitStream->Write(m_PhysicsType);
|
||||
outBitStream->Write(m_OriginalPosition);
|
||||
outBitStream->Write(m_OriginalRotation);
|
||||
if (!bIsInitialUpdate) m_DirtyModelInfo = false;
|
||||
}
|
||||
}
|
||||
|
||||
31
dGame/dComponents/ModelComponent.cpp
Normal file
31
dGame/dComponents/ModelComponent.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "ModelComponent.h"
|
||||
#include "Entity.h"
|
||||
|
||||
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
||||
m_OriginalRotation = m_Parent->GetDefaultRotation();
|
||||
|
||||
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
||||
}
|
||||
|
||||
void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
// ItemComponent Serialization. Pets do not get this serialization.
|
||||
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write<LWOOBJID>(m_userModelID != LWOOBJID_EMPTY ? m_userModelID : m_Parent->GetObjectID());
|
||||
outBitStream->Write<int>(0);
|
||||
outBitStream->Write0();
|
||||
}
|
||||
|
||||
//actual model component:
|
||||
outBitStream->Write1(); // Yes we are writing model info
|
||||
outBitStream->Write0(); // Is pickable
|
||||
outBitStream->Write<uint32_t>(2); // Physics type
|
||||
outBitStream->Write(m_OriginalPosition); // Original position
|
||||
outBitStream->Write(m_OriginalRotation); // Original rotation
|
||||
|
||||
outBitStream->Write1(); // We are writing behavior info
|
||||
outBitStream->Write<uint32_t>(0); // Number of behaviors
|
||||
outBitStream->Write1(); // Is this model paused
|
||||
if (bIsInitialUpdate) outBitStream->Write0(); // We are not writing model editing info
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user