Merge remote-tracking branch 'upstream/main' into PetFixes

This commit is contained in:
jadebenn
2024-03-05 23:02:35 -06:00
105 changed files with 1679 additions and 1029 deletions

View File

@@ -13,11 +13,25 @@ include_directories(
${PROJECT_SOURCE_DIR}/dGame
)
add_library(dGameBase ${DGAME_SOURCES})
add_library(dGameBase OBJECT ${DGAME_SOURCES})
target_precompile_headers(dGameBase PRIVATE ${HEADERS_DGAME})
target_link_libraries(dGameBase
PUBLIC dDatabase dPhysics
INTERFACE dComponents dEntity)
target_include_directories(dGameBase PUBLIC "." "dEntity"
PRIVATE "dComponents" "dGameMessages" "dBehaviors" "dMission" "dUtilities" "dInventory"
$<TARGET_PROPERTY:dPropertyBehaviors,INTERFACE_INCLUDE_DIRECTORIES>
"${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
"${PROJECT_SOURCE_DIR}/dCommon/dClient"
# dDatabase
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
# dPhysics
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Recast/Include"
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Detour/Include"
"${PROJECT_SOURCE_DIR}/dZoneManager"
)
add_subdirectory(dBehaviors)
add_subdirectory(dComponents)
@@ -28,7 +42,26 @@ add_subdirectory(dMission)
add_subdirectory(dPropertyBehaviors)
add_subdirectory(dUtilities)
add_library(dGame INTERFACE)
target_link_libraries(dGame INTERFACE
dGameBase dBehaviors dComponents dEntity dGameMessages dInventory dMission dPropertyBehaviors dUtilities dScripts
add_library(dGame STATIC
$<TARGET_OBJECTS:dGameBase>
$<TARGET_OBJECTS:dBehaviors>
$<TARGET_OBJECTS:dComponents>
$<TARGET_OBJECTS:dEntity>
$<TARGET_OBJECTS:dGameMessages>
$<TARGET_OBJECTS:dInventory>
$<TARGET_OBJECTS:dMission>
$<TARGET_OBJECTS:dPropertyBehaviors>
$<TARGET_OBJECTS:dUtilities>
)
target_link_libraries(dGame INTERFACE dNet)
target_include_directories(dGame INTERFACE
$<TARGET_PROPERTY:dGameBase,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:dBehaviors,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:dComponents,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:dEntity,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:dGameMessages,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:dInventory,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:dMission,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:dPropertyBehaviors,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:dUtilities,INTERFACE_INCLUDE_DIRECTORIES>
)

View File

@@ -841,11 +841,9 @@ bool Entity::HasComponent(const eReplicaComponentType componentId) const {
std::vector<ScriptComponent*> Entity::GetScriptComponents() {
std::vector<ScriptComponent*> comps;
for (std::pair<eReplicaComponentType, void*> p : m_Components) {
if (p.first == eReplicaComponentType::SCRIPT) {
comps.push_back(static_cast<ScriptComponent*>(p.second));
}
}
auto* scriptComponent = GetComponent<ScriptComponent>();
if (scriptComponent) comps.push_back(scriptComponent);
return comps;
}
@@ -2126,9 +2124,7 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
havokVehiclePhysicsComponent->SetIsOnGround(update.onGround);
havokVehiclePhysicsComponent->SetIsOnRail(update.onRail);
havokVehiclePhysicsComponent->SetVelocity(update.velocity);
havokVehiclePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO);
havokVehiclePhysicsComponent->SetAngularVelocity(update.angularVelocity);
havokVehiclePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO);
havokVehiclePhysicsComponent->SetRemoteInputInfo(update.remoteInputInfo);
} else {
// Need to get the mount's controllable physics
@@ -2139,9 +2135,7 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
possessedControllablePhysicsComponent->SetIsOnGround(update.onGround);
possessedControllablePhysicsComponent->SetIsOnRail(update.onRail);
possessedControllablePhysicsComponent->SetVelocity(update.velocity);
possessedControllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO);
possessedControllablePhysicsComponent->SetAngularVelocity(update.angularVelocity);
possessedControllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO);
}
Game::entityManager->SerializeEntity(possassableEntity);
}
@@ -2163,9 +2157,7 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
controllablePhysicsComponent->SetIsOnGround(update.onGround);
controllablePhysicsComponent->SetIsOnRail(update.onRail);
controllablePhysicsComponent->SetVelocity(update.velocity);
controllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO);
controllablePhysicsComponent->SetAngularVelocity(update.angularVelocity);
controllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO);
auto* ghostComponent = GetComponent<GhostComponent>();
if (ghostComponent) ghostComponent->SetGhostReferencePoint(update.position);
@@ -2197,9 +2189,3 @@ void Entity::SetRespawnRot(const NiQuaternion& rotation) {
auto* characterComponent = GetComponent<CharacterComponent>();
if (characterComponent) characterComponent->SetRespawnRot(rotation);
}
void Entity::SetScale(const float scale) {
if (scale == m_Scale) return;
m_Scale = scale;
Game::entityManager->SerializeEntity(this);
}

View File

@@ -295,7 +295,8 @@ public:
void ProcessPositionUpdate(PositionUpdate& update);
void SetScale(const float scale);
// Scale will only be communicated to the client when the construction packet is sent
void SetScale(const float scale) { m_Scale = scale; };
protected:
LWOOBJID m_ObjectID;

View File

