mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-04-17 04:57:49 +00:00
Merge branch 'main' into moreMovementAi
This commit is contained in:
@@ -2,64 +2,33 @@ set(DGAME_SOURCES "Character.cpp"
|
||||
"Entity.cpp"
|
||||
"EntityManager.cpp"
|
||||
"LeaderboardManager.cpp"
|
||||
"Player.cpp"
|
||||
"PlayerManager.cpp"
|
||||
"TeamManager.cpp"
|
||||
"TradingManager.cpp"
|
||||
"User.cpp"
|
||||
"UserManager.cpp")
|
||||
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}/dScripts
|
||||
${PROJECT_SOURCE_DIR}/dGame
|
||||
)
|
||||
|
||||
add_library(dGameBase ${DGAME_SOURCES})
|
||||
target_precompile_headers(dGameBase PRIVATE ${HEADERS_DGAME})
|
||||
target_link_libraries(dGameBase
|
||||
PUBLIC dDatabase dPhysics
|
||||
INTERFACE dComponents dEntity)
|
||||
|
||||
add_subdirectory(dBehaviors)
|
||||
|
||||
foreach(file ${DGAME_DBEHAVIORS_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dBehaviors/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dComponents)
|
||||
|
||||
foreach(file ${DGAME_DCOMPONENTS_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dComponents/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dEntity)
|
||||
|
||||
foreach(file ${DGAME_DENTITY_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dEntity/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dGameMessages)
|
||||
|
||||
foreach(file ${DGAME_DGAMEMESSAGES_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dGameMessages/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dInventory)
|
||||
|
||||
foreach(file ${DGAME_DINVENTORY_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dInventory/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dMission)
|
||||
|
||||
foreach(file ${DGAME_DMISSION_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dMission/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dPropertyBehaviors)
|
||||
|
||||
foreach(file ${DGAME_DPROPERTYBEHAVIORS_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dPropertyBehaviors/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dUtilities)
|
||||
|
||||
foreach(file ${DGAME_DUTILITIES_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dUtilities/${file}")
|
||||
endforeach()
|
||||
|
||||
foreach(file ${DSCRIPTS_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "${PROJECT_SOURCE_DIR}/dScripts/${file}")
|
||||
endforeach()
|
||||
|
||||
add_library(dGame STATIC ${DGAME_SOURCES})
|
||||
|
||||
target_link_libraries(dGame dDatabase Recast Detour)
|
||||
add_library(dGame INTERFACE)
|
||||
target_link_libraries(dGame INTERFACE
|
||||
dGameBase dBehaviors dComponents dEntity dGameMessages dInventory dMission dPropertyBehaviors dUtilities dScripts
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "User.h"
|
||||
#include "Database.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "BitStream.h"
|
||||
#include "Game.h"
|
||||
#include <chrono>
|
||||
@@ -25,104 +25,36 @@
|
||||
Character::Character(uint32_t id, User* parentUser) {
|
||||
//First load the name, etc:
|
||||
m_ID = id;
|
||||
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;"
|
||||
);
|
||||
|
||||
stmt->setInt64(1, id);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
|
||||
while (res->next()) {
|
||||
m_Name = res->getString(1).c_str();
|
||||
m_UnapprovedName = res->getString(2).c_str();
|
||||
m_NameRejected = res->getBoolean(3);
|
||||
m_PropertyCloneID = res->getUInt(4);
|
||||
m_PermissionMap = static_cast<ePermissionMap>(res->getUInt64(5));
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
//Load the xmlData now:
|
||||
sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt(
|
||||
"SELECT xml_data FROM charxml WHERE id=? LIMIT 1;"
|
||||
);
|
||||
|
||||
xmlStmt->setInt64(1, id);
|
||||
|
||||
sql::ResultSet* xmlRes = xmlStmt->executeQuery();
|
||||
while (xmlRes->next()) {
|
||||
m_XMLData = xmlRes->getString(1).c_str();
|
||||
}
|
||||
|
||||
delete xmlRes;
|
||||
delete xmlStmt;
|
||||
|
||||
m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing.
|
||||
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
||||
m_ZoneCloneID = 0;
|
||||
|
||||
m_Doc = nullptr;
|
||||
|
||||
//Quickly and dirtly parse the xmlData to get the info we need:
|
||||
DoQuickXMLDataParse();
|
||||
|
||||
//Set our objectID:
|
||||
m_ObjectID = m_ID;
|
||||
GeneralUtils::SetBit(m_ObjectID, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(m_ObjectID, eObjectBits::PERSISTENT);
|
||||
|
||||
m_ParentUser = parentUser;
|
||||
m_OurEntity = nullptr;
|
||||
m_BuildMode = false;
|
||||
m_Doc = nullptr;
|
||||
}
|
||||
|
||||
Character::~Character() {
|
||||
delete m_Doc;
|
||||
if (m_Doc) delete m_Doc;
|
||||
m_Doc = nullptr;
|
||||
m_OurEntity = nullptr;
|
||||
m_ParentUser = nullptr;
|
||||
}
|
||||
|
||||
void Character::UpdateFromDatabase() {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;"
|
||||
);
|
||||
void Character::UpdateInfoFromDatabase() {
|
||||
auto charInfo = Database::Get()->GetCharacterInfo(m_ID);
|
||||
|
||||
stmt->setInt64(1, m_ID);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
|
||||
while (res->next()) {
|
||||
m_Name = res->getString(1).c_str();
|
||||
m_UnapprovedName = res->getString(2).c_str();
|
||||
m_NameRejected = res->getBoolean(3);
|
||||
m_PropertyCloneID = res->getUInt(4);
|
||||
m_PermissionMap = static_cast<ePermissionMap>(res->getUInt64(5));
|
||||
if (charInfo) {
|
||||
m_Name = charInfo->name;
|
||||
m_UnapprovedName = charInfo->pendingName;
|
||||
m_NameRejected = charInfo->needsRename;
|
||||
m_PropertyCloneID = charInfo->cloneId;
|
||||
m_PermissionMap = charInfo->permissionMap;
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
//Load the xmlData now:
|
||||
sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt(
|
||||
"SELECT xml_data FROM charxml WHERE id=? LIMIT 1;"
|
||||
);
|
||||
xmlStmt->setInt64(1, m_ID);
|
||||
|
||||
sql::ResultSet* xmlRes = xmlStmt->executeQuery();
|
||||
while (xmlRes->next()) {
|
||||
m_XMLData = xmlRes->getString(1).c_str();
|
||||
}
|
||||
|
||||
delete xmlRes;
|
||||
delete xmlStmt;
|
||||
m_XMLData = Database::Get()->GetCharacterXml(m_ID);
|
||||
|
||||
m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing.
|
||||
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
||||
m_ZoneCloneID = 0;
|
||||
|
||||
delete m_Doc;
|
||||
m_Doc = nullptr;
|
||||
|
||||
//Quickly and dirtly parse the xmlData to get the info we need:
|
||||
@@ -137,6 +69,11 @@ void Character::UpdateFromDatabase() {
|
||||
m_BuildMode = false;
|
||||
}
|
||||
|
||||
void Character::UpdateFromDatabase() {
|
||||
if (m_Doc) delete m_Doc;
|
||||
UpdateInfoFromDatabase();
|
||||
}
|
||||
|
||||
void Character::DoQuickXMLDataParse() {
|
||||
if (m_XMLData.size() == 0) return;
|
||||
|
||||
@@ -145,16 +82,16 @@ void Character::DoQuickXMLDataParse() {
|
||||
if (!m_Doc) return;
|
||||
|
||||
if (m_Doc->Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
|
||||
Game::logger->Log("Character", "Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID);
|
||||
LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID);
|
||||
} else {
|
||||
Game::logger->Log("Character", "Failed to load xmlData!");
|
||||
LOG("Failed to load xmlData!");
|
||||
//Server::rakServer->CloseConnection(m_ParentUser->GetSystemAddress(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* mf = m_Doc->FirstChildElement("obj")->FirstChildElement("mf");
|
||||
if (!mf) {
|
||||
Game::logger->Log("Character", "Failed to find mf tag!");
|
||||
LOG("Failed to find mf tag!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -173,14 +110,14 @@ void Character::DoQuickXMLDataParse() {
|
||||
|
||||
tinyxml2::XMLElement* inv = m_Doc->FirstChildElement("obj")->FirstChildElement("inv");
|
||||
if (!inv) {
|
||||
Game::logger->Log("Character", "Char has no inv!");
|
||||
LOG("Char has no inv!");
|
||||
return;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* bag = inv->FirstChildElement("items")->FirstChildElement("in");
|
||||
|
||||
if (!bag) {
|
||||
Game::logger->Log("Character", "Couldn't find bag0!");
|
||||
LOG("Couldn't find bag0!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -314,7 +251,7 @@ void Character::SaveXMLToDatabase() {
|
||||
|
||||
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
||||
// lzid garbage, binary concat of zoneID, zoneInstance and zoneClone
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
|
||||
uint64_t lzidConcat = zoneInfo.GetCloneID();
|
||||
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID());
|
||||
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID());
|
||||
@@ -373,7 +310,7 @@ void Character::SaveXMLToDatabase() {
|
||||
|
||||
//Call upon the entity to update our xmlDoc:
|
||||
if (!m_OurEntity) {
|
||||
Game::logger->Log("Character", "%i:%s didn't have an entity set while saving! CHARACTER WILL NOT BE SAVED!", this->GetID(), this->GetName().c_str());
|
||||
LOG("%i:%s didn't have an entity set while saving! CHARACTER WILL NOT BE SAVED!", this->GetID(), this->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -384,7 +321,7 @@ void Character::SaveXMLToDatabase() {
|
||||
//For metrics, log the time it took to save:
|
||||
auto end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed = end - start;
|
||||
Game::logger->Log("Character", "%i:%s Saved character to Database in: %fs", this->GetID(), this->GetName().c_str(), elapsed.count());
|
||||
LOG("%i:%s Saved character to Database in: %fs", this->GetID(), this->GetName().c_str(), elapsed.count());
|
||||
}
|
||||
|
||||
void Character::SetIsNewLogin() {
|
||||
@@ -394,28 +331,23 @@ void Character::SetIsNewLogin() {
|
||||
|
||||
auto* currentChild = flags->FirstChildElement();
|
||||
while (currentChild) {
|
||||
auto* nextChild = currentChild->NextSiblingElement();
|
||||
if (currentChild->Attribute("si")) {
|
||||
flags->DeleteChild(currentChild);
|
||||
Game::logger->Log("Character", "Removed isLoggedIn flag from character %i:%s, saving character to database", GetID(), GetName().c_str());
|
||||
LOG("Removed isLoggedIn flag from character %i:%s, saving character to database", GetID(), GetName().c_str());
|
||||
WriteToDatabase();
|
||||
}
|
||||
currentChild = currentChild->NextSiblingElement();
|
||||
currentChild = nextChild;
|
||||
}
|
||||
}
|
||||
|
||||
void Character::WriteToDatabase() {
|
||||
//Dump our xml into m_XMLData:
|
||||
auto* printer = new tinyxml2::XMLPrinter(0, true, 0);
|
||||
m_Doc->Print(printer);
|
||||
m_XMLData = printer->CStr();
|
||||
tinyxml2::XMLPrinter printer(0, true, 0);
|
||||
m_Doc->Print(&printer);
|
||||
|
||||
//Finally, save to db:
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charxml SET xml_data=? WHERE id=?");
|
||||
stmt->setString(1, m_XMLData.c_str());
|
||||
stmt->setUInt(2, m_ID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
delete printer;
|
||||
Database::Get()->UpdateCharacterXml(m_ID, printer.CStr());
|
||||
}
|
||||
|
||||
void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||
@@ -519,7 +451,7 @@ void Character::LoadXmlRespawnCheckpoints() {
|
||||
auto* r = points->FirstChildElement("r");
|
||||
while (r != nullptr) {
|
||||
int32_t map = 0;
|
||||
NiPoint3 point = NiPoint3::ZERO;
|
||||
NiPoint3 point = NiPoint3Constant::ZERO;
|
||||
|
||||
r->QueryAttribute("w", &map);
|
||||
r->QueryAttribute("x", &point.x);
|
||||
@@ -554,15 +486,6 @@ void Character::OnZoneLoad() {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restrict old character to 1 million coins
|
||||
*/
|
||||
if (HasPermission(ePermissionMap::Old)) {
|
||||
if (GetCoins() > 1000000) {
|
||||
SetCoins(1000000, eLootSourceType::NONE);
|
||||
}
|
||||
}
|
||||
|
||||
auto* inventoryComponent = m_OurEntity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
@@ -590,7 +513,7 @@ void Character::SetRespawnPoint(LWOMAPID map, const NiPoint3& point) {
|
||||
const NiPoint3& Character::GetRespawnPoint(LWOMAPID map) const {
|
||||
const auto& pair = m_WorldRespawnCheckpoints.find(map);
|
||||
|
||||
if (pair == m_WorldRespawnCheckpoints.end()) return NiPoint3::ZERO;
|
||||
if (pair == m_WorldRespawnCheckpoints.end()) return NiPoint3Constant::ZERO;
|
||||
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include <vector>
|
||||
#include "../thirdparty/tinyxml2/tinyxml2.h"
|
||||
#include "tinyxml2.h"
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
|
||||
@@ -457,7 +457,10 @@ public:
|
||||
|
||||
void SetBillboardVisible(bool visible);
|
||||
|
||||
User* GetParentUser() const { return m_ParentUser; }
|
||||
|
||||
private:
|
||||
void UpdateInfoFromDatabase();
|
||||
/**
|
||||
* The ID of this character. First 32 bits of the ObjectID.
|
||||
*/
|
||||
|
||||
734
dGame/Entity.cpp
734
dGame/Entity.cpp
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ class Component;
|
||||
class Item;
|
||||
class Character;
|
||||
class EntityCallbackTimer;
|
||||
class PositionUpdate;
|
||||
enum class eTriggerEventType;
|
||||
enum class eGameMasterLevel : uint8_t;
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
@@ -46,10 +47,10 @@ namespace CppScripts {
|
||||
*/
|
||||
class Entity {
|
||||
public:
|
||||
explicit Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity = nullptr);
|
||||
virtual ~Entity();
|
||||
explicit Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser = nullptr, Entity* parentEntity = nullptr);
|
||||
~Entity();
|
||||
|
||||
virtual void Initialize();
|
||||
void Initialize();
|
||||
|
||||
bool operator==(const Entity& other) const;
|
||||
bool operator!=(const Entity& other) const;
|
||||
@@ -66,7 +67,7 @@ public:
|
||||
|
||||
eGameMasterLevel GetGMLevel() const { return m_GMLevel; }
|
||||
|
||||
uint8_t GetCollectibleID() const { return uint8_t(m_CollectibleID); }
|
||||
uint8_t GetCollectibleID() const;
|
||||
|
||||
Entity* GetParentEntity() const { return m_ParentEntity; }
|
||||
|
||||
@@ -103,9 +104,7 @@ public:
|
||||
|
||||
const NiQuaternion& GetRotation() const;
|
||||
|
||||
virtual User* GetParentUser() const;
|
||||
|
||||
virtual SystemAddress GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; };
|
||||
const SystemAddress& GetSystemAddress() const;
|
||||
|
||||
/**
|
||||
* Setters
|
||||
@@ -123,15 +122,13 @@ public:
|
||||
|
||||
void SetNetworkId(uint16_t id);
|
||||
|
||||
void SetPosition(NiPoint3 position);
|
||||
void SetPosition(const NiPoint3& position);
|
||||
|
||||
void SetRotation(NiQuaternion rotation);
|
||||
void SetRotation(const NiQuaternion& rotation);
|
||||
|
||||
virtual void SetRespawnPos(NiPoint3 position) {}
|
||||
void SetRespawnPos(const NiPoint3& position);
|
||||
|
||||
virtual void SetRespawnRot(NiQuaternion rotation) {}
|
||||
|
||||
virtual void SetSystemAddress(const SystemAddress& value) {};
|
||||
void SetRespawnRot(const NiQuaternion& rotation);
|
||||
|
||||
/**
|
||||
* Component management
|
||||
@@ -160,6 +157,8 @@ public:
|
||||
void AddChild(Entity* child);
|
||||
void RemoveChild(Entity* child);
|
||||
void RemoveParent();
|
||||
|
||||
// Adds a timer to start next frame with the given name and time.
|
||||
void AddTimer(std::string name, float time);
|
||||
void AddCallbackTimer(float time, std::function<void()> callback);
|
||||
bool HasTimer(const std::string& name);
|
||||
@@ -210,8 +209,8 @@ public:
|
||||
void RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled);
|
||||
|
||||
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 Kill(Entity* murderer = nullptr, const eKillType killType = eKillType::SILENT);
|
||||
void AddQuickBuildCompleteCallback(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();
|
||||
@@ -226,8 +225,8 @@ public:
|
||||
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; }
|
||||
const NiPoint3& GetRespawnPosition() const;
|
||||
const NiQuaternion& GetRespawnRotation() const;
|
||||
|
||||
void Sleep();
|
||||
void Wake();
|
||||
@@ -274,6 +273,9 @@ public:
|
||||
template<typename T>
|
||||
T GetVarAs(const std::u16string& name) const;
|
||||
|
||||
template<typename ComponentType, typename... VaArgs>
|
||||
ComponentType* AddComponent(VaArgs... args);
|
||||
|
||||
/**
|
||||
* Get the LDF data.
|
||||
*/
|
||||
@@ -287,10 +289,12 @@ public:
|
||||
/*
|
||||
* Collision
|
||||
*/
|
||||
std::vector<LWOOBJID>& GetTargetsInPhantom();
|
||||
std::vector<LWOOBJID> GetTargetsInPhantom();
|
||||
|
||||
Entity* GetScheduledKiller() { return m_ScheduleKiller; }
|
||||
|
||||
void ProcessPositionUpdate(PositionUpdate& update);
|
||||
|
||||
protected:
|
||||
LWOOBJID m_ObjectID;
|
||||
|
||||
@@ -321,9 +325,10 @@ protected:
|
||||
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;
|
||||
std::vector<EntityTimer> m_Timers;
|
||||
std::vector<EntityTimer> m_PendingTimers;
|
||||
std::vector<EntityCallbackTimer> m_CallbackTimers;
|
||||
std::vector<EntityCallbackTimer> m_PendingCallbackTimers;
|
||||
|
||||
bool m_ShouldDestroyAfterUpdate = false;
|
||||
|
||||
@@ -390,14 +395,8 @@ const T& Entity::GetVar(const std::u16string& name) const {
|
||||
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;
|
||||
|
||||
return GeneralUtils::TryParse<T>(data).value_or(LDFData<T>::Default);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -501,3 +500,36 @@ T Entity::GetNetworkVar(const std::u16string& name) {
|
||||
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a component of type ComponentType to this entity and forwards the arguments to the constructor.
|
||||
*
|
||||
* @tparam ComponentType The component class type to add. Must derive from Component.
|
||||
* @tparam VaArgs The argument types to forward to the constructor.
|
||||
* @param args The arguments to forward to the constructor. The first argument passed to the ComponentType constructor will be this entity.
|
||||
* @return ComponentType* The added component. Will never return null.
|
||||
*/
|
||||
template<typename ComponentType, typename... VaArgs>
|
||||
inline ComponentType* Entity::AddComponent(VaArgs... args) {
|
||||
static_assert(std::is_base_of_v<Component, ComponentType>, "ComponentType must be a Component");
|
||||
|
||||
// Get the component if it already exists, or default construct a nullptr
|
||||
auto*& componentToReturn = m_Components[ComponentType::ComponentType];
|
||||
|
||||
// If it doesn't exist, create it and forward the arguments to the constructor
|
||||
if (!componentToReturn) {
|
||||
componentToReturn = new ComponentType(this, std::forward<VaArgs>(args)...);
|
||||
} else {
|
||||
// In this case the block is already allocated and ready for use
|
||||
// so we use a placement new to construct the component again as was requested by the caller.
|
||||
// Placement new means we already have memory allocated for the object, so this just calls its constructor again.
|
||||
// This is useful for when we want to create a new object in the same memory location as an old one.
|
||||
componentToReturn->~Component();
|
||||
new(componentToReturn) ComponentType(this, std::forward<VaArgs>(args)...);
|
||||
}
|
||||
|
||||
// Finally return the created or already existing component.
|
||||
// Because of the assert above, this should always be a ComponentType* but I need a way to guarantee the map cannot be modifed outside this function
|
||||
// To allow a static cast here instead of a dynamic one.
|
||||
return dynamic_cast<ComponentType*>(componentToReturn);
|
||||
}
|
||||
|
||||
@@ -2,21 +2,19 @@
|
||||
#include "RakNetTypes.h"
|
||||
#include "Game.h"
|
||||
#include "User.h"
|
||||
#include "../dWorldServer/ObjectIDManager.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "Character.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dServer.h"
|
||||
#include "Spawner.h"
|
||||
#include "Player.h"
|
||||
#include "SkillComponent.h"
|
||||
#include "SwitchComponent.h"
|
||||
#include "UserManager.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "dZoneManager.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "MessageIdentifiers.h"
|
||||
#include "dConfig.h"
|
||||
#include "eTriggerEventType.h"
|
||||
@@ -24,6 +22,9 @@
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "eReplicaPacketType.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "GhostComponent.h"
|
||||
#include <ranges>
|
||||
|
||||
// Configure which zones have ghosting disabled, mostly small worlds.
|
||||
std::vector<LWOMAPID> EntityManager::m_GhostingExcludedZones = {
|
||||
@@ -47,10 +48,6 @@ std::vector<LWOMAPID> EntityManager::m_GhostingExcludedZones = {
|
||||
|
||||
// Configure some exceptions for ghosting, nessesary for some special objects.
|
||||
std::vector<LOT> EntityManager::m_GhostingExcludedLOTs = {
|
||||
// NT - Pipes
|
||||
9524,
|
||||
12408,
|
||||
|
||||
// AG - Footrace
|
||||
4967
|
||||
};
|
||||
@@ -93,7 +90,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
|
||||
|
||||
// Entities with no ID already set, often spawned entities, we'll generate a new sequencial ID
|
||||
if (info.id == 0) {
|
||||
id = ObjectIDManager::Instance()->GenerateObjectID();
|
||||
id = ObjectIDManager::GenerateObjectID();
|
||||
}
|
||||
|
||||
// Entities with an ID already set, often level entities, we'll use that ID as a base
|
||||
@@ -121,14 +118,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
|
||||
|
||||
info.id = id;
|
||||
|
||||
Entity* entity;
|
||||
|
||||
// Check if the entitty if a player, in case use the extended player entity class
|
||||
if (user != nullptr) {
|
||||
entity = new Player(id, info, user, parentEntity);
|
||||
} else {
|
||||
entity = new Entity(id, info, parentEntity);
|
||||
}
|
||||
Entity* entity = new Entity(id, info, user, parentEntity);
|
||||
|
||||
// Initialize the entity
|
||||
entity->Initialize();
|
||||
@@ -176,23 +166,25 @@ void EntityManager::DestroyEntity(Entity* entity) {
|
||||
}
|
||||
|
||||
void EntityManager::SerializeEntities() {
|
||||
for (auto entry = m_EntitiesToSerialize.begin(); entry != m_EntitiesToSerialize.end(); entry++) {
|
||||
auto* entity = GetEntity(*entry);
|
||||
for (size_t i = 0; i < m_EntitiesToSerialize.size(); i++) {
|
||||
const LWOOBJID toSerialize = m_EntitiesToSerialize[i];
|
||||
auto* entity = GetEntity(toSerialize);
|
||||
|
||||
if (!entity) continue;
|
||||
|
||||
m_SerializationCounter++;
|
||||
|
||||
RakNet::BitStream stream;
|
||||
stream.Write(static_cast<char>(ID_REPLICA_MANAGER_SERIALIZE));
|
||||
stream.Write(static_cast<unsigned short>(entity->GetNetworkId()));
|
||||
stream.Write<char>(ID_REPLICA_MANAGER_SERIALIZE);
|
||||
stream.Write<unsigned short>(entity->GetNetworkId());
|
||||
|
||||
entity->WriteBaseReplicaData(&stream, eReplicaPacketType::SERIALIZATION);
|
||||
entity->WriteComponents(&stream, eReplicaPacketType::SERIALIZATION);
|
||||
|
||||
if (entity->GetIsGhostingCandidate()) {
|
||||
for (auto* player : Player::GetAllPlayers()) {
|
||||
if (player->IsObserved(*entry)) {
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (ghostComponent && ghostComponent->IsObserved(toSerialize)) {
|
||||
Game::server->Send(&stream, player->GetSystemAddress(), false);
|
||||
}
|
||||
}
|
||||
@@ -204,11 +196,12 @@ void EntityManager::SerializeEntities() {
|
||||
}
|
||||
|
||||
void EntityManager::KillEntities() {
|
||||
for (auto entry = m_EntitiesToKill.begin(); entry != m_EntitiesToKill.end(); entry++) {
|
||||
auto* entity = GetEntity(*entry);
|
||||
for (size_t i = 0; i < m_EntitiesToKill.size(); i++) {
|
||||
const LWOOBJID toKill = m_EntitiesToKill[i];
|
||||
auto* entity = GetEntity(toKill);
|
||||
|
||||
if (!entity) {
|
||||
Game::logger->Log("EntityManager", "Attempting to kill null entity %llu", *entry);
|
||||
LOG("Attempting to kill null entity %llu", toKill);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -222,8 +215,9 @@ void EntityManager::KillEntities() {
|
||||
}
|
||||
|
||||
void EntityManager::DeleteEntities() {
|
||||
for (auto entry = m_EntitiesToDelete.begin(); entry != m_EntitiesToDelete.end(); entry++) {
|
||||
auto entityToDelete = GetEntity(*entry);
|
||||
for (size_t i = 0; i < m_EntitiesToDelete.size(); i++) {
|
||||
const LWOOBJID toDelete = m_EntitiesToDelete[i];
|
||||
auto entityToDelete = GetEntity(toDelete);
|
||||
if (entityToDelete) {
|
||||
// Get all this info first before we delete the player.
|
||||
auto networkIdToErase = entityToDelete->GetNetworkId();
|
||||
@@ -237,16 +231,16 @@ void EntityManager::DeleteEntities() {
|
||||
|
||||
if (ghostingToDelete != m_EntitiesToGhost.end()) m_EntitiesToGhost.erase(ghostingToDelete);
|
||||
} else {
|
||||
Game::logger->Log("EntityManager", "Attempted to delete non-existent entity %llu", *entry);
|
||||
LOG("Attempted to delete non-existent entity %llu", toDelete);
|
||||
}
|
||||
m_Entities.erase(*entry);
|
||||
m_Entities.erase(toDelete);
|
||||
}
|
||||
m_EntitiesToDelete.clear();
|
||||
}
|
||||
|
||||
void EntityManager::UpdateEntities(const float deltaTime) {
|
||||
for (const auto& e : m_Entities) {
|
||||
e.second->Update(deltaTime);
|
||||
for (auto* entity : m_Entities | std::views::values) {
|
||||
entity->Update(deltaTime);
|
||||
}
|
||||
|
||||
SerializeEntities();
|
||||
@@ -266,10 +260,10 @@ Entity* EntityManager::GetEntity(const LWOOBJID& objectId) const {
|
||||
|
||||
std::vector<Entity*> EntityManager::GetEntitiesInGroup(const std::string& group) {
|
||||
std::vector<Entity*> entitiesInGroup;
|
||||
for (const auto& entity : m_Entities) {
|
||||
for (const auto& entityGroup : entity.second->GetGroups()) {
|
||||
for (auto* entity : m_Entities | std::views::values) {
|
||||
for (const auto& entityGroup : entity->GetGroups()) {
|
||||
if (entityGroup == group) {
|
||||
entitiesInGroup.push_back(entity.second);
|
||||
entitiesInGroup.push_back(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,10 +273,12 @@ std::vector<Entity*> EntityManager::GetEntitiesInGroup(const std::string& group)
|
||||
|
||||
std::vector<Entity*> EntityManager::GetEntitiesByComponent(const eReplicaComponentType componentType) const {
|
||||
std::vector<Entity*> withComp;
|
||||
for (const auto& entity : m_Entities) {
|
||||
if (componentType != eReplicaComponentType::INVALID && !entity.second->HasComponent(componentType)) continue;
|
||||
if (componentType != eReplicaComponentType::INVALID) {
|
||||
for (auto* entity : m_Entities | std::views::values) {
|
||||
if (!entity->HasComponent(componentType)) continue;
|
||||
|
||||
withComp.push_back(entity.second);
|
||||
withComp.push_back(entity);
|
||||
}
|
||||
}
|
||||
return withComp;
|
||||
}
|
||||
@@ -290,14 +286,24 @@ std::vector<Entity*> EntityManager::GetEntitiesByComponent(const eReplicaCompone
|
||||
std::vector<Entity*> EntityManager::GetEntitiesByLOT(const LOT& lot) const {
|
||||
std::vector<Entity*> entities;
|
||||
|
||||
for (const auto& entity : m_Entities) {
|
||||
if (entity.second->GetLOT() == lot)
|
||||
entities.push_back(entity.second);
|
||||
for (auto* entity : m_Entities | std::views::values) {
|
||||
if (entity->GetLOT() == lot) entities.push_back(entity);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
std::vector<Entity*> EntityManager::GetEntitiesByProximity(NiPoint3 reference, float radius) const {
|
||||
std::vector<Entity*> entities;
|
||||
if (radius <= 1000.0f) { // The client has a 1000 unit limit on this same logic, so we'll use the same limit
|
||||
for (auto* entity : m_Entities | std::views::values) {
|
||||
if (NiPoint3::Distance(reference, entity->GetPosition()) <= radius) entities.push_back(entity);
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
|
||||
Entity* EntityManager::GetZoneControlEntity() const {
|
||||
return m_ZoneControlEntity;
|
||||
}
|
||||
@@ -306,12 +312,8 @@ Entity* EntityManager::GetSpawnPointEntity(const std::string& spawnName) const {
|
||||
// Lookup the spawn point entity in the map
|
||||
const auto& spawnPoint = m_SpawnPoints.find(spawnName);
|
||||
|
||||
if (spawnPoint == m_SpawnPoints.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the spawn point entity is valid just in case
|
||||
return GetEntity(spawnPoint->second);
|
||||
return spawnPoint == m_SpawnPoints.end() ? nullptr : GetEntity(spawnPoint->second);
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, LWOOBJID>& EntityManager::GetSpawnPointEntities() const {
|
||||
@@ -320,7 +322,7 @@ const std::unordered_map<std::string, LWOOBJID>& EntityManager::GetSpawnPointEnt
|
||||
|
||||
void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr, const bool skipChecks) {
|
||||
if (!entity) {
|
||||
Game::logger->Log("EntityManager", "Attempted to construct null entity");
|
||||
LOG("Attempted to construct null entity");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -337,29 +339,25 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
entity->SetNetworkId(networkId);
|
||||
}
|
||||
|
||||
const auto checkGhosting = entity->GetIsGhostingCandidate();
|
||||
|
||||
if (checkGhosting) {
|
||||
const auto& iter = std::find(m_EntitiesToGhost.begin(), m_EntitiesToGhost.end(), entity);
|
||||
|
||||
if (iter == m_EntitiesToGhost.end()) {
|
||||
if (entity->GetIsGhostingCandidate()) {
|
||||
if (std::find(m_EntitiesToGhost.begin(), m_EntitiesToGhost.end(), entity) == m_EntitiesToGhost.end()) {
|
||||
m_EntitiesToGhost.push_back(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkGhosting && sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
||||
CheckGhosting(entity);
|
||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
||||
CheckGhosting(entity);
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_SerializationCounter++;
|
||||
|
||||
RakNet::BitStream stream;
|
||||
|
||||
stream.Write(static_cast<char>(ID_REPLICA_MANAGER_CONSTRUCTION));
|
||||
stream.Write<uint8_t>(ID_REPLICA_MANAGER_CONSTRUCTION);
|
||||
stream.Write(true);
|
||||
stream.Write(static_cast<unsigned short>(entity->GetNetworkId()));
|
||||
stream.Write<uint16_t>(entity->GetNetworkId());
|
||||
|
||||
entity->WriteBaseReplicaData(&stream, eReplicaPacketType::CONSTRUCTION);
|
||||
entity->WriteComponents(&stream, eReplicaPacketType::CONSTRUCTION);
|
||||
@@ -368,11 +366,12 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
if (skipChecks) {
|
||||
Game::server->Send(&stream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
} else {
|
||||
for (auto* player : Player::GetAllPlayers()) {
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
if (player->GetPlayerReadyForUpdates()) {
|
||||
Game::server->Send(&stream, player->GetSystemAddress(), false);
|
||||
} else {
|
||||
player->AddLimboConstruction(entity->GetObjectID());
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,8 +379,6 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
Game::server->Send(&stream, sysAddr, false);
|
||||
}
|
||||
|
||||
// PacketUtils::SavePacket("[24]_"+std::to_string(entity->GetObjectID()) + "_" + std::to_string(m_SerializationCounter) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed());
|
||||
|
||||
if (entity->IsPlayer()) {
|
||||
if (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) {
|
||||
GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, sysAddr);
|
||||
@@ -393,13 +390,13 @@ void EntityManager::ConstructAllEntities(const SystemAddress& sysAddr) {
|
||||
//ZoneControl is special:
|
||||
ConstructEntity(m_ZoneControlEntity, sysAddr);
|
||||
|
||||
for (const auto& e : m_Entities) {
|
||||
if (e.second && (e.second->GetSpawnerID() != 0 || e.second->GetLOT() == 1) && !e.second->GetIsGhostingCandidate()) {
|
||||
ConstructEntity(e.second, sysAddr);
|
||||
for (auto* entity : m_Entities | std::views::values) {
|
||||
if (entity && (entity->GetSpawnerID() != 0 || entity->GetLOT() == 1) && !entity->GetIsGhostingCandidate()) {
|
||||
ConstructEntity(entity, sysAddr);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateGhosting(Player::GetPlayer(sysAddr));
|
||||
UpdateGhosting(PlayerManager::GetPlayer(sysAddr));
|
||||
}
|
||||
|
||||
void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr) {
|
||||
@@ -407,14 +404,15 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr)
|
||||
|
||||
RakNet::BitStream stream;
|
||||
|
||||
stream.Write(static_cast<char>(ID_REPLICA_MANAGER_DESTRUCTION));
|
||||
stream.Write(static_cast<unsigned short>(entity->GetNetworkId()));
|
||||
stream.Write<uint8_t>(ID_REPLICA_MANAGER_DESTRUCTION);
|
||||
stream.Write<uint16_t>(entity->GetNetworkId());
|
||||
|
||||
Game::server->Send(&stream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
for (auto* player : Player::GetAllPlayers()) {
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
if (!player->GetPlayerReadyForUpdates()) {
|
||||
player->RemoveLimboConstruction(entity->GetObjectID());
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (ghostComponent) ghostComponent->RemoveLimboConstruction(entity->GetObjectID());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -425,13 +423,11 @@ void EntityManager::SerializeEntity(Entity* entity) {
|
||||
if (std::find(m_EntitiesToSerialize.begin(), m_EntitiesToSerialize.end(), entity->GetObjectID()) == m_EntitiesToSerialize.end()) {
|
||||
m_EntitiesToSerialize.push_back(entity->GetObjectID());
|
||||
}
|
||||
|
||||
//PacketUtils::SavePacket(std::to_string(m_SerializationCounter) + "_[27]_"+std::to_string(entity->GetObjectID()) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed());
|
||||
}
|
||||
|
||||
void EntityManager::DestructAllEntities(const SystemAddress& sysAddr) {
|
||||
for (const auto& e : m_Entities) {
|
||||
DestructEntity(e.second, sysAddr);
|
||||
for (auto* entity : m_Entities | std::views::values) {
|
||||
DestructEntity(entity, sysAddr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,29 +435,19 @@ void EntityManager::SetGhostDistanceMax(float value) {
|
||||
m_GhostDistanceMaxSquared = value * value;
|
||||
}
|
||||
|
||||
float EntityManager::GetGhostDistanceMax() const {
|
||||
return std::sqrt(m_GhostDistanceMaxSquared);
|
||||
}
|
||||
|
||||
void EntityManager::SetGhostDistanceMin(float value) {
|
||||
m_GhostDistanceMinSqaured = value * value;
|
||||
}
|
||||
|
||||
float EntityManager::GetGhostDistanceMin() const {
|
||||
return std::sqrt(m_GhostDistanceMinSqaured);
|
||||
}
|
||||
|
||||
void EntityManager::QueueGhostUpdate(LWOOBJID playerID) {
|
||||
const auto& iter = std::find(m_PlayersToUpdateGhosting.begin(), m_PlayersToUpdateGhosting.end(), playerID);
|
||||
|
||||
if (iter == m_PlayersToUpdateGhosting.end()) {
|
||||
if (std::find(m_PlayersToUpdateGhosting.begin(), m_PlayersToUpdateGhosting.end(), playerID) == m_PlayersToUpdateGhosting.end()) {
|
||||
m_PlayersToUpdateGhosting.push_back(playerID);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityManager::UpdateGhosting() {
|
||||
for (const auto playerID : m_PlayersToUpdateGhosting) {
|
||||
auto* player = Player::GetPlayer(playerID);
|
||||
auto* player = PlayerManager::GetPlayer(playerID);
|
||||
|
||||
if (player == nullptr) {
|
||||
continue;
|
||||
@@ -473,40 +459,36 @@ void EntityManager::UpdateGhosting() {
|
||||
m_PlayersToUpdateGhosting.clear();
|
||||
}
|
||||
|
||||
void EntityManager::UpdateGhosting(Player* player) {
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
void EntityManager::UpdateGhosting(Entity* player) {
|
||||
if (!player) return;
|
||||
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
|
||||
if (missionComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!missionComponent || !ghostComponent) return;
|
||||
|
||||
const auto& referencePoint = player->GetGhostReferencePoint();
|
||||
const auto isOverride = player->GetGhostOverride();
|
||||
const auto& referencePoint = ghostComponent->GetGhostReferencePoint();
|
||||
const auto isOverride = ghostComponent->GetGhostOverride();
|
||||
|
||||
for (auto* entity : m_EntitiesToGhost) {
|
||||
const auto isAudioEmitter = entity->GetLOT() == 6368;
|
||||
|
||||
const auto& entityPoint = entity->GetPosition();
|
||||
|
||||
const int32_t id = entity->GetObjectID();
|
||||
const auto id = entity->GetObjectID();
|
||||
|
||||
const auto observed = player->IsObserved(id);
|
||||
const auto observed = ghostComponent->IsObserved(id);
|
||||
|
||||
const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint);
|
||||
|
||||
auto ghostingDistanceMax = m_GhostDistanceMaxSquared;
|
||||
auto ghostingDistanceMin = m_GhostDistanceMinSqaured;
|
||||
|
||||
const auto isAudioEmitter = entity->GetLOT() == 6368; // https://explorer.lu/objects/6368
|
||||
if (isAudioEmitter) {
|
||||
ghostingDistanceMax = ghostingDistanceMin;
|
||||
}
|
||||
|
||||
if (observed && distance > ghostingDistanceMax && !isOverride) {
|
||||
player->GhostEntity(id);
|
||||
ghostComponent->GhostEntity(id);
|
||||
|
||||
DestructEntity(entity, player->GetSystemAddress());
|
||||
|
||||
@@ -523,7 +505,7 @@ void EntityManager::UpdateGhosting(Player* player) {
|
||||
}
|
||||
}
|
||||
|
||||
player->ObserveEntity(id);
|
||||
ghostComponent->ObserveEntity(id);
|
||||
|
||||
ConstructEntity(entity, player->GetSystemAddress());
|
||||
|
||||
@@ -539,28 +521,26 @@ void EntityManager::CheckGhosting(Entity* entity) {
|
||||
|
||||
const auto& referencePoint = entity->GetPosition();
|
||||
|
||||
auto ghostingDistanceMax = m_GhostDistanceMaxSquared;
|
||||
auto ghostingDistanceMin = m_GhostDistanceMinSqaured;
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (!ghostComponent) continue;
|
||||
|
||||
const auto isAudioEmitter = entity->GetLOT() == 6368;
|
||||
const auto& entityPoint = ghostComponent->GetGhostReferencePoint();
|
||||
|
||||
for (auto* player : Player::GetAllPlayers()) {
|
||||
const auto& entityPoint = player->GetGhostReferencePoint();
|
||||
const auto id = entity->GetObjectID();
|
||||
|
||||
const int32_t id = entity->GetObjectID();
|
||||
|
||||
const auto observed = player->IsObserved(id);
|
||||
const auto observed = ghostComponent->IsObserved(id);
|
||||
|
||||
const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint);
|
||||
|
||||
if (observed && distance > ghostingDistanceMax) {
|
||||
player->GhostEntity(id);
|
||||
if (observed && distance > m_GhostDistanceMaxSquared) {
|
||||
ghostComponent->GhostEntity(id);
|
||||
|
||||
DestructEntity(entity, player->GetSystemAddress());
|
||||
|
||||
entity->SetObservers(entity->GetObservers() - 1);
|
||||
} else if (!observed && ghostingDistanceMin > distance) {
|
||||
player->ObserveEntity(id);
|
||||
} else if (!observed && m_GhostDistanceMinSqaured > distance) {
|
||||
ghostComponent->ObserveEntity(id);
|
||||
|
||||
ConstructEntity(entity, player->GetSystemAddress());
|
||||
|
||||
@@ -569,7 +549,7 @@ void EntityManager::CheckGhosting(Entity* entity) {
|
||||
}
|
||||
}
|
||||
|
||||
Entity* EntityManager::GetGhostCandidate(int32_t id) {
|
||||
Entity* EntityManager::GetGhostCandidate(LWOOBJID id) const {
|
||||
for (auto* entity : m_EntitiesToGhost) {
|
||||
if (entity->GetObjectID() == id) {
|
||||
return entity;
|
||||
@@ -595,26 +575,22 @@ void EntityManager::ScheduleForKill(Entity* entity) {
|
||||
|
||||
const auto objectId = entity->GetObjectID();
|
||||
|
||||
if (std::count(m_EntitiesToKill.begin(), m_EntitiesToKill.end(), objectId)) {
|
||||
return;
|
||||
if (std::find(m_EntitiesToKill.begin(), m_EntitiesToKill.end(), objectId) == m_EntitiesToKill.end()) {
|
||||
m_EntitiesToKill.push_back(objectId);
|
||||
}
|
||||
|
||||
m_EntitiesToKill.push_back(objectId);
|
||||
}
|
||||
|
||||
void EntityManager::ScheduleForDeletion(LWOOBJID entity) {
|
||||
if (std::count(m_EntitiesToDelete.begin(), m_EntitiesToDelete.end(), entity)) {
|
||||
return;
|
||||
if (std::find(m_EntitiesToDelete.begin(), m_EntitiesToDelete.end(), entity) == m_EntitiesToDelete.end()) {
|
||||
m_EntitiesToDelete.push_back(entity);
|
||||
}
|
||||
|
||||
m_EntitiesToDelete.push_back(entity);
|
||||
}
|
||||
|
||||
|
||||
void EntityManager::FireEventServerSide(Entity* origin, std::string args) {
|
||||
for (std::pair<LWOOBJID, Entity*> e : m_Entities) {
|
||||
if (e.second) {
|
||||
e.second->OnFireEventServerSide(origin, args);
|
||||
for (const auto entity : m_Entities | std::views::values) {
|
||||
if (entity) {
|
||||
entity->OnFireEventServerSide(origin, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
std::vector<Entity*> GetEntitiesInGroup(const std::string& group);
|
||||
std::vector<Entity*> GetEntitiesByComponent(eReplicaComponentType componentType) const;
|
||||
std::vector<Entity*> GetEntitiesByLOT(const LOT& lot) const;
|
||||
std::vector<Entity*> GetEntitiesByProximity(NiPoint3 reference, float radius) const;
|
||||
Entity* GetZoneControlEntity() const;
|
||||
|
||||
// Get spawn point entity by spawn name
|
||||
@@ -49,14 +50,12 @@ public:
|
||||
void DestructAllEntities(const SystemAddress& sysAddr);
|
||||
|
||||
void SetGhostDistanceMax(float value);
|
||||
float GetGhostDistanceMax() const;
|
||||
void SetGhostDistanceMin(float value);
|
||||
float GetGhostDistanceMin() const;
|
||||
void QueueGhostUpdate(LWOOBJID playerID);
|
||||
void UpdateGhosting();
|
||||
void UpdateGhosting(Player* player);
|
||||
void UpdateGhosting(Entity* player);
|
||||
void CheckGhosting(Entity* entity);
|
||||
Entity* GetGhostCandidate(int32_t id);
|
||||
Entity* GetGhostCandidate(LWOOBJID id) const;
|
||||
bool GetGhostingEnabled() const;
|
||||
|
||||
void ScheduleForKill(Entity* entity);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "Character.h"
|
||||
#include "Game.h"
|
||||
#include "GameMessages.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "dConfig.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "GeneralUtils.h"
|
||||
@@ -193,7 +193,7 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r
|
||||
SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
|
||||
WHERE leaderboardsRanked.ranking
|
||||
BETWEEN
|
||||
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9)
|
||||
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), CAST(lowestRanking.lowestRank AS SIGNED) - 9)
|
||||
AND
|
||||
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
|
||||
ORDER BY ranking ASC;
|
||||
@@ -236,8 +236,8 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r
|
||||
baseLookup += std::to_string(static_cast<uint32_t>(this->relatedPlayer));
|
||||
}
|
||||
baseLookup += " LIMIT 1";
|
||||
Game::logger->LogDebug("LeaderboardManager", "query is %s", baseLookup.c_str());
|
||||
std::unique_ptr<sql::PreparedStatement> baseQuery(Database::CreatePreppedStmt(baseLookup));
|
||||
LOG_DEBUG("query is %s", baseLookup.c_str());
|
||||
std::unique_ptr<sql::PreparedStatement> baseQuery(Database::Get()->CreatePreppedStmt(baseLookup));
|
||||
baseQuery->setInt(1, this->gameID);
|
||||
std::unique_ptr<sql::ResultSet> baseResult(baseQuery->executeQuery());
|
||||
|
||||
@@ -250,8 +250,8 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r
|
||||
std::unique_ptr<char[]> lookupBuffer = std::make_unique<char[]>(STRING_LENGTH);
|
||||
int32_t res = snprintf(lookupBuffer.get(), STRING_LENGTH, queryBase.c_str(), orderBase.data(), filter.c_str(), resultStart, resultEnd);
|
||||
DluAssert(res != -1);
|
||||
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt(lookupBuffer.get()));
|
||||
Game::logger->LogDebug("LeaderboardManager", "Query is %s vars are %i %i %i", lookupBuffer.get(), this->gameID, this->relatedPlayer, relatedPlayerLeaderboardId);
|
||||
std::unique_ptr<sql::PreparedStatement> query(Database::Get()->CreatePreppedStmt(lookupBuffer.get()));
|
||||
LOG_DEBUG("Query is %s vars are %i %i %i", lookupBuffer.get(), this->gameID, this->relatedPlayer, relatedPlayerLeaderboardId);
|
||||
query->setInt(1, this->gameID);
|
||||
if (this->infoType == InfoType::Friends) {
|
||||
query->setInt(2, this->relatedPlayer);
|
||||
@@ -301,7 +301,7 @@ std::string FormatInsert(const Leaderboard::Type& type, const Score& score, cons
|
||||
void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) {
|
||||
const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId);
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;"));
|
||||
std::unique_ptr<sql::PreparedStatement> query(Database::Get()->CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;"));
|
||||
query->setInt(1, playerID);
|
||||
query->setInt(2, activityId);
|
||||
std::unique_ptr<sql::ResultSet> myScoreResult(query->executeQuery());
|
||||
@@ -358,7 +358,7 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi
|
||||
}
|
||||
case Leaderboard::Type::None:
|
||||
default:
|
||||
Game::logger->Log("LeaderboardManager", "Unknown leaderboard type %i for game %i. Cannot save score!", leaderboardType, activityId);
|
||||
LOG("Unknown leaderboard type %i for game %i. Cannot save score!", leaderboardType, activityId);
|
||||
return;
|
||||
}
|
||||
bool newHighScore = lowerScoreBetter ? newScore < oldScore : newScore > oldScore;
|
||||
@@ -377,15 +377,15 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi
|
||||
} else {
|
||||
saveQuery = FormatInsert(leaderboardType, newScore, false);
|
||||
}
|
||||
Game::logger->Log("LeaderboardManager", "save query %s %i %i", saveQuery.c_str(), playerID, activityId);
|
||||
std::unique_ptr<sql::PreparedStatement> saveStatement(Database::CreatePreppedStmt(saveQuery));
|
||||
LOG("save query %s %i %i", saveQuery.c_str(), playerID, activityId);
|
||||
std::unique_ptr<sql::PreparedStatement> saveStatement(Database::Get()->CreatePreppedStmt(saveQuery));
|
||||
saveStatement->setInt(1, playerID);
|
||||
saveStatement->setInt(2, activityId);
|
||||
saveStatement->execute();
|
||||
|
||||
// track wins separately
|
||||
if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) {
|
||||
std::unique_ptr<sql::PreparedStatement> winUpdate(Database::CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;"));
|
||||
std::unique_ptr<sql::PreparedStatement> winUpdate(Database::Get()->CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;"));
|
||||
winUpdate->setInt(1, playerID);
|
||||
winUpdate->setInt(2, activityId);
|
||||
winUpdate->execute();
|
||||
@@ -402,7 +402,7 @@ Leaderboard::Type LeaderboardManager::GetLeaderboardType(const GameID gameID) {
|
||||
auto lookup = leaderboardCache.find(gameID);
|
||||
if (lookup != leaderboardCache.end()) return lookup->second;
|
||||
|
||||
auto* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
auto* activitiesTable = CDClientManager::GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([gameID](const CDActivities& entry) {
|
||||
return entry.ActivityID == gameID;
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "Singleton.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "LDFFormat.h"
|
||||
|
||||
|
||||
304
dGame/Player.cpp
304
dGame/Player.cpp
@@ -1,304 +0,0 @@
|
||||
#include "Player.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include "Character.h"
|
||||
#include "Database.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "UserManager.h"
|
||||
#include "EntityManager.h"
|
||||
#include "dLogger.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "WorldPackets.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "Mail.h"
|
||||
#include "User.h"
|
||||
#include "CppScripts.h"
|
||||
#include "Loot.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
std::vector<Player*> Player::m_Players = {};
|
||||
|
||||
Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Entity* parentEntity) : Entity(objectID, info, parentEntity) {
|
||||
m_ParentUser = user;
|
||||
m_Character = m_ParentUser->GetLastUsedChar();
|
||||
m_ParentUser->SetLoggedInChar(objectID);
|
||||
m_GMLevel = m_Character->GetGMLevel();
|
||||
m_SystemAddress = m_ParentUser->GetSystemAddress();
|
||||
m_DroppedLoot = {};
|
||||
m_DroppedCoins = 0;
|
||||
|
||||
m_GhostReferencePoint = NiPoint3::ZERO;
|
||||
m_GhostOverridePoint = NiPoint3::ZERO;
|
||||
m_GhostOverride = false;
|
||||
m_ObservedEntitiesLength = 256;
|
||||
m_ObservedEntitiesUsed = 0;
|
||||
m_ObservedEntities.resize(m_ObservedEntitiesLength);
|
||||
|
||||
m_Character->SetEntity(this);
|
||||
|
||||
const auto& iter = std::find(m_Players.begin(), m_Players.end(), this);
|
||||
|
||||
if (iter != m_Players.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Players.push_back(this);
|
||||
}
|
||||
|
||||
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::SetRespawnPos(const NiPoint3 position) {
|
||||
m_respawnPos = position;
|
||||
|
||||
m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position);
|
||||
}
|
||||
|
||||
void Player::SetRespawnRot(const NiQuaternion rotation) {
|
||||
m_respawnRot = rotation;
|
||||
}
|
||||
|
||||
NiPoint3 Player::GetRespawnPosition() const {
|
||||
return m_respawnPos;
|
||||
}
|
||||
|
||||
NiQuaternion Player::GetRespawnRotation() const {
|
||||
return m_respawnRot;
|
||||
}
|
||||
|
||||
void Player::SendMail(const LWOOBJID sender, const std::string& senderName, const std::string& subject, const std::string& body, LOT attachment, uint16_t attachmentCount) const {
|
||||
Mail::SendMail(sender, senderName, this, subject, body, attachment, attachmentCount);
|
||||
}
|
||||
|
||||
void Player::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) {
|
||||
const auto objid = GetObjectID();
|
||||
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneId, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
auto* entity = Game::entityManager->GetEntity(objid);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto sysAddr = entity->GetSystemAddress();
|
||||
|
||||
auto* character = entity->GetCharacter();
|
||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||
|
||||
if (character != nullptr && characterComponent != nullptr) {
|
||||
character->SetZoneID(zoneID);
|
||||
character->SetZoneInstance(zoneInstance);
|
||||
character->SetZoneClone(zoneClone);
|
||||
|
||||
characterComponent->SetLastRocketConfig(u"");
|
||||
|
||||
character->SaveXMLToDatabase();
|
||||
}
|
||||
|
||||
WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift);
|
||||
|
||||
Game::entityManager->DestructEntity(entity);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
void Player::AddLimboConstruction(LWOOBJID objectId) {
|
||||
const auto& iter = std::find(m_LimboConstructions.begin(), m_LimboConstructions.end(), objectId);
|
||||
|
||||
if (iter != m_LimboConstructions.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_LimboConstructions.push_back(objectId);
|
||||
}
|
||||
|
||||
void Player::RemoveLimboConstruction(LWOOBJID objectId) {
|
||||
const auto& iter = std::find(m_LimboConstructions.begin(), m_LimboConstructions.end(), objectId);
|
||||
|
||||
if (iter == m_LimboConstructions.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_LimboConstructions.erase(iter);
|
||||
}
|
||||
|
||||
void Player::ConstructLimboEntities() {
|
||||
for (const auto objectId : m_LimboConstructions) {
|
||||
auto* entity = Game::entityManager->GetEntity(objectId);
|
||||
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Game::entityManager->ConstructEntity(entity, m_SystemAddress);
|
||||
}
|
||||
|
||||
m_LimboConstructions.clear();
|
||||
}
|
||||
|
||||
std::map<LWOOBJID, Loot::Info>& Player::GetDroppedLoot() {
|
||||
return m_DroppedLoot;
|
||||
}
|
||||
|
||||
const NiPoint3& Player::GetGhostReferencePoint() const {
|
||||
return m_GhostOverride ? m_GhostOverridePoint : m_GhostReferencePoint;
|
||||
}
|
||||
|
||||
const NiPoint3& Player::GetOriginGhostReferencePoint() const {
|
||||
return m_GhostReferencePoint;
|
||||
}
|
||||
|
||||
void Player::SetGhostReferencePoint(const NiPoint3& value) {
|
||||
m_GhostReferencePoint = value;
|
||||
}
|
||||
|
||||
void Player::SetGhostOverridePoint(const NiPoint3& value) {
|
||||
m_GhostOverridePoint = value;
|
||||
}
|
||||
|
||||
const NiPoint3& Player::GetGhostOverridePoint() const {
|
||||
return m_GhostOverridePoint;
|
||||
}
|
||||
|
||||
void Player::SetGhostOverride(bool value) {
|
||||
m_GhostOverride = value;
|
||||
}
|
||||
|
||||
bool Player::GetGhostOverride() const {
|
||||
return m_GhostOverride;
|
||||
}
|
||||
|
||||
void Player::ObserveEntity(int32_t id) {
|
||||
for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) {
|
||||
if (m_ObservedEntities[i] == 0 || m_ObservedEntities[i] == id) {
|
||||
m_ObservedEntities[i] = id;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto index = m_ObservedEntitiesUsed++;
|
||||
|
||||
if (m_ObservedEntitiesUsed > m_ObservedEntitiesLength) {
|
||||
m_ObservedEntities.resize(m_ObservedEntitiesLength + m_ObservedEntitiesLength);
|
||||
|
||||
m_ObservedEntitiesLength = m_ObservedEntitiesLength + m_ObservedEntitiesLength;
|
||||
}
|
||||
|
||||
m_ObservedEntities[index] = id;
|
||||
}
|
||||
|
||||
bool Player::IsObserved(int32_t id) {
|
||||
for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) {
|
||||
if (m_ObservedEntities[i] == id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Player::GhostEntity(int32_t id) {
|
||||
for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) {
|
||||
if (m_ObservedEntities[i] == id) {
|
||||
m_ObservedEntities[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Player* Player::GetPlayer(const SystemAddress& sysAddr) {
|
||||
auto* entity = UserManager::Instance()->GetUser(sysAddr)->GetLastUsedChar()->GetEntity();
|
||||
|
||||
return static_cast<Player*>(entity);
|
||||
}
|
||||
|
||||
Player* Player::GetPlayer(const std::string& name) {
|
||||
const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER);
|
||||
|
||||
for (auto* character : characters) {
|
||||
if (!character->IsPlayer()) continue;
|
||||
|
||||
if (character->GetCharacter()->GetName() == name) {
|
||||
return static_cast<Player*>(character);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* Player::GetPlayer(LWOOBJID playerID) {
|
||||
for (auto* player : m_Players) {
|
||||
if (player->GetObjectID() == playerID) {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<Player*>& Player::GetAllPlayers() {
|
||||
return m_Players;
|
||||
}
|
||||
|
||||
uint64_t Player::GetDroppedCoins() {
|
||||
return m_DroppedCoins;
|
||||
}
|
||||
|
||||
void Player::SetDroppedCoins(uint64_t value) {
|
||||
m_DroppedCoins = value;
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
Game::logger->Log("Player", "Deleted player");
|
||||
|
||||
for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) {
|
||||
const auto id = m_ObservedEntities[i];
|
||||
|
||||
if (id == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* entity = Game::entityManager->GetGhostCandidate(id);
|
||||
|
||||
if (entity != nullptr) {
|
||||
entity->SetObservers(entity->GetObservers() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
m_LimboConstructions.clear();
|
||||
|
||||
const auto& iter = std::find(m_Players.begin(), m_Players.end(), this);
|
||||
|
||||
if (iter == m_Players.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPlayer()) {
|
||||
Entity* zoneControl = Game::entityManager->GetZoneControlEntity();
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerExit(zoneControl, this);
|
||||
}
|
||||
|
||||
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
|
||||
script->OnPlayerExit(scriptEntity, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Players.erase(iter);
|
||||
}
|
||||
136
dGame/Player.h
136
dGame/Player.h
@@ -1,136 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
/**
|
||||
* Extended Entity for player data and behavior.
|
||||
*
|
||||
* Contains properties only a player entity would require, like associated SystemAddress and User.
|
||||
*
|
||||
* Keeps track of which entities are observed by this user for ghosting.
|
||||
*/
|
||||
class Player final : public Entity
|
||||
{
|
||||
public:
|
||||
explicit Player(const LWOOBJID& objectID, EntityInfo info, User* user, Entity* parentEntity = nullptr);
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
User* GetParentUser() const override;
|
||||
|
||||
SystemAddress GetSystemAddress() const override;
|
||||
|
||||
NiPoint3 GetRespawnPosition() const override;
|
||||
|
||||
NiQuaternion GetRespawnRotation() const override;
|
||||
|
||||
const NiPoint3& GetGhostReferencePoint() const;
|
||||
|
||||
const NiPoint3& GetOriginGhostReferencePoint() const;
|
||||
|
||||
const NiPoint3& GetGhostOverridePoint() const;
|
||||
|
||||
bool GetGhostOverride() const;
|
||||
|
||||
std::map<LWOOBJID, Loot::Info>& GetDroppedLoot();
|
||||
|
||||
uint64_t GetDroppedCoins();
|
||||
|
||||
/**
|
||||
* Setters
|
||||
*/
|
||||
|
||||
void SetSystemAddress(const SystemAddress& value) override;
|
||||
|
||||
void SetRespawnPos(NiPoint3 position) override;
|
||||
|
||||
void SetRespawnRot(NiQuaternion rotation) override;
|
||||
|
||||
void SetGhostReferencePoint(const NiPoint3& value);
|
||||
|
||||
void SetGhostOverridePoint(const NiPoint3& value);
|
||||
|
||||
void SetGhostOverride(bool value);
|
||||
|
||||
void SetDroppedCoins(uint64_t value);
|
||||
|
||||
/**
|
||||
* Wrapper for sending an in-game mail.
|
||||
*
|
||||
* @param sender id of the sender. LWOOBJID_EMPTY for system mail
|
||||
* @param senderName name of the sender. Max 32 characters.
|
||||
* @param subject mail subject. Max 50 characters.
|
||||
* @param body mail body. Max 400 characters.
|
||||
* @param attachment LOT of the attached item. LOT_NULL if no attachment.
|
||||
* @param attachmentCount stack size for attachment.
|
||||
*/
|
||||
void SendMail(LWOOBJID sender, const std::string& senderName, const std::string& subject, const std::string& body, LOT attachment, uint16_t attachmentCount) const;
|
||||
|
||||
/**
|
||||
* Wrapper for transfering the player to another instance.
|
||||
*
|
||||
* @param zoneId zoneID for the new instance.
|
||||
* @param cloneId cloneID for the new instance.
|
||||
*/
|
||||
void SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId = 0);
|
||||
|
||||
/**
|
||||
* Ghosting
|
||||
*/
|
||||
|
||||
void AddLimboConstruction(LWOOBJID objectId);
|
||||
|
||||
void RemoveLimboConstruction(LWOOBJID objectId);
|
||||
|
||||
void ConstructLimboEntities();
|
||||
|
||||
void ObserveEntity(int32_t id);
|
||||
|
||||
bool IsObserved(int32_t id);
|
||||
|
||||
void GhostEntity(int32_t id);
|
||||
|
||||
/**
|
||||
* Static methods
|
||||
*/
|
||||
|
||||
static Player* GetPlayer(const SystemAddress& sysAddr);
|
||||
|
||||
static Player* GetPlayer(const std::string& name);
|
||||
|
||||
static Player* GetPlayer(LWOOBJID playerID);
|
||||
|
||||
static const std::vector<Player*>& GetAllPlayers();
|
||||
|
||||
~Player() override;
|
||||
private:
|
||||
SystemAddress m_SystemAddress;
|
||||
|
||||
NiPoint3 m_respawnPos;
|
||||
|
||||
NiQuaternion m_respawnRot;
|
||||
|
||||
User* m_ParentUser;
|
||||
|
||||
NiPoint3 m_GhostReferencePoint;
|
||||
|
||||
NiPoint3 m_GhostOverridePoint;
|
||||
|
||||
bool m_GhostOverride;
|
||||
|
||||
std::vector<int32_t> m_ObservedEntities;
|
||||
|
||||
int32_t m_ObservedEntitiesLength;
|
||||
|
||||
int32_t m_ObservedEntitiesUsed;
|
||||
|
||||
std::vector<LWOOBJID> m_LimboConstructions;
|
||||
|
||||
std::map<LWOOBJID, Loot::Info> m_DroppedLoot;
|
||||
|
||||
uint64_t m_DroppedCoins;
|
||||
|
||||
static std::vector<Player*> m_Players;
|
||||
};
|
||||
67
dGame/PlayerManager.cpp
Normal file
67
dGame/PlayerManager.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "PlayerManager.h"
|
||||
|
||||
#include "Character.h"
|
||||
#include "User.h"
|
||||
#include "UserManager.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
namespace {
|
||||
std::vector<Entity*> m_Players;
|
||||
};
|
||||
|
||||
const std::vector<Entity*>& PlayerManager::GetAllPlayers() {
|
||||
return m_Players;
|
||||
}
|
||||
|
||||
void PlayerManager::AddPlayer(Entity* player) {
|
||||
const auto& iter = std::find(m_Players.begin(), m_Players.end(), player);
|
||||
|
||||
if (iter == m_Players.end()) {
|
||||
m_Players.push_back(player);
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerManager::RemovePlayer(Entity* player) {
|
||||
const auto iter = std::find(m_Players.begin(), m_Players.end(), player);
|
||||
|
||||
const bool toReturn = iter != m_Players.end();
|
||||
if (toReturn) {
|
||||
m_Players.erase(iter);
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
Entity* PlayerManager::GetPlayer(const SystemAddress& sysAddr) {
|
||||
auto* entity = UserManager::Instance()->GetUser(sysAddr)->GetLastUsedChar()->GetEntity();
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity* PlayerManager::GetPlayer(const std::string& name) {
|
||||
const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER);
|
||||
|
||||
Entity* player = nullptr;
|
||||
for (auto* character : characters) {
|
||||
if (!character->IsPlayer()) continue;
|
||||
|
||||
if (GeneralUtils::CaseInsensitiveStringCompare(name, character->GetCharacter()->GetName())) {
|
||||
player = character;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
Entity* PlayerManager::GetPlayer(LWOOBJID playerID) {
|
||||
Entity* playerToReturn = nullptr;
|
||||
for (auto* player : m_Players) {
|
||||
if (player->GetObjectID() == playerID) {
|
||||
playerToReturn = player;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return playerToReturn;
|
||||
}
|
||||
25
dGame/PlayerManager.h
Normal file
25
dGame/PlayerManager.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef __PLAYERMANAGER__H__
|
||||
#define __PLAYERMANAGER__H__
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class Entity;
|
||||
struct SystemAddress;
|
||||
|
||||
namespace PlayerManager {
|
||||
void AddPlayer(Entity* player);
|
||||
|
||||
bool RemovePlayer(Entity* player);
|
||||
|
||||
Entity* GetPlayer(const SystemAddress& sysAddr);
|
||||
|
||||
Entity* GetPlayer(const std::string& name);
|
||||
|
||||
Entity* GetPlayer(LWOOBJID playerID);
|
||||
|
||||
const std::vector<Entity*>& GetAllPlayers();
|
||||
};
|
||||
|
||||
#endif //!__PLAYERMANAGER__H__
|
||||
@@ -1,8 +1,14 @@
|
||||
#include "TeamManager.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
TeamManager* TeamManager::m_Address = nullptr; //For singleton method
|
||||
|
||||
Team::Team() {
|
||||
lootOption = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
|
||||
}
|
||||
|
||||
TeamManager::TeamManager() {
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
struct Team
|
||||
{
|
||||
struct Team {
|
||||
Team();
|
||||
LWOOBJID teamID = LWOOBJID_EMPTY;
|
||||
char lootOption = 0;
|
||||
std::vector<LWOOBJID> members{};
|
||||
char lootRound = 0;
|
||||
};
|
||||
|
||||
class TeamManager
|
||||
{
|
||||
class TeamManager {
|
||||
public:
|
||||
static TeamManager* Instance() {
|
||||
if (!m_Address) {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
#include "EntityManager.h"
|
||||
#include "GameMessages.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "../dWorldServer/ObjectIDManager.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "Item.h"
|
||||
#include "Character.h"
|
||||
#include "CharacterComponent.h"
|
||||
@@ -67,7 +67,7 @@ void Trade::SetAccepted(LWOOBJID participant, bool value) {
|
||||
if (participant == m_ParticipantA) {
|
||||
m_AcceptedA = !value;
|
||||
|
||||
Game::logger->Log("Trade", "Accepted from A (%d), B: (%d)", value, m_AcceptedB);
|
||||
LOG("Accepted from A (%d), B: (%d)", value, m_AcceptedB);
|
||||
|
||||
auto* entityB = GetParticipantBEntity();
|
||||
|
||||
@@ -77,7 +77,7 @@ void Trade::SetAccepted(LWOOBJID participant, bool value) {
|
||||
} else if (participant == m_ParticipantB) {
|
||||
m_AcceptedB = !value;
|
||||
|
||||
Game::logger->Log("Trade", "Accepted from B (%d), A: (%d)", value, m_AcceptedA);
|
||||
LOG("Accepted from B (%d), A: (%d)", value, m_AcceptedA);
|
||||
|
||||
auto* entityA = GetParticipantAEntity();
|
||||
|
||||
@@ -104,7 +104,7 @@ void Trade::SetAccepted(LWOOBJID participant, bool value) {
|
||||
}
|
||||
|
||||
Complete();
|
||||
TradingManager::Instance()->CancelTrade(m_TradeId);
|
||||
TradingManager::Instance()->CancelTrade(LWOOBJID_EMPTY, m_TradeId, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ void Trade::Complete() {
|
||||
|
||||
// First verify both players have the coins and items requested for the trade.
|
||||
if (characterA->GetCoins() < m_CoinsA || characterB->GetCoins() < m_CoinsB) {
|
||||
Game::logger->Log("TradingManager", "Possible coin trade cheating attempt! Aborting trade.");
|
||||
LOG("Possible coin trade cheating attempt! Aborting trade.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -133,11 +133,11 @@ void Trade::Complete() {
|
||||
auto* itemToRemove = inventoryA->FindItemById(tradeItem.itemId);
|
||||
if (itemToRemove) {
|
||||
if (itemToRemove->GetCount() < tradeItem.itemCount) {
|
||||
Game::logger->Log("TradingManager", "Possible cheating attempt from %s in trading!!! Aborting trade", characterA->GetName().c_str());
|
||||
LOG("Possible cheating attempt from %s in trading!!! Aborting trade", characterA->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Game::logger->Log("TradingManager", "Possible cheating attempt from %s in trading due to item not being available!!!", characterA->GetName().c_str());
|
||||
LOG("Possible cheating attempt from %s in trading due to item not being available!!!", characterA->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -146,11 +146,11 @@ void Trade::Complete() {
|
||||
auto* itemToRemove = inventoryB->FindItemById(tradeItem.itemId);
|
||||
if (itemToRemove) {
|
||||
if (itemToRemove->GetCount() < tradeItem.itemCount) {
|
||||
Game::logger->Log("TradingManager", "Possible cheating attempt from %s in trading!!! Aborting trade", characterB->GetName().c_str());
|
||||
LOG("Possible cheating attempt from %s in trading!!! Aborting trade", characterB->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Game::logger->Log("TradingManager", "Possible cheating attempt from %s in trading due to item not being available!!! Aborting trade", characterB->GetName().c_str());
|
||||
LOG("Possible cheating attempt from %s in trading due to item not being available!!! Aborting trade", characterB->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -178,14 +178,14 @@ void Trade::Complete() {
|
||||
return;
|
||||
}
|
||||
|
||||
void Trade::Cancel() {
|
||||
void Trade::Cancel(const LWOOBJID canceller) {
|
||||
auto* entityA = GetParticipantAEntity();
|
||||
auto* entityB = GetParticipantBEntity();
|
||||
|
||||
if (entityA == nullptr || entityB == nullptr) return;
|
||||
|
||||
GameMessages::SendServerTradeCancel(entityA->GetObjectID(), entityA->GetSystemAddress());
|
||||
GameMessages::SendServerTradeCancel(entityB->GetObjectID(), entityB->GetSystemAddress());
|
||||
if (entityA->GetObjectID() != canceller || canceller == LWOOBJID_EMPTY) GameMessages::SendServerTradeCancel(entityA->GetObjectID(), entityA->GetSystemAddress());
|
||||
if (entityB->GetObjectID() != canceller || canceller == LWOOBJID_EMPTY) GameMessages::SendServerTradeCancel(entityB->GetObjectID(), entityB->GetSystemAddress());
|
||||
}
|
||||
|
||||
void Trade::SendUpdateToOther(LWOOBJID participant) {
|
||||
@@ -194,7 +194,7 @@ void Trade::SendUpdateToOther(LWOOBJID participant) {
|
||||
uint64_t coins;
|
||||
std::vector<TradeItem> itemIds;
|
||||
|
||||
Game::logger->Log("Trade", "Attempting to send trade update");
|
||||
LOG("Attempting to send trade update");
|
||||
|
||||
if (participant == m_ParticipantA) {
|
||||
other = GetParticipantBEntity();
|
||||
@@ -228,7 +228,7 @@ void Trade::SendUpdateToOther(LWOOBJID participant) {
|
||||
items.push_back(tradeItem);
|
||||
}
|
||||
|
||||
Game::logger->Log("Trade", "Sending trade update");
|
||||
LOG("Sending trade update");
|
||||
|
||||
GameMessages::SendServerTradeUpdate(other->GetObjectID(), coins, items, other->GetSystemAddress());
|
||||
}
|
||||
@@ -262,24 +262,26 @@ Trade* TradingManager::GetPlayerTrade(LWOOBJID playerId) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TradingManager::CancelTrade(LWOOBJID tradeId) {
|
||||
void TradingManager::CancelTrade(const LWOOBJID canceller, LWOOBJID tradeId, const bool sendCancelMessage) {
|
||||
auto* trade = GetTrade(tradeId);
|
||||
|
||||
if (trade == nullptr) return;
|
||||
|
||||
if (sendCancelMessage) trade->Cancel(canceller);
|
||||
|
||||
delete trade;
|
||||
|
||||
trades.erase(tradeId);
|
||||
}
|
||||
|
||||
Trade* TradingManager::NewTrade(LWOOBJID participantA, LWOOBJID participantB) {
|
||||
const LWOOBJID tradeId = ObjectIDManager::Instance()->GenerateObjectID();
|
||||
const LWOOBJID tradeId = ObjectIDManager::GenerateObjectID();
|
||||
|
||||
auto* trade = new Trade(tradeId, participantA, participantB);
|
||||
|
||||
trades[tradeId] = trade;
|
||||
|
||||
Game::logger->Log("TradingManager", "Created new trade between (%llu) <-> (%llu)", participantA, participantB);
|
||||
LOG("Created new trade between (%llu) <-> (%llu)", participantA, participantB);
|
||||
|
||||
return trade;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
void SetAccepted(LWOOBJID participant, bool value);
|
||||
|
||||
void Complete();
|
||||
void Cancel();
|
||||
void Cancel(const LWOOBJID canceller);
|
||||
|
||||
void SendUpdateToOther(LWOOBJID participant);
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
|
||||
Trade* GetTrade(LWOOBJID tradeId) const;
|
||||
Trade* GetPlayerTrade(LWOOBJID playerId) const;
|
||||
void CancelTrade(LWOOBJID tradeId);
|
||||
void CancelTrade(const LWOOBJID canceller, LWOOBJID tradeId, const bool sendCancelMessage = true);
|
||||
Trade* NewTrade(LWOOBJID participantA, LWOOBJID participantB);
|
||||
|
||||
private:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "Database.h"
|
||||
#include "Character.h"
|
||||
#include "dServer.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "Game.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "eServerDisconnectIdentifiers.h"
|
||||
@@ -23,41 +23,23 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
|
||||
|
||||
m_IsBestFriendMap = std::unordered_map<std::string, bool>();
|
||||
|
||||
//HACK HACK HACK
|
||||
//This needs to be re-enabled / updated whenever the mute stuff is moved to another table.
|
||||
//This was only done because otherwise the website's account page dies and the website is waiting on a migration to wordpress.
|
||||
|
||||
//sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gmlevel, mute_expire FROM accounts WHERE name=? LIMIT 1;");
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gm_level FROM accounts WHERE name=? LIMIT 1;");
|
||||
stmt->setString(1, username.c_str());
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
m_AccountID = res->getUInt(1);
|
||||
m_MaxGMLevel = static_cast<eGameMasterLevel>(res->getInt(2));
|
||||
auto userInfo = Database::Get()->GetAccountInfo(username);
|
||||
if (userInfo) {
|
||||
m_AccountID = userInfo->id;
|
||||
m_MaxGMLevel = userInfo->maxGmLevel;
|
||||
m_MuteExpire = 0; //res->getUInt64(3);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
//If we're loading a zone, we'll load the last used (aka current) character:
|
||||
if (Game::server->GetZoneID() != 0) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 1;");
|
||||
stmt->setUInt(1, m_AccountID);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
if (res->rowsCount() > 0) {
|
||||
while (res->next()) {
|
||||
LWOOBJID objID = res->getUInt64(1);
|
||||
Character* character = new Character(uint32_t(objID), this);
|
||||
m_Characters.push_back(character);
|
||||
Game::logger->Log("User", "Loaded %llu as it is the last used char", objID);
|
||||
}
|
||||
auto characterList = Database::Get()->GetAccountCharacterIds(m_AccountID);
|
||||
if (!characterList.empty()) {
|
||||
const uint32_t lastUsedCharacterId = characterList.front();
|
||||
Character* character = new Character(lastUsedCharacterId, this);
|
||||
character->UpdateFromDatabase();
|
||||
m_Characters.push_back(character);
|
||||
LOG("Loaded %i as it is the last used char", lastUsedCharacterId);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +74,7 @@ User& User::operator= (const User& other) {
|
||||
}
|
||||
|
||||
bool User::operator== (const User& other) const {
|
||||
if (m_Username == other.m_Username || m_SessionKey == other.m_SessionKey || m_SystemAddress == other.m_SystemAddress)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return m_Username == other.m_Username || m_SessionKey == other.m_SessionKey || m_SystemAddress == other.m_SystemAddress;
|
||||
}
|
||||
|
||||
Character* User::GetLastUsedChar() {
|
||||
@@ -127,7 +106,11 @@ void User::UserOutOfSync() {
|
||||
m_AmountOfTimesOutOfSync++;
|
||||
if (m_AmountOfTimesOutOfSync > m_MaxDesyncAllowed) {
|
||||
//YEET
|
||||
Game::logger->Log("User", "User %s was out of sync %i times out of %i, disconnecting for suspected speedhacking.", m_Username.c_str(), m_AmountOfTimesOutOfSync, m_MaxDesyncAllowed);
|
||||
LOG("User %s was out of sync %i times out of %i, disconnecting for suspected speedhacking.", m_Username.c_str(), m_AmountOfTimesOutOfSync, m_MaxDesyncAllowed);
|
||||
Game::server->Disconnect(this->m_SystemAddress, eServerDisconnectIdentifiers::PLAY_SCHEDULE_TIME_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
void User::UpdateBestFriendValue(const std::string_view playerName, const bool newValue) {
|
||||
m_IsBestFriendMap[playerName.data()] = newValue;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../thirdparty/raknet/Source/RakNetTypes.h"
|
||||
#include "RakNetTypes.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -43,8 +43,8 @@ public:
|
||||
bool GetLastChatMessageApproved() { return m_LastChatMessageApproved; }
|
||||
void SetLastChatMessageApproved(bool approved) { m_LastChatMessageApproved = approved; }
|
||||
|
||||
std::unordered_map<std::string, bool> GetIsBestFriendMap() { return m_IsBestFriendMap; }
|
||||
void SetIsBestFriendMap(std::unordered_map<std::string, bool> mapToSet) { m_IsBestFriendMap = mapToSet; }
|
||||
const std::unordered_map<std::string, bool>& GetIsBestFriendMap() { return m_IsBestFriendMap; }
|
||||
void UpdateBestFriendValue(const std::string_view playerName, const bool newValue);
|
||||
|
||||
bool GetIsMuted() const;
|
||||
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "User.h"
|
||||
#include <WorldPackets.h>
|
||||
#include "WorldPackets.h"
|
||||
#include "Character.h"
|
||||
#include <BitStream.h>
|
||||
#include "PacketUtils.h"
|
||||
#include "../dWorldServer/ObjectIDManager.h"
|
||||
#include "dLogger.h"
|
||||
#include "BitStream.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "Logger.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "dServer.h"
|
||||
@@ -28,6 +27,8 @@
|
||||
#include "eRenameResponse.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "CheatDetection.h"
|
||||
|
||||
UserManager* UserManager::m_Address = nullptr;
|
||||
|
||||
@@ -42,57 +43,53 @@ inline void StripCR(std::string& str) {
|
||||
void UserManager::Initialize() {
|
||||
std::string line;
|
||||
|
||||
AssetMemoryBuffer fnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_first.txt");
|
||||
if (!fnBuff.m_Success) {
|
||||
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_first.txt").string().c_str());
|
||||
auto fnStream = Game::assetManager->GetFile("names/minifigname_first.txt");
|
||||
if (!fnStream) {
|
||||
LOG("Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_first.txt").string().c_str());
|
||||
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
|
||||
}
|
||||
std::istream fnStream = std::istream(&fnBuff);
|
||||
|
||||
while (std::getline(fnStream, line, '\n')) {
|
||||
std::string name = line;
|
||||
StripCR(name);
|
||||
m_FirstNames.push_back(name);
|
||||
}
|
||||
fnBuff.close();
|
||||
|
||||
AssetMemoryBuffer mnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_middle.txt");
|
||||
if (!mnBuff.m_Success) {
|
||||
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_middle.txt").string().c_str());
|
||||
auto mnStream = Game::assetManager->GetFile("names/minifigname_middle.txt");
|
||||
if (!mnStream) {
|
||||
LOG("Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_middle.txt").string().c_str());
|
||||
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
|
||||
}
|
||||
std::istream mnStream = std::istream(&mnBuff);
|
||||
|
||||
while (std::getline(mnStream, line, '\n')) {
|
||||
std::string name = line;
|
||||
StripCR(name);
|
||||
m_MiddleNames.push_back(name);
|
||||
}
|
||||
mnBuff.close();
|
||||
|
||||
AssetMemoryBuffer lnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_last.txt");
|
||||
if (!lnBuff.m_Success) {
|
||||
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_last.txt").string().c_str());
|
||||
auto lnStream = Game::assetManager->GetFile("names/minifigname_last.txt");
|
||||
if (!lnStream) {
|
||||
LOG("Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_last.txt").string().c_str());
|
||||
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
|
||||
}
|
||||
std::istream lnStream = std::istream(&lnBuff);
|
||||
|
||||
while (std::getline(lnStream, line, '\n')) {
|
||||
std::string name = line;
|
||||
StripCR(name);
|
||||
m_LastNames.push_back(name);
|
||||
}
|
||||
lnBuff.close();
|
||||
|
||||
//Load our pre-approved names:
|
||||
AssetMemoryBuffer chatListBuff = Game::assetManager->GetFileAsBuffer("chatplus_en_us.txt");
|
||||
if (!chatListBuff.m_Success) {
|
||||
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str());
|
||||
// Load our pre-approved names:
|
||||
auto chatListStream = Game::assetManager->GetFile("chatplus_en_us.txt");
|
||||
if (!chatListStream) {
|
||||
LOG("Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str());
|
||||
throw std::runtime_error("Aborting initialization due to missing chat whitelist file.");
|
||||
}
|
||||
std::istream chatListStream = std::istream(&chatListBuff);
|
||||
|
||||
while (std::getline(chatListStream, line, '\n')) {
|
||||
StripCR(line);
|
||||
m_PreapprovedNames.push_back(line);
|
||||
}
|
||||
chatListBuff.close();
|
||||
}
|
||||
|
||||
UserManager::~UserManager() {
|
||||
@@ -123,7 +120,7 @@ User* UserManager::GetUser(const SystemAddress& sysAddr) {
|
||||
User* UserManager::GetUser(const std::string& username) {
|
||||
for (auto p : m_Users) {
|
||||
if (p.second) {
|
||||
if (p.second->GetUsername() == username) return p.second;
|
||||
if (GeneralUtils::CaseInsensitiveStringCompare(p.second->GetUsername(), username)) return p.second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +145,7 @@ bool UserManager::DeleteUser(const SystemAddress& sysAddr) {
|
||||
|
||||
void UserManager::DeletePendingRemovals() {
|
||||
for (auto* user : m_UsersToDelete) {
|
||||
Game::logger->Log("UserManager", "Deleted user %i", user->GetAccountID());
|
||||
LOG("Deleted user %i", user->GetAccountID());
|
||||
|
||||
delete user;
|
||||
}
|
||||
@@ -156,20 +153,6 @@ void UserManager::DeletePendingRemovals() {
|
||||
m_UsersToDelete.clear();
|
||||
}
|
||||
|
||||
bool UserManager::IsNameAvailable(const std::string& requestedName) {
|
||||
bool toReturn = false; //To allow for a clean exit
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? OR pending_name=? LIMIT 1;");
|
||||
stmt->setString(1, requestedName.c_str());
|
||||
stmt->setString(2, requestedName.c_str());
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
if (res->rowsCount() == 0) toReturn = true;
|
||||
|
||||
delete stmt;
|
||||
delete res;
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::string UserManager::GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex) {
|
||||
if (firstNameIndex > m_FirstNames.size() || middleNameIndex > m_MiddleNames.size() || lastNameIndex > m_LastNames.size()) return std::string("INVALID");
|
||||
return std::string(m_FirstNames[firstNameIndex] + m_MiddleNames[middleNameIndex] + m_LastNames[lastNameIndex]);
|
||||
@@ -198,279 +181,250 @@ bool UserManager::IsNamePreapproved(const std::string& requestedName) {
|
||||
void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
|
||||
User* u = GetUser(sysAddr);
|
||||
if (!u) return;
|
||||
std::vector<Character*>& chars = u->GetCharacters();
|
||||
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 4;");
|
||||
stmt->setUInt(1, u->GetAccountID());
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
if (res->rowsCount() > 0) {
|
||||
std::vector<Character*>& chars = u->GetCharacters();
|
||||
|
||||
for (size_t i = 0; i < chars.size(); ++i) {
|
||||
if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save
|
||||
{
|
||||
delete chars[i];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* skillComponent = chars[i]->GetEntity()->GetComponent<SkillComponent>();
|
||||
|
||||
if (skillComponent != nullptr) {
|
||||
skillComponent->Reset();
|
||||
}
|
||||
|
||||
Game::entityManager->DestroyEntity(chars[i]->GetEntity());
|
||||
|
||||
chars[i]->SaveXMLToDatabase();
|
||||
|
||||
chars[i]->GetEntity()->SetCharacter(nullptr);
|
||||
|
||||
for (size_t i = 0; i < chars.size(); ++i) {
|
||||
if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save
|
||||
{
|
||||
delete chars[i];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
chars.clear();
|
||||
auto* skillComponent = chars[i]->GetEntity()->GetComponent<SkillComponent>();
|
||||
|
||||
while (res->next()) {
|
||||
LWOOBJID objID = res->getUInt64(1);
|
||||
Character* character = new Character(uint32_t(objID), u);
|
||||
character->SetIsNewLogin();
|
||||
chars.push_back(character);
|
||||
if (skillComponent != nullptr) {
|
||||
skillComponent->Reset();
|
||||
}
|
||||
|
||||
Game::entityManager->DestroyEntity(chars[i]->GetEntity());
|
||||
|
||||
chars[i]->SaveXMLToDatabase();
|
||||
|
||||
chars[i]->GetEntity()->SetCharacter(nullptr);
|
||||
|
||||
delete chars[i];
|
||||
}
|
||||
|
||||
chars.clear();
|
||||
|
||||
for (const auto& characterId : Database::Get()->GetAccountCharacterIds(u->GetAccountID())) {
|
||||
Character* character = new Character(characterId, u);
|
||||
character->UpdateFromDatabase();
|
||||
character->SetIsNewLogin();
|
||||
chars.push_back(character);
|
||||
}
|
||||
|
||||
RakNet::BitStream bitStream;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE);
|
||||
|
||||
std::vector<Character*> characters = u->GetCharacters();
|
||||
bitStream.Write<uint8_t>(characters.size());
|
||||
bitStream.Write<uint8_t>(0); //TODO: Pick the most recent played index. character index in front, just picking 0
|
||||
|
||||
for (uint32_t i = 0; i < characters.size(); ++i) {
|
||||
bitStream.Write(characters[i]->GetObjectID());
|
||||
bitStream.Write<uint32_t>(0);
|
||||
|
||||
bitStream.Write(LUWString(characters[i]->GetName()));
|
||||
bitStream.Write(LUWString(characters[i]->GetUnapprovedName()));
|
||||
|
||||
bitStream.Write<uint8_t>(characters[i]->GetNameRejected());
|
||||
bitStream.Write<uint8_t>(false);
|
||||
|
||||
bitStream.Write(LUString("", 10));
|
||||
|
||||
bitStream.Write(characters[i]->GetShirtColor());
|
||||
bitStream.Write(characters[i]->GetShirtStyle());
|
||||
bitStream.Write(characters[i]->GetPantsColor());
|
||||
bitStream.Write(characters[i]->GetHairStyle());
|
||||
bitStream.Write(characters[i]->GetHairColor());
|
||||
bitStream.Write(characters[i]->GetLeftHand());
|
||||
bitStream.Write(characters[i]->GetRightHand());
|
||||
bitStream.Write(characters[i]->GetEyebrows());
|
||||
bitStream.Write(characters[i]->GetEyes());
|
||||
bitStream.Write(characters[i]->GetMouth());
|
||||
bitStream.Write<uint32_t>(0);
|
||||
|
||||
bitStream.Write<uint16_t>(characters[i]->GetZoneID());
|
||||
bitStream.Write<uint16_t>(characters[i]->GetZoneInstance());
|
||||
bitStream.Write(characters[i]->GetZoneClone());
|
||||
|
||||
bitStream.Write(characters[i]->GetLastLogin());
|
||||
|
||||
const auto& equippedItems = characters[i]->GetEquippedItems();
|
||||
bitStream.Write<uint16_t>(equippedItems.size());
|
||||
|
||||
for (uint32_t j = 0; j < equippedItems.size(); ++j) {
|
||||
bitStream.Write(equippedItems[j]);
|
||||
}
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
WorldPackets::SendCharacterList(sysAddr, u);
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) {
|
||||
User* u = GetUser(sysAddr);
|
||||
if (!u) return;
|
||||
|
||||
LUWString LUWStringName;
|
||||
uint32_t firstNameIndex;
|
||||
uint32_t middleNameIndex;
|
||||
uint32_t lastNameIndex;
|
||||
uint32_t shirtColor;
|
||||
uint32_t shirtStyle;
|
||||
uint32_t pantsColor;
|
||||
uint32_t hairStyle;
|
||||
uint32_t hairColor;
|
||||
uint32_t lh;
|
||||
uint32_t rh;
|
||||
uint32_t eyebrows;
|
||||
uint32_t eyes;
|
||||
uint32_t mouth;
|
||||
|
||||
std::string name = PacketUtils::ReadString(8, packet, true);
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
inStream.Read(LUWStringName);
|
||||
inStream.Read(firstNameIndex);
|
||||
inStream.Read(middleNameIndex);
|
||||
inStream.Read(lastNameIndex);
|
||||
inStream.IgnoreBytes(9);
|
||||
inStream.Read(shirtColor);
|
||||
inStream.Read(shirtStyle);
|
||||
inStream.Read(pantsColor);
|
||||
inStream.Read(hairStyle);
|
||||
inStream.Read(hairColor);
|
||||
inStream.Read(lh);
|
||||
inStream.Read(rh);
|
||||
inStream.Read(eyebrows);
|
||||
inStream.Read(eyes);
|
||||
inStream.Read(mouth);
|
||||
|
||||
uint32_t firstNameIndex = PacketUtils::ReadPacketU32(74, packet);
|
||||
uint32_t middleNameIndex = PacketUtils::ReadPacketU32(78, packet);
|
||||
uint32_t lastNameIndex = PacketUtils::ReadPacketU32(82, packet);
|
||||
const auto name = LUWStringName.GetAsString();
|
||||
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
|
||||
|
||||
uint32_t shirtColor = PacketUtils::ReadPacketU32(95, packet);
|
||||
uint32_t shirtStyle = PacketUtils::ReadPacketU32(99, packet);
|
||||
uint32_t pantsColor = PacketUtils::ReadPacketU32(103, packet);
|
||||
uint32_t hairStyle = PacketUtils::ReadPacketU32(107, packet);
|
||||
uint32_t hairColor = PacketUtils::ReadPacketU32(111, packet);
|
||||
uint32_t lh = PacketUtils::ReadPacketU32(115, packet);
|
||||
uint32_t rh = PacketUtils::ReadPacketU32(119, packet);
|
||||
uint32_t eyebrows = PacketUtils::ReadPacketU32(123, packet);
|
||||
uint32_t eyes = PacketUtils::ReadPacketU32(127, packet);
|
||||
uint32_t mouth = PacketUtils::ReadPacketU32(131, packet);
|
||||
|
||||
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
|
||||
LOT pantsLOT = FindCharPantsID(pantsColor);
|
||||
|
||||
if (name != "" && !UserManager::IsNameAvailable(name)) {
|
||||
Game::logger->Log("UserManager", "AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str());
|
||||
if (!name.empty() && Database::Get()->GetCharacterInfo(name)) {
|
||||
LOG("AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str());
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsNameAvailable(predefinedName)) {
|
||||
Game::logger->Log("UserManager", "AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
||||
if (Database::Get()->GetCharacterInfo(predefinedName)) {
|
||||
LOG("AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "") {
|
||||
Game::logger->Log("UserManager", "AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
||||
if (name.empty()) {
|
||||
LOG("AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str());
|
||||
} else {
|
||||
Game::logger->Log("UserManager", "AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.c_str(), predefinedName.c_str());
|
||||
LOG("AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.c_str(), predefinedName.c_str());
|
||||
}
|
||||
|
||||
//Now that the name is ok, we can get an objectID from Master:
|
||||
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) {
|
||||
sql::PreparedStatement* overlapStmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE id = ?");
|
||||
overlapStmt->setUInt(1, objectID);
|
||||
|
||||
auto* overlapResult = overlapStmt->executeQuery();
|
||||
|
||||
if (overlapResult->next()) {
|
||||
Game::logger->Log("UserManager", "Character object id unavailable, check objectidtracker!");
|
||||
ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) mutable {
|
||||
if (Database::Get()->GetCharacterInfo(objectID)) {
|
||||
LOG("Character object id unavailable, check object_id_tracker!");
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream xml;
|
||||
xml << "<obj v=\"1\"><mf hc=\"" << hairColor << "\" hs=\"" << hairStyle << "\" hd=\"0\" t=\"" << shirtColor << "\" l=\"" << pantsColor;
|
||||
xml << "<obj v=\"1\">";
|
||||
|
||||
xml << "<mf hc=\"" << hairColor << "\" hs=\"" << hairStyle << "\" hd=\"0\" t=\"" << shirtColor << "\" l=\"" << pantsColor;
|
||||
xml << "\" hdc=\"0\" cd=\"" << shirtStyle << "\" lh=\"" << lh << "\" rh=\"" << rh << "\" es=\"" << eyebrows << "\" ";
|
||||
xml << "ess=\"" << eyes << "\" ms=\"" << mouth << "\"/>";
|
||||
|
||||
xml << "<char acct=\"" << u->GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" ";
|
||||
xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" ";
|
||||
xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\"></char>";
|
||||
|
||||
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
|
||||
|
||||
xml << "<inv><bag><b t=\"0\" m=\"20\"/><b t=\"1\" m=\"40\"/><b t=\"2\" m=\"240\"/><b t=\"3\" m=\"240\"/><b t=\"14\" m=\"40\"/></bag><items><in t=\"0\">";
|
||||
std::string xmlSave1 = xml.str();
|
||||
|
||||
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforshirt) {
|
||||
std::stringstream xml2;
|
||||
LWOOBJID lwoidforshirt = ObjectIDManager::GenerateRandomObjectID();
|
||||
LWOOBJID lwoidforpants;
|
||||
|
||||
LWOOBJID lwoidforshirt = idforshirt;
|
||||
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT);
|
||||
xml2 << xmlSave1 << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
|
||||
do {
|
||||
lwoidforpants = ObjectIDManager::GenerateRandomObjectID();
|
||||
} while (lwoidforpants == lwoidforshirt); //Make sure we don't have the same ID for both shirt and pants
|
||||
|
||||
std::string xmlSave2 = xml2.str();
|
||||
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
|
||||
|
||||
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforpants) {
|
||||
LWOOBJID lwoidforpants = idforpants;
|
||||
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
|
||||
xml << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
|
||||
xml << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
|
||||
|
||||
std::stringstream xml3;
|
||||
xml3 << xmlSave2 << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
|
||||
xml << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
|
||||
|
||||
xml3 << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
|
||||
//Check to see if our name was pre-approved:
|
||||
bool nameOk = IsNamePreapproved(name);
|
||||
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
||||
|
||||
//Check to see if our name was pre-approved:
|
||||
bool nameOk = IsNamePreapproved(name);
|
||||
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
||||
// If predefined name is invalid, change it to be their object id
|
||||
// that way more than one player can create characters if the predefined name files are not provided
|
||||
if (predefinedName == "INVALID") {
|
||||
std::stringstream nameObjID;
|
||||
nameObjID << "minifig" << objectID;
|
||||
predefinedName = nameObjID.str();
|
||||
}
|
||||
|
||||
if (name != "") {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
|
||||
stmt->setUInt(1, objectID);
|
||||
stmt->setUInt(2, u->GetAccountID());
|
||||
stmt->setString(3, predefinedName.c_str());
|
||||
stmt->setString(4, name.c_str());
|
||||
stmt->setBoolean(5, false);
|
||||
stmt->setUInt64(6, time(NULL));
|
||||
std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName;
|
||||
std::string pendingName = !name.empty() && !nameOk ? name : "";
|
||||
|
||||
if (nameOk) {
|
||||
stmt->setString(3, name.c_str());
|
||||
stmt->setString(4, "");
|
||||
}
|
||||
ICharInfo::Info info;
|
||||
info.name = nameToAssign;
|
||||
info.pendingName = pendingName;
|
||||
info.id = objectID;
|
||||
info.accountId = u->GetAccountID();
|
||||
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
} else {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
|
||||
stmt->setUInt(1, objectID);
|
||||
stmt->setUInt(2, u->GetAccountID());
|
||||
stmt->setString(3, predefinedName.c_str());
|
||||
stmt->setString(4, "");
|
||||
stmt->setBoolean(5, false);
|
||||
stmt->setUInt64(6, time(NULL));
|
||||
Database::Get()->InsertNewCharacter(info);
|
||||
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
//Now finally insert our character xml:
|
||||
Database::Get()->InsertCharacterXml(objectID, xml.str());
|
||||
|
||||
//Now finally insert our character xml:
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charxml`(`id`, `xml_data`) VALUES (?,?)");
|
||||
stmt->setUInt(1, objectID);
|
||||
stmt->setString(2, xml3.str().c_str());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
|
||||
UserManager::RequestCharacterList(sysAddr);
|
||||
});
|
||||
});
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
|
||||
UserManager::RequestCharacterList(sysAddr);
|
||||
});
|
||||
}
|
||||
|
||||
void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) {
|
||||
User* u = GetUser(sysAddr);
|
||||
if (!u) {
|
||||
Game::logger->Log("UserManager", "Couldn't get user to delete character");
|
||||
LOG("Couldn't get user to delete character");
|
||||
return;
|
||||
}
|
||||
|
||||
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID objectID;
|
||||
inStream.Read(objectID);
|
||||
uint32_t charID = static_cast<uint32_t>(objectID);
|
||||
|
||||
Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)", objectID, charID);
|
||||
LOG("Received char delete req for ID: %llu (%u)", objectID, charID);
|
||||
|
||||
//Check if this user has this character:
|
||||
bool hasCharacter = false;
|
||||
std::vector<Character*>& characters = u->GetCharacters();
|
||||
for (size_t i = 0; i < characters.size(); ++i) {
|
||||
if (characters[i]->GetID() == charID) { hasCharacter = true; }
|
||||
}
|
||||
bool hasCharacter = CheatDetection::VerifyLwoobjidIsSender(
|
||||
objectID,
|
||||
sysAddr,
|
||||
CheckType::User,
|
||||
"User %i tried to delete a character that it does not own!",
|
||||
u->GetAccountID());
|
||||
|
||||
if (!hasCharacter) {
|
||||
Game::logger->Log("UserManager", "User %i tried to delete a character that it does not own!", u->GetAccountID());
|
||||
WorldPackets::SendCharacterDeleteResponse(sysAddr, false);
|
||||
} else {
|
||||
Game::logger->Log("UserManager", "Deleting character %i", charID);
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charxml WHERE id=? LIMIT 1;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM command_log WHERE character_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;");
|
||||
stmt->setUInt(1, charID);
|
||||
stmt->setUInt(2, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
|
||||
bitStream.Write(objectID);
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);"
|
||||
);
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM properties WHERE owner_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM ugc WHERE character_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM activity_log WHERE character_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM mail WHERE receiver_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charinfo WHERE id=? LIMIT 1;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
LOG("Deleting character %i", charID);
|
||||
Database::Get()->DeleteCharacter(charID);
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
|
||||
bitStream.Write(objectID);
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
|
||||
WorldPackets::SendCharacterDeleteResponse(sysAddr, true);
|
||||
}
|
||||
@@ -479,58 +433,58 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) {
|
||||
User* u = GetUser(sysAddr);
|
||||
if (!u) {
|
||||
Game::logger->Log("UserManager", "Couldn't get user to delete character");
|
||||
LOG("Couldn't get user to delete character");
|
||||
return;
|
||||
}
|
||||
|
||||
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID objectID;
|
||||
inStream.Read(objectID);
|
||||
GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER);
|
||||
GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT);
|
||||
|
||||
uint32_t charID = static_cast<uint32_t>(objectID);
|
||||
Game::logger->Log("UserManager", "Received char rename request for ID: %llu (%u)", objectID, charID);
|
||||
LOG("Received char rename request for ID: %llu (%u)", objectID, charID);
|
||||
|
||||
std::string newName = PacketUtils::ReadString(16, packet, true);
|
||||
LUWString LUWStringName;
|
||||
inStream.Read(LUWStringName);
|
||||
const auto newName = LUWStringName.GetAsString();
|
||||
|
||||
Character* character = nullptr;
|
||||
|
||||
//Check if this user has this character:
|
||||
bool hasCharacter = false;
|
||||
std::vector<Character*>& characters = u->GetCharacters();
|
||||
for (size_t i = 0; i < characters.size(); ++i) {
|
||||
if (characters[i]->GetID() == charID) { hasCharacter = true; character = characters[i]; }
|
||||
}
|
||||
bool ownsCharacter = CheatDetection::VerifyLwoobjidIsSender(
|
||||
objectID,
|
||||
sysAddr,
|
||||
CheckType::User,
|
||||
"User %i tried to rename a character that it does not own!",
|
||||
u->GetAccountID());
|
||||
|
||||
if (!hasCharacter || !character) {
|
||||
Game::logger->Log("UserManager", "User %i tried to rename a character that it does not own!", u->GetAccountID());
|
||||
auto unusedItr = std::find_if(u->GetCharacters().begin(), u->GetCharacters().end(), [&](Character* c) {
|
||||
if (c->GetID() == charID) {
|
||||
character = c;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!ownsCharacter || !character) {
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
|
||||
} else if (hasCharacter && character) {
|
||||
} else if (ownsCharacter && character) {
|
||||
if (newName == character->GetName()) {
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsNameAvailable(newName)) {
|
||||
if (!Database::Get()->GetCharacterInfo(newName)) {
|
||||
if (IsNamePreapproved(newName)) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET name=?, pending_name='', needs_rename=0, last_login=? WHERE id=? LIMIT 1");
|
||||
stmt->setString(1, newName);
|
||||
stmt->setUInt64(2, time(NULL));
|
||||
stmt->setUInt(3, character->GetID());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
|
||||
Game::logger->Log("UserManager", "Character %s now known as %s", character->GetName().c_str(), newName.c_str());
|
||||
Database::Get()->SetCharacterName(charID, newName);
|
||||
LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str());
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
||||
UserManager::RequestCharacterList(sysAddr);
|
||||
} else {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET pending_name=?, needs_rename=0, last_login=? WHERE id=? LIMIT 1");
|
||||
stmt->setString(1, newName);
|
||||
stmt->setUInt64(2, time(NULL));
|
||||
stmt->setUInt(3, character->GetID());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
|
||||
Game::logger->Log("UserManager", "Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str());
|
||||
Database::Get()->SetPendingCharacterName(charID, newName);
|
||||
LOG("Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str());
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
||||
UserManager::RequestCharacterList(sysAddr);
|
||||
}
|
||||
@@ -538,7 +492,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_IN_USE);
|
||||
}
|
||||
} else {
|
||||
Game::logger->Log("UserManager", "Unknown error occurred when renaming character, either hasCharacter or character variable != true.");
|
||||
LOG("Unknown error occurred when renaming character, either hasCharacter or character variable != true.");
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -546,7 +500,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID) {
|
||||
User* u = GetUser(sysAddr);
|
||||
if (!u) {
|
||||
Game::logger->Log("UserManager", "Couldn't get user to log in character");
|
||||
LOG("Couldn't get user to log in character");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -559,17 +513,13 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
|
||||
}
|
||||
|
||||
if (hasCharacter && character) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET last_login=? WHERE id=? LIMIT 1");
|
||||
stmt->setUInt64(1, time(NULL));
|
||||
stmt->setUInt(2, playerID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Get()->UpdateLastLoggedInCharacter(playerID);
|
||||
|
||||
uint32_t zoneID = character->GetZoneID();
|
||||
if (zoneID == LWOZONEID_INVALID) zoneID = 1000; //Send char to VE
|
||||
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneID, character->GetZoneClone(), false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
if (character) {
|
||||
character->SetZoneID(zoneID);
|
||||
character->SetZoneInstance(zoneInstance);
|
||||
@@ -579,22 +529,24 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
Game::logger->Log("UserManager", "Unknown error occurred when logging in a character, either hasCharacter or character variable != true.");
|
||||
LOG("Unknown error occurred when logging in a character, either hasCharacter or character variable != true.");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
|
||||
try {
|
||||
std::string shirtQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"character create shirt\" AND icc.color1 == ";
|
||||
shirtQuery += std::to_string(shirtColor);
|
||||
shirtQuery += " AND icc.decal == ";
|
||||
shirtQuery = shirtQuery + std::to_string(shirtStyle);
|
||||
auto tableData = CDClientDatabase::ExecuteQuery(shirtQuery);
|
||||
auto shirtLOT = tableData.getIntField(0, -1);
|
||||
auto stmt = CDClientDatabase::CreatePreppedStmt(
|
||||
"select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ? AND icc.decal == ?"
|
||||
);
|
||||
stmt.bind(1, "character create shirt");
|
||||
stmt.bind(2, static_cast<int>(shirtColor));
|
||||
stmt.bind(3, static_cast<int>(shirtStyle));
|
||||
auto tableData = stmt.execQuery();
|
||||
auto shirtLOT = tableData.getIntField(0, 4069);
|
||||
tableData.finalize();
|
||||
return shirtLOT;
|
||||
} catch (const std::exception&) {
|
||||
Game::logger->Log("Character Create", "Failed to execute query! Using backup...");
|
||||
} catch (const std::exception& ex) {
|
||||
LOG("Could not look up shirt %i %i: %s", shirtColor, shirtStyle, ex.what());
|
||||
// in case of no shirt found in CDServer, return problematic red vest.
|
||||
return 4069;
|
||||
}
|
||||
@@ -602,14 +554,17 @@ uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
|
||||
|
||||
uint32_t FindCharPantsID(uint32_t pantsColor) {
|
||||
try {
|
||||
std::string pantsQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"cc pants\" AND icc.color1 == ";
|
||||
pantsQuery += std::to_string(pantsColor);
|
||||
auto tableData = CDClientDatabase::ExecuteQuery(pantsQuery);
|
||||
auto pantsLOT = tableData.getIntField(0, -1);
|
||||
auto stmt = CDClientDatabase::CreatePreppedStmt(
|
||||
"select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ?"
|
||||
);
|
||||
stmt.bind(1, "cc pants");
|
||||
stmt.bind(2, static_cast<int>(pantsColor));
|
||||
auto tableData = stmt.execQuery();
|
||||
auto pantsLOT = tableData.getIntField(0, 2508);
|
||||
tableData.finalize();
|
||||
return pantsLOT;
|
||||
} catch (const std::exception&) {
|
||||
Game::logger->Log("Character Create", "Failed to execute query! Using backup...");
|
||||
} catch (const std::exception& ex) {
|
||||
LOG("Could not look up pants %i: %s", pantsColor, ex.what());
|
||||
// in case of no pants color found in CDServer, return red pants.
|
||||
return 2508;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ public:
|
||||
bool DeleteUser(const SystemAddress& sysAddr); //Returns true on succesful deletion
|
||||
void DeletePendingRemovals();
|
||||
|
||||
bool IsNameAvailable(const std::string& requestedName);
|
||||
std::string GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex);
|
||||
bool IsNamePreapproved(const std::string& requestedName);
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void AirMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
uint32_t handle{};
|
||||
|
||||
if (!bitStream->Read(handle)) {
|
||||
Game::logger->Log("AirMovementBehavior", "Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,14 +26,14 @@ void AirMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
uint32_t behaviorId{};
|
||||
|
||||
if (!bitStream->Read(behaviorId)) {
|
||||
Game::logger->Log("AirMovementBehavior", "Unable to read behaviorId from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read behaviorId from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
}
|
||||
|
||||
LWOOBJID target{};
|
||||
|
||||
if (!bitStream->Read(target)) {
|
||||
Game::logger->Log("AirMovementBehavior", "Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "AndBehavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void AndBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
for (auto* behavior : this->m_behaviors) {
|
||||
|
||||
@@ -15,7 +15,7 @@ void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
if (buffComponent == nullptr) return;
|
||||
|
||||
buffComponent->ApplyBuff(m_BuffId, m_Duration, context->originator, addImmunity, cancelOnDamaged, cancelOnDeath,
|
||||
cancelOnLogout, cancelonRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone);
|
||||
cancelOnLogout, cancelonRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, m_ApplyOnTeammates);
|
||||
}
|
||||
|
||||
void ApplyBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
@@ -45,4 +45,5 @@ void ApplyBuffBehavior::Load() {
|
||||
cancelOnUi = GetBoolean("cancel_on_ui");
|
||||
cancelOnUnequip = GetBoolean("cancel_on_unequip");
|
||||
cancelOnZone = GetBoolean("cancel_on_zone");
|
||||
m_ApplyOnTeammates = GetBoolean("apply_on_teammates");
|
||||
}
|
||||
|
||||
@@ -31,4 +31,6 @@ public:
|
||||
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
void Load() override;
|
||||
private:
|
||||
bool m_ApplyOnTeammates;
|
||||
};
|
||||
|
||||
@@ -4,150 +4,130 @@
|
||||
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
uint32_t targetCount{};
|
||||
|
||||
if (!bitStream->Read(targetCount)) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "Unable to read targetCount from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read targetCount from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->m_useTargetPosition && branch.target == LWOOBJID_EMPTY) return;
|
||||
|
||||
if (targetCount == 0){
|
||||
PlayFx(u"miss", context->originator);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetCount > this->m_maxTargets) {
|
||||
LOG("Serialized size is greater than max targets! Size: %i, Max: %i", targetCount, this->m_maxTargets);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
auto caster = context->caster;
|
||||
if (this->m_useTargetAsCaster) context->caster = branch.target;
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
targets.reserve(targetCount);
|
||||
|
||||
for (auto i = 0u; i < targetCount; ++i) {
|
||||
LWOOBJID target{};
|
||||
|
||||
if (!bitStream->Read(target)) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "failed to read in target %i from bitStream, aborting target Handle!", i);
|
||||
return;
|
||||
LOG("failed to read in target %i from bitStream, aborting target Handle!", i);
|
||||
};
|
||||
|
||||
targets.push_back(target);
|
||||
}
|
||||
|
||||
for (auto target : targets) {
|
||||
branch.target = target;
|
||||
|
||||
this->m_action->Handle(context, bitStream, branch);
|
||||
}
|
||||
context->caster = caster;
|
||||
PlayFx(u"cast", context->originator);
|
||||
}
|
||||
|
||||
void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* self = Game::entityManager->GetEntity(context->caster);
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "Invalid self for (%llu)!", context->originator);
|
||||
auto* caster = Game::entityManager->GetEntity(context->caster);
|
||||
if (!caster) return;
|
||||
|
||||
return;
|
||||
// determine the position we are casting the AOE from
|
||||
auto reference = branch.isProjectile ? branch.referencePosition : caster->GetPosition();
|
||||
if (this->m_useTargetPosition) {
|
||||
if (branch.target == LWOOBJID_EMPTY) return;
|
||||
auto branchTarget = Game::entityManager->GetEntity(branch.target);
|
||||
if (branchTarget) reference = branchTarget->GetPosition();
|
||||
}
|
||||
|
||||
auto reference = branch.isProjectile ? branch.referencePosition : self->GetPosition();
|
||||
reference += this->m_offset;
|
||||
|
||||
std::vector<Entity*> targets;
|
||||
|
||||
auto* presetTarget = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (presetTarget != nullptr) {
|
||||
if (this->m_radius * this->m_radius >= Vector3::DistanceSquared(reference, presetTarget->GetPosition())) {
|
||||
targets.push_back(presetTarget);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t includeFaction = m_includeFaction;
|
||||
|
||||
if (self->GetLOT() == 14466) // TODO: Fix edge case
|
||||
{
|
||||
includeFaction = 1;
|
||||
}
|
||||
|
||||
// Gets all of the valid targets, passing in if should target enemies and friends
|
||||
for (auto validTarget : context->GetValidTargets(m_ignoreFaction, includeFaction, m_TargetSelf == 1, m_targetEnemy == 1, m_targetFriend == 1)) {
|
||||
auto* entity = Game::entityManager->GetEntity(validTarget);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::find(targets.begin(), targets.end(), entity) != targets.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (destroyableComponent->HasFaction(m_ignoreFaction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto distance = Vector3::DistanceSquared(reference, entity->GetPosition());
|
||||
|
||||
if (this->m_radius * this->m_radius >= distance && (this->m_maxTargets == 0 || targets.size() < this->m_maxTargets)) {
|
||||
targets.push_back(entity);
|
||||
}
|
||||
}
|
||||
std::vector<Entity*> targets {};
|
||||
targets = Game::entityManager->GetEntitiesByProximity(reference, this->m_radius);
|
||||
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
||||
|
||||
// sort by distance
|
||||
std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b) {
|
||||
const auto aDistance = Vector3::DistanceSquared(a->GetPosition(), reference);
|
||||
const auto bDistance = Vector3::DistanceSquared(b->GetPosition(), reference);
|
||||
const auto aDistance = NiPoint3::Distance(a->GetPosition(), reference);
|
||||
const auto bDistance = NiPoint3::Distance(b->GetPosition(), reference);
|
||||
return aDistance < bDistance;
|
||||
}
|
||||
);
|
||||
|
||||
return aDistance > bDistance;
|
||||
});
|
||||
// resize if we have more than max targets allows
|
||||
if (targets.size() > this->m_maxTargets) targets.resize(this->m_maxTargets);
|
||||
|
||||
const uint32_t size = targets.size();
|
||||
bitStream->Write<uint32_t>(targets.size());
|
||||
|
||||
bitStream->Write(size);
|
||||
|
||||
if (size == 0) {
|
||||
if (targets.size() == 0) {
|
||||
PlayFx(u"miss", context->originator);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
context->foundTarget = true;
|
||||
// write all the targets to the bitstream
|
||||
for (auto* target : targets) {
|
||||
bitStream->Write(target->GetObjectID());
|
||||
}
|
||||
|
||||
context->foundTarget = true;
|
||||
|
||||
for (auto* target : targets) {
|
||||
bitStream->Write(target->GetObjectID());
|
||||
|
||||
PlayFx(u"cast", context->originator, target->GetObjectID());
|
||||
}
|
||||
|
||||
for (auto* target : targets) {
|
||||
branch.target = target->GetObjectID();
|
||||
|
||||
this->m_action->Calculate(context, bitStream, branch);
|
||||
// then cast all the actions
|
||||
for (auto* target : targets) {
|
||||
branch.target = target->GetObjectID();
|
||||
this->m_action->Calculate(context, bitStream, branch);
|
||||
}
|
||||
PlayFx(u"cast", context->originator);
|
||||
}
|
||||
}
|
||||
|
||||
void AreaOfEffectBehavior::Load() {
|
||||
this->m_action = GetAction("action");
|
||||
this->m_action = GetAction("action"); // required
|
||||
this->m_radius = GetFloat("radius", 0.0f); // required
|
||||
this->m_maxTargets = GetInt("max targets", 100);
|
||||
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
|
||||
this->m_useTargetPosition = GetBoolean("use_target_position", false);
|
||||
this->m_useTargetAsCaster = GetBoolean("use_target_as_caster", false);
|
||||
this->m_offset = NiPoint3(
|
||||
GetFloat("offset_x", 0.0f),
|
||||
GetFloat("offset_y", 0.0f),
|
||||
GetFloat("offset_z", 0.0f)
|
||||
);
|
||||
|
||||
this->m_radius = GetFloat("radius");
|
||||
|
||||
this->m_maxTargets = GetInt("max targets");
|
||||
|
||||
this->m_ignoreFaction = GetInt("ignore_faction");
|
||||
|
||||
this->m_includeFaction = GetInt("include_faction");
|
||||
|
||||
this->m_TargetSelf = GetInt("target_self");
|
||||
|
||||
this->m_targetEnemy = GetInt("target_enemy");
|
||||
|
||||
this->m_targetFriend = GetInt("target_friend");
|
||||
// params after this are needed for filter targets
|
||||
const auto parameters = GetParameterNames();
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.first.rfind("include_faction", 0) == 0) {
|
||||
this->m_includeFactionList.push_front(parameter.second);
|
||||
} else if (parameter.first.rfind("ignore_faction", 0) == 0) {
|
||||
this->m_ignoreFactionList.push_front(parameter.second);
|
||||
}
|
||||
}
|
||||
this->m_targetSelf = GetBoolean("target_self", false);
|
||||
this->m_targetEnemy = GetBoolean("target_enemy", false);
|
||||
this->m_targetFriend = GetBoolean("target_friend", false);
|
||||
this->m_targetTeam = GetBoolean("target_team", false);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,26 @@
|
||||
#pragma once
|
||||
#include "Behavior.h"
|
||||
#include <forward_list>
|
||||
|
||||
class AreaOfEffectBehavior final : public Behavior
|
||||
{
|
||||
public:
|
||||
Behavior* m_action;
|
||||
|
||||
uint32_t m_maxTargets;
|
||||
|
||||
float m_radius;
|
||||
|
||||
int32_t m_ignoreFaction;
|
||||
|
||||
int32_t m_includeFaction;
|
||||
|
||||
int32_t m_TargetSelf;
|
||||
|
||||
int32_t m_targetEnemy;
|
||||
|
||||
int32_t m_targetFriend;
|
||||
|
||||
/*
|
||||
* Inherited
|
||||
*/
|
||||
explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {
|
||||
}
|
||||
|
||||
explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {}
|
||||
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
void Load() override;
|
||||
private:
|
||||
Behavior* m_action;
|
||||
uint32_t m_maxTargets;
|
||||
float m_radius;
|
||||
bool m_useTargetPosition;
|
||||
bool m_useTargetAsCaster;
|
||||
NiPoint3 m_offset;
|
||||
|
||||
std::forward_list<int32_t> m_ignoreFactionList {};
|
||||
std::forward_list<int32_t> m_includeFactionList {};
|
||||
bool m_targetSelf;
|
||||
bool m_targetEnemy;
|
||||
bool m_targetFriend;
|
||||
bool m_targetTeam;
|
||||
};
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void AttackDelayBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
uint32_t handle{};
|
||||
|
||||
if (!bitStream->Read(handle)) {
|
||||
Game::logger->Log("AttackDelayBehavior", "Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "BasicAttackBehavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "EntityManager.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "WorldConfig.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "eBasicAttackSuccessTypes.h"
|
||||
@@ -13,8 +15,14 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
||||
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent != nullptr) {
|
||||
PlayFx(u"onhit", entity->GetObjectID());
|
||||
PlayFx(u"onhit", entity->GetObjectID()); //This damage animation doesn't seem to play consistently
|
||||
destroyableComponent->Damage(this->m_MaxDamage, context->originator, context->skillID);
|
||||
|
||||
//Handle player damage cooldown
|
||||
if (entity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
||||
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||
}
|
||||
}
|
||||
|
||||
this->m_OnSuccess->Handle(context, bitStream, branch);
|
||||
@@ -26,10 +34,10 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
||||
|
||||
uint16_t allocatedBits{};
|
||||
if (!bitStream->Read(allocatedBits) || allocatedBits == 0) {
|
||||
Game::logger->LogDebug("BasicAttackBehavior", "No allocated bits");
|
||||
LOG_DEBUG("No allocated bits");
|
||||
return;
|
||||
}
|
||||
Game::logger->LogDebug("BasicAttackBehavior", "Number of allocated bits %i", allocatedBits);
|
||||
LOG_DEBUG("Number of allocated bits %i", allocatedBits);
|
||||
const auto baseAddress = bitStream->GetReadOffset();
|
||||
|
||||
DoHandleBehavior(context, bitStream, branch);
|
||||
@@ -40,13 +48,13 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
||||
void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* targetEntity = Game::entityManager->GetEntity(branch.target);
|
||||
if (!targetEntity) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Target targetEntity %llu not found.", branch.target);
|
||||
LOG("Target targetEntity %llu not found.", branch.target);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyableComponent = targetEntity->GetComponent<DestroyableComponent>();
|
||||
if (!destroyableComponent) {
|
||||
Game::logger->Log("BasicAttackBehavior", "No destroyable found on the obj/lot %llu/%i", branch.target, targetEntity->GetLOT());
|
||||
LOG("No destroyable found on the obj/lot %llu/%i", branch.target, targetEntity->GetLOT());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,7 +63,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
bool isSuccess{};
|
||||
|
||||
if (!bitStream->Read(isBlocked)) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Unable to read isBlocked");
|
||||
LOG("Unable to read isBlocked");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,30 +75,31 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
}
|
||||
|
||||
if (!bitStream->Read(isImmune)) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Unable to read isImmune");
|
||||
LOG("Unable to read isImmune");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isImmune) {
|
||||
LOG_DEBUG("Target targetEntity %llu is immune!", branch.target);
|
||||
this->m_OnFailImmune->Handle(context, bitStream, branch);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bitStream->Read(isSuccess)) {
|
||||
Game::logger->Log("BasicAttackBehavior", "failed to read success from bitstream");
|
||||
LOG("failed to read success from bitstream");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSuccess) {
|
||||
uint32_t armorDamageDealt{};
|
||||
if (!bitStream->Read(armorDamageDealt)) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Unable to read armorDamageDealt");
|
||||
LOG("Unable to read armorDamageDealt");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t healthDamageDealt{};
|
||||
if (!bitStream->Read(healthDamageDealt)) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Unable to read healthDamageDealt");
|
||||
LOG("Unable to read healthDamageDealt");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -103,7 +112,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
|
||||
bool died{};
|
||||
if (!bitStream->Read(died)) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Unable to read died");
|
||||
LOG("Unable to read died");
|
||||
return;
|
||||
}
|
||||
auto previousArmor = destroyableComponent->GetArmor();
|
||||
@@ -114,7 +123,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
|
||||
uint8_t successState{};
|
||||
if (!bitStream->Read(successState)) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Unable to read success state");
|
||||
LOG("Unable to read success state");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,7 +136,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
break;
|
||||
default:
|
||||
if (static_cast<eBasicAttackSuccessTypes>(successState) != eBasicAttackSuccessTypes::FAILIMMUNE) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Unknown success state (%i)!", successState);
|
||||
LOG("Unknown success state (%i)!", successState);
|
||||
return;
|
||||
}
|
||||
this->m_OnFailImmune->Handle(context, bitStream, branch);
|
||||
@@ -157,13 +166,13 @@ void BasicAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream*
|
||||
void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* targetEntity = Game::entityManager->GetEntity(branch.target);
|
||||
if (!targetEntity) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Target entity %llu is null!", branch.target);
|
||||
LOG("Target entity %llu is null!", branch.target);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyableComponent = targetEntity->GetComponent<DestroyableComponent>();
|
||||
if (!destroyableComponent || !destroyableComponent->GetParent()) {
|
||||
Game::logger->Log("BasicAttackBehavior", "No destroyable component on %llu", branch.target);
|
||||
LOG("No destroyable component on %llu", branch.target);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -178,11 +187,11 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isImmune = destroyableComponent->IsImmune();
|
||||
|
||||
const bool isImmune = destroyableComponent->IsImmune() || destroyableComponent->IsCooldownImmune();
|
||||
bitStream->Write(isImmune);
|
||||
|
||||
if (isImmune) {
|
||||
LOG_DEBUG("Target targetEntity %llu is immune!", branch.target);
|
||||
this->m_OnFailImmune->Calculate(context, bitStream, branch);
|
||||
return;
|
||||
}
|
||||
@@ -203,6 +212,11 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
|
||||
bitStream->Write(isSuccess);
|
||||
|
||||
//Handle player damage cooldown
|
||||
if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||
destroyableComponent->SetDamageCooldownTimer(Game::zoneManager->GetWorldConfig()->globalImmunityTime);
|
||||
}
|
||||
|
||||
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
||||
if (isSuccess) {
|
||||
if (healthDamageDealt >= 1) {
|
||||
@@ -227,7 +241,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
break;
|
||||
default:
|
||||
if (static_cast<eBasicAttackSuccessTypes>(successState) != eBasicAttackSuccessTypes::FAILIMMUNE) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Unknown success state (%i)!", successState);
|
||||
LOG("Unknown success state (%i)!", successState);
|
||||
break;
|
||||
}
|
||||
this->m_OnFailImmune->Calculate(context, bitStream, branch);
|
||||
@@ -236,6 +250,8 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
}
|
||||
|
||||
void BasicAttackBehavior::Load() {
|
||||
this->m_DontApplyImmune = GetBoolean("dont_apply_immune");
|
||||
|
||||
this->m_MinDamage = GetInt("min damage");
|
||||
if (this->m_MinDamage == 0) this->m_MinDamage = 1;
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ public:
|
||||
/**
|
||||
* @brief Reads a 16bit short from the bitStream and when the actual behavior handling finishes with all of its branches, the bitStream
|
||||
* is then offset to after the allocated bits for this stream.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void DoHandleBehavior(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch);
|
||||
|
||||
/**
|
||||
* @brief Handles a client initialized Basic Attack Behavior cast to be deserialized and verified on the server.
|
||||
*
|
||||
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
||||
*
|
||||
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
||||
* @param bitStream The bitStream to deserialize. BitStreams will always check their bounds before reading in a behavior
|
||||
* and will fail gracefully if an overread is detected.
|
||||
* @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down.
|
||||
@@ -27,13 +27,13 @@ public:
|
||||
/**
|
||||
* @brief Writes a 16bit short to the bitStream and when the actual behavior calculation finishes with all of its branches, the number
|
||||
* of bits used is then written to where the 16bit short initially was.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
/**
|
||||
* @brief Calculates a server initialized Basic Attack Behavior cast to be serialized to the client
|
||||
*
|
||||
*
|
||||
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
||||
* @param bitStream The bitStream to serialize to.
|
||||
* @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down.
|
||||
@@ -44,10 +44,12 @@ public:
|
||||
* @brief Loads this Behaviors parameters from the database. For this behavior specifically:
|
||||
* max and min damage will always be the same. If min is less than max, they are both set to max.
|
||||
* If an action is not in the database, then no action is taken for that result.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void Load() override;
|
||||
private:
|
||||
bool m_DontApplyImmune;
|
||||
|
||||
uint32_t m_MinDamage;
|
||||
|
||||
uint32_t m_MaxDamage;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Behavior.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "BehaviorTemplates.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include <unordered_map>
|
||||
@@ -64,6 +64,7 @@
|
||||
#include "FallSpeedBehavior.h"
|
||||
#include "ChangeIdleFlagsBehavior.h"
|
||||
#include "DarkInspirationBehavior.h"
|
||||
#include "ConsumeItemBehavior.h"
|
||||
|
||||
//CDClient includes
|
||||
#include "CDBehaviorParameterTable.h"
|
||||
@@ -81,7 +82,7 @@ CDBehaviorParameterTable* Behavior::BehaviorParameterTable = nullptr;
|
||||
|
||||
Behavior* Behavior::GetBehavior(const uint32_t behaviorId) {
|
||||
if (BehaviorParameterTable == nullptr) {
|
||||
BehaviorParameterTable = CDClientManager::Instance().GetTable<CDBehaviorParameterTable>();
|
||||
BehaviorParameterTable = CDClientManager::GetTable<CDBehaviorParameterTable>();
|
||||
}
|
||||
|
||||
const auto pair = Cache.find(behaviorId);
|
||||
@@ -175,7 +176,7 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
|
||||
case BehaviorTemplates::BEHAVIOR_SPEED:
|
||||
behavior = new SpeedBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION:
|
||||
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION:
|
||||
behavior = new DarkInspirationBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF:
|
||||
@@ -200,7 +201,9 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
|
||||
case BehaviorTemplates::BEHAVIOR_SKILL_EVENT:
|
||||
behavior = new SkillEventBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM: break;
|
||||
case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM:
|
||||
behavior = new ConsumeItemBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_SKILL_CAST_FAILED:
|
||||
behavior = new SkillCastFailedBehavior(behaviorId);
|
||||
break;
|
||||
@@ -278,12 +281,12 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
|
||||
case BehaviorTemplates::BEHAVIOR_MOUNT: break;
|
||||
case BehaviorTemplates::BEHAVIOR_SKILL_SET: break;
|
||||
default:
|
||||
//Game::logger->Log("Behavior", "Failed to load behavior with invalid template id (%i)!", templateId);
|
||||
//LOG("Failed to load behavior with invalid template id (%i)!", templateId);
|
||||
break;
|
||||
}
|
||||
|
||||
if (behavior == nullptr) {
|
||||
//Game::logger->Log("Behavior", "Failed to load unimplemented template id (%i)!", templateId);
|
||||
//LOG("Failed to load unimplemented template id (%i)!", templateId);
|
||||
|
||||
behavior = new EmptyBehavior(behaviorId);
|
||||
}
|
||||
@@ -294,7 +297,7 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
|
||||
}
|
||||
|
||||
BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) {
|
||||
auto behaviorTemplateTable = CDClientManager::Instance().GetTable<CDBehaviorTemplateTable>();
|
||||
auto behaviorTemplateTable = CDClientManager::GetTable<CDBehaviorTemplateTable>();
|
||||
|
||||
BehaviorTemplates templateID = BehaviorTemplates::BEHAVIOR_EMPTY;
|
||||
// Find behavior template by its behavior id. Default to 0.
|
||||
@@ -306,7 +309,7 @@ BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) {
|
||||
}
|
||||
|
||||
if (templateID == BehaviorTemplates::BEHAVIOR_EMPTY && behaviorId != 0) {
|
||||
Game::logger->Log("Behavior", "Failed to load behavior template with id (%i)!", behaviorId);
|
||||
LOG("Failed to load behavior template with id (%i)!", behaviorId);
|
||||
}
|
||||
|
||||
return templateID;
|
||||
@@ -365,11 +368,11 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
|
||||
|
||||
if (!type.empty()) {
|
||||
typeQuery.bind(1, typeString.c_str());
|
||||
typeQuery.bind(2, (int)effectId);
|
||||
typeQuery.bind(2, static_cast<int>(effectId));
|
||||
|
||||
result = typeQuery.execQuery();
|
||||
} else {
|
||||
idQuery.bind(1, (int)effectId);
|
||||
idQuery.bind(1, static_cast<int>(effectId));
|
||||
|
||||
result = idQuery.execQuery();
|
||||
}
|
||||
@@ -402,7 +405,7 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
|
||||
}
|
||||
|
||||
Behavior::Behavior(const uint32_t behaviorId) {
|
||||
auto behaviorTemplateTable = CDClientManager::Instance().GetTable<CDBehaviorTemplateTable>();
|
||||
auto behaviorTemplateTable = CDClientManager::GetTable<CDBehaviorTemplateTable>();
|
||||
|
||||
CDBehaviorTemplate templateInDatabase{};
|
||||
|
||||
@@ -426,7 +429,7 @@ Behavior::Behavior(const uint32_t behaviorId) {
|
||||
|
||||
// Make sure we do not proceed if we are trying to load an invalid behavior
|
||||
if (templateInDatabase.behaviorID == 0) {
|
||||
Game::logger->Log("Behavior", "Failed to load behavior with id (%i)!", behaviorId);
|
||||
LOG("Failed to load behavior with id (%i)!", behaviorId);
|
||||
|
||||
this->m_effectId = 0;
|
||||
this->m_effectHandle = nullptr;
|
||||
@@ -445,7 +448,7 @@ Behavior::Behavior(const uint32_t behaviorId) {
|
||||
|
||||
float Behavior::GetFloat(const std::string& name, const float defaultValue) const {
|
||||
// Get the behavior parameter entry and return its value.
|
||||
if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::Instance().GetTable<CDBehaviorParameterTable>();
|
||||
if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::GetTable<CDBehaviorParameterTable>();
|
||||
return BehaviorParameterTable->GetValue(this->m_behaviorId, name, defaultValue);
|
||||
}
|
||||
|
||||
@@ -473,7 +476,7 @@ Behavior* Behavior::GetAction(float value) const {
|
||||
std::map<std::string, float> Behavior::GetParameterNames() const {
|
||||
std::map<std::string, float> templatesInDatabase;
|
||||
// Find behavior template by its behavior id.
|
||||
if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::Instance().GetTable<CDBehaviorParameterTable>();
|
||||
if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::GetTable<CDBehaviorParameterTable>();
|
||||
if (BehaviorParameterTable) {
|
||||
templatesInDatabase = BehaviorParameterTable->GetParametersByBehaviorID(this->m_behaviorId);
|
||||
}
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
#include "EntityManager.h"
|
||||
#include "SkillComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "dServer.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "BitStreamUtils.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "DestroyableComponent.h"
|
||||
#include "EchoSyncSkill.h"
|
||||
#include "PhantomPhysicsComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "TeamManager.h"
|
||||
#include "eConnectionType.h"
|
||||
|
||||
BehaviorSyncEntry::BehaviorSyncEntry() {
|
||||
@@ -30,7 +31,7 @@ uint32_t BehaviorContext::GetUniqueSkillId() const {
|
||||
auto* entity = Game::entityManager->GetEntity(this->originator);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator);
|
||||
LOG("Invalid entity for (%llu)!", this->originator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -38,7 +39,7 @@ uint32_t BehaviorContext::GetUniqueSkillId() const {
|
||||
auto* component = entity->GetComponent<SkillComponent>();
|
||||
|
||||
if (component == nullptr) {
|
||||
Game::logger->Log("BehaviorContext", "No skill component attached to (%llu)!", this->originator);;
|
||||
LOG("No skill component attached to (%llu)!", this->originator);;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -125,7 +126,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream* bit
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Game::logger->Log("BehaviorContext", "Failed to find behavior sync entry with sync id (%i)!", syncId);
|
||||
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -134,7 +135,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream* bit
|
||||
const auto branch = entry.branchContext;
|
||||
|
||||
if (behavior == nullptr) {
|
||||
Game::logger->Log("BehaviorContext", "Invalid behavior for sync id (%i)!", syncId);
|
||||
LOG("Invalid behavior for sync id (%i)!", syncId);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -248,12 +249,12 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
|
||||
entry.behavior->SyncCalculation(this, bitStream, entry.branchContext);
|
||||
|
||||
if (!clientInitalized) {
|
||||
echo.sBitStream.assign((char*)bitStream->GetData(), bitStream->GetNumberOfBytesUsed());
|
||||
echo.sBitStream.assign(reinterpret_cast<char*>(bitStream->GetData()), bitStream->GetNumberOfBytesUsed());
|
||||
|
||||
// Write message
|
||||
RakNet::BitStream message;
|
||||
|
||||
PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
|
||||
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
|
||||
message.Write(this->originator);
|
||||
echo.Serialize(&message);
|
||||
|
||||
@@ -307,46 +308,123 @@ void BehaviorContext::Reset() {
|
||||
this->scheduledUpdates.clear();
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf, bool targetEnemy, bool targetFriend) const {
|
||||
auto* entity = Game::entityManager->GetEntity(this->caster);
|
||||
void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_list<int32_t>& ignoreFactionList, std::forward_list<int32_t>& includeFactionList, bool targetSelf, bool targetEnemy, bool targetFriend, bool targetTeam) const {
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator);
|
||||
|
||||
return targets;
|
||||
// if we aren't targeting anything, then clear the targets vector
|
||||
if (!targetSelf && !targetEnemy && !targetFriend && !targetTeam && ignoreFactionList.empty() && includeFactionList.empty()) {
|
||||
targets.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ignoreFaction && !includeFaction) {
|
||||
for (auto entry : entity->GetTargetsInPhantom()) {
|
||||
auto* instance = Game::entityManager->GetEntity(entry);
|
||||
|
||||
if (instance == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
targets.push_back(entry);
|
||||
}
|
||||
// if the caster is not there, return empty targets list
|
||||
auto* caster = Game::entityManager->GetEntity(this->caster);
|
||||
if (!caster) {
|
||||
LOG_DEBUG("Invalid caster for (%llu)!", this->originator);
|
||||
targets.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) {
|
||||
DestroyableComponent* destroyableComponent;
|
||||
if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
|
||||
return targets;
|
||||
auto index = targets.begin();
|
||||
while (index != targets.end()) {
|
||||
auto candidate = *index;
|
||||
|
||||
// make sure we don't have a nullptr
|
||||
if (!candidate) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
for (auto* candidate : entities) {
|
||||
const auto id = candidate->GetObjectID();
|
||||
// handle targeting the caster
|
||||
if (candidate == caster){
|
||||
// if we aren't targeting self, erase, otherise increment and continue
|
||||
if (!targetSelf) index = targets.erase(index);
|
||||
else index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction, targetEnemy, targetFriend)) {
|
||||
targets.push_back(id);
|
||||
// make sure that the entity is targetable
|
||||
if (!CheckTargetingRequirements(candidate)) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// get factions to check against
|
||||
// CheckTargetingRequirements checks for a destroyable component
|
||||
// but we check again because bounds check are necessary
|
||||
auto candidateDestroyableComponent = candidate->GetComponent<DestroyableComponent>();
|
||||
if (!candidateDestroyableComponent) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if they are dead, then earse and continue
|
||||
if (candidateDestroyableComponent->GetIsDead()){
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if their faction is explicitly included, increment and continue
|
||||
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
|
||||
if (CheckFactionList(includeFactionList, candidateFactions)){
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if they are a team member
|
||||
if (targetTeam){
|
||||
auto* team = TeamManager::Instance()->GetTeam(this->caster);
|
||||
if (team){
|
||||
// if we find a team member keep it and continue to skip enemy checks
|
||||
if(std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()){
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
// if the caster doesn't have a destroyable component, return an empty targets list
|
||||
auto* casterDestroyableComponent = caster->GetComponent<DestroyableComponent>();
|
||||
if (!casterDestroyableComponent) {
|
||||
targets.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// if we arent targeting a friend, and they are a friend OR
|
||||
// if we are not targeting enemies and they are an enemy OR.
|
||||
// if we are ignoring their faction is explicitly ignored
|
||||
// erase and continue
|
||||
auto isEnemy = casterDestroyableComponent->IsEnemy(candidate);
|
||||
if ((!targetFriend && !isEnemy) ||
|
||||
(!targetEnemy && isEnemy) ||
|
||||
CheckFactionList(ignoreFactionList, candidateFactions)) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// some basic checks as well as the check that matters for this: if the quickbuild is complete
|
||||
bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const {
|
||||
// if the target is a nullptr, then it's not valid
|
||||
if (!target) return false;
|
||||
|
||||
// ignore quickbuilds that aren't completed
|
||||
auto* targetQuickbuildComponent = target->GetComponent<QuickBuildComponent>();
|
||||
if (targetQuickbuildComponent && targetQuickbuildComponent->GetState() != eQuickBuildState::COMPLETED) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if any of the object factions are in the faction list
|
||||
bool BehaviorContext::CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const {
|
||||
if (factionList.empty() || objectsFactions.empty()) return false;
|
||||
for (auto faction : factionList){
|
||||
if(std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "GameMessages.h"
|
||||
|
||||
#include <vector>
|
||||
#include <forward_list>
|
||||
|
||||
class Behavior;
|
||||
|
||||
@@ -106,7 +107,11 @@ struct BehaviorContext
|
||||
|
||||
void Reset();
|
||||
|
||||
std::vector<LWOOBJID> GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false) const;
|
||||
void FilterTargets(std::vector<Entity*>& targetsReference, std::forward_list<int32_t>& ignoreFaction, std::forward_list<int32_t>& includeFaction, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false, const bool targetTeam = false) const;
|
||||
|
||||
bool CheckTargetingRequirements(const Entity* target) const;
|
||||
|
||||
bool CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const;
|
||||
|
||||
explicit BehaviorContext(LWOOBJID originator, bool calculation = false);
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
#ifndef BEHAVIORSLOT_H
|
||||
#define BEHAVIORSLOT_H
|
||||
#include <cstdint>
|
||||
|
||||
enum class BehaviorSlot
|
||||
{
|
||||
enum class BehaviorSlot : int32_t {
|
||||
Invalid = -1,
|
||||
Primary,
|
||||
Offhand,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
void BlockBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
@@ -13,7 +13,7 @@ void BlockBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -43,7 +43,7 @@ void BlockBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branc
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
@@ -13,7 +13,7 @@ void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BuffBehavior", "Invalid target (%llu)!", target);
|
||||
LOG("Invalid target (%llu)!", target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
auto* component = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (component == nullptr) {
|
||||
Game::logger->Log("BuffBehavior", "Invalid target, no destroyable component (%llu)!", target);
|
||||
LOG("Invalid target, no destroyable component (%llu)!", target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ void BuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BuffBehavior", "Invalid target (%llu)!", target);
|
||||
LOG("Invalid target (%llu)!", target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ void BuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch
|
||||
auto* component = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (component == nullptr) {
|
||||
Game::logger->Log("BuffBehavior", "Invalid target, no destroyable component (%llu)!", target);
|
||||
LOG("Invalid target, no destroyable component (%llu)!", target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
|
||||
"ChangeOrientationBehavior.cpp"
|
||||
"ChargeUpBehavior.cpp"
|
||||
"ClearTargetBehavior.cpp"
|
||||
"ConsumeItemBehavior.cpp"
|
||||
"DamageAbsorptionBehavior.cpp"
|
||||
"DamageReductionBehavior.cpp"
|
||||
"DarkInspirationBehavior.cpp"
|
||||
@@ -53,4 +54,9 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
|
||||
"TargetCasterBehavior.cpp"
|
||||
"TauntBehavior.cpp"
|
||||
"VentureVisionBehavior.cpp"
|
||||
"VerifyBehavior.cpp" PARENT_SCOPE)
|
||||
"VerifyBehavior.cpp")
|
||||
|
||||
add_library(dBehaviors STATIC ${DGAME_DBEHAVIORS_SOURCES})
|
||||
target_link_libraries(dBehaviors PUBLIC dPhysics)
|
||||
target_include_directories(dBehaviors PUBLIC ".")
|
||||
target_precompile_headers(dBehaviors REUSE_FROM dGameBase)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "PossessableComponent.h"
|
||||
|
||||
void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
@@ -17,7 +17,7 @@ void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
return;
|
||||
}
|
||||
|
||||
Game::logger->Log("Car boost", "Activating car boost!");
|
||||
LOG("Activating car boost!");
|
||||
|
||||
auto* possessableComponent = entity->GetComponent<PossessableComponent>();
|
||||
if (possessableComponent != nullptr) {
|
||||
@@ -27,7 +27,7 @@ void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
|
||||
auto* characterComponent = possessor->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
Game::logger->Log("Car boost", "Tracking car boost!");
|
||||
LOG("Tracking car boost!");
|
||||
characterComponent->UpdatePlayerStatistic(RacingCarBoostsActivated);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "ChainBehavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void ChainBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
uint32_t chainIndex{};
|
||||
|
||||
if (!bitStream->Read(chainIndex)) {
|
||||
Game::logger->Log("ChainBehavior", "Unable to read chainIndex from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read chainIndex from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ void ChainBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
if (chainIndex < this->m_behaviors.size()) {
|
||||
this->m_behaviors.at(chainIndex)->Handle(context, bitStream, branch);
|
||||
} else {
|
||||
Game::logger->Log("ChainBehavior", "chainIndex out of bounds, aborting handle of chain %i bits unread %i", chainIndex, bitStream->GetNumberOfUnreadBits());
|
||||
LOG("chainIndex out of bounds, aborting handle of chain %i bits unread %i", chainIndex, bitStream->GetNumberOfUnreadBits());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void ChargeUpBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
uint32_t handle{};
|
||||
|
||||
if (!bitStream->Read(handle)) {
|
||||
Game::logger->Log("ChargeUpBehavior", "Unable to read handle from bitStream, aborting Handle! variable_type");
|
||||
LOG("Unable to read handle from bitStream, aborting Handle! variable_type");
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
31
dGame/dBehaviors/ConsumeItemBehavior.cpp
Normal file
31
dGame/dBehaviors/ConsumeItemBehavior.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "ConsumeItemBehavior.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "InventoryComponent.h"
|
||||
|
||||
void ConsumeItemBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto action_to_cast = m_ActionNotConsumed;
|
||||
if (this->m_ConsumeLOT != -1) {
|
||||
auto caster = Game::entityManager->GetEntity(context->caster);
|
||||
if (!caster) return;
|
||||
|
||||
auto inventoryComponent = caster->GetComponent<InventoryComponent>();
|
||||
if (!inventoryComponent) return;
|
||||
|
||||
if (inventoryComponent->RemoveItem(this->m_ConsumeLOT, this->m_NumToConsume, eInventoryType::INVALID, false, true)){
|
||||
action_to_cast = m_ActionConsumed;
|
||||
}
|
||||
}
|
||||
if(action_to_cast) action_to_cast->Handle(context, bitStream, branch);
|
||||
}
|
||||
|
||||
void ConsumeItemBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
Handle(context, bitStream, branch);
|
||||
}
|
||||
|
||||
void ConsumeItemBehavior::Load() {
|
||||
this->m_ConsumeLOT = GetInt("consume_lot", -1);
|
||||
this->m_NumToConsume = GetInt("num_to_consume", 1);
|
||||
this->m_ActionNotConsumed = GetAction("action_not_consumed");
|
||||
this->m_ActionConsumed = GetAction("action_consumed");
|
||||
}
|
||||
17
dGame/dBehaviors/ConsumeItemBehavior.h
Normal file
17
dGame/dBehaviors/ConsumeItemBehavior.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "Behavior.h"
|
||||
|
||||
class ConsumeItemBehavior final : public Behavior
|
||||
{
|
||||
public:
|
||||
explicit ConsumeItemBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {}
|
||||
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
void Load() override;
|
||||
|
||||
private:
|
||||
LOT m_ConsumeLOT;
|
||||
uint32_t m_NumToConsume;
|
||||
Behavior* m_ActionNotConsumed;
|
||||
Behavior* m_ActionConsumed;
|
||||
};
|
||||
@@ -4,14 +4,14 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchCon
|
||||
auto* target = Game::entityManager->GetEntity(second);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", second);
|
||||
LOG("Failed to find target (%llu)!", second);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
void DamageReductionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("DamageReductionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ void DamageReductionBehavior::Timer(BehaviorContext* context, BehaviorBranchCont
|
||||
auto* target = Game::entityManager->GetEntity(second);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("DamageReductionBehavior", "Failed to find target (%llu)!", second);
|
||||
LOG("Failed to find target (%llu)!", second);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ void DarkInspirationBehavior::Handle(BehaviorContext* context, RakNet::BitStream
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->LogDebug("DarkInspirationBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG_DEBUG("Failed to find target (%llu)!", branch.target);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ void DarkInspirationBehavior::Calculate(BehaviorContext* context, RakNet::BitStr
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->LogDebug("DarkInspirationBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG_DEBUG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) {
|
||||
@@ -13,7 +13,7 @@ void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream*
|
||||
|
||||
uint32_t handle{};
|
||||
if (!bitStream->Read(handle)) {
|
||||
Game::logger->Log("ForceMovementBehavior", "Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
}
|
||||
context->RegisterSyncBehavior(handle, this, branch, this->m_Duration);
|
||||
@@ -22,13 +22,13 @@ void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream*
|
||||
void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
uint32_t next{};
|
||||
if (!bitStream->Read(next)) {
|
||||
Game::logger->Log("ForceMovementBehavior", "Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
}
|
||||
|
||||
LWOOBJID target{};
|
||||
if (!bitStream->Read(target)) {
|
||||
Game::logger->Log("ForceMovementBehavior", "Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "HealBehavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "EntityManager.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
@@ -11,7 +11,7 @@ void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_strea
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("HealBehavior", "Failed to find entity for (%llu)!", branch.target);
|
||||
LOG("Failed to find entity for (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_strea
|
||||
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);
|
||||
LOG("Failed to find destroyable component for %(llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include "dpWorld.h"
|
||||
#include "EntityManager.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
void ImaginationBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "eStateChangeType.h"
|
||||
@@ -13,7 +13,7 @@ void ImmunityBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (!target) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ void ImmunityBehavior::Timer(BehaviorContext* context, BehaviorBranchContext bra
|
||||
auto* target = Game::entityManager->GetEntity(second);
|
||||
|
||||
if (!target) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", second);
|
||||
LOG("Failed to find target (%llu)!", second);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "EntityManager.h"
|
||||
#include "SkillComponent.h"
|
||||
|
||||
@@ -12,7 +12,7 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
bool unknown = false;
|
||||
|
||||
if (!bitStream->Read(unknown)) {
|
||||
Game::logger->Log("InterruptBehavior", "Unable to read unknown1 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read unknown1 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
bool unknown = false;
|
||||
|
||||
if (!bitStream->Read(unknown)) {
|
||||
Game::logger->Log("InterruptBehavior", "Unable to read unknown2 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read unknown2 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
bool unknown = false;
|
||||
|
||||
if (!bitStream->Read(unknown)) {
|
||||
Game::logger->Log("InterruptBehavior", "Unable to read unknown3 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read unknown3 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,10 +38,12 @@ void JetPackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit
|
||||
}
|
||||
|
||||
void JetPackBehavior::Load() {
|
||||
this->m_WarningEffectID = GetInt("warning_effect_id");
|
||||
this->m_Airspeed = GetFloat("airspeed");
|
||||
this->m_MaxAirspeed = GetFloat("max_airspeed");
|
||||
this->m_VerticalVelocity = GetFloat("vertical_velocity");
|
||||
this->m_EnableHover = GetBoolean("enable_hover");
|
||||
this->m_BypassChecks = GetBoolean("bypass_checks", true);
|
||||
this->m_WarningEffectID = GetInt("warning_effect_id", -1);
|
||||
this->m_Airspeed = GetFloat("airspeed", 10);
|
||||
this->m_MaxAirspeed = GetFloat("max_airspeed", 15);
|
||||
this->m_VerticalVelocity = GetFloat("vertical_velocity", 1);
|
||||
this->m_EnableHover = GetBoolean("enable_hover", false);
|
||||
|
||||
// TODO: Implement proper jetpack checks, so we can set this default to false
|
||||
this->m_BypassChecks = GetBoolean("bypass_checks", true);
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#include "GameMessages.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void KnockbackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
bool unknown{};
|
||||
|
||||
if (!bitStream->Read(unknown)) {
|
||||
Game::logger->Log("KnockbackBehavior", "Unable to read unknown from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read unknown from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "MovementSwitchBehavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
uint32_t movementType{};
|
||||
@@ -15,7 +15,7 @@ void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream*
|
||||
this->m_movingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) {
|
||||
return;
|
||||
}
|
||||
Game::logger->Log("MovementSwitchBehavior", "Unable to read movementType from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read movementType from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "EntityManager.h"
|
||||
#include "SkillComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
@@ -41,7 +41,7 @@ void OverTimeBehavior::Load() {
|
||||
m_Action = GetInt("action");
|
||||
// Since m_Action is a skillID and not a behavior, get is correlated behaviorID.
|
||||
|
||||
CDSkillBehaviorTable* skillTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
CDSkillBehaviorTable* skillTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
|
||||
m_ActionBehaviorId = skillTable->GetSkillByID(m_Action).behaviorID;
|
||||
|
||||
m_Delay = GetFloat("delay");
|
||||
|
||||
@@ -3,23 +3,23 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "SkillComponent.h"
|
||||
#include "../dWorldServer/ObjectIDManager.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "eObjectBits.h"
|
||||
|
||||
void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
LWOOBJID target{};
|
||||
|
||||
if (!bitStream->Read(target)) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Unable to read target from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read target from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(context->originator);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Failed to find originator (%llu)!", context->originator);
|
||||
LOG("Failed to find originator (%llu)!", context->originator);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -27,15 +27,15 @@ void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
||||
|
||||
if (skillComponent == nullptr) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Failed to find skill component for (%llu)!", -context->originator);
|
||||
LOG("Failed to find skill component for (%llu)!", -context->originator);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_useMouseposit && !branch.isSync) {
|
||||
NiPoint3 targetPosition = NiPoint3::ZERO;
|
||||
NiPoint3 targetPosition = NiPoint3Constant::ZERO;
|
||||
if (!bitStream->Read(targetPosition)) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Unable to read targetPosition from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read targetPosition from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
}
|
||||
@@ -46,7 +46,7 @@ void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
LWOOBJID projectileId{};
|
||||
|
||||
if (!bitStream->Read(projectileId)) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Unable to read projectileId from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read projectileId from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -64,7 +64,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
|
||||
auto* entity = Game::entityManager->GetEntity(context->originator);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Failed to find originator (%llu)!", context->originator);
|
||||
LOG("Failed to find originator (%llu)!", context->originator);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -72,7 +72,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
|
||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
||||
|
||||
if (skillComponent == nullptr) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Failed to find skill component for (%llu)!", context->originator);
|
||||
LOG("Failed to find skill component for (%llu)!", context->originator);
|
||||
|
||||
return;
|
||||
|
||||
@@ -81,7 +81,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
|
||||
auto* other = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (other == nullptr) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Invalid projectile target (%llu)!", branch.target);
|
||||
LOG("Invalid projectile target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
|
||||
const auto maxTime = this->m_maxDistance / this->m_projectileSpeed;
|
||||
|
||||
for (auto i = 0u; i < this->m_projectileCount; ++i) {
|
||||
auto id = static_cast<LWOOBJID>(ObjectIDManager::Instance()->GenerateObjectID());
|
||||
auto id = static_cast<LWOOBJID>(ObjectIDManager::GenerateObjectID());
|
||||
|
||||
GeneralUtils::SetBit(id, eObjectBits::SPAWNED);
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ void PropertyTeleportBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
if (zoneClone != 0) ChatPackets::SendSystemMessage(sysAddr, u"Transfering to your property!");
|
||||
else ChatPackets::SendSystemMessage(sysAddr, u"Transfering back to previous world!");
|
||||
|
||||
Game::logger->Log("PropertyTeleportBehavior", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
if (entity->GetCharacter()) {
|
||||
entity->GetCharacter()->SetZoneID(zoneID);
|
||||
entity->GetCharacter()->SetZoneInstance(zoneInstance);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include "dpWorld.h"
|
||||
#include "EntityManager.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "Game.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
@@ -11,7 +11,7 @@ void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_str
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("RepairBehavior", "Failed to find entity for (%llu)!", branch.target);
|
||||
LOG("Failed to find entity for (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_str
|
||||
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);
|
||||
LOG("Failed to find destroyable component for %(llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityInfo.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
@@ -15,7 +15,7 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
auto* origin = Game::entityManager->GetEntity(context->originator);
|
||||
|
||||
if (origin == nullptr) {
|
||||
Game::logger->Log("SpawnBehavior", "Failed to find self entity (%llu)!", context->originator);
|
||||
LOG("Failed to find self entity (%llu)!", context->originator);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("SpawnBehavior", "Failed to spawn entity (%i)!", this->m_lot);
|
||||
LOG("Failed to spawn entity (%i)!", this->m_lot);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -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* rebuildComponent = entity->GetComponent<RebuildComponent>();
|
||||
auto* quickBuildComponent = entity->GetComponent<QuickBuildComponent>();
|
||||
|
||||
if (rebuildComponent != nullptr) {
|
||||
rebuildComponent->SetRepositionPlayer(false);
|
||||
if (quickBuildComponent != nullptr) {
|
||||
quickBuildComponent->SetRepositionPlayer(false);
|
||||
}
|
||||
|
||||
Game::entityManager->ConstructEntity(entity);
|
||||
@@ -82,7 +82,7 @@ void SpawnBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext
|
||||
auto* entity = Game::entityManager->GetEntity(second);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("SpawnBehavior", "Failed to find spawned entity (%llu)!", second);
|
||||
LOG("Failed to find spawned entity (%llu)!", second);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
void SpeedBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
@@ -17,14 +17,14 @@ void StunBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
|
||||
bool blocked{};
|
||||
if (!bitStream->Read(blocked)) {
|
||||
Game::logger->Log("StunBehavior", "Unable to read blocked from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read blocked from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("StunBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
|
||||
auto* self = Game::entityManager->GetEntity(context->originator);
|
||||
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("StunBehavior", "Invalid self entity (%llu)!", context->originator);
|
||||
LOG("Invalid self entity (%llu)!", context->originator);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
|
||||
bitStream->Write(blocked);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("StunBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "SwitchBehavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "EntityManager.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "BuffComponent.h"
|
||||
@@ -11,7 +11,7 @@ void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
|
||||
if (this->m_imagination > 0 || !this->m_isEnemyFaction) {
|
||||
if (!bitStream->Read(state)) {
|
||||
Game::logger->Log("SwitchBehavior", "Unable to read state from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read state from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
}
|
||||
@@ -28,7 +28,7 @@ void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
return;
|
||||
}
|
||||
|
||||
Game::logger->LogDebug("SwitchBehavior", "[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination());
|
||||
LOG_DEBUG("[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination());
|
||||
|
||||
if (state) {
|
||||
this->m_actionTrue->Handle(context, bitStream, branch);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "EntityManager.h"
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ void SwitchMultipleBehavior::Handle(BehaviorContext* context, RakNet::BitStream*
|
||||
float value{};
|
||||
|
||||
if (!bitStream->Read(value)) {
|
||||
Game::logger->Log("SwitchMultipleBehavior", "Unable to read value from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read value from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ void SwitchMultipleBehavior::Load() {
|
||||
"(select bP2.value FROM BehaviorParameter bP2 WHERE bP2.behaviorID = ?1 AND bP2.parameterID LIKE 'value %' "
|
||||
"AND replace(bP1.parameterID, 'behavior ', '') = replace(bP2.parameterID, 'value ', '')) as value "
|
||||
"FROM BehaviorParameter bP1 WHERE bP1.behaviorID = ?1 AND bP1.parameterID LIKE 'behavior %';");
|
||||
query.bind(1, (int)this->m_behaviorId);
|
||||
query.bind(1, static_cast<int>(this->m_behaviorId));
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
#include "TacArcBehavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "Entity.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
if (this->m_targetEnemy && this->m_usePickedTarget && branch.target > 0) {
|
||||
this->m_action->Handle(context, bitStream, branch);
|
||||
std::vector<Entity*> targets = {};
|
||||
|
||||
return;
|
||||
if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
|
||||
auto target = Game::entityManager->GetEntity(branch.target);
|
||||
if (!target) LOG("target %llu is null", branch.target);
|
||||
else {
|
||||
targets.push_back(target);
|
||||
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
||||
if (!targets.empty()) {
|
||||
this->m_action->Handle(context, bitStream, branch);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hit = false;
|
||||
|
||||
if (!bitStream->Read(hit)) {
|
||||
Game::logger->Log("TacArcBehavior", "Unable to read hit from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
bool hasTargets = false;
|
||||
if (!bitStream->Read(hasTargets)) {
|
||||
LOG("Unable to read hasTargets from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -29,159 +37,113 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
bool blocked = false;
|
||||
|
||||
if (!bitStream->Read(blocked)) {
|
||||
Game::logger->Log("TacArcBehavior", "Unable to read blocked from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read blocked from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
if (blocked) {
|
||||
this->m_blockedAction->Handle(context, bitStream, branch);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
if (hasTargets) {
|
||||
uint32_t count = 0;
|
||||
|
||||
if (!bitStream->Read(count)) {
|
||||
Game::logger->Log("TacArcBehavior", "Unable to read count from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read count from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
if (count > m_maxTargets && m_maxTargets > 0) {
|
||||
count = m_maxTargets;
|
||||
if (count > m_maxTargets) {
|
||||
LOG("Bitstream has too many targets Max:%i Recv:%i", this->m_maxTargets, count);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
|
||||
for (auto i = 0u; i < count; ++i) {
|
||||
for (auto i = 0u; i < count; i++) {
|
||||
LWOOBJID id{};
|
||||
|
||||
if (!bitStream->Read(id)) {
|
||||
Game::logger->Log("TacArcBehavior", "Unable to read id from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
LOG("Unable to read id from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
targets.push_back(id);
|
||||
if (id != LWOOBJID_EMPTY) {
|
||||
auto* canidate = Game::entityManager->GetEntity(id);
|
||||
if (canidate) targets.push_back(canidate);
|
||||
} else {
|
||||
LOG("Bitstream has LWOOBJID_EMPTY as a target!");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto target : targets) {
|
||||
branch.target = target;
|
||||
|
||||
branch.target = target->GetObjectID();
|
||||
this->m_action->Handle(context, bitStream, branch);
|
||||
}
|
||||
} else {
|
||||
this->m_missAction->Handle(context, bitStream, branch);
|
||||
}
|
||||
} else this->m_missAction->Handle(context, bitStream, branch);
|
||||
}
|
||||
|
||||
void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* self = Game::entityManager->GetEntity(context->originator);
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("TacArcBehavior", "Invalid self for (%llu)!", context->originator);
|
||||
LOG("Invalid self for (%llu)!", context->originator);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
|
||||
|
||||
if ((this->m_usePickedTarget || context->clientInitalized) && branch.target > 0) {
|
||||
const auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
std::vector<Entity*> targets = {};
|
||||
if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
|
||||
auto target = Game::entityManager->GetEntity(branch.target);
|
||||
targets.push_back(target);
|
||||
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
||||
if (!targets.empty()) {
|
||||
this->m_action->Handle(context, bitStream, branch);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the game is specific about who to target, check that
|
||||
if (destroyableComponent == nullptr || ((!m_targetFriend && !m_targetEnemy
|
||||
|| m_targetFriend && destroyableComponent->IsFriend(target)
|
||||
|| m_targetEnemy && destroyableComponent->IsEnemy(target)))) {
|
||||
this->m_action->Calculate(context, bitStream, branch);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto* combatAi = self->GetComponent<BaseCombatAIComponent>();
|
||||
|
||||
const auto casterPosition = self->GetPosition();
|
||||
|
||||
auto reference = self->GetPosition(); //+ m_offset;
|
||||
auto reference = self->GetPosition() + m_offset;
|
||||
|
||||
std::vector<Entity*> targets;
|
||||
targets.clear();
|
||||
|
||||
std::vector<LWOOBJID> validTargets;
|
||||
std::vector<Entity*> validTargets = Game::entityManager->GetEntitiesByProximity(reference, this->m_maxRange);
|
||||
|
||||
if (combatAi != nullptr) {
|
||||
if (combatAi->GetTarget() != LWOOBJID_EMPTY) {
|
||||
validTargets.push_back(combatAi->GetTarget());
|
||||
}
|
||||
}
|
||||
|
||||
// Find all valid targets, based on whether we target enemies or friends
|
||||
for (const auto& contextTarget : context->GetValidTargets()) {
|
||||
if (destroyableComponent != nullptr) {
|
||||
const auto* targetEntity = Game::entityManager->GetEntity(contextTarget);
|
||||
|
||||
if (m_targetEnemy && destroyableComponent->IsEnemy(targetEntity)
|
||||
|| m_targetFriend && destroyableComponent->IsFriend(targetEntity)) {
|
||||
validTargets.push_back(contextTarget);
|
||||
}
|
||||
} else {
|
||||
validTargets.push_back(contextTarget);
|
||||
}
|
||||
}
|
||||
// filter all valid targets, based on whether we target enemies or friends
|
||||
context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
||||
|
||||
for (auto validTarget : validTargets) {
|
||||
if (targets.size() >= this->m_maxTargets) {
|
||||
break;
|
||||
}
|
||||
if (targets.size() >= this->m_maxTargets) break;
|
||||
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue;
|
||||
if (validTarget->GetIsDead()) continue;
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(validTarget);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("TacArcBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
|
||||
const auto targetPos = validTarget->GetPosition();
|
||||
|
||||
// make sure we aren't too high or low in comparison to the targer
|
||||
const auto heightDifference = std::abs(reference.y - targetPos.y);
|
||||
if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::find(targets.begin(), targets.end(), entity) != targets.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entity->GetIsDead()) continue;
|
||||
|
||||
const auto otherPosition = entity->GetPosition();
|
||||
|
||||
const auto heightDifference = std::abs(otherPosition.y - casterPosition.y);
|
||||
|
||||
/*if (otherPosition.y > reference.y && heightDifference > this->m_upperBound || otherPosition.y < reference.y && heightDifference > this->m_lowerBound)
|
||||
{
|
||||
continue;
|
||||
}*/
|
||||
|
||||
const auto forward = self->GetRotation().GetForwardVector();
|
||||
|
||||
// forward is a normalized vector of where the caster is facing.
|
||||
// otherPosition is the position of the target.
|
||||
// targetPos is the position of the target.
|
||||
// reference is the position of the caster.
|
||||
// If we cast a ray forward from the caster, does it come within m_farWidth of the target?
|
||||
|
||||
const auto distance = Vector3::Distance(reference, otherPosition);
|
||||
const auto distance = Vector3::Distance(reference, targetPos);
|
||||
|
||||
if (m_method == 2) {
|
||||
NiPoint3 rayPoint = casterPosition + forward * distance;
|
||||
|
||||
if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, otherPosition) > this->m_farWidth * this->m_farWidth) {
|
||||
if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, targetPos) > this->m_farWidth * this->m_farWidth)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto normalized = (reference - otherPosition) / distance;
|
||||
|
||||
auto normalized = (reference - targetPos) / distance;
|
||||
const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180);
|
||||
|
||||
if (distance >= this->m_minDistance && this->m_maxDistance >= distance && degreeAngle <= 2 * this->m_angle) {
|
||||
targets.push_back(entity);
|
||||
if (distance >= this->m_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) {
|
||||
targets.push_back(validTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,33 +155,26 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
});
|
||||
|
||||
const auto hit = !targets.empty();
|
||||
|
||||
bitStream->Write(hit);
|
||||
|
||||
if (this->m_checkEnv) {
|
||||
const auto blocked = false; // TODO
|
||||
|
||||
bitStream->Write(blocked);
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
if (combatAi != nullptr) {
|
||||
combatAi->LookAt(targets[0]->GetPosition());
|
||||
}
|
||||
if (combatAi) combatAi->LookAt(targets[0]->GetPosition());
|
||||
|
||||
context->foundTarget = true; // We want to continue with this behavior
|
||||
|
||||
const auto count = static_cast<uint32_t>(targets.size());
|
||||
|
||||
bitStream->Write(count);
|
||||
|
||||
for (auto* target : targets) {
|
||||
bitStream->Write(target->GetObjectID());
|
||||
}
|
||||
|
||||
for (auto* target : targets) {
|
||||
branch.target = target->GetObjectID();
|
||||
|
||||
this->m_action->Calculate(context, bitStream, branch);
|
||||
}
|
||||
} else {
|
||||
@@ -228,43 +183,48 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
}
|
||||
|
||||
void TacArcBehavior::Load() {
|
||||
this->m_usePickedTarget = GetBoolean("use_picked_target");
|
||||
this->m_maxRange = GetFloat("max range");
|
||||
this->m_height = GetFloat("height", 2.2f);
|
||||
this->m_distanceWeight = GetFloat("distance_weight", 0.0f);
|
||||
this->m_angleWeight = GetFloat("angle_weight", 0.0f);
|
||||
this->m_angle = GetFloat("angle", 45.0f);
|
||||
this->m_minRange = GetFloat("min range", 0.0f);
|
||||
this->m_offset = NiPoint3(
|
||||
GetFloat("offset_x", 0.0f),
|
||||
GetFloat("offset_y", 0.0f),
|
||||
GetFloat("offset_z", 0.0f)
|
||||
);
|
||||
this->m_method = GetInt("method", 1);
|
||||
this->m_upperBound = std::abs(GetFloat("upper_bound", 4.4f));
|
||||
this->m_lowerBound = std::abs(GetFloat("lower_bound", 0.4f));
|
||||
this->m_usePickedTarget = GetBoolean("use_picked_target", false);
|
||||
this->m_useTargetPostion = GetBoolean("use_target_position", false);
|
||||
this->m_checkEnv = GetBoolean("check_env", false);
|
||||
this->m_useAttackPriority = GetBoolean("use_attack_priority", false);
|
||||
|
||||
this->m_action = GetAction("action");
|
||||
|
||||
this->m_missAction = GetAction("miss action");
|
||||
|
||||
this->m_checkEnv = GetBoolean("check_env");
|
||||
|
||||
this->m_blockedAction = GetAction("blocked action");
|
||||
|
||||
this->m_minDistance = GetFloat("min range");
|
||||
this->m_maxTargets = GetInt("max targets", 100);
|
||||
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
|
||||
|
||||
this->m_maxDistance = GetFloat("max range");
|
||||
this->m_farHeight = GetFloat("far_height", 5.0f);
|
||||
this->m_farWidth = GetFloat("far_width", 5.0f);
|
||||
this->m_nearHeight = GetFloat("near_height", 5.0f);
|
||||
this->m_nearWidth = GetFloat("near_width", 5.0f);
|
||||
|
||||
this->m_maxTargets = GetInt("max targets");
|
||||
|
||||
this->m_targetEnemy = GetBoolean("target_enemy");
|
||||
|
||||
this->m_targetFriend = GetBoolean("target_friend");
|
||||
|
||||
this->m_targetTeam = GetBoolean("target_team");
|
||||
|
||||
this->m_angle = GetFloat("angle");
|
||||
|
||||
this->m_upperBound = GetFloat("upper_bound");
|
||||
|
||||
this->m_lowerBound = GetFloat("lower_bound");
|
||||
|
||||
this->m_farHeight = GetFloat("far_height");
|
||||
|
||||
this->m_farWidth = GetFloat("far_width");
|
||||
|
||||
this->m_method = GetInt("method");
|
||||
|
||||
this->m_offset = {
|
||||
GetFloat("offset_x"),
|
||||
GetFloat("offset_y"),
|
||||
GetFloat("offset_z")
|
||||
};
|
||||
// params after this are needed for filter targets
|
||||
const auto parameters = GetParameterNames();
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.first.rfind("include_faction", 0) == 0) {
|
||||
this->m_includeFactionList.push_front(parameter.second);
|
||||
} else if (parameter.first.rfind("ignore_faction", 0) == 0) {
|
||||
this->m_ignoreFactionList.push_front(parameter.second);
|
||||
}
|
||||
}
|
||||
this->m_targetSelf = GetBoolean("target_caster", false);
|
||||
this->m_targetEnemy = GetBoolean("target_enemy", false);
|
||||
this->m_targetFriend = GetBoolean("target_friend", false);
|
||||
this->m_targetTeam = GetBoolean("target_team", false);
|
||||
}
|
||||
|
||||
@@ -2,56 +2,42 @@
|
||||
#include "Behavior.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "NiPoint3.h"
|
||||
#include <forward_list>
|
||||
|
||||
class TacArcBehavior final : public Behavior
|
||||
{
|
||||
class TacArcBehavior final : public Behavior {
|
||||
public:
|
||||
bool m_usePickedTarget;
|
||||
|
||||
Behavior* m_action;
|
||||
|
||||
bool m_checkEnv;
|
||||
|
||||
Behavior* m_missAction;
|
||||
|
||||
Behavior* m_blockedAction;
|
||||
|
||||
float m_minDistance;
|
||||
|
||||
float m_maxDistance;
|
||||
|
||||
uint32_t m_maxTargets;
|
||||
|
||||
bool m_targetEnemy;
|
||||
|
||||
bool m_targetFriend;
|
||||
|
||||
bool m_targetTeam;
|
||||
|
||||
float m_angle;
|
||||
|
||||
float m_upperBound;
|
||||
|
||||
float m_lowerBound;
|
||||
|
||||
float m_farHeight;
|
||||
|
||||
float m_farWidth;
|
||||
|
||||
uint32_t m_method;
|
||||
|
||||
NiPoint3 m_offset;
|
||||
|
||||
/*
|
||||
* Inherited
|
||||
*/
|
||||
|
||||
explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id) {
|
||||
}
|
||||
|
||||
explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id) {}
|
||||
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
void Load() override;
|
||||
private:
|
||||
float m_maxRange;
|
||||
float m_height;
|
||||
float m_distanceWeight;
|
||||
float m_angleWeight;
|
||||
float m_angle;
|
||||
float m_minRange;
|
||||
NiPoint3 m_offset;
|
||||
uint32_t m_method;
|
||||
float m_upperBound;
|
||||
float m_lowerBound;
|
||||
bool m_usePickedTarget;
|
||||
bool m_useTargetPostion;
|
||||
bool m_checkEnv;
|
||||
bool m_useAttackPriority;
|
||||
Behavior* m_action;
|
||||
Behavior* m_missAction;
|
||||
Behavior* m_blockedAction;
|
||||
uint32_t m_maxTargets;
|
||||
float m_farHeight;
|
||||
float m_farWidth;
|
||||
float m_nearHeight;
|
||||
float m_nearWidth;
|
||||
|
||||
std::forward_list<int32_t> m_ignoreFactionList {};
|
||||
std::forward_list<int32_t> m_includeFactionList {};
|
||||
bool m_targetSelf;
|
||||
bool m_targetEnemy;
|
||||
bool m_targetFriend;
|
||||
bool m_targetTeam;
|
||||
};
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
void TauntBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("TauntBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ void TauntBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("TauntBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
LOG("Failed to find target (%llu)!", branch.target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "NiPoint3.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
void VerifyBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
@@ -18,7 +18,7 @@ void VerifyBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
auto* self = Game::entityManager->GetEntity(context->originator);
|
||||
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("VerifyBehavior", "Invalid self for (%llu)", context->originator);
|
||||
LOG("Invalid self for (%llu)", context->originator);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "ScriptedActivityComponent.h"
|
||||
#include "ActivityComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "MissionComponent.h"
|
||||
@@ -6,12 +6,11 @@
|
||||
#include "dZoneManager.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include <WorldPackets.h>
|
||||
#include "Logger.h"
|
||||
#include "WorldPackets.h"
|
||||
#include "EntityManager.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "Player.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "dServer.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dZoneManager.h"
|
||||
@@ -29,45 +28,37 @@
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "LeaderboardManager.h"
|
||||
|
||||
ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activityID) : Component(parent) {
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Component(parent) {
|
||||
/*
|
||||
* This is precisely what the client does functionally
|
||||
* Use the component id as the default activity id and load its data from the database
|
||||
* if activityID is specified and if that column exists in the activities table, update the activity info with that data.
|
||||
*/
|
||||
|
||||
m_ActivityID = activityID;
|
||||
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<Leaderboard::Type>(activity.leaderboardType) == Leaderboard::Type::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;
|
||||
}
|
||||
}
|
||||
LoadActivityData(activityID);
|
||||
if (m_Parent->HasVar(u"activityID")) {
|
||||
m_ActivityID = parent->GetVar<int32_t>(u"activityID");
|
||||
LoadActivityData(m_ActivityID);
|
||||
}
|
||||
|
||||
auto* destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent) {
|
||||
// check for LMIs and set the loot LMIs
|
||||
CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
||||
// First lookup the loot matrix id for this component id.
|
||||
CDActivityRewardsTable* activityRewardsTable = CDClientManager::GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
|
||||
|
||||
uint32_t startingLMI = 0;
|
||||
|
||||
// If we have one, set the starting loot matrix id to that.
|
||||
if (activityRewards.size() > 0) {
|
||||
startingLMI = activityRewards[0].LootMatrixIndex;
|
||||
}
|
||||
|
||||
if (startingLMI > 0) {
|
||||
// now time for bodge :)
|
||||
// We may have more than 1 loot matrix index to use depending ont the size of the team that is looting the activity.
|
||||
// So this logic will get the rest of the loot matrix indices for this activity.
|
||||
|
||||
std::vector<CDActivityRewards> objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
|
||||
for (const auto& item : objectTemplateActivities) {
|
||||
@@ -78,31 +69,49 @@ ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activit
|
||||
}
|
||||
}
|
||||
}
|
||||
void ActivityComponent::LoadActivityData(const int32_t activityId) {
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([activityId](CDActivities entry) {return (entry.ActivityID == activityId); });
|
||||
|
||||
ScriptedActivityComponent::~ScriptedActivityComponent()
|
||||
= default;
|
||||
|
||||
void ScriptedActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
||||
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);
|
||||
bool soloRacing = Game::config->GetValue("solo_racing") == "1";
|
||||
for (CDActivities activity : activities) {
|
||||
m_ActivityInfo = activity;
|
||||
if (static_cast<Leaderboard::Type>(activity.leaderboardType) == Leaderboard::Type::Racing && soloRacing) {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
}
|
||||
if (m_ActivityInfo.instanceMapID == -1) {
|
||||
const auto& transferOverride = m_Parent->GetVarAsString(u"transferZoneID");
|
||||
if (!transferOverride.empty()) {
|
||||
m_ActivityInfo.instanceMapID =
|
||||
GeneralUtils::TryParse<uint32_t>(transferOverride).value_or(m_ActivityInfo.instanceMapID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::ReloadConfig() {
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||
void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
||||
outBitStream->Write(m_DirtyActivityInfo);
|
||||
if (m_DirtyActivityInfo) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bIsInitialUpdate) m_DirtyActivityInfo = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::ReloadConfig() {
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([this](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") {
|
||||
if (static_cast<Leaderboard::Type>(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
} else {
|
||||
@@ -112,11 +121,7 @@ void ScriptedActivityComponent::ReloadConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
|
||||
if (m_ActivityInfo.ActivityID == 103) {
|
||||
return;
|
||||
}
|
||||
|
||||
void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
|
||||
if (id == "LobbyExit") {
|
||||
PlayerLeave(player->GetObjectID());
|
||||
} else if (id == "PlayButton") {
|
||||
@@ -124,11 +129,8 @@ void ScriptedActivityComponent::HandleMessageBoxResponse(Entity* player, const s
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::PlayerJoin(Entity* player) {
|
||||
if (m_ActivityInfo.ActivityID == 103 || PlayerIsInQueue(player) || !IsValidActivity(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerJoin(Entity* player) {
|
||||
if (PlayerIsInQueue(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);
|
||||
@@ -136,11 +138,9 @@ void ScriptedActivityComponent::PlayerJoin(Entity* player) {
|
||||
auto* instance = NewInstance();
|
||||
instance->AddParticipant(player);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::PlayerJoinLobby(Entity* player) {
|
||||
void ActivityComponent::PlayerJoinLobby(Entity* player) {
|
||||
if (!m_Parent->HasComponent(eReplicaComponentType::QUICK_BUILD))
|
||||
GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby
|
||||
LobbyPlayer* newLobbyPlayer = new LobbyPlayer();
|
||||
@@ -189,7 +189,7 @@ void ScriptedActivityComponent::PlayerJoinLobby(Entity* player) {
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::PlayerLeave(LWOOBJID playerID) {
|
||||
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) {
|
||||
@@ -214,7 +214,7 @@ void ScriptedActivityComponent::PlayerLeave(LWOOBJID playerID) {
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::Update(float deltaTime) {
|
||||
void ActivityComponent::Update(float deltaTime) {
|
||||
std::vector<Lobby*> lobbiesToRemove{};
|
||||
// Ticks all the lobbies, not applicable for non-instance activities
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
@@ -273,7 +273,7 @@ void ScriptedActivityComponent::Update(float deltaTime) {
|
||||
|
||||
// The timer has elapsed, start the instance
|
||||
if (lobby->timer <= 0.0f) {
|
||||
Game::logger->Log("ScriptedActivityComponent", "Setting up instance.");
|
||||
LOG("Setting up instance.");
|
||||
ActivityInstance* instance = NewInstance();
|
||||
LoadPlayersIntoInstance(instance, lobby->players);
|
||||
instance->StartZone();
|
||||
@@ -287,7 +287,7 @@ void ScriptedActivityComponent::Update(float deltaTime) {
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::RemoveLobby(Lobby* lobby) {
|
||||
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);
|
||||
@@ -296,29 +296,12 @@ void ScriptedActivityComponent::RemoveLobby(Lobby* lobby) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptedActivityComponent::HasLobby() const {
|
||||
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 ScriptedActivityComponent::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_Parent->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(Game::zoneManager->GetZone()->GetWorldID()); // Gets them out of this stuck state
|
||||
|
||||
return false;
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptedActivityComponent::PlayerIsInQueue(Entity* player) {
|
||||
bool ActivityComponent::PlayerIsInQueue(Entity* player) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (player->GetObjectID() == lobbyPlayer->entityID) return true;
|
||||
@@ -328,7 +311,7 @@ bool ScriptedActivityComponent::PlayerIsInQueue(Entity* player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptedActivityComponent::IsPlayedBy(Entity* player) const {
|
||||
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())
|
||||
@@ -339,7 +322,7 @@ bool ScriptedActivityComponent::IsPlayedBy(Entity* player) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptedActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
|
||||
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)
|
||||
@@ -350,7 +333,7 @@ bool ScriptedActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptedActivityComponent::TakeCost(Entity* player) const {
|
||||
bool ActivityComponent::TakeCost(Entity* player) const {
|
||||
if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0)
|
||||
return true;
|
||||
|
||||
@@ -366,7 +349,7 @@ bool ScriptedActivityComponent::TakeCost(Entity* player) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::PlayerReady(Entity* player, bool bReady) {
|
||||
void ActivityComponent::PlayerReady(Entity* player, bool bReady) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (lobbyPlayer->entityID == player->GetObjectID()) {
|
||||
@@ -389,13 +372,13 @@ void ScriptedActivityComponent::PlayerReady(Entity* player, bool bReady) {
|
||||
}
|
||||
}
|
||||
|
||||
ActivityInstance* ScriptedActivityComponent::NewInstance() {
|
||||
ActivityInstance* ActivityComponent::NewInstance() {
|
||||
auto* instance = new ActivityInstance(m_Parent, m_ActivityInfo);
|
||||
m_Instances.push_back(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
|
||||
void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
|
||||
for (LobbyPlayer* player : lobby) {
|
||||
auto* entity = player->GetEntity();
|
||||
if (entity == nullptr || !TakeCost(entity)) {
|
||||
@@ -406,11 +389,11 @@ void ScriptedActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instan
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<ActivityInstance*>& ScriptedActivityComponent::GetInstances() const {
|
||||
const std::vector<ActivityInstance*>& ActivityComponent::GetInstances() const {
|
||||
return m_Instances;
|
||||
}
|
||||
|
||||
ActivityInstance* ScriptedActivityComponent::GetInstance(const LWOOBJID playerID) {
|
||||
ActivityInstance* ActivityComponent::GetInstance(const LWOOBJID playerID) {
|
||||
for (const auto* instance : GetInstances()) {
|
||||
for (const auto* participant : instance->GetParticipants()) {
|
||||
if (participant->GetObjectID() == playerID)
|
||||
@@ -421,14 +404,14 @@ ActivityInstance* ScriptedActivityComponent::GetInstance(const LWOOBJID playerID
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::ClearInstances() {
|
||||
void ActivityComponent::ClearInstances() {
|
||||
for (ActivityInstance* instance : m_Instances) {
|
||||
delete instance;
|
||||
}
|
||||
m_Instances.clear();
|
||||
}
|
||||
|
||||
ActivityPlayer* ScriptedActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
|
||||
ActivityPlayer* ActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
|
||||
for (auto* activityData : m_ActivityPlayers) {
|
||||
if (activityData->playerID == playerID) {
|
||||
return activityData;
|
||||
@@ -438,13 +421,14 @@ ActivityPlayer* ScriptedActivityComponent::GetActivityPlayerData(LWOOBJID player
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
|
||||
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);
|
||||
m_DirtyActivityInfo = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
return;
|
||||
@@ -452,38 +436,39 @@ void ScriptedActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
|
||||
}
|
||||
}
|
||||
|
||||
ActivityPlayer* ScriptedActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
|
||||
ActivityPlayer* ActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr)
|
||||
return data;
|
||||
|
||||
m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} });
|
||||
m_DirtyActivityInfo = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
return GetActivityPlayerData(playerID);
|
||||
}
|
||||
|
||||
float_t ScriptedActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) {
|
||||
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)];
|
||||
value = data->values[std::min(index, static_cast<uint32_t>(9))];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t 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;
|
||||
data->values[std::min(index, static_cast<uint32_t>(9))] = value;
|
||||
}
|
||||
|
||||
m_DirtyActivityInfo = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void ScriptedActivityComponent::PlayerRemove(LWOOBJID playerID) {
|
||||
void ActivityComponent::PlayerRemove(LWOOBJID playerID) {
|
||||
for (auto* instance : GetInstances()) {
|
||||
auto participants = instance->GetParticipants();
|
||||
for (const auto* participant : participants) {
|
||||
@@ -516,7 +501,7 @@ void ActivityInstance::StartZone() {
|
||||
// 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);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
|
||||
|
||||
bitStream.Write(leader->GetObjectID());
|
||||
bitStream.Write(m_Participants.size());
|
||||
@@ -539,7 +524,7 @@ void ActivityInstance::StartZone() {
|
||||
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);
|
||||
LOG("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);
|
||||
@@ -561,14 +546,14 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
|
||||
}
|
||||
|
||||
// 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); });
|
||||
auto* activityRewardsTable = CDClientManager::GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([this](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>();
|
||||
auto* currencyTableTable = CDClientManager::GetTable<CDCurrencyTableTable>();
|
||||
std::vector<CDCurrencyTable> currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == activityRewards[0].CurrencyIndex && entry.npcminlevel == 1); });
|
||||
|
||||
if (!currencyTable.empty()) {
|
||||
@@ -576,7 +561,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
|
||||
maxCoins = currencyTable[0].maxvalue;
|
||||
}
|
||||
|
||||
LootGenerator::Instance().DropLoot(participant, m_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
|
||||
Loot::DropLoot(participant, m_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
|
||||
}
|
||||
}
|
||||
|
||||
373
dGame/dComponents/ActivityComponent.h
Normal file
373
dGame/dComponents/ActivityComponent.h
Normal file
@@ -0,0 +1,373 @@
|
||||
#ifndef ACTIVITYCOMPONENT_H
|
||||
#define ACTIVITYCOMPONENT_H
|
||||
|
||||
#include "CDClientManager.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_Parent = 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 ScriptedActivityComponent)
|
||||
*/
|
||||
Entity* m_Parent;
|
||||
|
||||
/**
|
||||
* 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 scripted 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 activityID);
|
||||
|
||||
void LoadActivityData(const int32_t activityId);
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* If the Activity info is dirty
|
||||
*/
|
||||
bool m_DirtyActivityInfo = true;
|
||||
};
|
||||
|
||||
#endif // ACTIVITYCOMPONENT_H
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include <BitStream.h>
|
||||
#include "BitStream.h"
|
||||
|
||||
#include "Entity.h"
|
||||
#include "EntityManager.h"
|
||||
@@ -20,11 +20,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "SkillComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
#include "dNavMesh.h"
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
@@ -34,13 +35,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
m_MovementAI = nullptr;
|
||||
m_Disabled = false;
|
||||
m_SkillEntries = {};
|
||||
m_MovementAI = nullptr;
|
||||
m_SoftTimer = 5.0f;
|
||||
|
||||
//Grab the aggro information from BaseCombatAI:
|
||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
||||
componentQuery.bind(1, (int)id);
|
||||
componentQuery.bind(1, static_cast<int>(id));
|
||||
|
||||
auto componentResult = componentQuery.execQuery();
|
||||
|
||||
@@ -77,7 +77,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
*/
|
||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
||||
skillQuery.bind(1, (int)parent->GetLOT());
|
||||
skillQuery.bind(1, static_cast<int>(parent->GetLOT()));
|
||||
|
||||
auto result = skillQuery.execQuery();
|
||||
|
||||
@@ -107,10 +107,10 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
|
||||
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
|
||||
|
||||
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
if (physicsComponentTable != nullptr) {
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
@@ -129,17 +129,17 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
m_dpEntity->SetPosition(m_Parent->GetPosition());
|
||||
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
|
||||
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
dpWorld::Instance().AddEntity(m_dpEntityEnemy);
|
||||
dpWorld::AddEntity(m_dpEntity);
|
||||
dpWorld::AddEntity(m_dpEntityEnemy);
|
||||
|
||||
}
|
||||
|
||||
BaseCombatAIComponent::~BaseCombatAIComponent() {
|
||||
if (m_dpEntity)
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
dpWorld::RemoveEntity(m_dpEntity);
|
||||
|
||||
if (m_dpEntityEnemy)
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
||||
dpWorld::RemoveEntity(m_dpEntityEnemy);
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
@@ -185,7 +185,7 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
bool stunnedThisFrame = m_Stunned;
|
||||
CalculateCombat(deltaTime); // Putting this here for now
|
||||
|
||||
if (m_StartPosition == NiPoint3::ZERO) {
|
||||
if (m_StartPosition == NiPoint3Constant::ZERO) {
|
||||
m_StartPosition = m_Parent->GetPosition();
|
||||
}
|
||||
|
||||
@@ -243,12 +243,12 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
bool hadRemainingDowntime = m_SkillTime > 0.0f;
|
||||
if (m_SkillTime > 0.0f) m_SkillTime -= deltaTime;
|
||||
|
||||
auto* rebuild = m_Parent->GetComponent<RebuildComponent>();
|
||||
auto* rebuild = m_Parent->GetComponent<QuickBuildComponent>();
|
||||
|
||||
if (rebuild != nullptr) {
|
||||
const auto state = rebuild->GetState();
|
||||
|
||||
if (state != eRebuildState::COMPLETED) {
|
||||
if (state != eQuickBuildState::COMPLETED) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -523,7 +523,7 @@ bool BaseCombatAIComponent::IsMech() {
|
||||
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
||||
outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate);
|
||||
if (m_DirtyStateOrTarget || bIsInitialUpdate) {
|
||||
outBitStream->Write(uint32_t(m_State));
|
||||
outBitStream->Write(m_State);
|
||||
outBitStream->Write(m_Target);
|
||||
m_DirtyStateOrTarget = false;
|
||||
}
|
||||
@@ -540,7 +540,7 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BaseCombatAIComponent", "Invalid entity for checking validity (%llu)!", target);
|
||||
LOG("Invalid entity for checking validity (%llu)!", target);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -554,17 +554,17 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||
auto* referenceDestroyable = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (referenceDestroyable == nullptr) {
|
||||
Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_Parent->GetObjectID());
|
||||
LOG("Invalid reference destroyable component on (%llu)!", m_Parent->GetObjectID());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* quickbuild = entity->GetComponent<RebuildComponent>();
|
||||
auto* quickbuild = entity->GetComponent<QuickBuildComponent>();
|
||||
|
||||
if (quickbuild != nullptr) {
|
||||
const auto state = quickbuild->GetState();
|
||||
|
||||
if (state != eRebuildState::COMPLETED) {
|
||||
if (state != eQuickBuildState::COMPLETED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -654,8 +654,8 @@ void BaseCombatAIComponent::Wander() {
|
||||
|
||||
auto destination = m_StartPosition + delta;
|
||||
|
||||
if (dpWorld::Instance().IsLoaded()) {
|
||||
destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination);
|
||||
if (dpWorld::IsLoaded()) {
|
||||
destination.y = dpWorld::GetNavMesh()->GetHeightAtPoint(destination);
|
||||
}
|
||||
|
||||
if (Vector3::DistanceSquared(destination, m_MovementAI->GetParent()->GetPosition()) < 2 * 2) {
|
||||
|
||||
@@ -19,7 +19,7 @@ class Entity;
|
||||
/**
|
||||
* The current state of the AI
|
||||
*/
|
||||
enum class AiState : int {
|
||||
enum class AiState : uint32_t {
|
||||
idle = 0, // Doing nothing
|
||||
aggro, // Waiting for an enemy to cross / running back to spawn
|
||||
tether, // Chasing an enemy
|
||||
@@ -45,9 +45,9 @@ struct AiSkillEntry
|
||||
/**
|
||||
* Handles the AI of entities, making them wander, tether and attack their enemies
|
||||
*/
|
||||
class BaseCombatAIComponent : public Component {
|
||||
class BaseCombatAIComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
|
||||
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
|
||||
~BaseCombatAIComponent() override;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
#include "dZoneManager.h"
|
||||
#include "SwitchComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "GameMessages.h"
|
||||
#include <BitStream.h>
|
||||
#include "BitStream.h"
|
||||
#include "eTriggerEventType.h"
|
||||
|
||||
BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
|
||||
@@ -81,13 +81,13 @@ void BouncerComponent::LookupPetSwitch() {
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
Game::logger->Log("BouncerComponent", "Loaded pet bouncer");
|
||||
LOG("Loaded pet bouncer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_PetSwitchLoaded) {
|
||||
Game::logger->Log("BouncerComponent", "Failed to load pet bouncer");
|
||||
LOG("Failed to load pet bouncer");
|
||||
|
||||
m_Parent->AddCallbackTimer(0.5f, [this]() {
|
||||
LookupPetSwitch();
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
/**
|
||||
* Attached to bouncer entities, allowing other entities to bounce off of it
|
||||
*/
|
||||
class BouncerComponent : public Component {
|
||||
class BouncerComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
|
||||
BouncerComponent(Entity* parentEntity);
|
||||
~BouncerComponent() override;
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
#include "BuffComponent.h"
|
||||
#include <BitStream.h>
|
||||
#include "BitStream.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include <stdexcept>
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "GameMessages.h"
|
||||
#include "SkillComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "TeamManager.h"
|
||||
|
||||
std::unordered_map<int32_t, std::vector<BuffParameter>> BuffComponent::m_Cache{};
|
||||
|
||||
namespace {
|
||||
std::map<std::string, std::string> BuffFx = {
|
||||
{ "overtime", "OTB_" },
|
||||
{ "max_health", "HEALTH_" },
|
||||
{ "max_imagination", "IMAGINATION_" },
|
||||
{ "max_armor", "ARMOR_" },
|
||||
{ "speed", "SPEED_" },
|
||||
{ "loot", "LOOT_" }
|
||||
};
|
||||
}
|
||||
|
||||
BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
|
||||
}
|
||||
|
||||
@@ -22,32 +34,38 @@ BuffComponent::~BuffComponent() {
|
||||
|
||||
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
||||
if (!bIsInitialUpdate) return;
|
||||
if (m_Buffs.empty()) {
|
||||
outBitStream->Write0();
|
||||
} else {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write(!m_Buffs.empty());
|
||||
if (!m_Buffs.empty()) {
|
||||
outBitStream->Write<uint32_t>(m_Buffs.size());
|
||||
|
||||
for (const auto& buff : m_Buffs) {
|
||||
outBitStream->Write<uint32_t>(buff.first);
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
for (const auto& [id, buff] : m_Buffs) {
|
||||
outBitStream->Write<uint32_t>(id);
|
||||
outBitStream->Write(buff.time != 0.0f);
|
||||
if (buff.time != 0.0f) outBitStream->Write<uint32_t>(buff.time * 1000.0f);
|
||||
outBitStream->Write(buff.cancelOnDeath);
|
||||
outBitStream->Write(buff.cancelOnZone);
|
||||
outBitStream->Write(buff.cancelOnDamaged);
|
||||
outBitStream->Write(buff.cancelOnRemoveBuff);
|
||||
outBitStream->Write(buff.cancelOnUi);
|
||||
outBitStream->Write(buff.cancelOnLogout);
|
||||
outBitStream->Write(buff.cancelOnUnequip);
|
||||
outBitStream->Write0(); // Cancel on Damage Absorb Ran Out. Generally false from what I can tell
|
||||
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
auto* team = TeamManager::Instance()->GetTeam(buff.source);
|
||||
bool addedByTeammate = false;
|
||||
if (team) {
|
||||
addedByTeammate = std::count(team->members.begin(), team->members.end(), m_Parent->GetObjectID()) > 0;
|
||||
}
|
||||
|
||||
outBitStream->Write<uint32_t>(0);
|
||||
outBitStream->Write(addedByTeammate); // Added by teammate. If source is in the same team as the target, this is true. Otherwise, false.
|
||||
outBitStream->Write(buff.applyOnTeammates);
|
||||
if (addedByTeammate) outBitStream->Write(buff.source);
|
||||
|
||||
outBitStream->Write<uint32_t>(buff.refCount);
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0(); // something to do with immunity buffs?
|
||||
}
|
||||
|
||||
void BuffComponent::Update(float deltaTime) {
|
||||
@@ -77,23 +95,67 @@ void BuffComponent::Update(float deltaTime) {
|
||||
|
||||
if (buff.second.time <= 0.0f) {
|
||||
RemoveBuff(buff.first);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_BuffsToRemove.empty()) return;
|
||||
|
||||
for (const auto& buff : m_BuffsToRemove) {
|
||||
m_Buffs.erase(buff);
|
||||
}
|
||||
|
||||
m_BuffsToRemove.clear();
|
||||
}
|
||||
|
||||
const std::string& GetFxName(const std::string& buffname) {
|
||||
const auto& toReturn = BuffFx[buffname];
|
||||
if (toReturn.empty()) {
|
||||
LOG_DEBUG("No fx name for %s", buffname.c_str());
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
void BuffComponent::ApplyBuffFx(uint32_t buffId, const BuffParameter& buff) {
|
||||
std::string fxToPlay;
|
||||
const auto& buffName = GetFxName(buff.name);
|
||||
|
||||
if (buffName.empty()) return;
|
||||
|
||||
fxToPlay += std::to_string(buffId);
|
||||
LOG_DEBUG("Playing %s %i", fxToPlay.c_str(), buff.effectId);
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), buff.effectId, u"cast", fxToPlay, LWOOBJID_EMPTY, 1.07f, 1.0f, false);
|
||||
}
|
||||
|
||||
void BuffComponent::RemoveBuffFx(uint32_t buffId, const BuffParameter& buff) {
|
||||
std::string fxToPlay;
|
||||
const auto& buffName = GetFxName(buff.name);
|
||||
|
||||
if (buffName.empty()) return;
|
||||
|
||||
fxToPlay += std::to_string(buffId);
|
||||
LOG_DEBUG("Stopping %s", fxToPlay.c_str());
|
||||
GameMessages::SendStopFXEffect(m_Parent, false, fxToPlay);
|
||||
}
|
||||
|
||||
void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOOBJID source, bool addImmunity,
|
||||
bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff,
|
||||
bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) {
|
||||
bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, bool applyOnTeammates) {
|
||||
// Prevent buffs from stacking.
|
||||
if (HasBuff(id)) {
|
||||
m_Buffs[id].refCount++;
|
||||
m_Buffs[id].time = duration;
|
||||
return;
|
||||
}
|
||||
|
||||
GameMessages::SendAddBuff(const_cast<LWOOBJID&>(m_Parent->GetObjectID()), source, (uint32_t)id,
|
||||
(uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
|
||||
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone);
|
||||
auto* team = TeamManager::Instance()->GetTeam(source);
|
||||
bool addedByTeammate = false;
|
||||
if (team) {
|
||||
addedByTeammate = std::count(team->members.begin(), team->members.end(), m_Parent->GetObjectID()) > 0;
|
||||
}
|
||||
|
||||
GameMessages::SendAddBuff(const_cast<LWOOBJID&>(m_Parent->GetObjectID()), source, static_cast<uint32_t>(id),
|
||||
static_cast<uint32_t>(duration) * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
|
||||
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, addedByTeammate, applyOnTeammates);
|
||||
|
||||
float tick = 0;
|
||||
float stacks = 0;
|
||||
@@ -102,7 +164,7 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.name == "overtime") {
|
||||
auto* behaviorTemplateTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
auto* behaviorTemplateTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
|
||||
|
||||
behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID;
|
||||
stacks = static_cast<int32_t>(parameter.values[1]);
|
||||
@@ -121,20 +183,46 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
buff.stacks = stacks;
|
||||
buff.source = source;
|
||||
buff.behaviorID = behaviorID;
|
||||
buff.cancelOnDamaged = cancelOnDamaged;
|
||||
buff.cancelOnDeath = cancelOnDeath;
|
||||
buff.cancelOnLogout = cancelOnLogout;
|
||||
buff.cancelOnRemoveBuff = cancelOnRemoveBuff;
|
||||
buff.cancelOnUi = cancelOnUi;
|
||||
buff.cancelOnUnequip = cancelOnUnequip;
|
||||
buff.cancelOnZone = cancelOnZone;
|
||||
buff.refCount = 1;
|
||||
|
||||
m_Buffs.emplace(id, buff);
|
||||
|
||||
auto* parent = GetParent();
|
||||
if (!cancelOnDeath) return;
|
||||
|
||||
m_Parent->AddDieCallback([parent, id]() {
|
||||
LOG_DEBUG("Removing buff %i because parent died", id);
|
||||
if (!parent) return;
|
||||
auto* buffComponent = parent->GetComponent<BuffComponent>();
|
||||
if (buffComponent) buffComponent->RemoveBuff(id, false, false, true);
|
||||
});
|
||||
}
|
||||
|
||||
void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity) {
|
||||
void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity, bool ignoreRefCount) {
|
||||
const auto& iter = m_Buffs.find(id);
|
||||
|
||||
if (iter == m_Buffs.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ignoreRefCount && !iter->second.cancelOnRemoveBuff) {
|
||||
iter->second.refCount--;
|
||||
LOG_DEBUG("refCount for buff %i is now %i", id, iter->second.refCount);
|
||||
if (iter->second.refCount > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
|
||||
|
||||
m_Buffs.erase(iter);
|
||||
m_BuffsToRemove.push_back(id);
|
||||
|
||||
RemoveBuffEffect(id);
|
||||
}
|
||||
@@ -146,6 +234,7 @@ bool BuffComponent::HasBuff(int32_t id) {
|
||||
void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
for (const auto& parameter : parameters) {
|
||||
ApplyBuffFx(id, parameter);
|
||||
if (parameter.name == "max_health") {
|
||||
const auto maxHealth = parameter.value;
|
||||
|
||||
@@ -182,6 +271,7 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
void BuffComponent::RemoveBuffEffect(int32_t id) {
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
for (const auto& parameter : parameters) {
|
||||
RemoveBuffFx(id, parameter);
|
||||
if (parameter.name == "max_health") {
|
||||
const auto maxHealth = parameter.value;
|
||||
|
||||
@@ -251,13 +341,25 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
auto* buffEntry = buffElement->FirstChildElement("b");
|
||||
|
||||
while (buffEntry != nullptr) {
|
||||
while (buffEntry) {
|
||||
int32_t id = buffEntry->IntAttribute("id");
|
||||
float t = buffEntry->FloatAttribute("t");
|
||||
float tk = buffEntry->FloatAttribute("tk");
|
||||
float tt = buffEntry->FloatAttribute("tt");
|
||||
int32_t s = buffEntry->FloatAttribute("s");
|
||||
LWOOBJID sr = buffEntry->Int64Attribute("sr");
|
||||
int32_t b = buffEntry->IntAttribute("b");
|
||||
int32_t refCount = buffEntry->IntAttribute("refCount");
|
||||
|
||||
bool cancelOnDamaged = buffEntry->BoolAttribute("cancelOnDamaged");
|
||||
bool cancelOnDeath = buffEntry->BoolAttribute("cancelOnDeath");
|
||||
bool cancelOnLogout = buffEntry->BoolAttribute("cancelOnLogout");
|
||||
bool cancelOnRemoveBuff = buffEntry->BoolAttribute("cancelOnRemoveBuff");
|
||||
bool cancelOnUi = buffEntry->BoolAttribute("cancelOnUi");
|
||||
bool cancelOnUnequip = buffEntry->BoolAttribute("cancelOnUnequip");
|
||||
bool cancelOnZone = buffEntry->BoolAttribute("cancelOnZone");
|
||||
bool applyOnTeammates = buffEntry->BoolAttribute("applyOnTeammates");
|
||||
|
||||
|
||||
Buff buff;
|
||||
buff.id = id;
|
||||
@@ -266,6 +368,18 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
buff.stacks = s;
|
||||
buff.source = sr;
|
||||
buff.behaviorID = b;
|
||||
buff.refCount = refCount;
|
||||
buff.tickTime = tt;
|
||||
|
||||
buff.cancelOnDamaged = cancelOnDamaged;
|
||||
buff.cancelOnDeath = cancelOnDeath;
|
||||
buff.cancelOnLogout = cancelOnLogout;
|
||||
buff.cancelOnRemoveBuff = cancelOnRemoveBuff;
|
||||
buff.cancelOnUi = cancelOnUi;
|
||||
buff.cancelOnUnequip = cancelOnUnequip;
|
||||
buff.cancelOnZone = cancelOnZone;
|
||||
buff.applyOnTeammates = applyOnTeammates;
|
||||
|
||||
|
||||
m_Buffs.emplace(id, buff);
|
||||
|
||||
@@ -288,15 +402,28 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
buffElement->DeleteChildren();
|
||||
}
|
||||
|
||||
for (const auto& buff : m_Buffs) {
|
||||
for (const auto& [id, buff] : m_Buffs) {
|
||||
auto* buffEntry = doc->NewElement("b");
|
||||
// TODO: change this if to if (buff.cancelOnZone || buff.cancelOnLogout) handling at some point. No current way to differentiate between zone transfer and logout.
|
||||
if (buff.cancelOnZone) continue;
|
||||
|
||||
buffEntry->SetAttribute("id", buff.first);
|
||||
buffEntry->SetAttribute("t", buff.second.time);
|
||||
buffEntry->SetAttribute("tk", buff.second.tick);
|
||||
buffEntry->SetAttribute("s", buff.second.stacks);
|
||||
buffEntry->SetAttribute("sr", buff.second.source);
|
||||
buffEntry->SetAttribute("b", buff.second.behaviorID);
|
||||
buffEntry->SetAttribute("id", id);
|
||||
buffEntry->SetAttribute("t", buff.time);
|
||||
buffEntry->SetAttribute("tk", buff.tick);
|
||||
buffEntry->SetAttribute("tt", buff.tickTime);
|
||||
buffEntry->SetAttribute("s", buff.stacks);
|
||||
buffEntry->SetAttribute("sr", buff.source);
|
||||
buffEntry->SetAttribute("b", buff.behaviorID);
|
||||
buffEntry->SetAttribute("refCount", buff.refCount);
|
||||
|
||||
buffEntry->SetAttribute("cancelOnDamaged", buff.cancelOnDamaged);
|
||||
buffEntry->SetAttribute("cancelOnDeath", buff.cancelOnDeath);
|
||||
buffEntry->SetAttribute("cancelOnLogout", buff.cancelOnLogout);
|
||||
buffEntry->SetAttribute("cancelOnRemoveBuff", buff.cancelOnRemoveBuff);
|
||||
buffEntry->SetAttribute("cancelOnUi", buff.cancelOnUi);
|
||||
buffEntry->SetAttribute("cancelOnUnequip", buff.cancelOnUnequip);
|
||||
buffEntry->SetAttribute("cancelOnZone", buff.cancelOnZone);
|
||||
buffEntry->SetAttribute("applyOnTeammates", buff.applyOnTeammates);
|
||||
|
||||
buffElement->LinkEndChild(buffEntry);
|
||||
}
|
||||
@@ -309,9 +436,8 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
query.bind(1, (int)buffId);
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
query.bind(1, static_cast<int>(buffId));
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
@@ -321,11 +447,12 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
||||
BuffParameter param;
|
||||
|
||||
param.buffId = buffId;
|
||||
param.name = result.getStringField(1);
|
||||
param.value = result.getFloatField(2);
|
||||
param.name = result.getStringField("ParameterName");
|
||||
param.value = result.getFloatField("NumberValue");
|
||||
param.effectId = result.getIntField("EffectID");
|
||||
|
||||
if (!result.fieldIsNull(3)) {
|
||||
std::istringstream stream(result.getStringField(3));
|
||||
std::istringstream stream(result.getStringField("StringValue"));
|
||||
std::string token;
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
@@ -334,7 +461,7 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
||||
|
||||
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());
|
||||
LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ 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 BuffParameter {
|
||||
int32_t buffId;
|
||||
std::string name;
|
||||
float value;
|
||||
@@ -26,8 +25,7 @@ struct BuffParameter
|
||||
/**
|
||||
* 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;
|
||||
@@ -35,14 +33,23 @@ struct Buff
|
||||
int32_t stacks = 0;
|
||||
LWOOBJID source = 0;
|
||||
int32_t behaviorID = 0;
|
||||
bool cancelOnDamaged = false;
|
||||
bool cancelOnDeath = false;
|
||||
bool cancelOnLogout = false;
|
||||
bool cancelOnRemoveBuff = false;
|
||||
bool cancelOnUi = false;
|
||||
bool cancelOnUnequip = false;
|
||||
bool cancelOnZone = false;
|
||||
bool applyOnTeammates = false;
|
||||
uint32_t refCount = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows for the application of buffs to the parent entity, altering health, armor and imagination.
|
||||
*/
|
||||
class BuffComponent : public Component {
|
||||
class BuffComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
|
||||
explicit BuffComponent(Entity* parent);
|
||||
|
||||
@@ -74,14 +81,17 @@ public:
|
||||
*/
|
||||
void ApplyBuff(int32_t id, float duration, LWOOBJID source, bool addImmunity = false, bool cancelOnDamaged = false,
|
||||
bool cancelOnDeath = true, bool cancelOnLogout = false, bool cancelOnRemoveBuff = true,
|
||||
bool cancelOnUi = false, bool cancelOnUnequip = false, bool cancelOnZone = false);
|
||||
bool cancelOnUi = false, bool cancelOnUnequip = false, bool cancelOnZone = false, bool applyOnTeammates = false);
|
||||
|
||||
void ApplyBuffFx(uint32_t buffId, const BuffParameter& buffName);
|
||||
void RemoveBuffFx(uint32_t buffId, const BuffParameter& buffName);
|
||||
|
||||
/**
|
||||
* Removes a buff from the parent entity, reversing its effects
|
||||
* @param id the id of the buff to remove
|
||||
* @param removeImmunity whether or not to remove immunity on removing the buff
|
||||
*/
|
||||
void RemoveBuff(int32_t id, bool fromUnEquip = false, bool removeImmunity = false);
|
||||
void RemoveBuff(int32_t id, bool fromUnEquip = false, bool removeImmunity = false, bool ignoreRefCount = false);
|
||||
|
||||
/**
|
||||
* Returns whether or not the entity has a buff identified by `id`
|
||||
@@ -130,6 +140,9 @@ private:
|
||||
*/
|
||||
std::map<int32_t, Buff> m_Buffs;
|
||||
|
||||
// Buffs to remove at the end of the update frame.
|
||||
std::vector<int32_t> m_BuffsToRemove;
|
||||
|
||||
/**
|
||||
* Parameters (=effects) for each buff
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "GameMessages.h"
|
||||
#include "Entity.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "Item.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
@@ -24,7 +24,7 @@ void BuildBorderComponent::OnUse(Entity* originator) {
|
||||
if (!entities.empty()) {
|
||||
buildArea = entities[0]->GetObjectID();
|
||||
|
||||
Game::logger->Log("BuildBorderComponent", "Using PropertyPlaque");
|
||||
LOG("Using PropertyPlaque");
|
||||
}
|
||||
|
||||
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
|
||||
@@ -41,7 +41,7 @@ void BuildBorderComponent::OnUse(Entity* originator) {
|
||||
|
||||
inventoryComponent->PushEquippedItems();
|
||||
|
||||
Game::logger->Log("BuildBorderComponent", "Starting with %llu", buildArea);
|
||||
LOG("Starting with %llu", buildArea);
|
||||
|
||||
if (PropertyManagementComponent::Instance() != nullptr) {
|
||||
GameMessages::SendStartArrangingWithItem(
|
||||
@@ -56,7 +56,7 @@ void BuildBorderComponent::OnUse(Entity* originator) {
|
||||
4,
|
||||
0,
|
||||
-1,
|
||||
NiPoint3::ZERO,
|
||||
NiPoint3Constant::ZERO,
|
||||
0
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
/**
|
||||
* Component for the build border, allowing the user to start building when interacting with it
|
||||
*/
|
||||
class BuildBorderComponent : public Component {
|
||||
class BuildBorderComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
|
||||
BuildBorderComponent(Entity* parent);
|
||||
~BuildBorderComponent() override;
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
|
||||
set(DGAME_DCOMPONENTS_SOURCES
|
||||
"ActivityComponent.cpp"
|
||||
"BaseCombatAIComponent.cpp"
|
||||
"BouncerComponent.cpp"
|
||||
"BuffComponent.cpp"
|
||||
"BuildBorderComponent.cpp"
|
||||
"CharacterComponent.cpp"
|
||||
"CollectibleComponent.cpp"
|
||||
"Component.cpp"
|
||||
"ControllablePhysicsComponent.cpp"
|
||||
"DestroyableComponent.cpp"
|
||||
"DonationVendorComponent.cpp"
|
||||
"GhostComponent.cpp"
|
||||
"InventoryComponent.cpp"
|
||||
"ItemComponent.cpp"
|
||||
"LevelProgressionComponent.cpp"
|
||||
"LUPExhibitComponent.cpp"
|
||||
"MissionComponent.cpp"
|
||||
@@ -18,27 +23,35 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
|
||||
"MovingPlatformComponent.cpp"
|
||||
"PetComponent.cpp"
|
||||
"PhantomPhysicsComponent.cpp"
|
||||
"PhysicsComponent.cpp"
|
||||
"PlayerForcedMovementComponent.cpp"
|
||||
"PossessableComponent.cpp"
|
||||
"PossessorComponent.cpp"
|
||||
"PropertyComponent.cpp"
|
||||
"PropertyEntranceComponent.cpp"
|
||||
"PropertyManagementComponent.cpp"
|
||||
"PropertyVendorComponent.cpp"
|
||||
"ProximityMonitorComponent.cpp"
|
||||
"RacingControlComponent.cpp"
|
||||
"RailActivatorComponent.cpp"
|
||||
"RebuildComponent.cpp"
|
||||
"QuickBuildComponent.cpp"
|
||||
"RenderComponent.cpp"
|
||||
"RigidbodyPhantomPhysicsComponent.cpp"
|
||||
"RocketLaunchLupComponent.cpp"
|
||||
"MultiZoneEntranceComponent.cpp"
|
||||
"RocketLaunchpadControlComponent.cpp"
|
||||
"ScriptedActivityComponent.cpp"
|
||||
"ShootingGalleryComponent.cpp"
|
||||
"SimplePhysicsComponent.cpp"
|
||||
"SkillComponent.cpp"
|
||||
"SoundTriggerComponent.cpp"
|
||||
"SwitchComponent.cpp"
|
||||
"TriggerComponent.cpp"
|
||||
"VehiclePhysicsComponent.cpp"
|
||||
"VendorComponent.cpp" PARENT_SCOPE)
|
||||
"HavokVehiclePhysicsComponent.cpp"
|
||||
"VendorComponent.cpp"
|
||||
"MiniGameControlComponent.cpp"
|
||||
)
|
||||
|
||||
add_library(dComponents STATIC ${DGAME_DCOMPONENTS_SOURCES})
|
||||
target_include_directories(dComponents PRIVATE ${PROJECT_SOURCE_DIR}/dScripts/02_server/Map/General) # PetDigServer.h
|
||||
target_precompile_headers(dComponents REUSE_FROM dGameBase)
|
||||
target_link_libraries(dComponents
|
||||
PUBLIC dPhysics dDatabase
|
||||
INTERFACE dUtilities dCommon dBehaviors dChatFilter dMission dInventory)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "CharacterComponent.h"
|
||||
#include <BitStream.h>
|
||||
#include "BitStream.h"
|
||||
#include "tinyxml2.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dServer.h"
|
||||
#include "dZoneManager.h"
|
||||
@@ -10,14 +10,21 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "VehiclePhysicsComponent.h"
|
||||
#include "HavokVehiclePhysicsComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Item.h"
|
||||
#include "Amf3.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "eGameActivity.h"
|
||||
#include "User.h"
|
||||
#include "Database.h"
|
||||
#include "CDRewardCodesTable.h"
|
||||
#include "Mail.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "WorldPackets.h"
|
||||
#include <ctime>
|
||||
|
||||
CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) {
|
||||
CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) {
|
||||
m_Character = character;
|
||||
|
||||
m_IsRacing = false;
|
||||
@@ -39,6 +46,7 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character) : C
|
||||
m_CurrentActivity = eGameActivity::NONE;
|
||||
m_CountryCode = 0;
|
||||
m_LastUpdateTimestamp = std::time(nullptr);
|
||||
m_SystemAddress = systemAddress;
|
||||
}
|
||||
|
||||
bool CharacterComponent::LandingAnimDisabled(int zoneID) {
|
||||
@@ -73,10 +81,14 @@ CharacterComponent::~CharacterComponent() {
|
||||
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write(m_ClaimCodes[0] != 0);
|
||||
if (m_ClaimCodes[0] != 0) outBitStream->Write(m_ClaimCodes[0]);
|
||||
outBitStream->Write(m_ClaimCodes[1] != 0);
|
||||
if (m_ClaimCodes[1] != 0) outBitStream->Write(m_ClaimCodes[1]);
|
||||
outBitStream->Write(m_ClaimCodes[2] != 0);
|
||||
if (m_ClaimCodes[2] != 0) outBitStream->Write(m_ClaimCodes[2]);
|
||||
outBitStream->Write(m_ClaimCodes[3] != 0);
|
||||
if (m_ClaimCodes[3] != 0) outBitStream->Write(m_ClaimCodes[3]);
|
||||
|
||||
outBitStream->Write(m_Character->GetHairColor());
|
||||
outBitStream->Write(m_Character->GetHairStyle());
|
||||
@@ -126,7 +138,7 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write(m_IsLanding);
|
||||
if (m_IsLanding) {
|
||||
outBitStream->Write(uint16_t(m_LastRocketConfig.size()));
|
||||
outBitStream->Write<uint16_t>(m_LastRocketConfig.size());
|
||||
for (uint16_t character : m_LastRocketConfig) {
|
||||
outBitStream->Write(character);
|
||||
}
|
||||
@@ -148,7 +160,7 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
|
||||
outBitStream->Write(m_DirtySocialInfo);
|
||||
if (m_DirtySocialInfo) {
|
||||
outBitStream->Write(m_GuildID);
|
||||
outBitStream->Write<unsigned char>(static_cast<unsigned char>(m_GuildName.size()));
|
||||
outBitStream->Write<unsigned char>(m_GuildName.size());
|
||||
if (!m_GuildName.empty())
|
||||
outBitStream->WriteBits(reinterpret_cast<const unsigned char*>(m_GuildName.c_str()), static_cast<unsigned char>(m_GuildName.size()) * sizeof(wchar_t) * 8);
|
||||
|
||||
@@ -178,13 +190,20 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!");
|
||||
LOG("Failed to find char tag while loading XML!");
|
||||
return;
|
||||
}
|
||||
if (character->QueryAttribute("rpt", &m_Reputation) == tinyxml2::XML_NO_ATTRIBUTE) {
|
||||
SetReputation(0);
|
||||
}
|
||||
|
||||
character->QueryUnsigned64Attribute("co", &m_ClaimCodes[0]);
|
||||
character->QueryUnsigned64Attribute("co1", &m_ClaimCodes[1]);
|
||||
character->QueryUnsigned64Attribute("co2", &m_ClaimCodes[2]);
|
||||
character->QueryUnsigned64Attribute("co3", &m_ClaimCodes[3]);
|
||||
|
||||
AwardClaimCodes();
|
||||
|
||||
character->QueryInt64Attribute("ls", &m_Uscore);
|
||||
|
||||
// Load the statistics
|
||||
@@ -213,7 +232,7 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
uint32_t mapID;
|
||||
child->QueryAttribute("map", &mapID);
|
||||
|
||||
m_ZoneStatistics.insert({ (LWOMAPID)mapID, statistics });
|
||||
m_ZoneStatistics.insert({ static_cast<LWOMAPID>(mapID), statistics });
|
||||
|
||||
child = child->NextSiblingElement();
|
||||
}
|
||||
@@ -283,7 +302,7 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* minifig = doc->FirstChildElement("obj")->FirstChildElement("mf");
|
||||
if (!minifig) {
|
||||
Game::logger->Log("CharacterComponent", "Failed to find mf tag while updating XML!");
|
||||
LOG("Failed to find mf tag while updating XML!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -303,10 +322,15 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("CharacterComponent", "Failed to find char tag while updating XML!");
|
||||
LOG("Failed to find char tag while updating XML!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ClaimCodes[0] != 0) character->SetAttribute("co", m_ClaimCodes[0]);
|
||||
if (m_ClaimCodes[1] != 0) character->SetAttribute("co1", m_ClaimCodes[1]);
|
||||
if (m_ClaimCodes[2] != 0) character->SetAttribute("co2", m_ClaimCodes[2]);
|
||||
if (m_ClaimCodes[3] != 0) character->SetAttribute("co3", m_ClaimCodes[3]);
|
||||
|
||||
character->SetAttribute("ls", m_Uscore);
|
||||
// Custom attribute to keep track of reputation.
|
||||
character->SetAttribute("rpt", GetReputation());
|
||||
@@ -351,7 +375,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
//
|
||||
|
||||
auto newUpdateTimestamp = std::time(nullptr);
|
||||
Game::logger->Log("TotalTimePlayed", "Time since last save: %d", newUpdateTimestamp - m_LastUpdateTimestamp);
|
||||
LOG("Time since last save: %d", newUpdateTimestamp - m_LastUpdateTimestamp);
|
||||
|
||||
m_TotalTimePlayed += newUpdateTimestamp - m_LastUpdateTimestamp;
|
||||
character->SetAttribute("time", m_TotalTimePlayed);
|
||||
@@ -381,7 +405,7 @@ Item* CharacterComponent::GetRocket(Entity* player) {
|
||||
}
|
||||
|
||||
if (!rocket) {
|
||||
Game::logger->Log("CharacterComponent", "Unable to find rocket to equip!");
|
||||
LOG("Unable to find rocket to equip!");
|
||||
return rocket;
|
||||
}
|
||||
return rocket;
|
||||
@@ -477,7 +501,7 @@ void CharacterComponent::TrackArmorDelta(int32_t armor) {
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackRebuildComplete() {
|
||||
void CharacterComponent::TrackQuickBuildComplete() {
|
||||
UpdatePlayerStatistic(QuickBuildsCompleted);
|
||||
|
||||
const auto mapID = Game::zoneManager->GetZoneID().GetMapID();
|
||||
@@ -494,9 +518,9 @@ void CharacterComponent::TrackPositionUpdate(const NiPoint3& newPosition) {
|
||||
const auto distance = NiPoint3::Distance(newPosition, m_Parent->GetPosition());
|
||||
|
||||
if (m_IsRacing) {
|
||||
UpdatePlayerStatistic(DistanceDriven, (uint64_t)distance);
|
||||
UpdatePlayerStatistic(DistanceDriven, static_cast<uint64_t>(distance));
|
||||
} else {
|
||||
UpdatePlayerStatistic(MetersTraveled, (uint64_t)distance);
|
||||
UpdatePlayerStatistic(MetersTraveled, static_cast<uint64_t>(distance));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,7 +723,7 @@ std::string CharacterComponent::StatisticsToString() const {
|
||||
}
|
||||
|
||||
uint64_t CharacterComponent::GetStatisticFromSplit(std::vector<std::string> split, uint32_t index) {
|
||||
return split.size() > index ? std::stoul(split.at(index)) : 0;
|
||||
return split.size() > index ? std::stoull(split.at(index)) : 0;
|
||||
}
|
||||
|
||||
ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) {
|
||||
@@ -737,3 +761,77 @@ void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventu
|
||||
arrayToSend.Insert(ventureVisionType, showFaction);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", arrayToSend);
|
||||
}
|
||||
|
||||
void CharacterComponent::AwardClaimCodes() {
|
||||
if (!m_Parent || !m_Parent->GetCharacter()) return;
|
||||
auto* user = m_Parent->GetCharacter()->GetParentUser();
|
||||
if (!user) return;
|
||||
|
||||
auto rewardCodes = Database::Get()->GetRewardCodesByAccountID(user->GetAccountID());
|
||||
if (rewardCodes.empty()) return;
|
||||
|
||||
auto* cdrewardCodes = CDClientManager::GetTable<CDRewardCodesTable>();
|
||||
for (auto const rewardCode : rewardCodes) {
|
||||
LOG_DEBUG("Processing RewardCode %i", rewardCode);
|
||||
const uint32_t rewardCodeIndex = rewardCode >> 6;
|
||||
const uint32_t bitIndex = rewardCode % 64;
|
||||
if (GeneralUtils::CheckBit(m_ClaimCodes[rewardCodeIndex], bitIndex)) continue;
|
||||
m_ClaimCodes[rewardCodeIndex] = GeneralUtils::SetBit(m_ClaimCodes[rewardCodeIndex], bitIndex);
|
||||
|
||||
// Don't send it on this one since it's default and the mail doesn't make sense
|
||||
if (rewardCode == 30) continue;
|
||||
|
||||
auto attachmentLOT = cdrewardCodes->GetAttachmentLOT(rewardCode);
|
||||
std::ostringstream subject;
|
||||
subject << "%[RewardCodes_" << rewardCode << "_subjectText]";
|
||||
std::ostringstream body;
|
||||
body << "%[RewardCodes_" << rewardCode << "_bodyText]";
|
||||
Mail::SendMail(LWOOBJID_EMPTY, "%[MAIL_SYSTEM_NOTIFICATION]", m_Parent, subject.str(), body.str(), attachmentLOT, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterComponent::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) const {
|
||||
const auto objid = m_Parent->GetObjectID();
|
||||
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneId, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
auto* entity = Game::entityManager->GetEntity(objid);
|
||||
|
||||
if (!entity) return;
|
||||
|
||||
const auto sysAddr = entity->GetSystemAddress();
|
||||
|
||||
auto* character = entity->GetCharacter();
|
||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||
|
||||
if (character && characterComponent) {
|
||||
character->SetZoneID(zoneID);
|
||||
character->SetZoneInstance(zoneInstance);
|
||||
character->SetZoneClone(zoneClone);
|
||||
|
||||
characterComponent->SetLastRocketConfig(u"");
|
||||
|
||||
character->SaveXMLToDatabase();
|
||||
}
|
||||
|
||||
WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift);
|
||||
|
||||
Game::entityManager->DestructEntity(entity);
|
||||
});
|
||||
}
|
||||
|
||||
const SystemAddress& CharacterComponent::GetSystemAddress() const {
|
||||
return m_SystemAddress;
|
||||
}
|
||||
|
||||
void CharacterComponent::SetRespawnPos(const NiPoint3& position) {
|
||||
if (!m_Character) return;
|
||||
|
||||
m_respawnPos = position;
|
||||
|
||||
m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position);
|
||||
|
||||
}
|
||||
|
||||
void CharacterComponent::SetRespawnRot(const NiQuaternion& rotation) {
|
||||
m_respawnRot = rotation;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "CDMissionsTable.h"
|
||||
#include "tinyxml2.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include <array>
|
||||
#include "Loot.h"
|
||||
|
||||
enum class eGameActivity : uint32_t;
|
||||
|
||||
@@ -60,11 +62,11 @@ enum StatisticID {
|
||||
/**
|
||||
* Represents a character, including their rockets and stats
|
||||
*/
|
||||
class CharacterComponent : public Component {
|
||||
class CharacterComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
|
||||
|
||||
CharacterComponent(Entity* parent, Character* character);
|
||||
CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress);
|
||||
~CharacterComponent() override;
|
||||
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
@@ -232,7 +234,7 @@ public:
|
||||
/**
|
||||
* Handles completing a rebuild by updating the statistics
|
||||
*/
|
||||
void TrackRebuildComplete();
|
||||
void TrackQuickBuildComplete();
|
||||
|
||||
/**
|
||||
* Tracks a player completing the race, also updates stats
|
||||
@@ -280,6 +282,30 @@ public:
|
||||
|
||||
LWOOBJID GetCurrentInteracting() {return m_CurrentInteracting;};
|
||||
|
||||
/**
|
||||
* Sends a player to another zone with an optional clone ID
|
||||
*
|
||||
* @param zoneId zoneID for the new instance.
|
||||
* @param cloneId cloneID for the new instance.
|
||||
*/
|
||||
void SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId = 0) const;
|
||||
|
||||
const SystemAddress& GetSystemAddress() const;
|
||||
|
||||
const NiPoint3& GetRespawnPosition() const { return m_respawnPos; };
|
||||
|
||||
void SetRespawnPos(const NiPoint3& position);
|
||||
|
||||
const NiQuaternion& GetRespawnRotation() const { return m_respawnRot; };
|
||||
|
||||
void SetRespawnRot(const NiQuaternion& rotation);
|
||||
|
||||
std::map<LWOOBJID, Loot::Info>& GetDroppedLoot() { return m_DroppedLoot; };
|
||||
|
||||
uint64_t GetDroppedCoins() const { return m_DroppedCoins; };
|
||||
|
||||
void SetDroppedCoins(const uint64_t value) { m_DroppedCoins = value; };
|
||||
|
||||
/**
|
||||
* Character info regarding this character, including clothing styles, etc.
|
||||
*/
|
||||
@@ -566,6 +592,20 @@ private:
|
||||
LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY;
|
||||
|
||||
LWOOBJID m_CurrentInteracting = LWOOBJID_EMPTY;
|
||||
|
||||
std::array<uint64_t, 4> m_ClaimCodes{};
|
||||
|
||||
void AwardClaimCodes();
|
||||
|
||||
SystemAddress m_SystemAddress;
|
||||
|
||||
NiPoint3 m_respawnPos;
|
||||
|
||||
NiQuaternion m_respawnRot;
|
||||
|
||||
std::map<LWOOBJID, Loot::Info> m_DroppedLoot;
|
||||
|
||||
uint64_t m_DroppedCoins = 0;
|
||||
};
|
||||
|
||||
#endif // CHARACTERCOMPONENT_H
|
||||
|
||||
5
dGame/dComponents/CollectibleComponent.cpp
Normal file
5
dGame/dComponents/CollectibleComponent.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "CollectibleComponent.h"
|
||||
|
||||
void CollectibleComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
|
||||
outBitStream->Write(GetCollectibleId());
|
||||
}
|
||||
18
dGame/dComponents/CollectibleComponent.h
Normal file
18
dGame/dComponents/CollectibleComponent.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef __COLLECTIBLECOMPONENT__H__
|
||||
#define __COLLECTIBLECOMPONENT__H__
|
||||
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class CollectibleComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
|
||||
CollectibleComponent(Entity* parentEntity, int32_t collectibleId) : Component(parentEntity), m_CollectibleId(collectibleId) {}
|
||||
|
||||
int16_t GetCollectibleId() const { return m_CollectibleId; }
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool isConstruction) override;
|
||||
private:
|
||||
int16_t m_CollectibleId = 0;
|
||||
};
|
||||
|
||||
#endif //!__COLLECTIBLECOMPONENT__H__
|
||||
@@ -1,14 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../thirdparty/tinyxml2/tinyxml2.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
/**
|
||||
* Component base class, provides methods for game loop updates, usage events and loading and saving to XML.
|
||||
*/
|
||||
class Component
|
||||
{
|
||||
class Component {
|
||||
public:
|
||||
Component(Entity* parent);
|
||||
virtual ~Component();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "Entity.h"
|
||||
#include "BitStream.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "Game.h"
|
||||
|
||||
#include "dpWorld.h"
|
||||
@@ -15,15 +15,14 @@
|
||||
#include "LevelProgressionComponent.h"
|
||||
#include "eStateChangeType.h"
|
||||
|
||||
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Component(entity) {
|
||||
m_Position = {};
|
||||
m_Rotation = NiQuaternion::IDENTITY;
|
||||
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : PhysicsComponent(entity) {
|
||||
m_Velocity = {};
|
||||
m_AngularVelocity = {};
|
||||
m_InJetpackMode = false;
|
||||
m_IsOnGround = true;
|
||||
m_IsOnRail = false;
|
||||
m_DirtyPosition = true;
|
||||
m_DirtyVelocity = true;
|
||||
m_DirtyAngularVelocity = true;
|
||||
m_dpEntity = nullptr;
|
||||
m_Static = false;
|
||||
m_SpeedMultiplier = 1;
|
||||
@@ -53,18 +52,18 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
|
||||
return;
|
||||
|
||||
if (entity->GetLOT() == 1) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
|
||||
LOG("Using patch to load minifig physics");
|
||||
|
||||
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);
|
||||
dpWorld::AddEntity(m_dpEntity);
|
||||
}
|
||||
}
|
||||
|
||||
ControllablePhysicsComponent::~ControllablePhysicsComponent() {
|
||||
if (m_dpEntity) {
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
dpWorld::RemoveEntity(m_dpEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +160,7 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!");
|
||||
LOG("Failed to find char tag!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -181,13 +180,13 @@ void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag while updating XML!");
|
||||
LOG("Failed to find char tag while updating XML!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
||||
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
|
||||
character->SetAttribute("lzx", m_Position.x);
|
||||
character->SetAttribute("lzy", m_Position.y);
|
||||
character->SetAttribute("lzz", m_Position.z);
|
||||
@@ -199,24 +198,14 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
|
||||
if (m_Static || m_Position == pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Position = pos;
|
||||
m_DirtyPosition = true;
|
||||
|
||||
if (m_Static) return;
|
||||
PhysicsComponent::SetPosition(pos);
|
||||
if (m_dpEntity) m_dpEntity->SetPosition(pos);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
|
||||
if (m_Static || m_Rotation == rot) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Rotation = rot;
|
||||
m_DirtyPosition = true;
|
||||
|
||||
if (m_Static) return;
|
||||
PhysicsComponent::SetRotation(rot);
|
||||
if (m_dpEntity) m_dpEntity->SetRotation(rot);
|
||||
}
|
||||
|
||||
@@ -270,7 +259,7 @@ void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
|
||||
if (pos != m_ActivePickupRadiusScales.end()) {
|
||||
m_ActivePickupRadiusScales.erase(pos);
|
||||
} else {
|
||||
Game::logger->LogDebug("ControllablePhysicsComponent", "Warning: Could not find pickup radius %f in list of active radii. List has %i active radii.", value, m_ActivePickupRadiusScales.size());
|
||||
LOG_DEBUG("Warning: Could not find pickup radius %f in list of active radii. List has %i active radii.", value, m_ActivePickupRadiusScales.size());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -295,7 +284,7 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
|
||||
if (pos != m_ActiveSpeedBoosts.end()) {
|
||||
m_ActiveSpeedBoosts.erase(pos);
|
||||
} else {
|
||||
Game::logger->LogDebug("ControllablePhysicsComponent", "Warning: Could not find speedboost %f in list of active speedboosts. List has %i active speedboosts.", value, m_ActiveSpeedBoosts.size());
|
||||
LOG_DEBUG("Warning: Could not find speedboost %f in list of active speedboosts. List has %i active speedboosts.", value, m_ActiveSpeedBoosts.size());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -313,7 +302,7 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
|
||||
|
||||
void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims) {
|
||||
if (m_IsInBubble) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Already in bubble");
|
||||
LOG("Already in bubble");
|
||||
return;
|
||||
}
|
||||
m_BubbleType = bubbleType;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
#include "tinyxml2.h"
|
||||
#include "Component.h"
|
||||
#include "PhysicsComponent.h"
|
||||
#include "dpCollisionChecks.h"
|
||||
#include "PhantomPhysicsComponent.h"
|
||||
#include "eBubbleType.h"
|
||||
@@ -19,9 +19,9 @@ enum class eStateChangeType : uint32_t;
|
||||
/**
|
||||
* Handles the movement of controllable Entities, e.g. enemies and players
|
||||
*/
|
||||
class ControllablePhysicsComponent : public Component {
|
||||
class ControllablePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
|
||||
ControllablePhysicsComponent(Entity* entity);
|
||||
~ControllablePhysicsComponent() override;
|
||||
@@ -36,26 +36,14 @@ public:
|
||||
* If the entity is static, this is a no-op.
|
||||
* @param pos The position to set
|
||||
*/
|
||||
void SetPosition(const NiPoint3& pos);
|
||||
|
||||
/**
|
||||
* Returns the current position of the entity
|
||||
* @return The current position of the entity
|
||||
*/
|
||||
const NiPoint3& GetPosition() const { return m_Position; }
|
||||
void SetPosition(const NiPoint3& pos) override;
|
||||
|
||||
/**
|
||||
* Sets the rotation of this entity, ensures this change is serialized next tick. If the entity is static, this is
|
||||
* a no-op.
|
||||
* @param rot the rotation to set
|
||||
*/
|
||||
void SetRotation(const NiQuaternion& rot);
|
||||
|
||||
/**
|
||||
* Returns the current rotation of this entity
|
||||
* @return the current rotation of this entity
|
||||
*/
|
||||
const NiQuaternion& GetRotation() const { return m_Rotation; }
|
||||
void SetRotation(const NiQuaternion& rot) override;
|
||||
|
||||
/**
|
||||
* Sets the current velocity of this entity, ensures that this change is serialized next tick. If the entity is
|
||||
@@ -311,19 +299,9 @@ private:
|
||||
dpEntity* m_dpEntity;
|
||||
|
||||
/**
|
||||
* Whether or not the position is dirty, forcing a serialization update of the position
|
||||
* Whether or not the velocity is dirty, forcing a serialization of the velocity
|
||||
*/
|
||||
bool m_DirtyPosition;
|
||||
|
||||
/**
|
||||
* The current position of the entity
|
||||
*/
|
||||
NiPoint3 m_Position;
|
||||
|
||||
/**
|
||||
* The current rotation of the entity
|
||||
*/
|
||||
NiQuaternion m_Rotation;
|
||||
bool m_DirtyVelocity;
|
||||
|
||||
/**
|
||||
* The current velocity of the entity
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include <BitStream.h>
|
||||
#include "dLogger.h"
|
||||
#include "BitStream.h"
|
||||
#include "Logger.h"
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "CDDestructibleComponentTable.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "CppScripts.h"
|
||||
#include "Loot.h"
|
||||
#include "Character.h"
|
||||
@@ -72,24 +72,27 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_ImmuneToImaginationLossCount = 0;
|
||||
m_ImmuneToQuickbuildInterruptCount = 0;
|
||||
m_ImmuneToPullToPointCount = 0;
|
||||
m_DeathBehavior = -1;
|
||||
|
||||
m_DamageCooldownTimer = 0.0f;
|
||||
}
|
||||
|
||||
DestroyableComponent::~DestroyableComponent() {
|
||||
}
|
||||
|
||||
void DestroyableComponent::Reinitialize(LOT templateID) {
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::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 quickBuildComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::QUICK_BUILD);
|
||||
|
||||
int32_t componentID = 0;
|
||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
||||
if (rebuildComponentID > 0) componentID = rebuildComponentID;
|
||||
if (quickBuildComponentID > 0) componentID = quickBuildComponentID;
|
||||
if (buffComponentID > 0) componentID = buffComponentID;
|
||||
|
||||
CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
|
||||
CDDestructibleComponentTable* destCompTable = CDClientManager::GetTable<CDDestructibleComponentTable>();
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
if (componentID > 0) {
|
||||
@@ -151,7 +154,7 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
||||
outBitStream->Write(m_fMaxArmor);
|
||||
outBitStream->Write(m_fMaxImagination);
|
||||
|
||||
outBitStream->Write(uint32_t(m_FactionIDs.size()));
|
||||
outBitStream->Write<uint32_t>(m_FactionIDs.size());
|
||||
for (size_t i = 0; i < m_FactionIDs.size(); ++i) {
|
||||
outBitStream->Write(m_FactionIDs[i]);
|
||||
}
|
||||
@@ -178,10 +181,14 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyableComponent::Update(float deltaTime) {
|
||||
m_DamageCooldownTimer -= deltaTime;
|
||||
}
|
||||
|
||||
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
||||
if (!dest) {
|
||||
Game::logger->Log("DestroyableComponent", "Failed to find dest tag!");
|
||||
LOG("Failed to find dest tag!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -203,7 +210,7 @@ void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
||||
if (!dest) {
|
||||
Game::logger->Log("DestroyableComponent", "Failed to find dest tag!");
|
||||
LOG("Failed to find dest tag!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -244,13 +251,14 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(difference));
|
||||
args.Insert("type", "health");
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
@@ -285,13 +293,14 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(value));
|
||||
args.Insert("type", "armor");
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
@@ -325,13 +334,14 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(difference));
|
||||
args.Insert("type", "imagination");
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
}
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
@@ -363,16 +373,17 @@ void DestroyableComponent::SetIsShielded(bool value) {
|
||||
|
||||
void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignoreChecks) {
|
||||
// Ignore factionID -1
|
||||
if (factionID == -1 && !ignoreChecks) {
|
||||
return;
|
||||
}
|
||||
if (factionID == -1 && !ignoreChecks) return;
|
||||
|
||||
// if we already have that faction, don't add it again
|
||||
if (std::find(m_FactionIDs.begin(), m_FactionIDs.end(), factionID) != m_FactionIDs.end()) return;
|
||||
|
||||
m_FactionIDs.push_back(factionID);
|
||||
m_DirtyHealth = true;
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT enemyList FROM Factions WHERE faction = ?;");
|
||||
query.bind(1, (int)factionID);
|
||||
query.bind(1, static_cast<int>(factionID));
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
@@ -407,6 +418,14 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
|
||||
}
|
||||
|
||||
bool DestroyableComponent::IsEnemy(const Entity* other) const {
|
||||
if (m_Parent->IsPlayer() && other->IsPlayer()) {
|
||||
auto* thisCharacterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (!thisCharacterComponent) return false;
|
||||
auto* otherCharacterComponent = other->GetComponent<CharacterComponent>();
|
||||
if (!otherCharacterComponent) return false;
|
||||
if (thisCharacterComponent->GetPvpEnabled() && otherCharacterComponent->GetPvpEnabled()) return true;
|
||||
return false;
|
||||
}
|
||||
const auto* otherDestroyableComponent = other->GetComponent<DestroyableComponent>();
|
||||
if (otherDestroyableComponent != nullptr) {
|
||||
for (const auto enemyFaction : m_EnemyFactionIDs) {
|
||||
@@ -454,6 +473,10 @@ bool DestroyableComponent::IsImmune() const {
|
||||
return m_IsGMImmune || m_ImmuneToBasicAttackCount > 0;
|
||||
}
|
||||
|
||||
bool DestroyableComponent::IsCooldownImmune() const {
|
||||
return m_DamageCooldownTimer > 0.0f;
|
||||
}
|
||||
|
||||
bool DestroyableComponent::IsKnockbackImmune() const {
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
@@ -485,43 +508,6 @@ Entity* DestroyableComponent::GetKiller() const {
|
||||
return Game::entityManager->GetEntity(m_KillerID);
|
||||
}
|
||||
|
||||
bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignoreFactions, const bool targetEnemy, const bool targetFriend) const {
|
||||
auto* targetEntity = Game::entityManager->GetEntity(target);
|
||||
|
||||
if (targetEntity == nullptr) {
|
||||
Game::logger->Log("DestroyableComponent", "Invalid entity for checking validity (%llu)!", target);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* targetDestroyable = targetEntity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (targetDestroyable == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* targetQuickbuild = targetEntity->GetComponent<RebuildComponent>();
|
||||
|
||||
if (targetQuickbuild != nullptr) {
|
||||
const auto state = targetQuickbuild->GetState();
|
||||
|
||||
if (state != eRebuildState::COMPLETED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreFactions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get if the target entity is an enemy and friend
|
||||
bool isEnemy = IsEnemy(targetEntity);
|
||||
bool isFriend = IsFriend(targetEntity);
|
||||
|
||||
// Return true if the target type matches what we are targeting
|
||||
return (isEnemy && targetEnemy) || (isFriend && targetFriend);
|
||||
}
|
||||
|
||||
|
||||
void DestroyableComponent::Heal(const uint32_t health) {
|
||||
auto current = static_cast<uint32_t>(GetHealth());
|
||||
const auto max = static_cast<uint32_t>(GetMaxHealth());
|
||||
@@ -573,7 +559,8 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsImmune()) {
|
||||
if (IsImmune() || IsCooldownImmune()) {
|
||||
LOG_DEBUG("Target targetEntity %llu is immune!", m_Parent->GetObjectID());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -661,28 +648,28 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
}
|
||||
|
||||
//check if hardcore mode is enabled
|
||||
if (Game::entityManager->GetHardcoreMode()) {
|
||||
if (Game::entityManager->GetHardcoreMode()) {
|
||||
DoHardcoreModeDrops(source);
|
||||
}
|
||||
}
|
||||
|
||||
Smash(source, eKillType::VIOLENT, u"", skillID);
|
||||
}
|
||||
|
||||
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());
|
||||
LOG_DEBUG("Added script %llu to entity %llu", scriptObjId, m_Parent->GetObjectID());
|
||||
LOG_DEBUG("Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
}
|
||||
|
||||
void DestroyableComponent::Unsubscribe(LWOOBJID scriptObjId) {
|
||||
auto foundScript = m_SubscribedScripts.find(scriptObjId);
|
||||
if (foundScript != m_SubscribedScripts.end()) {
|
||||
m_SubscribedScripts.erase(foundScript);
|
||||
Game::logger->LogDebug("DestroyableComponent", "Removed script %llu from entity %llu", scriptObjId, m_Parent->GetObjectID());
|
||||
LOG_DEBUG("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);
|
||||
LOG_DEBUG("Tried to remove a script for Entity %llu but script %llu didnt exist", m_Parent->GetObjectID(), scriptObjId);
|
||||
}
|
||||
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
LOG_DEBUG("Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
}
|
||||
|
||||
void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage) {
|
||||
@@ -763,18 +750,18 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
auto* member = Game::entityManager->GetEntity(specificOwner);
|
||||
|
||||
if (member) LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
if (member) Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
} else {
|
||||
for (const auto memberId : team->members) { // Free for all
|
||||
auto* member = Game::entityManager->GetEntity(memberId);
|
||||
|
||||
if (member == nullptr) continue;
|
||||
|
||||
LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else { // drop loot for non team user
|
||||
LootGenerator::Instance().DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
Loot::DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -792,7 +779,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
coinsTotal -= coinsToLose;
|
||||
|
||||
LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
|
||||
Loot::DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
|
||||
character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
|
||||
}
|
||||
}
|
||||
@@ -812,7 +799,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
}
|
||||
}
|
||||
|
||||
m_Parent->Kill(owner);
|
||||
m_Parent->Kill(owner, killType);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
|
||||
@@ -823,16 +810,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;
|
||||
@@ -845,7 +832,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;
|
||||
@@ -972,7 +959,7 @@ 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_Parent->IsPlayer()) {
|
||||
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
||||
@@ -990,9 +977,9 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
|
||||
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;
|
||||
|
||||
@@ -17,13 +17,14 @@ 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 : public Component {
|
||||
class DestroyableComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
|
||||
|
||||
DestroyableComponent(Entity* parentEntity);
|
||||
~DestroyableComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
@@ -166,6 +167,11 @@ public:
|
||||
*/
|
||||
bool IsImmune() const;
|
||||
|
||||
/**
|
||||
* @return whether this entity is currently immune to attacks due to a damage cooldown period
|
||||
*/
|
||||
bool IsCooldownImmune() const;
|
||||
|
||||
/**
|
||||
* Sets if this entity has GM immunity, making it not killable
|
||||
* @param value the GM immunity of this entity
|
||||
@@ -372,14 +378,6 @@ public:
|
||||
*/
|
||||
Entity* GetKiller() const;
|
||||
|
||||
/**
|
||||
* Checks if the target ID is a valid enemy of this entity
|
||||
* @param target the target ID to check for
|
||||
* @param ignoreFactions whether or not check for the factions, e.g. just return true if the entity cannot be smashed
|
||||
* @return if the target ID is a valid enemy
|
||||
*/
|
||||
bool CheckValidity(LWOOBJID target, bool ignoreFactions = false, bool targetEnemy = true, bool targetFriend = false) const;
|
||||
|
||||
/**
|
||||
* Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks.
|
||||
* @param damage the damage to attempt to apply
|
||||
@@ -415,15 +413,23 @@ public:
|
||||
);
|
||||
|
||||
// Getters for status immunities
|
||||
const bool GetImmuneToBasicAttack() {return m_ImmuneToBasicAttackCount > 0;};
|
||||
const bool GetImmuneToDamageOverTime() {return m_ImmuneToDamageOverTimeCount > 0;};
|
||||
const bool GetImmuneToKnockback() {return m_ImmuneToKnockbackCount > 0;};
|
||||
const bool GetImmuneToInterrupt() {return m_ImmuneToInterruptCount > 0;};
|
||||
const bool GetImmuneToSpeed() {return m_ImmuneToSpeedCount > 0;};
|
||||
const bool GetImmuneToImaginationGain() {return m_ImmuneToImaginationGainCount > 0;};
|
||||
const bool GetImmuneToImaginationLoss() {return m_ImmuneToImaginationLossCount > 0;};
|
||||
const bool GetImmuneToQuickbuildInterrupt() {return m_ImmuneToQuickbuildInterruptCount > 0;};
|
||||
const bool GetImmuneToPullToPoint() {return m_ImmuneToPullToPointCount > 0;};
|
||||
const bool GetImmuneToBasicAttack() { return m_ImmuneToBasicAttackCount > 0; };
|
||||
const bool GetImmuneToDamageOverTime() { return m_ImmuneToDamageOverTimeCount > 0; };
|
||||
const bool GetImmuneToKnockback() { return m_ImmuneToKnockbackCount > 0; };
|
||||
const bool GetImmuneToInterrupt() { return m_ImmuneToInterruptCount > 0; };
|
||||
const bool GetImmuneToSpeed() { return m_ImmuneToSpeedCount > 0; };
|
||||
const bool GetImmuneToImaginationGain() { return m_ImmuneToImaginationGainCount > 0; };
|
||||
const bool GetImmuneToImaginationLoss() { return m_ImmuneToImaginationLossCount > 0; };
|
||||
const bool GetImmuneToQuickbuildInterrupt() { return m_ImmuneToQuickbuildInterruptCount > 0; };
|
||||
const bool GetImmuneToPullToPoint() { return m_ImmuneToPullToPointCount > 0; };
|
||||
|
||||
// Damage cooldown setters/getters
|
||||
void SetDamageCooldownTimer(float value) { m_DamageCooldownTimer = value; }
|
||||
float GetDamageCooldownTimer() { return m_DamageCooldownTimer; }
|
||||
|
||||
// Death behavior setters/getters
|
||||
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
|
||||
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
|
||||
|
||||
/**
|
||||
* Utility to reset all stats to the default stats based on items and completed missions
|
||||
@@ -606,6 +612,16 @@ private:
|
||||
uint32_t m_ImmuneToImaginationLossCount;
|
||||
uint32_t m_ImmuneToQuickbuildInterruptCount;
|
||||
uint32_t m_ImmuneToPullToPointCount;
|
||||
|
||||
/**
|
||||
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
|
||||
*/
|
||||
int32_t m_DeathBehavior;
|
||||
|
||||
/**
|
||||
* Damage immunity cooldown timer. Set to a value that then counts down to create a damage cooldown for players
|
||||
*/
|
||||
float m_DamageCooldownTimer;
|
||||
};
|
||||
|
||||
#endif // DESTROYABLECOMPONENT_H
|
||||
|
||||
@@ -22,10 +22,8 @@ DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorCompone
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;"));
|
||||
query->setInt(1, m_ActivityId);
|
||||
std::unique_ptr<sql::ResultSet> donation_total(query->executeQuery());
|
||||
if (donation_total->next()) m_TotalDonated = donation_total->getInt("donation_total");
|
||||
auto donationTotal = Database::Get()->GetDonationTotal(m_ActivityId);
|
||||
if (donationTotal) m_TotalDonated = donationTotal.value();
|
||||
m_TotalRemaining = m_Goal - m_TotalDonated;
|
||||
m_PercentComplete = m_TotalDonated/static_cast<float>(m_Goal);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ class Entity;
|
||||
|
||||
class DonationVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
|
||||
DonationVendorComponent(Entity* parent);
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
||||
uint32_t GetActivityID() {return m_ActivityId;};
|
||||
|
||||
57
dGame/dComponents/GhostComponent.cpp
Normal file
57
dGame/dComponents/GhostComponent.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "GhostComponent.h"
|
||||
|
||||
GhostComponent::GhostComponent(Entity* parent) : Component(parent) {
|
||||
m_GhostReferencePoint = NiPoint3Constant::ZERO;
|
||||
m_GhostOverridePoint = NiPoint3Constant::ZERO;
|
||||
m_GhostOverride = false;
|
||||
}
|
||||
|
||||
GhostComponent::~GhostComponent() {
|
||||
for (auto& observedEntity : m_ObservedEntities) {
|
||||
if (observedEntity == LWOOBJID_EMPTY) continue;
|
||||
|
||||
auto* entity = Game::entityManager->GetGhostCandidate(observedEntity);
|
||||
if (!entity) continue;
|
||||
|
||||
entity->SetObservers(entity->GetObservers() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void GhostComponent::SetGhostReferencePoint(const NiPoint3& value) {
|
||||
m_GhostReferencePoint = value;
|
||||
}
|
||||
|
||||
void GhostComponent::SetGhostOverridePoint(const NiPoint3& value) {
|
||||
m_GhostOverridePoint = value;
|
||||
}
|
||||
|
||||
void GhostComponent::AddLimboConstruction(LWOOBJID objectId) {
|
||||
m_LimboConstructions.insert(objectId);
|
||||
}
|
||||
|
||||
void GhostComponent::RemoveLimboConstruction(LWOOBJID objectId) {
|
||||
m_LimboConstructions.erase(objectId);
|
||||
}
|
||||
|
||||
void GhostComponent::ConstructLimboEntities() {
|
||||
for (const auto& objectId : m_LimboConstructions) {
|
||||
auto* entity = Game::entityManager->GetEntity(objectId);
|
||||
if (!entity) continue;
|
||||
|
||||
Game::entityManager->ConstructEntity(entity, m_Parent->GetSystemAddress());
|
||||
}
|
||||
|
||||
m_LimboConstructions.clear();
|
||||
}
|
||||
|
||||
void GhostComponent::ObserveEntity(LWOOBJID id) {
|
||||
m_ObservedEntities.insert(id);
|
||||
}
|
||||
|
||||
bool GhostComponent::IsObserved(LWOOBJID id) {
|
||||
return m_ObservedEntities.contains(id);
|
||||
}
|
||||
|
||||
void GhostComponent::GhostEntity(LWOOBJID id) {
|
||||
m_ObservedEntities.erase(id);
|
||||
}
|
||||
54
dGame/dComponents/GhostComponent.h
Normal file
54
dGame/dComponents/GhostComponent.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef __GHOSTCOMPONENT__H__
|
||||
#define __GHOSTCOMPONENT__H__
|
||||
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include <unordered_set>
|
||||
|
||||
class NiPoint3;
|
||||
|
||||
class GhostComponent final : public Component {
|
||||
public:
|
||||
static inline const eReplicaComponentType ComponentType = eReplicaComponentType::GHOST;
|
||||
GhostComponent(Entity* parent);
|
||||
~GhostComponent() override;
|
||||
|
||||
void SetGhostOverride(bool value) { m_GhostOverride = value; };
|
||||
|
||||
const NiPoint3& GetGhostReferencePoint() const { return m_GhostOverride ? m_GhostOverridePoint : m_GhostReferencePoint; };
|
||||
|
||||
const NiPoint3& GetOriginGhostReferencePoint() const { return m_GhostReferencePoint; };
|
||||
|
||||
const NiPoint3& GetGhostOverridePoint() const { return m_GhostOverridePoint; };
|
||||
|
||||
bool GetGhostOverride() const { return m_GhostOverride; };
|
||||
|
||||
void SetGhostReferencePoint(const NiPoint3& value);
|
||||
|
||||
void SetGhostOverridePoint(const NiPoint3& value);
|
||||
|
||||
void AddLimboConstruction(const LWOOBJID objectId);
|
||||
|
||||
void RemoveLimboConstruction(const LWOOBJID objectId);
|
||||
|
||||
void ConstructLimboEntities();
|
||||
|
||||
void ObserveEntity(const LWOOBJID id);
|
||||
|
||||
bool IsObserved(const LWOOBJID id);
|
||||
|
||||
void GhostEntity(const LWOOBJID id);
|
||||
|
||||
private:
|
||||
NiPoint3 m_GhostReferencePoint;
|
||||
|
||||
NiPoint3 m_GhostOverridePoint;
|
||||
|
||||
std::unordered_set<LWOOBJID> m_ObservedEntities;
|
||||
|
||||
std::unordered_set<LWOOBJID> m_LimboConstructions;
|
||||
|
||||
bool m_GhostOverride;
|
||||
};
|
||||
|
||||
#endif //!__GHOSTCOMPONENT__H__
|
||||
@@ -1,11 +1,9 @@
|
||||
#include "VehiclePhysicsComponent.h"
|
||||
#include "HavokVehiclePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
|
||||
VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : Component(parent) {
|
||||
m_Position = NiPoint3::ZERO;
|
||||
m_Rotation = NiQuaternion::IDENTITY;
|
||||
m_Velocity = NiPoint3::ZERO;
|
||||
m_AngularVelocity = NiPoint3::ZERO;
|
||||
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : PhysicsComponent(parent) {
|
||||
m_Velocity = NiPoint3Constant::ZERO;
|
||||
m_AngularVelocity = NiPoint3Constant::ZERO;
|
||||
m_IsOnGround = true;
|
||||
m_IsOnRail = false;
|
||||
m_DirtyPosition = true;
|
||||
@@ -14,65 +12,45 @@ VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : Component(par
|
||||
m_EndBehavior = GeneralUtils::GenerateRandomNumber<uint32_t>(0, 7);
|
||||
}
|
||||
|
||||
VehiclePhysicsComponent::~VehiclePhysicsComponent() {
|
||||
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetPosition(const NiPoint3& pos) {
|
||||
if (pos == m_Position) return;
|
||||
m_DirtyPosition = true;
|
||||
m_Position = pos;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) {
|
||||
if (rot == m_Rotation) return;
|
||||
m_DirtyPosition = true;
|
||||
m_Rotation = rot;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) {
|
||||
void HavokVehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) {
|
||||
if (vel == m_Velocity) return;
|
||||
m_DirtyPosition = true;
|
||||
m_Velocity = vel;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
|
||||
void HavokVehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
|
||||
if (vel == m_AngularVelocity) return;
|
||||
m_DirtyPosition = true;
|
||||
m_AngularVelocity = vel;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetIsOnGround(bool val) {
|
||||
void HavokVehiclePhysicsComponent::SetIsOnGround(bool val) {
|
||||
if (val == m_IsOnGround) return;
|
||||
m_DirtyPosition = true;
|
||||
m_IsOnGround = val;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetIsOnRail(bool val) {
|
||||
void HavokVehiclePhysicsComponent::SetIsOnRail(bool val) {
|
||||
if (val == m_IsOnRail) return;
|
||||
m_DirtyPosition = true;
|
||||
m_IsOnRail = val;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
|
||||
void HavokVehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
|
||||
if (m_RemoteInputInfo == remoteInputInfo) return;
|
||||
this->m_RemoteInputInfo = remoteInputInfo;
|
||||
m_DirtyRemoteInput = true;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetDirtyPosition(bool val) {
|
||||
m_DirtyPosition = val;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetDirtyVelocity(bool val) {
|
||||
void HavokVehiclePhysicsComponent::SetDirtyVelocity(bool val) {
|
||||
m_DirtyVelocity = val;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) {
|
||||
void HavokVehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) {
|
||||
m_DirtyAngularVelocity = val;
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
||||
void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
|
||||
|
||||
if (bIsInitialUpdate || m_DirtyPosition) {
|
||||
@@ -132,7 +110,7 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
|
||||
outBitStream->Write0();
|
||||
}
|
||||
|
||||
void VehiclePhysicsComponent::Update(float deltaTime) {
|
||||
void HavokVehiclePhysicsComponent::Update(float deltaTime) {
|
||||
if (m_SoftUpdate > 5) {
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
m_SoftUpdate = 0;
|
||||
@@ -2,65 +2,23 @@
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "PhysicsComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
struct RemoteInputInfo {
|
||||
void operator=(const RemoteInputInfo& other) {
|
||||
m_RemoteInputX = other.m_RemoteInputX;
|
||||
m_RemoteInputY = other.m_RemoteInputY;
|
||||
m_IsPowersliding = other.m_IsPowersliding;
|
||||
m_IsModified = other.m_IsModified;
|
||||
}
|
||||
|
||||
bool operator==(const RemoteInputInfo& other) {
|
||||
return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified;
|
||||
}
|
||||
|
||||
float m_RemoteInputX;
|
||||
float m_RemoteInputY;
|
||||
bool m_IsPowersliding;
|
||||
bool m_IsModified;
|
||||
};
|
||||
#include "PositionUpdate.h"
|
||||
|
||||
/**
|
||||
* Physics component for vehicles.
|
||||
*/
|
||||
class VehiclePhysicsComponent : public Component {
|
||||
class HavokVehiclePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::VEHICLE_PHYSICS;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS;
|
||||
|
||||
VehiclePhysicsComponent(Entity* parentEntity);
|
||||
~VehiclePhysicsComponent() override;
|
||||
HavokVehiclePhysicsComponent(Entity* parentEntity);
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
/**
|
||||
* Sets the position
|
||||
* @param pos the new position
|
||||
*/
|
||||
void SetPosition(const NiPoint3& pos);
|
||||
|
||||
/**
|
||||
* Gets the position
|
||||
* @return the position
|
||||
*/
|
||||
const NiPoint3& GetPosition() const { return m_Position; }
|
||||
|
||||
/**
|
||||
* Sets the rotation
|
||||
* @param rot the new rotation
|
||||
*/
|
||||
void SetRotation(const NiQuaternion& rot);
|
||||
|
||||
/**
|
||||
* Gets the rotation
|
||||
* @return the rotation
|
||||
*/
|
||||
const NiQuaternion& GetRotation() const { return m_Rotation; }
|
||||
|
||||
/**
|
||||
* Sets the velocity
|
||||
* @param vel the new velocity
|
||||
@@ -115,10 +73,6 @@ public:
|
||||
void SetRemoteInputInfo(const RemoteInputInfo&);
|
||||
|
||||
private:
|
||||
bool m_DirtyPosition;
|
||||
NiPoint3 m_Position;
|
||||
NiQuaternion m_Rotation;
|
||||
|
||||
bool m_DirtyVelocity;
|
||||
NiPoint3 m_Velocity;
|
||||
|
||||
@@ -5,21 +5,20 @@
|
||||
#include "Entity.h"
|
||||
#include "Item.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "Logger.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "../dWorldServer/ObjectIDManager.h"
|
||||
#include "ObjectIDManager.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "SkillComponent.h"
|
||||
#include "Character.h"
|
||||
#include "EntityManager.h"
|
||||
#include "ItemSet.h"
|
||||
#include "Player.h"
|
||||
#include "PetComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "ModuleAssemblyComponent.h"
|
||||
#include "VehiclePhysicsComponent.h"
|
||||
#include "HavokVehiclePhysicsComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
@@ -31,6 +30,7 @@
|
||||
#include "eMissionTaskType.h"
|
||||
#include "eStateChangeType.h"
|
||||
#include "eUseItemResponse.h"
|
||||
#include "Mail.h"
|
||||
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDInventoryComponentTable.h"
|
||||
@@ -55,10 +55,10 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
|
||||
return;
|
||||
}
|
||||
|
||||
auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
auto* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
const auto componentId = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::INVENTORY);
|
||||
|
||||
auto* inventoryComponentTable = CDClientManager::Instance().GetTable<CDInventoryComponentTable>();
|
||||
auto* inventoryComponentTable = CDClientManager::GetTable<CDInventoryComponentTable>();
|
||||
auto items = inventoryComponentTable->Query([=](const CDInventoryComponent entry) { return entry.id == componentId; });
|
||||
|
||||
auto slot = 0u;
|
||||
@@ -68,7 +68,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
|
||||
continue;
|
||||
}
|
||||
|
||||
const LWOOBJID id = ObjectIDManager::Instance()->GenerateObjectID();
|
||||
const LWOOBJID id = ObjectIDManager::GenerateObjectID();
|
||||
|
||||
const auto& info = Inventory::FindItemComponent(item.itemid);
|
||||
|
||||
@@ -86,7 +86,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
|
||||
const auto proxyLOT = static_cast<LOT>(std::stoi(proxyLotAsString));
|
||||
|
||||
const auto& proxyInfo = Inventory::FindItemComponent(proxyLOT);
|
||||
const LWOOBJID proxyId = ObjectIDManager::Instance()->GenerateObjectID();
|
||||
const LWOOBJID proxyId = ObjectIDManager::GenerateObjectID();
|
||||
|
||||
// Use item.count since we equip item.count number of the item this is a requested proxy of
|
||||
UpdateSlot(proxyInfo.equipLocation, { proxyId, proxyLOT, item.count, slot++ });
|
||||
@@ -175,14 +175,14 @@ void InventoryComponent::AddItem(
|
||||
const bool bound,
|
||||
int32_t preferredSlot) {
|
||||
if (count == 0) {
|
||||
Game::logger->Log("InventoryComponent", "Attempted to add 0 of item (%i) to the inventory!", lot);
|
||||
LOG("Attempted to add 0 of item (%i) to the inventory!", lot);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Inventory::IsValidItem(lot)) {
|
||||
if (lot > 0) {
|
||||
Game::logger->Log("InventoryComponent", "Attempted to add invalid item (%i) to the inventory!", lot);
|
||||
LOG("Attempted to add invalid item (%i) to the inventory!", lot);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -200,7 +200,7 @@ void InventoryComponent::AddItem(
|
||||
const auto slot = preferredSlot != -1 && inventory->IsSlotEmpty(preferredSlot) ? preferredSlot : inventory->FindEmptySlot();
|
||||
|
||||
if (slot == -1) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find empty slot for inventory (%i)!", inventoryType);
|
||||
LOG("Failed to find empty slot for inventory (%i)!", inventoryType);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -264,17 +264,11 @@ void InventoryComponent::AddItem(
|
||||
}
|
||||
|
||||
if (slot == -1) {
|
||||
auto* player = dynamic_cast<Player*>(GetParent());
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
outOfSpace += size;
|
||||
|
||||
switch (sourceType) {
|
||||
case 0:
|
||||
player->SendMail(LWOOBJID_EMPTY, "Darkflame Universe", "Lost Reward", "You received an item and didn't have room for it.", lot, size);
|
||||
Mail::SendMail(LWOOBJID_EMPTY, "Darkflame Universe", m_Parent, "Lost Reward", "You received an item and didn't have room for it.", lot, size);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
@@ -300,38 +294,26 @@ void InventoryComponent::AddItem(
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInventoryType inventoryType, const bool ignoreBound) {
|
||||
bool InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInventoryType inventoryType, const bool ignoreBound, const bool silent) {
|
||||
if (count == 0) {
|
||||
Game::logger->Log("InventoryComponent", "Attempted to remove 0 of item (%i) from the inventory!", lot);
|
||||
|
||||
return;
|
||||
LOG("Attempted to remove 0 of item (%i) from the inventory!", lot);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inventoryType == INVALID) {
|
||||
inventoryType = Inventory::FindInventoryTypeForLot(lot);
|
||||
}
|
||||
|
||||
if (inventoryType == INVALID) inventoryType = Inventory::FindInventoryTypeForLot(lot);
|
||||
auto* inventory = GetInventory(inventoryType);
|
||||
|
||||
if (inventory == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!inventory) return false;
|
||||
|
||||
auto left = std::min<uint32_t>(count, inventory->GetLotCount(lot));
|
||||
if (left != count) return false;
|
||||
|
||||
while (left > 0) {
|
||||
auto* item = FindItemByLot(lot, inventoryType, false, ignoreBound);
|
||||
|
||||
if (item == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!item) break;
|
||||
const auto delta = std::min<uint32_t>(left, item->GetCount());
|
||||
|
||||
item->SetCount(item->GetCount() - delta);
|
||||
|
||||
item->SetCount(item->GetCount() - delta, silent);
|
||||
left -= delta;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType inventory, const uint32_t count, const bool showFlyingLot, bool isModMoveAndEquip, const bool ignoreEquipped, const int32_t preferredSlot) {
|
||||
@@ -496,7 +478,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) {
|
||||
auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv");
|
||||
|
||||
if (inventoryElement == nullptr) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find 'inv' xml element!");
|
||||
LOG("Failed to find 'inv' xml element!");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -504,7 +486,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) {
|
||||
auto* bags = inventoryElement->FirstChildElement("bag");
|
||||
|
||||
if (bags == nullptr) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find 'bags' xml element!");
|
||||
LOG("Failed to find 'bags' xml element!");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -530,7 +512,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) {
|
||||
auto* items = inventoryElement->FirstChildElement("items");
|
||||
|
||||
if (items == nullptr) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find 'items' xml element!");
|
||||
LOG("Failed to find 'items' xml element!");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -545,7 +527,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) {
|
||||
auto* inventory = GetInventory(static_cast<eInventoryType>(type));
|
||||
|
||||
if (inventory == nullptr) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find inventory (%i)!", type);
|
||||
LOG("Failed to find inventory (%i)!", type);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -618,7 +600,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
|
||||
auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv");
|
||||
|
||||
if (inventoryElement == nullptr) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find 'inv' xml element!");
|
||||
LOG("Failed to find 'inv' xml element!");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -641,7 +623,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
|
||||
auto* bags = inventoryElement->FirstChildElement("bag");
|
||||
|
||||
if (bags == nullptr) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find 'bags' xml element!");
|
||||
LOG("Failed to find 'bags' xml element!");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -660,7 +642,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
|
||||
auto* items = inventoryElement->FirstChildElement("items");
|
||||
|
||||
if (items == nullptr) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find 'items' xml element!");
|
||||
LOG("Failed to find 'items' xml element!");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -927,30 +909,30 @@ void InventoryComponent::UnEquipItem(Item* item) {
|
||||
|
||||
|
||||
void InventoryComponent::EquipScripts(Item* equippedItem) {
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
if (!compRegistryTable) return;
|
||||
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1);
|
||||
if (scriptComponentID > -1) {
|
||||
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable<CDScriptComponentTable>();
|
||||
CDScriptComponentTable* scriptCompTable = CDClientManager::GetTable<CDScriptComponentTable>();
|
||||
CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
|
||||
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
|
||||
if (!itemScript) {
|
||||
Game::logger->Log("InventoryComponent", "null script?");
|
||||
LOG("null script?");
|
||||
}
|
||||
itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::UnequipScripts(Item* unequippedItem) {
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
if (!compRegistryTable) return;
|
||||
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1);
|
||||
if (scriptComponentID > -1) {
|
||||
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable<CDScriptComponentTable>();
|
||||
CDScriptComponentTable* scriptCompTable = CDClientManager::GetTable<CDScriptComponentTable>();
|
||||
CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
|
||||
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
|
||||
if (!itemScript) {
|
||||
Game::logger->Log("InventoryComponent", "null script?");
|
||||
LOG("null script?");
|
||||
}
|
||||
itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId());
|
||||
}
|
||||
@@ -993,7 +975,7 @@ void InventoryComponent::HandlePossession(Item* item) {
|
||||
auto* mount = Game::entityManager->CreateEntity(info, nullptr, m_Parent);
|
||||
|
||||
// Check to see if the mount is a vehicle, if so, flip it
|
||||
auto* vehicleComponent = mount->GetComponent<VehiclePhysicsComponent>();
|
||||
auto* vehicleComponent = mount->GetComponent<HavokVehiclePhysicsComponent>();
|
||||
if (vehicleComponent) characterComponent->SetIsRacing(true);
|
||||
|
||||
// Setup the destroyable stats
|
||||
@@ -1158,19 +1140,7 @@ void InventoryComponent::AddItemSkills(const LOT lot) {
|
||||
|
||||
const auto skill = FindSkill(lot);
|
||||
|
||||
if (skill == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index != m_Skills.end()) {
|
||||
const auto old = index->second;
|
||||
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
}
|
||||
|
||||
GameMessages::SendAddSkill(m_Parent, skill, static_cast<int>(slot));
|
||||
|
||||
m_Skills.insert_or_assign(slot, skill);
|
||||
SetSkill(slot, skill);
|
||||
}
|
||||
|
||||
void InventoryComponent::RemoveItemSkills(const LOT lot) {
|
||||
@@ -1197,7 +1167,7 @@ void InventoryComponent::RemoveItemSkills(const LOT lot) {
|
||||
if (slot == BehaviorSlot::Primary) {
|
||||
m_Skills.insert_or_assign(BehaviorSlot::Primary, 1);
|
||||
|
||||
GameMessages::SendAddSkill(m_Parent, 1, static_cast<int>(BehaviorSlot::Primary));
|
||||
GameMessages::SendAddSkill(m_Parent, 1, BehaviorSlot::Primary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1252,7 +1222,7 @@ void InventoryComponent::SpawnPet(Item* item) {
|
||||
EntityInfo info{};
|
||||
info.lot = item->GetLot();
|
||||
info.pos = m_Parent->GetPosition();
|
||||
info.rot = NiQuaternion::IDENTITY;
|
||||
info.rot = NiQuaternionConstant::IDENTITY;
|
||||
info.spawnerID = m_Parent->GetObjectID();
|
||||
|
||||
auto* pet = Game::entityManager->CreateEntity(info);
|
||||
@@ -1310,7 +1280,7 @@ bool InventoryComponent::IsTransferInventory(eInventoryType type) {
|
||||
}
|
||||
|
||||
uint32_t InventoryComponent::FindSkill(const LOT lot) {
|
||||
auto* table = CDClientManager::Instance().GetTable<CDObjectSkillsTable>();
|
||||
auto* table = CDClientManager::GetTable<CDObjectSkillsTable>();
|
||||
|
||||
const auto results = table->Query([=](const CDObjectSkills& entry) {
|
||||
return entry.objectTemplate == static_cast<unsigned int>(lot);
|
||||
@@ -1328,8 +1298,8 @@ uint32_t InventoryComponent::FindSkill(const LOT lot) {
|
||||
std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip) const {
|
||||
std::vector<uint32_t> buffs;
|
||||
if (item == nullptr) return buffs;
|
||||
auto* table = CDClientManager::Instance().GetTable<CDObjectSkillsTable>();
|
||||
auto* behaviors = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
auto* table = CDClientManager::GetTable<CDObjectSkillsTable>();
|
||||
auto* behaviors = CDClientManager::GetTable<CDSkillBehaviorTable>();
|
||||
|
||||
const auto results = table->Query([=](const CDObjectSkills& entry) {
|
||||
return entry.objectTemplate == static_cast<unsigned int>(item->GetLot());
|
||||
@@ -1342,7 +1312,7 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip
|
||||
const auto entry = behaviors->GetSkillByID(result.skillID);
|
||||
|
||||
if (entry.skillID == 0) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to find buff behavior for skill (%i)!", result.skillID);
|
||||
LOG("Failed to find buff behavior for skill (%i)!", result.skillID);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -1365,7 +1335,7 @@ void InventoryComponent::SetNPCItems(const std::vector<LOT>& items) {
|
||||
auto slot = 0u;
|
||||
|
||||
for (const auto& item : items) {
|
||||
const LWOOBJID id = ObjectIDManager::Instance()->GenerateObjectID();
|
||||
const LWOOBJID id = ObjectIDManager::GenerateObjectID();
|
||||
|
||||
const auto& info = Inventory::FindItemComponent(item);
|
||||
|
||||
@@ -1409,7 +1379,7 @@ std::vector<Item*> InventoryComponent::GenerateProxies(Item* parent) {
|
||||
try {
|
||||
lots.push_back(std::stoi(segment));
|
||||
} catch (std::invalid_argument& exception) {
|
||||
Game::logger->Log("InventoryComponent", "Failed to parse proxy (%s): (%s)!", segment.c_str(), exception.what());
|
||||
LOG("Failed to parse proxy (%s): (%s)!", segment.c_str(), exception.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1627,3 +1597,29 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) {
|
||||
petInventoryElement->LinkEndChild(petElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){
|
||||
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
|
||||
if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary;
|
||||
else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand;
|
||||
else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck;
|
||||
else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head;
|
||||
else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable;
|
||||
else return false;
|
||||
return SetSkill(behaviorSlot, skillId);
|
||||
}
|
||||
|
||||
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
|
||||
if (skillId == 0) return false;
|
||||
const auto index = m_Skills.find(slot);
|
||||
if (index != m_Skills.end()) {
|
||||
const auto old = index->second;
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
}
|
||||
|
||||
GameMessages::SendAddSkill(m_Parent, skillId, slot);
|
||||
m_Skills.insert_or_assign(slot, skillId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,10 +35,9 @@ 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 : public Component
|
||||
{
|
||||
class InventoryComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
||||
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr);
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
@@ -118,8 +117,9 @@ public:
|
||||
* @param count the number of items to remove
|
||||
* @param inventoryType optional inventory type to remove the item from
|
||||
* @param ignoreBound ignores bound items
|
||||
* @param silent silently remove the item
|
||||
*/
|
||||
void RemoveItem(LOT lot, uint32_t count, eInventoryType inventoryType = INVALID, bool ignoreBound = false);
|
||||
bool RemoveItem(LOT lot, uint32_t count, eInventoryType inventoryType = INVALID, bool ignoreBound = false, bool silent = false);
|
||||
|
||||
/**
|
||||
* Moves an existing item to an inventory of the entity
|
||||
@@ -367,6 +367,11 @@ public:
|
||||
*/
|
||||
void UnequipScripts(Item* unequippedItem);
|
||||
|
||||
std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
|
||||
|
||||
bool SetSkill(int slot, uint32_t skillId);
|
||||
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
|
||||
|
||||
~InventoryComponent() override;
|
||||
|
||||
private:
|
||||
|
||||
5
dGame/dComponents/ItemComponent.cpp
Normal file
5
dGame/dComponents/ItemComponent.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "ItemComponent.h"
|
||||
|
||||
void ItemComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
|
||||
outBitStream->Write0();
|
||||
}
|
||||
16
dGame/dComponents/ItemComponent.h
Normal file
16
dGame/dComponents/ItemComponent.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef __ITEMCOMPONENT__H__
|
||||
#define __ITEMCOMPONENT__H__
|
||||
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class ItemComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ITEM;
|
||||
|
||||
ItemComponent(Entity* entity) : Component(entity) {}
|
||||
|
||||
void Serialize(RakNet::BitStream* bitStream, bool isConstruction) override;
|
||||
};
|
||||
|
||||
#endif //!__ITEMCOMPONENT__H__
|
||||
@@ -1,44 +1,24 @@
|
||||
#include "LUPExhibitComponent.h"
|
||||
|
||||
#include "EntityManager.h"
|
||||
|
||||
LUPExhibitComponent::LUPExhibitComponent(Entity* parent) : Component(parent) {
|
||||
m_Exhibits = { 11121, 11295, 11423, 11979 };
|
||||
|
||||
m_ExhibitIndex = 0;
|
||||
|
||||
m_Exhibit = m_Exhibits[m_ExhibitIndex];
|
||||
|
||||
|
||||
}
|
||||
|
||||
LUPExhibitComponent::~LUPExhibitComponent() {
|
||||
|
||||
}
|
||||
|
||||
void LUPExhibitComponent::Update(float deltaTime) {
|
||||
m_UpdateTimer += deltaTime;
|
||||
|
||||
if (m_UpdateTimer > 20.0f) {
|
||||
NextExhibit();
|
||||
|
||||
NextLUPExhibit();
|
||||
m_UpdateTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void LUPExhibitComponent::NextExhibit() {
|
||||
m_ExhibitIndex++;
|
||||
|
||||
if (m_ExhibitIndex >= m_Exhibits.size()) {
|
||||
m_ExhibitIndex = 0;
|
||||
}
|
||||
|
||||
m_Exhibit = m_Exhibits[m_ExhibitIndex];
|
||||
|
||||
void LUPExhibitComponent::NextLUPExhibit() {
|
||||
m_LUPExhibitIndex++;
|
||||
m_DirtyLUPExhibit = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
||||
outBitStream->Write1(); // Dirty flag?
|
||||
outBitStream->Write(m_Exhibit);
|
||||
outBitStream->Write(m_DirtyLUPExhibit);
|
||||
if (m_DirtyLUPExhibit) {
|
||||
outBitStream->Write(m_LUPExhibits[m_LUPExhibitIndex % m_LUPExhibits.size()]);
|
||||
if (!bIsInitialUpdate) m_DirtyLUPExhibit = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,43 +3,26 @@
|
||||
#include "Component.h"
|
||||
#include "Entity.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include "dCommonVars.h"
|
||||
|
||||
/**
|
||||
* 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 : public Component
|
||||
class LUPExhibitComponent final : public Component
|
||||
{
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LUP_EXHIBIT;
|
||||
|
||||
LUPExhibitComponent(Entity* parent);
|
||||
~LUPExhibitComponent();
|
||||
LUPExhibitComponent(Entity* parent) : Component(parent) {};
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
/**
|
||||
* After the timer runs out, this changes the currently exhibited LOT to the next one
|
||||
*/
|
||||
void NextExhibit();
|
||||
void NextLUPExhibit();
|
||||
private:
|
||||
/**
|
||||
* The LOT that's currently on exhibit
|
||||
*/
|
||||
LOT m_Exhibit;
|
||||
|
||||
/**
|
||||
* The time since we've last updated the exhibit
|
||||
*/
|
||||
float m_UpdateTimer;
|
||||
|
||||
/**
|
||||
* The list of possible exhibits to show
|
||||
*/
|
||||
std::vector<LOT> m_Exhibits;
|
||||
|
||||
/**
|
||||
* The current index in the exhibit list
|
||||
*/
|
||||
size_t m_ExhibitIndex;
|
||||
float m_UpdateTimer = 0.0f;
|
||||
std::array<LOT, 4> m_LUPExhibits = { 11121, 11295, 11423, 11979 };
|
||||
uint8_t m_LUPExhibitIndex = 0;
|
||||
bool m_DirtyLUPExhibit = true;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component
|
||||
void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl");
|
||||
if (!level) {
|
||||
Game::logger->Log("LevelProgressionComponent", "Failed to find lvl tag while updating XML!");
|
||||
LOG("Failed to find lvl tag while updating XML!");
|
||||
return;
|
||||
}
|
||||
level->SetAttribute("l", m_Level);
|
||||
@@ -27,7 +27,7 @@ void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl");
|
||||
if (!level) {
|
||||
Game::logger->Log("LevelProgressionComponent", "Failed to find lvl tag while loading XML!");
|
||||
LOG("Failed to find lvl tag while loading XML!");
|
||||
return;
|
||||
}
|
||||
level->QueryAttribute("l", &m_Level);
|
||||
@@ -44,7 +44,7 @@ void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool
|
||||
}
|
||||
|
||||
void LevelProgressionComponent::HandleLevelUp() {
|
||||
auto* rewardsTable = CDClientManager::Instance().GetTable<CDRewardsTable>();
|
||||
auto* rewardsTable = CDClientManager::GetTable<CDRewardsTable>();
|
||||
|
||||
const auto& rewards = rewardsTable->GetByLevelID(m_Level);
|
||||
bool rewardingItem = rewards.size() > 0;
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
class LevelProgressionComponent : public Component {
|
||||
class LevelProgressionComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION;
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION;
|
||||
|
||||
/**
|
||||
* Constructor for this component
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user