mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-05-23 23:32:29 +00:00
CombatAI and Vendor
This commit is contained in:
parent
ec9278286b
commit
34cfd45d40
@ -1,8 +1,9 @@
|
|||||||
#include "eMissionTaskType.h"
|
|
||||||
|
|
||||||
#ifndef __ACHIEVEMENTCACHEKEY__H__
|
#ifndef __ACHIEVEMENTCACHEKEY__H__
|
||||||
#define __ACHIEVEMENTCACHEKEY__H__
|
#define __ACHIEVEMENTCACHEKEY__H__
|
||||||
|
|
||||||
|
#include "eMissionTaskType.h"
|
||||||
|
#include "GeneralUtils.h"
|
||||||
|
|
||||||
class AchievementCacheKey {
|
class AchievementCacheKey {
|
||||||
public:
|
public:
|
||||||
AchievementCacheKey() {
|
AchievementCacheKey() {
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#include "BaseCombatAIComponent.h"
|
#include "BaseCombatAIComponent.h"
|
||||||
#include <BitStream.h>
|
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "BitStream.h"
|
||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
#include "EntityManager.h"
|
#include "EntityManager.h"
|
||||||
#include "ControllablePhysicsComponent.h"
|
#include "ControllablePhysicsComponent.h"
|
||||||
@ -15,10 +19,6 @@
|
|||||||
#include "CDClientManager.h"
|
#include "CDClientManager.h"
|
||||||
#include "DestroyableComponent.h"
|
#include "DestroyableComponent.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "SkillComponent.h"
|
#include "SkillComponent.h"
|
||||||
#include "QuickBuildComponent.h"
|
#include "QuickBuildComponent.h"
|
||||||
#include "DestroyableComponent.h"
|
#include "DestroyableComponent.h"
|
||||||
@ -26,21 +26,25 @@
|
|||||||
#include "CDComponentsRegistryTable.h"
|
#include "CDComponentsRegistryTable.h"
|
||||||
#include "CDPhysicsComponentTable.h"
|
#include "CDPhysicsComponentTable.h"
|
||||||
|
|
||||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t componentId) : Component(parent) {
|
||||||
m_Target = LWOOBJID_EMPTY;
|
m_Target = LWOOBJID_EMPTY;
|
||||||
SetAiState(AiState::spawn);
|
m_ComponentId = componentId;
|
||||||
|
SetAiState(AiState::Spawn);
|
||||||
m_Timer = 1.0f;
|
m_Timer = 1.0f;
|
||||||
m_StartPosition = parent->GetPosition();
|
m_StartPosition = parent->GetPosition();
|
||||||
m_MovementAI = nullptr;
|
|
||||||
m_Disabled = false;
|
m_Disabled = false;
|
||||||
m_SkillEntries = {};
|
m_SkillEntries = {};
|
||||||
m_MovementAI = nullptr;
|
m_MovementAI = nullptr;
|
||||||
m_SoftTimer = 5.0f;
|
m_SoftTimer = 5.0f;
|
||||||
|
m_dpEntity = nullptr;
|
||||||
|
m_dpEntityEnemy = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseCombatAIComponent::LoadTemplateData() {
|
||||||
//Grab the aggro information from BaseCombatAI:
|
//Grab the aggro information from BaseCombatAI:
|
||||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||||
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
||||||
componentQuery.bind(1, (int)id);
|
componentQuery.bind(1, m_ComponentId);
|
||||||
|
|
||||||
auto componentResult = componentQuery.execQuery();
|
auto componentResult = componentQuery.execQuery();
|
||||||
|
|
||||||
@ -63,21 +67,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
|||||||
|
|
||||||
componentResult.finalize();
|
componentResult.finalize();
|
||||||
|
|
||||||
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
|
||||||
// radii if it is greater than the one in the database.
|
|
||||||
if (m_ParentEntity) {
|
|
||||||
auto aggroRadius = m_ParentEntity->GetVar<float>(u"aggroRadius");
|
|
||||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
|
||||||
auto tetherRadius = m_ParentEntity->GetVar<float>(u"tetherRadius");
|
|
||||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find skills
|
* Find skills
|
||||||
*/
|
*/
|
||||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
||||||
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
||||||
skillQuery.bind(1, (int)parent->GetLOT());
|
skillQuery.bind(1, m_ParentEntity->GetLOT());
|
||||||
|
|
||||||
auto result = skillQuery.execQuery();
|
auto result = skillQuery.execQuery();
|
||||||
|
|
||||||
@ -90,35 +85,42 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
|||||||
|
|
||||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||||
|
|
||||||
std::stringstream behaviorQuery;
|
m_SkillEntries.push_back(AiSkillEntry(skillId, 0, abilityCooldown, behavior));
|
||||||
|
|
||||||
AiSkillEntry entry = { skillId, 0, abilityCooldown, behavior };
|
|
||||||
|
|
||||||
m_SkillEntries.push_back(entry);
|
|
||||||
|
|
||||||
result.nextRow();
|
result.nextRow();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseCombatAIComponent::LoadConfigData() {
|
||||||
|
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
||||||
|
// radii if it is greater than the one in the database.
|
||||||
|
if (m_ParentEntity) {
|
||||||
|
auto aggroRadius = m_ParentEntity->GetVar<float>(u"aggroRadius");
|
||||||
|
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
||||||
|
auto tetherRadius = m_ParentEntity->GetVar<float>(u"tetherRadius");
|
||||||
|
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseCombatAIComponent::Startup() {
|
||||||
Stun(1.0f);
|
Stun(1.0f);
|
||||||
|
|
||||||
/*
|
// Add physics
|
||||||
* Add physics
|
|
||||||
*/
|
|
||||||
|
|
||||||
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
|
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
|
||||||
|
|
||||||
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
auto* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||||
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
if (!componentRegistryTable) return;
|
||||||
|
|
||||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
auto componentID = componentRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||||
|
|
||||||
if (physicsComponentTable != nullptr) {
|
auto* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
||||||
auto* info = physicsComponentTable->GetByID(componentID);
|
|
||||||
if (info != nullptr) {
|
|
||||||
collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!physicsComponentTable) return;
|
||||||
|
|
||||||
|
auto* info = physicsComponentTable->GetByID(componentID);
|
||||||
|
if (info) collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||||
|
|
||||||
|
// Why are these new'd here and then deleted by the dpworld??
|
||||||
//Create a phantom physics volume so we can detect when we're aggro'd.
|
//Create a phantom physics volume so we can detect when we're aggro'd.
|
||||||
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius);
|
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius);
|
||||||
m_dpEntityEnemy = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius, false);
|
m_dpEntityEnemy = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius, false);
|
||||||
@ -135,11 +137,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
|||||||
}
|
}
|
||||||
|
|
||||||
BaseCombatAIComponent::~BaseCombatAIComponent() {
|
BaseCombatAIComponent::~BaseCombatAIComponent() {
|
||||||
if (m_dpEntity)
|
if (m_dpEntity) dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
|
||||||
|
|
||||||
if (m_dpEntityEnemy)
|
if (m_dpEntityEnemy) dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
||||||
dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
m_MovementAI = nullptr;
|
||||||
|
m_dpEntity = nullptr;
|
||||||
|
m_dpEntityEnemy = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseCombatAIComponent::Update(const float deltaTime) {
|
void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||||
@ -174,14 +177,12 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
|||||||
|
|
||||||
if (m_SoftTimer <= 0.0f) {
|
if (m_SoftTimer <= 0.0f) {
|
||||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||||
|
|
||||||
m_SoftTimer = 5.0f;
|
m_SoftTimer = 5.0f;
|
||||||
} else {
|
} else {
|
||||||
m_SoftTimer -= deltaTime;
|
m_SoftTimer -= deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Disabled || m_ParentEntity->IsDead())
|
if (m_Disabled || m_ParentEntity->IsDead()) return;
|
||||||
return;
|
|
||||||
bool stunnedThisFrame = m_Stunned;
|
bool stunnedThisFrame = m_Stunned;
|
||||||
CalculateCombat(deltaTime); // Putting this here for now
|
CalculateCombat(deltaTime); // Putting this here for now
|
||||||
|
|
||||||
@ -189,16 +190,13 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
|||||||
m_StartPosition = m_ParentEntity->GetPosition();
|
m_StartPosition = m_ParentEntity->GetPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_MovementAI == nullptr) {
|
if (!m_MovementAI) {
|
||||||
m_MovementAI = m_ParentEntity->GetComponent<MovementAIComponent>();
|
m_MovementAI = m_ParentEntity->GetComponent<MovementAIComponent>();
|
||||||
if (m_MovementAI == nullptr) {
|
if (!m_MovementAI) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stunnedThisFrame) {
|
if (stunnedThisFrame) {
|
||||||
m_MovementAI->Stop();
|
m_MovementAI->Stop();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,24 +206,25 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (m_State) {
|
switch (m_State) {
|
||||||
case AiState::spawn:
|
case AiState::Spawn:
|
||||||
Stun(2.0f);
|
Stun(2.0f);
|
||||||
SetAiState(AiState::idle);
|
SetAiState(AiState::Idle);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AiState::idle:
|
case AiState::Idle:
|
||||||
Wander();
|
Wander();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AiState::aggro:
|
case AiState::Aggro:
|
||||||
OnAggro();
|
OnAggro();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AiState::tether:
|
case AiState::Tether:
|
||||||
OnTether();
|
OnTether();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
Game::logger->Log("BaseCombatAIComponent", "Entity %i is in an invalid state %i", m_ParentEntity->GetLOT(), m_State);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,9 +255,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
|||||||
|
|
||||||
auto* skillComponent = m_ParentEntity->GetComponent<SkillComponent>();
|
auto* skillComponent = m_ParentEntity->GetComponent<SkillComponent>();
|
||||||
|
|
||||||
if (skillComponent == nullptr) {
|
if (!skillComponent) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
skillComponent->CalculateUpdate(deltaTime);
|
skillComponent->CalculateUpdate(deltaTime);
|
||||||
|
|
||||||
@ -330,19 +327,19 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
|||||||
SetTarget(newTarget);
|
SetTarget(newTarget);
|
||||||
|
|
||||||
if (m_Target != LWOOBJID_EMPTY) {
|
if (m_Target != LWOOBJID_EMPTY) {
|
||||||
if (m_State == AiState::idle) {
|
if (m_State == AiState::Idle) {
|
||||||
m_Timer = 0;
|
m_Timer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetAiState(AiState::aggro);
|
SetAiState(AiState::Aggro);
|
||||||
} else {
|
} else {
|
||||||
SetAiState(AiState::idle);
|
SetAiState(AiState::Idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasSkillToCast) return;
|
if (!hasSkillToCast) return;
|
||||||
|
|
||||||
if (m_Target == LWOOBJID_EMPTY) {
|
if (m_Target == LWOOBJID_EMPTY) {
|
||||||
SetAiState(AiState::idle);
|
SetAiState(AiState::Idle);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -367,7 +364,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
|||||||
m_MovementAI->Stop();
|
m_MovementAI->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
SetAiState(AiState::aggro);
|
SetAiState(AiState::Aggro);
|
||||||
|
|
||||||
m_Timer = 0;
|
m_Timer = 0;
|
||||||
|
|
||||||
@ -383,8 +380,6 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LWOOBJID BaseCombatAIComponent::FindTarget() {
|
LWOOBJID BaseCombatAIComponent::FindTarget() {
|
||||||
//const auto reference = m_MovementAI == nullptr ? m_StartPosition : m_MovementAI->ApproximateLocation();
|
|
||||||
|
|
||||||
NiPoint3 reference = m_StartPosition;
|
NiPoint3 reference = m_StartPosition;
|
||||||
|
|
||||||
if (m_MovementAI) reference = m_MovementAI->ApproximateLocation();
|
if (m_MovementAI) reference = m_MovementAI->ApproximateLocation();
|
||||||
@ -510,23 +505,10 @@ std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
|
|||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseCombatAIComponent::IsMech() {
|
|
||||||
switch (m_ParentEntity->GetLOT()) {
|
|
||||||
case 6253:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||||
outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate);
|
outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate);
|
||||||
if (m_DirtyStateOrTarget || bIsInitialUpdate) {
|
if (m_DirtyStateOrTarget || bIsInitialUpdate) {
|
||||||
outBitStream->Write(uint32_t(m_State));
|
outBitStream->Write(m_State);
|
||||||
outBitStream->Write(m_Target);
|
outBitStream->Write(m_Target);
|
||||||
m_DirtyStateOrTarget = false;
|
m_DirtyStateOrTarget = false;
|
||||||
}
|
}
|
||||||
@ -542,7 +524,7 @@ void BaseCombatAIComponent::SetAiState(AiState newState) {
|
|||||||
bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||||
auto* entity = EntityManager::Instance()->GetEntity(target);
|
auto* entity = EntityManager::Instance()->GetEntity(target);
|
||||||
|
|
||||||
if (entity == nullptr) {
|
if (!entity) {
|
||||||
Game::logger->Log("BaseCombatAIComponent", "Invalid entity for checking validity (%llu)!", target);
|
Game::logger->Log("BaseCombatAIComponent", "Invalid entity for checking validity (%llu)!", target);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -550,13 +532,11 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
|||||||
|
|
||||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||||
|
|
||||||
if (destroyable == nullptr) {
|
if (!destroyable) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* referenceDestroyable = m_ParentEntity->GetComponent<DestroyableComponent>();
|
auto* referenceDestroyable = m_ParentEntity->GetComponent<DestroyableComponent>();
|
||||||
|
|
||||||
if (referenceDestroyable == nullptr) {
|
if (!referenceDestroyable) {
|
||||||
Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_ParentEntity->GetObjectID());
|
Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_ParentEntity->GetObjectID());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -564,7 +544,7 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
|||||||
|
|
||||||
auto* quickbuild = entity->GetComponent<QuickBuildComponent>();
|
auto* quickbuild = entity->GetComponent<QuickBuildComponent>();
|
||||||
|
|
||||||
if (quickbuild != nullptr) {
|
if (quickbuild) {
|
||||||
const auto state = quickbuild->GetState();
|
const auto state = quickbuild->GetState();
|
||||||
|
|
||||||
if (state != eRebuildState::COMPLETED) {
|
if (state != eRebuildState::COMPLETED) {
|
||||||
@ -576,7 +556,7 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
|||||||
|
|
||||||
auto candidateList = destroyable->GetFactionIDs();
|
auto candidateList = destroyable->GetFactionIDs();
|
||||||
|
|
||||||
for (auto value : candidateList) {
|
for (const auto value : candidateList) {
|
||||||
if (std::find(enemyList.begin(), enemyList.end(), value) != enemyList.end()) {
|
if (std::find(enemyList.begin(), enemyList.end(), value) != enemyList.end()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -598,8 +578,7 @@ Entity* BaseCombatAIComponent::GetTargetEntity() const {
|
|||||||
|
|
||||||
void BaseCombatAIComponent::Taunt(LWOOBJID offender, float threat) {
|
void BaseCombatAIComponent::Taunt(LWOOBJID offender, float threat) {
|
||||||
// Can't taunt self
|
// Can't taunt self
|
||||||
if (offender == m_ParentEntity->GetObjectID())
|
if (offender == m_ParentEntity->GetObjectID()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_ThreatEntries[offender] += threat;
|
m_ThreatEntries[offender] += threat;
|
||||||
m_DirtyThreat = true;
|
m_DirtyThreat = true;
|
||||||
@ -634,9 +613,7 @@ void BaseCombatAIComponent::ClearThreat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BaseCombatAIComponent::Wander() {
|
void BaseCombatAIComponent::Wander() {
|
||||||
if (!m_MovementAI->AtFinalWaypoint()) {
|
if (!m_MovementAI->AtFinalWaypoint()) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_MovementAI->SetHaltDistance(0);
|
m_MovementAI->SetHaltDistance(0);
|
||||||
|
|
||||||
@ -679,9 +656,7 @@ void BaseCombatAIComponent::OnAggro() {
|
|||||||
|
|
||||||
auto* target = GetTargetEntity();
|
auto* target = GetTargetEntity();
|
||||||
|
|
||||||
if (target == nullptr) {
|
if (!target) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_MovementAI->SetHaltDistance(m_AttackRadius);
|
m_MovementAI->SetHaltDistance(m_AttackRadius);
|
||||||
|
|
||||||
@ -704,7 +679,7 @@ void BaseCombatAIComponent::OnAggro() {
|
|||||||
|
|
||||||
m_MovementAI->SetDestination(targetPos);
|
m_MovementAI->SetDestination(targetPos);
|
||||||
|
|
||||||
SetAiState(AiState::tether);
|
SetAiState(AiState::Tether);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Timer += 0.5f;
|
m_Timer += 0.5f;
|
||||||
@ -730,7 +705,7 @@ void BaseCombatAIComponent::OnTether() {
|
|||||||
|
|
||||||
m_MovementAI->SetDestination(m_StartPosition);
|
m_MovementAI->SetDestination(m_StartPosition);
|
||||||
|
|
||||||
SetAiState(AiState::aggro);
|
SetAiState(AiState::Aggro);
|
||||||
} else {
|
} else {
|
||||||
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
|
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
|
||||||
|
|
||||||
@ -742,64 +717,20 @@ void BaseCombatAIComponent::OnTether() {
|
|||||||
m_Timer += 0.5f;
|
m_Timer += 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseCombatAIComponent::GetStunned() const {
|
|
||||||
return m_Stunned;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseCombatAIComponent::SetStunned(const bool value) {
|
|
||||||
m_Stunned = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseCombatAIComponent::GetStunImmune() const {
|
|
||||||
return m_StunImmune;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseCombatAIComponent::SetStunImmune(bool value) {
|
|
||||||
m_StunImmune = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
float BaseCombatAIComponent::GetTetherSpeed() const {
|
|
||||||
return m_TetherSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseCombatAIComponent::SetTetherSpeed(float value) {
|
|
||||||
m_TetherSpeed = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseCombatAIComponent::Stun(const float time) {
|
void BaseCombatAIComponent::Stun(const float time) {
|
||||||
if (m_StunImmune || m_StunTime > time) {
|
if (m_StunImmune || m_StunTime > time) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_StunTime = time;
|
m_StunTime = time;
|
||||||
|
|
||||||
m_Stunned = true;
|
m_Stunned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float BaseCombatAIComponent::GetAggroRadius() const {
|
|
||||||
return m_AggroRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseCombatAIComponent::SetAggroRadius(const float value) {
|
|
||||||
m_AggroRadius = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseCombatAIComponent::LookAt(const NiPoint3& point) {
|
void BaseCombatAIComponent::LookAt(const NiPoint3& point) {
|
||||||
if (m_Stunned) {
|
if (m_Stunned) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ParentEntity->SetRotation(NiQuaternion::LookAt(m_ParentEntity->GetPosition(), point));
|
m_ParentEntity->SetRotation(NiQuaternion::LookAt(m_ParentEntity->GetPosition(), point));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseCombatAIComponent::SetDisabled(bool value) {
|
|
||||||
m_Disabled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseCombatAIComponent::GetDistabled() const {
|
|
||||||
return m_Disabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseCombatAIComponent::Sleep() {
|
void BaseCombatAIComponent::Sleep() {
|
||||||
m_dpEntity->SetSleeping(true);
|
m_dpEntity->SetSleeping(true);
|
||||||
m_dpEntityEnemy->SetSleeping(true);
|
m_dpEntityEnemy->SetSleeping(true);
|
||||||
|
@ -20,20 +20,25 @@ class Entity;
|
|||||||
/**
|
/**
|
||||||
* The current state of the AI
|
* The current state of the AI
|
||||||
*/
|
*/
|
||||||
enum class AiState : int {
|
enum class AiState : int32_t {
|
||||||
idle = 0, // Doing nothing
|
Idle = 0, // Doing nothing
|
||||||
aggro, // Waiting for an enemy to cross / running back to spawn
|
Aggro, // Waiting for an enemy to cross / running back to spawn
|
||||||
tether, // Chasing an enemy
|
Tether, // Chasing an enemy
|
||||||
spawn, // Spawning into the world
|
Spawn, // Spawning into the world
|
||||||
dead // Killed
|
Dead // Killed
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a skill that can be cast by this enemy, including its cooldowns, which determines how often the skill
|
* Represents a skill that can be cast by this enemy, including its cooldowns, which determines how often the skill
|
||||||
* may be cast.
|
* may be cast.
|
||||||
*/
|
*/
|
||||||
struct AiSkillEntry
|
struct AiSkillEntry {
|
||||||
{
|
AiSkillEntry(uint32_t skillId, float cooldown, float abilityCooldown, Behavior* behavior) {
|
||||||
|
this->skillId = skillId;
|
||||||
|
this->cooldown = cooldown;
|
||||||
|
this->abilityCooldown = abilityCooldown;
|
||||||
|
this->behavior = behavior;
|
||||||
|
}
|
||||||
uint32_t skillId;
|
uint32_t skillId;
|
||||||
|
|
||||||
float cooldown;
|
float cooldown;
|
||||||
@ -53,6 +58,9 @@ public:
|
|||||||
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
|
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
|
||||||
~BaseCombatAIComponent() override;
|
~BaseCombatAIComponent() override;
|
||||||
|
|
||||||
|
void LoadTemplateData() override;
|
||||||
|
void LoadConfigData() override;
|
||||||
|
void Startup() override;
|
||||||
void Update(float deltaTime) override;
|
void Update(float deltaTime) override;
|
||||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||||
|
|
||||||
@ -147,37 +155,37 @@ public:
|
|||||||
* Gets whether or not the entity is currently stunned
|
* Gets whether or not the entity is currently stunned
|
||||||
* @return whether the entity is currently stunned
|
* @return whether the entity is currently stunned
|
||||||
*/
|
*/
|
||||||
bool GetStunned() const;
|
bool GetStunned() const { return m_Stunned; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (un)stuns the entity, determining whether it'll be able to attack other entities
|
* (un)stuns the entity, determining whether it'll be able to attack other entities
|
||||||
* @param value whether the enemy is stunned
|
* @param value whether the enemy is stunned
|
||||||
*/
|
*/
|
||||||
void SetStunned(bool value);
|
void SetStunned(bool value) { m_Stunned = value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets if this entity may be stunned
|
* Gets if this entity may be stunned
|
||||||
* @return if this entity may be stunned
|
* @return if this entity may be stunned
|
||||||
*/
|
*/
|
||||||
bool GetStunImmune() const;
|
bool GetStunImmune() const { return m_StunImmune; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the stun immune value, determining if the entity may be stunned
|
* Set the stun immune value, determining if the entity may be stunned
|
||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
void SetStunImmune(bool value);
|
void SetStunImmune(bool value) { m_StunImmune = value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current speed at which an entity runs when tethering
|
* Gets the current speed at which an entity runs when tethering
|
||||||
* @return the current speed at which an entity runs when tethering
|
* @return the current speed at which an entity runs when tethering
|
||||||
*/
|
*/
|
||||||
float GetTetherSpeed() const;
|
float GetTetherSpeed() const { return m_TetherSpeed; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the speed at which an entity will tether
|
* Sets the speed at which an entity will tether
|
||||||
* @param value the new tether speed
|
* @param value the new tether speed
|
||||||
*/
|
*/
|
||||||
void SetTetherSpeed(float value);
|
void SetTetherSpeed(float value) { m_TetherSpeed = value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stuns the entity for a certain amount of time, will not work if the entity is stun immune
|
* Stuns the entity for a certain amount of time, will not work if the entity is stun immune
|
||||||
@ -189,13 +197,13 @@ public:
|
|||||||
* Gets the radius that will cause this entity to get aggro'd, causing a target chase
|
* Gets the radius that will cause this entity to get aggro'd, causing a target chase
|
||||||
* @return the aggro radius of the entity
|
* @return the aggro radius of the entity
|
||||||
*/
|
*/
|
||||||
float GetAggroRadius() const;
|
float GetAggroRadius() const { return m_AggroRadius; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the aggro radius, causing the entity to start chasing enemies in this range
|
* Sets the aggro radius, causing the entity to start chasing enemies in this range
|
||||||
* @param value the aggro radius to set
|
* @param value the aggro radius to set
|
||||||
*/
|
*/
|
||||||
void SetAggroRadius(float value);
|
void SetAggroRadius(float value) { m_AggroRadius = value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the entity look at a certain point in space
|
* Makes the entity look at a certain point in space
|
||||||
@ -207,13 +215,13 @@ public:
|
|||||||
* (dis)ables the AI, causing it to stop/start attacking enemies
|
* (dis)ables the AI, causing it to stop/start attacking enemies
|
||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
void SetDisabled(bool value);
|
void SetDisabled(bool value) { m_Disabled = value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current state of the AI, whether or not it's looking for enemies to attack
|
* Gets the current state of the AI, whether or not it's looking for enemies to attack
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
bool GetDistabled() const;
|
bool GetDistabled() const { return m_Disabled; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns the entity asleep, stopping updates to its physics volumes
|
* Turns the entity asleep, stopping updates to its physics volumes
|
||||||
@ -387,7 +395,9 @@ private:
|
|||||||
* Whether the current entity is a mech enemy, needed as mechs tether radius works differently
|
* Whether the current entity is a mech enemy, needed as mechs tether radius works differently
|
||||||
* @return whether this entity is a mech
|
* @return whether this entity is a mech
|
||||||
*/
|
*/
|
||||||
bool IsMech();
|
bool IsMech() const { return m_ParentEntity->GetLOT() == 6253; };
|
||||||
|
|
||||||
|
int32_t m_ComponentId;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BASECOMBATAICOMPONENT_H
|
#endif // BASECOMBATAICOMPONENT_H
|
||||||
|
@ -128,7 +128,7 @@ LWOSpawnComponent
|
|||||||
LWOSpringpadComponent
|
LWOSpringpadComponent
|
||||||
LWOSwitchComponent
|
LWOSwitchComponent
|
||||||
LWOTriggerComponent
|
LWOTriggerComponent
|
||||||
LocalPlayer (not a component)
|
LocalPlayer - This is a function call in the client which, if the generated Entity is a player, the below components are added. This **must** be done for all players.
|
||||||
├~~ LWOInteractionManagerComponent
|
├~~ LWOInteractionManagerComponent
|
||||||
├~~ LWOUserControlComponent
|
├~~ LWOUserControlComponent
|
||||||
├~~ LWOFriendsListComponent
|
├~~ LWOFriendsListComponent
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
VendorComponent::VendorComponent(Entity* parent) : Component(parent) {
|
VendorComponent::VendorComponent(Entity* parent) : Component(parent) {
|
||||||
m_HasStandardCostItems = false;
|
m_HasStandardCostItems = false;
|
||||||
m_HasMultiCostItems = false;
|
m_HasMultiCostItems = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VendorComponent::Startup() {
|
||||||
SetupConstants();
|
SetupConstants();
|
||||||
RefreshInventory(true);
|
RefreshInventory(true);
|
||||||
}
|
}
|
||||||
@ -31,24 +34,23 @@ void VendorComponent::OnUse(Entity* originator) {
|
|||||||
GameMessages::SendVendorStatusUpdate(m_ParentEntity, originator->GetSystemAddress());
|
GameMessages::SendVendorStatusUpdate(m_ParentEntity, originator->GetSystemAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VendorComponent::RefreshInventory(bool isCreation) {
|
void VendorComponent::RefreshInventory(bool isCreation) {
|
||||||
SetHasStandardCostItems(false);
|
SetHasStandardCostItems(false);
|
||||||
SetHasMultiCostItems(false);
|
SetHasMultiCostItems(false);
|
||||||
|
|
||||||
//Custom code for Max vanity NPC
|
// Custom code for Max vanity NPC
|
||||||
if (m_ParentEntity->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
|
if (m_ParentEntity->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
|
||||||
if (!isCreation) return;
|
if (!isCreation) return;
|
||||||
SetHasStandardCostItems(true);
|
SetHasStandardCostItems(true);
|
||||||
m_Inventory.insert({ 11909, 0 }); // Top hat w frog
|
m_Inventory.push_back(SoldItem(11909, 0)); // Top hat w frog
|
||||||
m_Inventory.insert({ 7785, 0 }); // Flash bulb
|
m_Inventory.push_back(SoldItem(7785, 0)); // Flash bulb
|
||||||
m_Inventory.insert({ 12764, 0 }); // Big fountain soda
|
m_Inventory.push_back(SoldItem(12764, 0)); // Big fountain soda
|
||||||
m_Inventory.insert({ 12241, 0 }); // Hot cocoa (from fb)
|
m_Inventory.push_back(SoldItem(12241, 0)); // Hot cocoa (from fb)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_Inventory.clear();
|
m_Inventory.clear();
|
||||||
auto* lootMatrixTable = CDClientManager::Instance().GetTable<CDLootMatrixTable>();
|
auto* lootMatrixTable = CDClientManager::Instance().GetTable<CDLootMatrixTable>();
|
||||||
std::vector<CDLootMatrix> lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == m_LootMatrixID); });
|
const auto lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == m_LootMatrixID); });
|
||||||
|
|
||||||
if (lootMatrices.empty()) return;
|
if (lootMatrices.empty()) return;
|
||||||
// Done with lootMatrix table
|
// Done with lootMatrix table
|
||||||
@ -59,16 +61,16 @@ void VendorComponent::RefreshInventory(bool isCreation) {
|
|||||||
|
|
||||||
for (const auto& lootMatrix : lootMatrices) {
|
for (const auto& lootMatrix : lootMatrices) {
|
||||||
int lootTableID = lootMatrix.LootTableIndex;
|
int lootTableID = lootMatrix.LootTableIndex;
|
||||||
std::vector<CDLootTable> vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); });
|
auto vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); });
|
||||||
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
|
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
|
||||||
for (CDLootTable item : vendorItems) {
|
for (const auto& item : vendorItems) {
|
||||||
if (!m_HasStandardCostItems || !m_HasMultiCostItems){
|
if (!m_HasStandardCostItems || !m_HasMultiCostItems) {
|
||||||
auto itemComponentID = compRegistryTable->GetByIDAndType(item.itemid, eReplicaComponentType::ITEM);
|
auto itemComponentID = compRegistryTable->GetByIDAndType(item.itemid, eReplicaComponentType::ITEM);
|
||||||
auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID);
|
auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID);
|
||||||
if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true);
|
if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true);
|
||||||
if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true);
|
if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true);
|
||||||
}
|
}
|
||||||
m_Inventory.insert({ item.itemid, item.sortPriority });
|
m_Inventory.push_back(SoldItem(item.itemid, item.sortPriority));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
|
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
|
||||||
@ -76,43 +78,47 @@ void VendorComponent::RefreshInventory(bool isCreation) {
|
|||||||
for (size_t i = 0; i < randomCount; i++) {
|
for (size_t i = 0; i < randomCount; i++) {
|
||||||
if (vendorItems.empty()) break;
|
if (vendorItems.empty()) break;
|
||||||
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
||||||
const auto& randomItem = vendorItems[randomItemIndex];
|
const auto& randomItem = vendorItems.at(randomItemIndex);
|
||||||
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
||||||
if (!m_HasStandardCostItems || !m_HasMultiCostItems){
|
if (!m_HasStandardCostItems || !m_HasMultiCostItems) {
|
||||||
auto itemComponentID = compRegistryTable->GetByIDAndType(randomItem.itemid, eReplicaComponentType::ITEM);
|
auto itemComponentID = compRegistryTable->GetByIDAndType(randomItem.itemid, eReplicaComponentType::ITEM, -1);
|
||||||
|
if (itemComponentID == -1) {
|
||||||
|
Game::logger->Log("VendorComponent", "Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_ParentEntity->GetLOT());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID);
|
auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID);
|
||||||
if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true);
|
if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true);
|
||||||
if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true);
|
if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true);
|
||||||
}
|
}
|
||||||
m_Inventory.insert({ randomItem.itemid, randomItem.sortPriority });
|
m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Because I (Max) want a vendor to sell these cameras
|
// Because I (Max) want a vendor to sell these cameras
|
||||||
if (m_ParentEntity->GetLOT() == 13569) {
|
if (m_ParentEntity->GetLOT() == 13569) {
|
||||||
auto randomCamera = GeneralUtils::GenerateRandomNumber<int32_t>(0, 2);
|
auto randomCamera = GeneralUtils::GenerateRandomNumber<int32_t>(0, 2);
|
||||||
|
|
||||||
|
LOT camera = 0;
|
||||||
|
DluAssert(randomCamera >= 0 && randomCamera <= 2);
|
||||||
switch (randomCamera) {
|
switch (randomCamera) {
|
||||||
case 0:
|
case 0:
|
||||||
m_Inventory.insert({ 16253, 0 }); //Grungagroid
|
camera = 16253; // Grungagroid
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_Inventory.insert({ 16254, 0 }); //Hipstabrick
|
camera = 16254; // Hipstabrick
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
m_Inventory.insert({ 16204, 0 }); //Megabrixel snapshot
|
camera = 16204; // Megabrixel snapshot
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
m_Inventory.push_back(SoldItem(camera, 0)); //Megabrixel snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback timer to refresh this inventory.
|
// Callback timer to refresh this inventory.
|
||||||
m_ParentEntity->AddCallbackTimer(m_RefreshTimeSeconds, [this]() {
|
m_ParentEntity->AddCallbackTimer(m_RefreshTimeSeconds, [this]() {
|
||||||
RefreshInventory();
|
RefreshInventory();
|
||||||
}
|
});
|
||||||
);
|
|
||||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||||
GameMessages::SendVendorStatusUpdate(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS);
|
GameMessages::SendVendorStatusUpdate(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
}
|
}
|
||||||
@ -124,10 +130,11 @@ void VendorComponent::SetupConstants() {
|
|||||||
auto* vendorComponentTable = CDClientManager::Instance().GetTable<CDVendorComponentTable>();
|
auto* vendorComponentTable = CDClientManager::Instance().GetTable<CDVendorComponentTable>();
|
||||||
std::vector<CDVendorComponent> vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); });
|
std::vector<CDVendorComponent> vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); });
|
||||||
if (vendorComps.empty()) return;
|
if (vendorComps.empty()) return;
|
||||||
m_BuyScalar = vendorComps[0].buyScalar;
|
auto vendorData = vendorComps.at(0);
|
||||||
m_SellScalar = vendorComps[0].sellScalar;
|
m_BuyScalar = vendorData.buyScalar;
|
||||||
m_RefreshTimeSeconds = vendorComps[0].refreshTimeSeconds;
|
m_SellScalar = vendorData.sellScalar;
|
||||||
m_LootMatrixID = vendorComps[0].LootMatrixIndex;
|
m_RefreshTimeSeconds = vendorData.refreshTimeSeconds;
|
||||||
|
m_LootMatrixID = vendorData.LootMatrixIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#ifndef VENDORCOMPONENT_H
|
#ifndef VENDORCOMPONENT_H
|
||||||
#define VENDORCOMPONENT_H
|
#define VENDORCOMPONENT_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include "CDClientManager.h"
|
#include "CDClientManager.h"
|
||||||
#include "Component.h"
|
#include "Component.h"
|
||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
@ -9,6 +10,15 @@
|
|||||||
#include "RakNetTypes.h"
|
#include "RakNetTypes.h"
|
||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
|
|
||||||
|
struct SoldItem {
|
||||||
|
SoldItem(const LOT lot, const int32_t sortPriority) {
|
||||||
|
this->lot = lot;
|
||||||
|
this->sortPriority = sortPriority;
|
||||||
|
};
|
||||||
|
LOT lot = 0;
|
||||||
|
int32_t sortPriority = 0;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component for vendor NPCs. A vendor sells items to the player.
|
* A component for vendor NPCs. A vendor sells items to the player.
|
||||||
*/
|
*/
|
||||||
@ -17,37 +27,31 @@ public:
|
|||||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR;
|
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR;
|
||||||
|
|
||||||
VendorComponent(Entity* parent);
|
VendorComponent(Entity* parent);
|
||||||
|
void Startup() override;
|
||||||
|
|
||||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||||
|
|
||||||
void OnUse(Entity* originator) override;
|
void OnUse(Entity* originator) override;
|
||||||
|
|
||||||
float GetBuyScalar() const {
|
float GetBuyScalar() const { return m_BuyScalar; }
|
||||||
return m_BuyScalar;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GetSellScalar() const {
|
float GetSellScalar() const { return m_SellScalar; }
|
||||||
return m_SellScalar;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetBuyScalar(float value) {
|
void SetBuyScalar(const float value) { m_BuyScalar = value; }
|
||||||
m_BuyScalar = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSellScalar(float value) {
|
void SetSellScalar(const float value) { m_SellScalar = value; }
|
||||||
m_SellScalar = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<LOT, int>& GetInventory() {
|
std::vector<SoldItem>& GetInventory() {
|
||||||
return m_Inventory;
|
return m_Inventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetHasMultiCostItems(bool hasMultiCostItems) {
|
void SetHasMultiCostItems(const bool hasMultiCostItems) {
|
||||||
if (m_HasMultiCostItems == hasMultiCostItems) return;
|
if (m_HasMultiCostItems == hasMultiCostItems) return;
|
||||||
m_HasMultiCostItems = hasMultiCostItems;
|
m_HasMultiCostItems = hasMultiCostItems;
|
||||||
m_DirtyVendor = true;
|
m_DirtyVendor = true;
|
||||||
}
|
}
|
||||||
void SetHasStandardCostItems(bool hasStandardCostItems) {
|
|
||||||
|
void SetHasStandardCostItems(const bool hasStandardCostItems) {
|
||||||
if (m_HasStandardCostItems == hasStandardCostItems) return;
|
if (m_HasStandardCostItems == hasStandardCostItems) return;
|
||||||
m_HasStandardCostItems = hasStandardCostItems;
|
m_HasStandardCostItems = hasStandardCostItems;
|
||||||
m_DirtyVendor = true;
|
m_DirtyVendor = true;
|
||||||
@ -64,37 +68,39 @@ public:
|
|||||||
void SetupConstants();
|
void SetupConstants();
|
||||||
|
|
||||||
bool SellsItem(const LOT item) const {
|
bool SellsItem(const LOT item) const {
|
||||||
return m_Inventory.find(item) != m_Inventory.end();
|
return std::count_if(m_Inventory.begin(), m_Inventory.end(), [item](const SoldItem& lhs) {
|
||||||
|
return lhs.lot == item;
|
||||||
|
}) > 0;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* The buy scalar.
|
* The buy scalar.
|
||||||
*/
|
*/
|
||||||
float m_BuyScalar;
|
float m_BuyScalar = 0.0f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sell scalar.
|
* The sell scalar.
|
||||||
*/
|
*/
|
||||||
float m_SellScalar;
|
float m_SellScalar = 0.0f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The refresh time of this vendors' inventory.
|
* The refresh time of this vendors' inventory.
|
||||||
*/
|
*/
|
||||||
float m_RefreshTimeSeconds;
|
float m_RefreshTimeSeconds = 0.0f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loot matrix id of this vendor.
|
* Loot matrix id of this vendor.
|
||||||
*/
|
*/
|
||||||
uint32_t m_LootMatrixID;
|
uint32_t m_LootMatrixID = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of items the vendor sells.
|
* The list of items the vendor sells.
|
||||||
*/
|
*/
|
||||||
std::map<LOT, int> m_Inventory;
|
std::vector<SoldItem> m_Inventory;
|
||||||
|
|
||||||
bool m_DirtyVendor;
|
bool m_DirtyVendor = false;
|
||||||
bool m_HasStandardCostItems;
|
bool m_HasStandardCostItems = false;
|
||||||
bool m_HasMultiCostItems;
|
bool m_HasMultiCostItems = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VENDORCOMPONENT_H
|
#endif // VENDORCOMPONENT_H
|
||||||
|
@ -1286,7 +1286,7 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s
|
|||||||
auto* vendor = entity->GetComponent<VendorComponent>();
|
auto* vendor = entity->GetComponent<VendorComponent>();
|
||||||
if (!vendor) return;
|
if (!vendor) return;
|
||||||
|
|
||||||
std::map<LOT, int> vendorItems = vendor->GetInventory();
|
auto vendorItems = vendor->GetInventory();
|
||||||
|
|
||||||
bitStream.Write(entity->GetObjectID());
|
bitStream.Write(entity->GetObjectID());
|
||||||
bitStream.Write(eGameMessageType::VENDOR_STATUS_UPDATE);
|
bitStream.Write(eGameMessageType::VENDOR_STATUS_UPDATE);
|
||||||
@ -1294,9 +1294,9 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s
|
|||||||
bitStream.Write(bUpdateOnly);
|
bitStream.Write(bUpdateOnly);
|
||||||
bitStream.Write(static_cast<uint32_t>(vendorItems.size()));
|
bitStream.Write(static_cast<uint32_t>(vendorItems.size()));
|
||||||
|
|
||||||
for (std::pair<LOT, int> item : vendorItems) {
|
for (const auto&[lot, sortPriority] : vendorItems) {
|
||||||
bitStream.Write(static_cast<int>(item.first));
|
bitStream.Write<int32_t>(lot);
|
||||||
bitStream.Write(static_cast<int>(item.second));
|
bitStream.Write<int32_t>(sortPriority);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST
|
||||||
|
Loading…
x
Reference in New Issue
Block a user