@@ -21,7 +21,6 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
"DamageReductionBehavior.cpp"
"DarkInspirationBehavior.cpp"
"DurationBehavior.cpp"
"EmptyBehavior.cpp"
"EndBehavior.cpp"
"FallSpeedBehavior.cpp"
"ForceMovementBehavior.cpp"
@@ -56,7 +55,15 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
"VentureVisionBehavior.cpp"
"VerifyBehavior.cpp")
add_library(dBehaviors STATIC ${DGAME_DBEHAVIORS_SOURCES})
target_link_libraries(dBehaviors PUBLIC dPhysics)
target_include_directories(dBehaviors PUBLIC ".")
add_library(dBehaviors OBJECT ${DGAME_DBEHAVIORS_SOURCES})
target_link_libraries(dBehaviors PUBLIC dDatabaseCDClient dPhysics)
target_include_directories(dBehaviors PUBLIC "."
"${PROJECT_SOURCE_DIR}/dGame/dGameMessages" # via BehaviorContext.h
PRIVATE
"${PROJECT_SOURCE_DIR}/dGame/dComponents" # direct BuffComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dUtilities" # Preconditions.h via QuickBuildComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dEntity" # via dZoneManager.h, Spawner.h
"${PROJECT_SOURCE_DIR}/dGame/dInventory" # via CharacterComponent.h
"${PROJECT_SOURCE_DIR}/dZoneManager" # via BasicAttackBehavior.cpp
)
target_precompile_headers(dBehaviors REUSE_FROM dGameBase)

View File

@@ -1,2 +0,0 @@
#include "EmptyBehavior.h"

View File

@@ -50,9 +50,32 @@ set(DGAME_DCOMPONENTS_SOURCES
"MiniGameControlComponent.cpp"
)
add_library(dComponents STATIC ${DGAME_DCOMPONENTS_SOURCES})
target_include_directories(dComponents PRIVATE ${PROJECT_SOURCE_DIR}/dScripts/02_server/Map/General) # PetDigServer.h
add_library(dComponents OBJECT ${DGAME_DCOMPONENTS_SOURCES})
target_include_directories(dComponents PUBLIC "."
"${PROJECT_SOURCE_DIR}/dGame/dPropertyBehaviors" # via ModelComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dPropertyBehaviors/ControlBehaviorMessages"
"${PROJECT_SOURCE_DIR}/dGame/dMission" # via MissionComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dBehaviors" # via InventoryComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dInventory" # via InventoryComponent.h
PRIVATE
"${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
# dPhysics (via dpWorld.h)
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Recast/Include"
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Detour/Include"
"${PROJECT_SOURCE_DIR}/dScripts/02_server/Map/General" # PetDigServer.h
"${PROJECT_SOURCE_DIR}/dGame/dGameMessages" # direct
"${PROJECT_SOURCE_DIR}/dGame/dUtilities" # direct Loot.h
"${PROJECT_SOURCE_DIR}/dGame/dEntity" # via dZoneManager/Spawner.h
"${PROJECT_SOURCE_DIR}/dZoneManager" # via BouncerComponent.cpp, ActivityComponent.cpp
"${PROJECT_SOURCE_DIR}/dChatFilter" # via PetComponent.cpp
)
target_precompile_headers(dComponents REUSE_FROM dGameBase)
target_link_libraries(dComponents
PUBLIC dPhysics dDatabase
INTERFACE dUtilities dCommon dBehaviors dChatFilter dMission dInventory)
target_link_libraries(dComponents INTERFACE dBehaviors)

View File

@@ -5,7 +5,6 @@
#include "RakNetTypes.h"
#include "Character.h"
#include "Component.h"
#include "Item.h"
#include <string>
#include "CDMissionsTable.h"
#include "tinyxml2.h"
@@ -15,6 +14,8 @@
enum class eGameActivity : uint32_t;
class Item;
/**
* The statistics that can be achieved per zone
*/

View File

@@ -21,8 +21,6 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Phy
m_InJetpackMode = false;
m_IsOnGround = true;
m_IsOnRail = false;
m_DirtyVelocity = true;
m_DirtyAngularVelocity = true;
m_dpEntity = nullptr;
m_Static = false;
m_SpeedMultiplier = 1;
@@ -135,26 +133,28 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
outBitStream.Write(m_IsOnGround);
outBitStream.Write(m_IsOnRail);
outBitStream.Write(m_DirtyVelocity);
if (m_DirtyVelocity) {
bool isNotZero = m_Velocity != NiPoint3Constant::ZERO;
outBitStream.Write(isNotZero);
if (isNotZero) {
outBitStream.Write(m_Velocity.x);
outBitStream.Write(m_Velocity.y);
outBitStream.Write(m_Velocity.z);
}
outBitStream.Write(m_DirtyAngularVelocity);
if (m_DirtyAngularVelocity) {
isNotZero = m_AngularVelocity != NiPoint3Constant::ZERO;
outBitStream.Write(isNotZero);
if (isNotZero) {
outBitStream.Write(m_AngularVelocity.x);
outBitStream.Write(m_AngularVelocity.y);
outBitStream.Write(m_AngularVelocity.z);
}
outBitStream.Write0();
}
if (!bIsInitialUpdate) {
outBitStream.Write(m_IsTeleporting);
m_IsTeleporting = false;
if (!bIsInitialUpdate) {
m_DirtyPosition = false;
outBitStream.Write(m_IsTeleporting);
m_IsTeleporting = false;
}
}
}
@@ -211,33 +211,29 @@ void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
}
void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
if (m_Static) {
return;
}
if (m_Static || m_Velocity == vel) return;
m_Velocity = vel;
m_DirtyPosition = true;
m_DirtyVelocity = true;
if (m_dpEntity) m_dpEntity->SetVelocity(vel);
}
void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
if (m_Static) {
return;
}
if (m_Static || m_AngularVelocity == vel) return;
m_AngularVelocity = vel;
m_DirtyPosition = true;
m_DirtyAngularVelocity = true;
}
void ControllablePhysicsComponent::SetIsOnGround(bool val) {
if (m_IsOnGround == val) return;
m_DirtyPosition = true;
m_IsOnGround = val;
}
void ControllablePhysicsComponent::SetIsOnRail(bool val) {
if (m_IsOnRail == val) return;
m_DirtyPosition = true;
m_IsOnRail = val;
}
@@ -245,15 +241,6 @@ void ControllablePhysicsComponent::SetIsOnRail(bool val) {
void ControllablePhysicsComponent::SetDirtyPosition(bool val) {
m_DirtyPosition = val;
}
void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
m_DirtyVelocity = val;
}
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
m_DirtyAngularVelocity = val;
}
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
m_ActivePickupRadiusScales.push_back(value);
if (value > m_PickupRadius) {
@@ -309,7 +296,7 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
Game::entityManager->SerializeEntity(m_Parent);
}
void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims){
void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims) {
if (m_IsInBubble) {
LOG("Already in bubble");
return;
@@ -321,7 +308,7 @@ void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bo
Game::entityManager->SerializeEntity(m_Parent);
}
void ControllablePhysicsComponent::DeactivateBubbleBuff(){
void ControllablePhysicsComponent::DeactivateBubbleBuff() {
m_DirtyBubble = true;
m_IsInBubble = false;
Game::entityManager->SerializeEntity(m_Parent);
@@ -336,9 +323,9 @@ void ControllablePhysicsComponent::SetStunImmunity(
const bool bImmuneToStunJump,
const bool bImmuneToStunMove,
const bool bImmuneToStunTurn,
const bool bImmuneToStunUseItem){
const bool bImmuneToStunUseItem) {
if (state == eStateChangeType::POP){
if (state == eStateChangeType::POP) {
if (bImmuneToStunAttack && m_ImmuneToStunAttackCount > 0) m_ImmuneToStunAttackCount -= 1;
if (bImmuneToStunEquip && m_ImmuneToStunEquipCount > 0) m_ImmuneToStunEquipCount -= 1;
if (bImmuneToStunInteract && m_ImmuneToStunInteractCount > 0) m_ImmuneToStunInteractCount -= 1;

View File

@@ -104,18 +104,6 @@ public:
*/
void SetDirtyPosition(bool val);
/**
* Mark the velocity as dirty, forcing a serializtion update next tick
* @param val whether or not the velocity is dirty
*/
void SetDirtyVelocity(bool val);
/**
* Mark the angular velocity as dirty, forcing a serialization update next tick
* @param val whether or not the angular velocity is dirty
*/
void SetDirtyAngularVelocity(bool val);
/**
* Sets whether or not the entity is currently wearing a jetpack
* @param val whether or not the entity is currently wearing a jetpack
@@ -310,21 +298,11 @@ private:
*/
dpEntity* m_dpEntity;
/**
* Whether or not the velocity is dirty, forcing a serialization of the velocity
*/
bool m_DirtyVelocity;
/**
* The current velocity of the entity
*/
NiPoint3 m_Velocity;
/**
* Whether or not the angular velocity is dirty, forcing a serialization
*/
bool m_DirtyAngularVelocity;
/**
* The current angular velocity of the entity
*/

View File

@@ -7,8 +7,6 @@ HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : Phy
m_IsOnGround = true;
m_IsOnRail = false;
m_DirtyPosition = true;
m_DirtyVelocity = true;
m_DirtyAngularVelocity = true;
m_EndBehavior = GeneralUtils::GenerateRandomNumber<uint32_t>(0, 7);
}
@@ -37,17 +35,9 @@ void HavokVehiclePhysicsComponent::SetIsOnRail(bool val) {
}
void HavokVehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
if (m_RemoteInputInfo == remoteInputInfo) return;
if (remoteInputInfo == m_RemoteInputInfo) return;
this->m_RemoteInputInfo = remoteInputInfo;
m_DirtyRemoteInput = true;
}
void HavokVehiclePhysicsComponent::SetDirtyVelocity(bool val) {
m_DirtyVelocity = val;
}
void HavokVehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) {
m_DirtyAngularVelocity = val;
m_DirtyPosition = true;
}
void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
@@ -67,34 +57,32 @@ void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
outBitStream.Write(m_IsOnGround);
outBitStream.Write(m_IsOnRail);
outBitStream.Write(bIsInitialUpdate || m_DirtyVelocity);
if (bIsInitialUpdate || m_DirtyVelocity) {
bool isNotZero = m_Velocity != NiPoint3Constant::ZERO;
outBitStream.Write(isNotZero);
if (isNotZero) {
outBitStream.Write(m_Velocity.x);
outBitStream.Write(m_Velocity.y);
outBitStream.Write(m_Velocity.z);
m_DirtyVelocity = false;
}
outBitStream.Write(bIsInitialUpdate || m_DirtyAngularVelocity);
if (bIsInitialUpdate || m_DirtyAngularVelocity) {
isNotZero = m_AngularVelocity != NiPoint3Constant::ZERO;
outBitStream.Write(isNotZero);
if (isNotZero) {
outBitStream.Write(m_AngularVelocity.x);
outBitStream.Write(m_AngularVelocity.y);
outBitStream.Write(m_AngularVelocity.z);
m_DirtyAngularVelocity = false;
}
outBitStream.Write0(); // local_space_info. TODO: Implement this
outBitStream.Write(m_DirtyRemoteInput || bIsInitialUpdate); // remote_input_info
if (m_DirtyRemoteInput || bIsInitialUpdate) {
outBitStream.Write(m_RemoteInputInfo.m_RemoteInputX);
outBitStream.Write(m_RemoteInputInfo.m_RemoteInputY);
outBitStream.Write(m_RemoteInputInfo.m_IsPowersliding);
outBitStream.Write(m_RemoteInputInfo.m_IsModified);
m_DirtyRemoteInput = false;
}
// This structure only has this bool flag set to false if a ptr to the peVehicle is null, which we don't have
// therefore, this will always be 1, even if all the values in the structure are 0.
outBitStream.Write1(); // has remote_input_info
outBitStream.Write(m_RemoteInputInfo.m_RemoteInputX);
outBitStream.Write(m_RemoteInputInfo.m_RemoteInputY);
outBitStream.Write(m_RemoteInputInfo.m_IsPowersliding);
outBitStream.Write(m_RemoteInputInfo.m_IsModified);
outBitStream.Write(125.0f); // remote_input_ping TODO: Figure out how this should be calculated as it seems to be constant through the whole race.
@@ -110,12 +98,3 @@ void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
outBitStream.Write0();
}
void HavokVehiclePhysicsComponent::Update(float deltaTime) {
if (m_SoftUpdate > 5) {
Game::entityManager->SerializeEntity(m_Parent);
m_SoftUpdate = 0;
} else {
m_SoftUpdate += deltaTime;
}
}

View File

@@ -17,8 +17,6 @@ public:
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void Update(float deltaTime) override;
/**
* Sets the velocity
* @param vel the new velocity
@@ -67,22 +65,16 @@ public:
*/
const bool GetIsOnRail() const { return m_IsOnRail; }
void SetDirtyPosition(bool val);
void SetDirtyVelocity(bool val);
void SetDirtyAngularVelocity(bool val);
void SetRemoteInputInfo(const RemoteInputInfo&);
private:
bool m_DirtyVelocity;
NiPoint3 m_Velocity;
bool m_DirtyAngularVelocity;
NiPoint3 m_AngularVelocity;
bool m_IsOnGround;
bool m_IsOnRail;
float m_SoftUpdate = 0;
uint32_t m_EndBehavior;
RemoteInputInfo m_RemoteInputInfo;
bool m_DirtyRemoteInput;
};

View File

@@ -162,7 +162,7 @@ void MovingPlatformComponent::StartPathing() {
const auto& nextWaypoint = m_Path->pathWaypoints[subComponent->mNextWaypointIndex];
subComponent->mPosition = currentWaypoint.position;
subComponent->mSpeed = currentWaypoint.movingPlatform.speed;
subComponent->mSpeed = currentWaypoint.speed;
subComponent->mWaitTime = currentWaypoint.movingPlatform.wait;
targetPosition = nextWaypoint.position;
@@ -213,7 +213,7 @@ void MovingPlatformComponent::ContinuePathing() {
const auto& nextWaypoint = m_Path->pathWaypoints[subComponent->mNextWaypointIndex];
subComponent->mPosition = currentWaypoint.position;
subComponent->mSpeed = currentWaypoint.movingPlatform.speed;
subComponent->mSpeed = currentWaypoint.speed;
subComponent->mWaitTime = currentWaypoint.movingPlatform.wait; // + 2;
pathSize = m_Path->pathWaypoints.size() - 1;

View File

@@ -40,9 +40,19 @@ void VendorComponent::RefreshInventory(bool isCreation) {
SetHasMultiCostItems(false);
m_Inventory.clear();
// Custom code for Max vanity NPC and Mr.Ree cameras
if(isCreation && m_Parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
SetupMaxCustomVendor();
// Custom code for Vanity Vendor Invetory Override
if(m_Parent->HasVar(u"vendorInvOverride")) {
std::vector<std::string> items = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"vendorInvOverride"), ',');
uint32_t sortPriority = -1;
for (auto& itemString : items) {
itemString.erase(remove_if(itemString.begin(), itemString.end(), isspace), itemString.end());
auto item = GeneralUtils::TryParse<uint32_t>(itemString);
if (!item) continue;
if (SetupItem(item.value())) {
sortPriority++;
m_Inventory.push_back(SoldItem(item.value(), sortPriority));
}
}
return;
}
@@ -52,24 +62,12 @@ void VendorComponent::RefreshInventory(bool isCreation) {
if (lootMatrices.empty()) return;
auto* lootTableTable = CDClientManager::GetTable<CDLootTableTable>();
auto* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
auto* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
for (const auto& lootMatrix : lootMatrices) {
auto vendorItems = lootTableTable->GetTable(lootMatrix.LootTableIndex);
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
for (const auto& item : vendorItems) {
if (!m_HasStandardCostItems || !m_HasMultiCostItems) {
auto itemComponentID = compRegistryTable->GetByIDAndType(item.itemid, eReplicaComponentType::ITEM, -1);
if (itemComponentID == -1) {
LOG("Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT());
continue;
}
auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID);
if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true);
if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true);
}
m_Inventory.push_back(SoldItem(item.itemid, item.sortPriority));
if (SetupItem(item.itemid)) m_Inventory.push_back(SoldItem(item.itemid, item.sortPriority));
}
} else {
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
@@ -79,17 +77,7 @@ void VendorComponent::RefreshInventory(bool isCreation) {
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
const auto& randomItem = vendorItems.at(randomItemIndex);
vendorItems.erase(vendorItems.begin() + randomItemIndex);
if (!m_HasStandardCostItems || !m_HasMultiCostItems) {
auto itemComponentID = compRegistryTable->GetByIDAndType(randomItem.itemid, eReplicaComponentType::ITEM, -1);
if (itemComponentID == -1) {
LOG("Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT());
continue;
}
auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID);
if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true);
if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true);
}
m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
}
}
}
@@ -126,15 +114,6 @@ bool VendorComponent::SellsItem(const LOT item) const {
}) > 0;
}
void VendorComponent::SetupMaxCustomVendor(){
SetHasStandardCostItems(true);
m_Inventory.push_back(SoldItem(11909, 0)); // Top hat w frog
m_Inventory.push_back(SoldItem(7785, 0)); // Flash bulb
m_Inventory.push_back(SoldItem(12764, 0)); // Big fountain soda
m_Inventory.push_back(SoldItem(12241, 0)); // Hot cocoa (from fb)
}
void VendorComponent::HandleMrReeCameras(){
if (m_Parent->GetLOT() == 13569) {
SetHasStandardCostItems(true);
@@ -211,5 +190,25 @@ void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR);
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
}
bool VendorComponent::SetupItem(LOT item) {
auto* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
auto* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
auto itemComponentID = compRegistryTable->GetByIDAndType(item, eReplicaComponentType::ITEM, -1);
if (itemComponentID == -1) {
LOG("Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT());
return false;
}
if (!m_HasStandardCostItems || !m_HasMultiCostItems) {
auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID);
if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true);
if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true);
}
return true;
}
}

View File

@@ -50,8 +50,8 @@ public:
void Buy(Entity* buyer, LOT lot, uint32_t count);
private:
void SetupMaxCustomVendor();
void HandleMrReeCameras();
bool SetupItem(LOT item);
float m_BuyScalar = 0.0f;
float m_SellScalar = 0.0f;
float m_RefreshTimeSeconds = 0.0f;

View File

@@ -2,6 +2,6 @@ set(DGAME_DENTITY_SOURCES
"EntityCallbackTimer.cpp"
"EntityTimer.cpp")
add_library(dEntity STATIC ${DGAME_DENTITY_SOURCES})
add_library(dEntity OBJECT ${DGAME_DENTITY_SOURCES})
target_include_directories(dEntity PUBLIC ".")
target_precompile_headers(dEntity REUSE_FROM dGameBase)

View File

@@ -4,6 +4,20 @@ set(DGAME_DGAMEMESSAGES_SOURCES
"PropertyDataMessage.cpp"
"PropertySelectQueryProperty.cpp")
add_library(dGameMessages STATIC ${DGAME_DGAMEMESSAGES_SOURCES})
target_link_libraries(dGameMessages PUBLIC dDatabase)
add_library(dGameMessages OBJECT ${DGAME_DGAMEMESSAGES_SOURCES})
target_link_libraries(dGameMessages
PUBLIC dDatabase
INTERFACE dGameBase # TradingManager
)
target_include_directories(dGameMessages PUBLIC "."
PRIVATE
"${PROJECT_SOURCE_DIR}/dGame/dComponents" # direct MissionComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dUtilities" # direct SlashCommandHandler.h
"${PROJECT_SOURCE_DIR}/dGame/dPropertyBehaviors" # direct ControlBehaviors.h
"${PROJECT_SOURCE_DIR}/dGame/dMission" # via MissionComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dBehaviors" # via InventoryComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dInventory" # via InventoryComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dEntity" # via dZoneManager/Spawner.h
"${PROJECT_SOURCE_DIR}/dZoneManager" # via GameMessages.cpp, GameMessageHandler.cpp
)
target_precompile_headers(dGameMessages REUSE_FROM dGameBase)

View File

@@ -324,8 +324,8 @@ void GameMessages::SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& s
bitStream.Write(entity->GetObjectID());
bitStream.Write(eGameMessageType::PLAY_ND_AUDIO_EMITTER);
bitStream.Write0();
bitStream.Write0();
bitStream.Write0(); // callback message data {lwoobjid}
bitStream.Write0(); // audio emitterid {uint32_t}
uint32_t length = audioGUID.size();
bitStream.Write(length);
@@ -333,9 +333,9 @@ void GameMessages::SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& s
bitStream.Write<char>(audioGUID[k]);
}
bitStream.Write<uint32_t>(0);
bitStream.Write0();
bitStream.Write0();
bitStream.Write<uint32_t>(0); // size of NDAudioMetaEventName (then print the string like the guid)
bitStream.Write0(); // result {bool}
bitStream.Write0(); // m_TargetObjectIDForNDAudioCallbackMessages {lwoobjid}
SEND_PACKET_BROADCAST;
}

View File

@@ -5,11 +5,32 @@ set(DGAME_DINVENTORY_SOURCES
"ItemSet.cpp"
"ItemSetPassiveAbility.cpp")
add_library(dInventory OBJECT ${DGAME_DINVENTORY_SOURCES})
target_include_directories(dInventory PUBLIC "."
"${PROJECT_SOURCE_DIR}/dGame/dUtilities" # Item.h uses Preconditions.h
"${PROJECT_SOURCE_DIR}/dCommon/eEnums" # Item.h uses dCommonVars.h
PRIVATE
"${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
"${PROJECT_SOURCE_DIR}/dCommon/dClient" # Item.cpp uses AssetManager
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables"
"${PROJECT_SOURCE_DIR}/dGame/dGameMessages" # direct
"${PROJECT_SOURCE_DIR}/dGame/dComponents" # direct InventoryComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dBehaviors" # via InventoryComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dEntity" # via dZoneManager/Spawner.h
"${PROJECT_SOURCE_DIR}/dGame/dMission" # via MissionComponent.h
"${PROJECT_SOURCE_DIR}/dZoneManager" # via Item.cpp
)
target_precompile_headers(dInventory REUSE_FROM dGameBase)
# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible.
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_source_files_properties("Item.cpp" PROPERTIES COMPILE_FLAGS "-Wno-stringop-overflow")
endif()
add_library(dInventory STATIC ${DGAME_DINVENTORY_SOURCES})
target_precompile_headers(dInventory REUSE_FROM dGameBase)
# INTERFACE link w/o dependency
#set_property(TARGET dInventory APPEND PROPERTY INTERFACE_LINK_LIBRARIES
# dNet dDatabaseCDClient
#)

View File

@@ -3,6 +3,16 @@ set(DGAME_DMISSION_SOURCES
"MissionPrerequisites.cpp"
"MissionTask.cpp")
add_library(dMission STATIC ${DGAME_DMISSION_SOURCES})
add_library(dMission OBJECT ${DGAME_DMISSION_SOURCES})
target_link_libraries(dMission PUBLIC dDatabase)
target_include_directories(dMission PUBLIC "."
PRIVATE
"${PROJECT_SOURCE_DIR}/dGame/dComponents"
"${PROJECT_SOURCE_DIR}/dGame/dInventory" # via CharacterComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dUtilities" # via CharacterComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dGameMessages" # via LevelProgressionComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dEntity" # via dZoneManager/Spawner.h
"${PROJECT_SOURCE_DIR}/dGame/dBehaviors" # via InventoryComponent.h
"${PROJECT_SOURCE_DIR}/dZoneManager" # via Mission.cpp, MissionTask.cpp
)
target_precompile_headers(dMission REUSE_FROM dGameBase)

View File

@@ -12,5 +12,15 @@ foreach(file ${DGAME_DPROPERTYBEHAVIORS_CONTROLBEHAVIORMESSAGES})
set(DGAME_DPROPERTYBEHAVIORS_SOURCES ${DGAME_DPROPERTYBEHAVIORS_SOURCES} "ControlBehaviorMessages/${file}")
endforeach()
add_library(dPropertyBehaviors STATIC ${DGAME_DPROPERTYBEHAVIORS_SOURCES})
add_library(dPropertyBehaviors OBJECT ${DGAME_DPROPERTYBEHAVIORS_SOURCES})
target_link_libraries(dPropertyBehaviors PRIVATE dDatabaseCDClient)
target_include_directories(dPropertyBehaviors PUBLIC "." "ControlBehaviorMessages"
PRIVATE
"${PROJECT_SOURCE_DIR}/dCommon/dClient" # ControlBehaviors.cpp uses AssetManager
"${PROJECT_SOURCE_DIR}/dGame/dUtilities" # ObjectIdManager.h
"${PROJECT_SOURCE_DIR}/dGame/dGameMessages" # GameMessages.h
"${PROJECT_SOURCE_DIR}/dGame/dComponents" # ModelComponent.h
)
target_precompile_headers(dPropertyBehaviors REUSE_FROM dGameBase)
target_link_libraries(dPropertyBehaviors INTERFACE dComponents)

View File

@@ -8,8 +8,18 @@ set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp"
"SlashCommandHandler.cpp"
"VanityUtilities.cpp")
add_library(dUtilities STATIC ${DGAME_DUTILITIES_SOURCES})
add_library(dUtilities OBJECT ${DGAME_DUTILITIES_SOURCES})
target_precompile_headers(dUtilities REUSE_FROM dGameBase)
target_include_directories(dUtilities PUBLIC "."
PRIVATE
"${PROJECT_SOURCE_DIR}/dGame/dComponents"
"${PROJECT_SOURCE_DIR}/dGame/dInventory" # transitive via PossessableComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dGameMessages"
"${PROJECT_SOURCE_DIR}/dGame/dBehaviors" # transitive via InventoryComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dMission" # transitive via MissionComponent.h
"${PROJECT_SOURCE_DIR}/dGame/dEntity" # transitive via dZoneManager/Spawner.h
"${PROJECT_SOURCE_DIR}/dZoneManager" # Mail.cpp
)
target_link_libraries(dUtilities
PUBLIC dDatabase dPhysics
INTERFACE dZoneManager)

View File

@@ -1,6 +1,7 @@
#pragma once
#include "dCommonVars.h"
#include "eLootSourceType.h"
#include <unordered_map>
class Entity;

View File

@@ -22,9 +22,18 @@
#include <fstream>
std::vector<VanityObject> VanityUtilities::m_Objects = {};
std::set<std::string> VanityUtilities::m_LoadedFiles = {};
namespace {
std::vector<VanityObject> objects;
std::set<std::string> loadedFiles;
}
void SetupNPCTalk(Entity* npc);
void NPCTalk(Entity* npc);
void ParseXml(const std::string& file);
LWOOBJID SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location);
Entity* SpawnObject(const VanityObject& object, const VanityObjectLocation& location);
VanityObject* GetObject(const std::string& name);
void VanityUtilities::SpawnVanity() {
const uint32_t zoneID = Game::server->GetZoneID();
@@ -36,21 +45,19 @@ void VanityUtilities::SpawnVanity() {
info.pos = { 259.5f, 246.4f, -705.2f };
info.rot = { 0.0f, 0.0f, 1.0f, 0.0f };
info.spawnerID = Game::entityManager->GetZoneControlEntity()->GetObjectID();
info.settings = { new LDFData<bool>(u"hasCustomText", true),
new LDFData<std::string>(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string())) };
info.settings = {
new LDFData<bool>(u"hasCustomText", true),
new LDFData<std::string>(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string()))
};
auto* entity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(entity);
}
}
if (Game::config->GetValue("disable_vanity") == "1") {
return;
}
if (Game::config->GetValue("disable_vanity") == "1") return;
for (const auto& npc : m_Objects) {
for (const auto& npc : objects) {
if (npc.m_ID == LWOOBJID_EMPTY) continue;
if (npc.m_LOT == 176){
Game::zoneManager->RemoveSpawner(npc.m_ID);
@@ -61,13 +68,13 @@ void VanityUtilities::SpawnVanity() {
}
}
m_Objects.clear();
m_LoadedFiles.clear();
objects.clear();
loadedFiles.clear();
ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/root.xml").string());
ParseXml((BinaryPathFinder::GetBinaryDir() / "vanity/root.xml").string());
// Loop through all objects
for (auto& object : m_Objects) {
for (auto& object : objects) {
if (object.m_Locations.find(Game::server->GetZoneID()) == object.m_Locations.end()) continue;
const std::vector<VanityObjectLocation>& locations = object.m_Locations.at(Game::server->GetZoneID());
@@ -79,12 +86,6 @@ void VanityUtilities::SpawnVanity() {
float rate = GeneralUtils::GenerateRandomNumber<float>(0, 1);
if (location.m_Chance < rate) continue;
if (object.m_Config.empty()) {
object.m_Config = {
new LDFData<std::vector<std::u16string>>(u"syncLDF", { u"custom_script_client" }),
new LDFData<std::u16string>(u"custom_script_client", u"scripts\\ai\\SPEC\\MISSION_MINIGAME_CLIENT.lua")
};
}
if (object.m_LOT == 176){
object.m_ID = SpawnSpawner(object, location);
} else {
@@ -94,20 +95,13 @@ void VanityUtilities::SpawnVanity() {
object.m_ID = objectEntity->GetObjectID();
if (!object.m_Phrases.empty()){
objectEntity->SetVar<std::vector<std::string>>(u"chats", object.m_Phrases);
auto* scriptComponent = objectEntity->GetComponent<ScriptComponent>();
if (scriptComponent && !object.m_Script.empty()) {
scriptComponent->SetScript(object.m_Script);
scriptComponent->SetSerialized(false);
}
SetupNPCTalk(objectEntity);
}
}
}
}
LWOOBJID VanityUtilities::SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location) {
LWOOBJID SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location) {
SceneObject obj;
obj.lot = object.m_LOT;
// guratantee we have no collisions
@@ -121,7 +115,7 @@ LWOOBJID VanityUtilities::SpawnSpawner(const VanityObject& object, const VanityO
return obj.id;
}
Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObjectLocation& location) {
Entity* SpawnObject(const VanityObject& object, const VanityObjectLocation& location) {
EntityInfo info;
info.lot = object.m_LOT;
info.pos = location.m_Position;
@@ -131,18 +125,16 @@ Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObj
info.settings = object.m_Config;
auto* entity = Game::entityManager->CreateEntity(info);
entity->SetVar(u"npcName", object.m_Name);
if (!object.m_Name.empty()) entity->SetVar(u"npcName", object.m_Name);
if (entity->GetVar<bool>(u"noGhosting")) entity->SetIsGhostingCandidate(false);
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent && !object.m_Equipment.empty()) {
inventoryComponent->SetNPCItems(object.m_Equipment);
}
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) {
if (destroyableComponent) {
destroyableComponent->SetIsGMImmune(true);
destroyableComponent->SetMaxHealth(0);
destroyableComponent->SetHealth(0);
@@ -153,12 +145,12 @@ Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObj
return entity;
}
void VanityUtilities::ParseXML(const std::string& file) {
if (m_LoadedFiles.contains(file)){
void ParseXml(const std::string& file) {
if (loadedFiles.contains(file)){
LOG("Trying to load vanity file %s twice!!!", file.c_str());
return;
}
m_LoadedFiles.insert(file);
loadedFiles.insert(file);
// Read the entire file
std::ifstream xmlFile(file);
std::string xml((std::istreambuf_iterator<char>(xmlFile)), std::istreambuf_iterator<char>());
@@ -176,24 +168,26 @@ void VanityUtilities::ParseXML(const std::string& file) {
if (enabled != "1") {
continue;
}
ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity" / filename).string());
ParseXml((BinaryPathFinder::GetBinaryDir() / "vanity" / filename).string());
}
}
// Read the objects
auto* objects = doc.FirstChildElement("objects");
if (objects) {
for (auto* object = objects->FirstChildElement("object"); object != nullptr; object = object->NextSiblingElement("object")) {
auto* objectsElement = doc.FirstChildElement("objects");
const uint32_t currentZoneID = Game::server->GetZoneID();
if (objectsElement) {
for (auto* object = objectsElement->FirstChildElement("object"); object != nullptr; object = object->NextSiblingElement("object")) {
// for use later when adding to the vector of VanityObjects
bool useLocationsAsRandomSpawnPoint = false;
// Get the NPC name
auto* name = object->Attribute("name");
if (!name) name = "";
// Get the NPC lot
auto* lot = object->Attribute("lot");
auto lot = GeneralUtils::TryParse<LOT>(object->Attribute("lot")).value_or(LOT_NULL);
if (lot == nullptr) {
if (lot == LOT_NULL) {
LOG("Failed to parse object lot");
continue;
}
@@ -211,17 +205,17 @@ void VanityUtilities::ParseXML(const std::string& file) {
std::vector<std::string> splitEquipment = GeneralUtils::SplitString(equipmentString, ',');
for (auto& item : splitEquipment) {
inventory.push_back(std::stoi(item));
// remove spaces for tryParse to work
item.erase(remove_if(item.begin(), item.end(), isspace), item.end());
auto itemInt = GeneralUtils::TryParse<uint32_t>(item);
if (itemInt) inventory.push_back(itemInt.value());
}
}
}
// Get the phrases
auto* phrases = object->FirstChildElement("phrases");
std::vector<std::string> phraseList = {};
if (phrases) {
for (auto* phrase = phrases->FirstChildElement("phrase"); phrase != nullptr;
phrase = phrase->NextSiblingElement("phrase")) {
@@ -235,41 +229,34 @@ void VanityUtilities::ParseXML(const std::string& file) {
}
}
// Get the script
auto* scriptElement = object->FirstChildElement("script");
std::string scriptName = "";
if (scriptElement != nullptr) {
auto* scriptNameAttribute = scriptElement->Attribute("name");
if (scriptNameAttribute) scriptName = scriptNameAttribute;
}
auto* configElement = object->FirstChildElement("config");
std::vector<std::u16string> keys = {};
std::vector<LDFBaseData*> config = {};
if(configElement) {
for (auto* key = configElement->FirstChildElement("key"); key != nullptr;
key = key->NextSiblingElement("key")) {
// Get the config data
auto* data = key->Attribute("data");
auto* data = key->GetText();
if (!data) continue;
LDFBaseData* configData = LDFBaseData::DataFromString(data);
if (configData->GetKey() == u"useLocationsAsRandomSpawnPoint" && configData->GetValueType() == eLDFType::LDF_TYPE_BOOLEAN){
useLocationsAsRandomSpawnPoint = static_cast<bool>(configData);
continue;
}
keys.push_back(configData->GetKey());
config.push_back(configData);
}
}
if (!keys.empty()) config.push_back(new LDFData<std::vector<std::u16string>>(u"syncLDF", keys));
VanityObject objectData;
objectData.m_Name = name;
objectData.m_LOT = std::stoi(lot);
objectData.m_Equipment = inventory;
objectData.m_Phrases = phraseList;
objectData.m_Script = scriptName;
objectData.m_Config = config;
VanityObject objectData {
.m_Name = name,
.m_LOT = lot,
.m_Equipment = inventory,
.m_Phrases = phraseList,
.m_Config = config
};
// Get the locations
auto* locations = object->FirstChildElement("locations");
@@ -283,64 +270,67 @@ void VanityUtilities::ParseXML(const std::string& file) {
location = location->NextSiblingElement("location")) {
// Get the location data
auto* zoneID = location->Attribute("zone");
auto* x = location->Attribute("x");
auto* y = location->Attribute("y");
auto* z = location->Attribute("z");
auto* rw = location->Attribute("rw");
auto* rx = location->Attribute("rx");
auto* ry = location->Attribute("ry");
auto* rz = location->Attribute("rz");
auto zoneID = GeneralUtils::TryParse<uint32_t>(location->Attribute("zone"));
auto x = GeneralUtils::TryParse<float>(location->Attribute("x"));
auto y = GeneralUtils::TryParse<float>(location->Attribute("y"));
auto z = GeneralUtils::TryParse<float>(location->Attribute("z"));
auto rw = GeneralUtils::TryParse<float>(location->Attribute("rw"));
auto rx = GeneralUtils::TryParse<float>(location->Attribute("rx"));
auto ry = GeneralUtils::TryParse<float>(location->Attribute("ry"));
auto rz = GeneralUtils::TryParse<float>(location->Attribute("rz"));
if (zoneID == nullptr || x == nullptr || y == nullptr || z == nullptr || rw == nullptr || rx == nullptr || ry == nullptr
|| rz == nullptr) {
if (!zoneID || !x || !y || !z || !rw || !rx || !ry || !rz) {
LOG("Failed to parse NPC location data");
continue;
}
VanityObjectLocation locationData;
locationData.m_Position = { std::stof(x), std::stof(y), std::stof(z) };
locationData.m_Rotation = { std::stof(rw), std::stof(rx), std::stof(ry), std::stof(rz) };
locationData.m_Chance = 1.0f;
if (zoneID.value() != currentZoneID) {
LOG_DEBUG("Skipping location because it is in %i and not the current zone (%i)", zoneID.value(), currentZoneID);
continue;
}
VanityObjectLocation locationData {
.m_Position = { x.value(), y.value(), z.value() },
.m_Rotation = { rw.value(), rx.value(), ry.value(), rz.value() },
};
if (location->Attribute("chance")) {
locationData.m_Chance = std::stof(location->Attribute("chance"));
locationData.m_Chance = GeneralUtils::TryParse<float>(location->Attribute("chance")).value_or(1.0f);
}
if (location->Attribute("scale")) {
locationData.m_Scale = std::stof(location->Attribute("scale"));
locationData.m_Scale = GeneralUtils::TryParse<float>(location->Attribute("scale")).value_or(1.0f);
}
const auto& it = objectData.m_Locations.find(std::stoi(zoneID));
const auto& it = objectData.m_Locations.find(zoneID.value());
if (it != objectData.m_Locations.end()) {
it->second.push_back(locationData);
} else {
std::vector<VanityObjectLocation> locations;
locations.push_back(locationData);
objectData.m_Locations.insert(std::make_pair(std::stoi(zoneID), locations));
objectData.m_Locations.insert(std::make_pair(zoneID.value(), locations));
}
if (!(std::find(keys.begin(), keys.end(), u"teleport") != keys.end())) {
m_Objects.push_back(objectData);
if (!useLocationsAsRandomSpawnPoint) {
objects.push_back(objectData);
objectData.m_Locations.clear();
}
}
if (std::find(keys.begin(), keys.end(), u"teleport") != keys.end()) {
m_Objects.push_back(objectData);
}
if (useLocationsAsRandomSpawnPoint && !objectData.m_Locations.empty()) {
objects.push_back(objectData);
}
}
}
}
VanityObject* VanityUtilities::GetObject(const std::string& name) {
for (size_t i = 0; i < m_Objects.size(); i++) {
if (m_Objects[i].m_Name == name) {
return &m_Objects[i];
for (size_t i = 0; i < objects.size(); i++) {
if (objects[i].m_Name == name) {
return &objects[i];
}
}
return nullptr;
}
@@ -350,7 +340,7 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) {
// Read the file into a string
std::ifstream t(file);
std::stringstream output;
// If the file does not exist, return an empty string.
// If the file does not exist, return a useful error.
if (!t.good()) {
output << "File ";
output << file.substr(file.rfind("/") + 1);
@@ -408,13 +398,13 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) {
return output.str();
}
void VanityUtilities::SetupNPCTalk(Entity* npc) {
void SetupNPCTalk(Entity* npc) {
npc->AddCallbackTimer(15.0f, [npc]() { NPCTalk(npc); });
npc->SetProximityRadius(20.0f, "talk");
}
void VanityUtilities::NPCTalk(Entity* npc) {
void NPCTalk(Entity* npc) {
auto* proximityMonitorComponent = npc->GetComponent<ProximityMonitorComponent>();
if (!proximityMonitorComponent->GetProximityObjects("talk").empty()) {

View File

@@ -5,58 +5,30 @@
#include <map>
#include <set>
struct VanityObjectLocation
{
struct VanityObjectLocation {
float m_Chance = 1.0f;
NiPoint3 m_Position;
NiQuaternion m_Rotation;
float m_Scale = 1.0f;
};
struct VanityObject
{
struct VanityObject {
LWOOBJID m_ID = LWOOBJID_EMPTY;
std::string m_Name;
LOT m_LOT;
LOT m_LOT = LOT_NULL;
std::vector<LOT> m_Equipment;
std::vector<std::string> m_Phrases;
std::string m_Script;
std::map<uint32_t, std::vector<VanityObjectLocation>> m_Locations;
std::vector<LDFBaseData*> m_Config;
};
class VanityUtilities
{
public:
static void SpawnVanity();
namespace VanityUtilities {
void SpawnVanity();
static Entity* SpawnObject(
const VanityObject& object,
const VanityObjectLocation& location
);
VanityObject* GetObject(const std::string& name);
static LWOOBJID SpawnSpawner(
const VanityObject& object,
const VanityObjectLocation& location
);
static std::string ParseMarkdown(
std::string ParseMarkdown(
const std::string& file
);
static void ParseXML(
const std::string& file
);
static VanityObject* GetObject(const std::string& name);
private:
static void SetupNPCTalk(Entity* npc);
static void NPCTalk(Entity* npc);
static std::vector<VanityObject> m_Objects;
static std::set<std::string> m_LoadedFiles;
};